diff --git a/module.json b/module.json index e3ad50c..d1f5b33 100755 --- a/module.json +++ b/module.json @@ -22,5 +22,12 @@ "path": "lang/en.json" } ], - "scripts": ["scripts/update-compendium.js"] + "scripts": [ + "scripts/sf2e-silver-to-credits.js", + "scripts/update-compendium.js", + "scripts/convert-prices-to-credits.js", + "scripts/add-coins-dialog.js", + "scripts/party-sheet-prices.js" + ], + "styles": ["styles/sf2e-silver-to-credits.css"] } diff --git a/scripts/add-coins-dialog.js b/scripts/add-coins-dialog.js new file mode 100644 index 0000000..83bcc8f --- /dev/null +++ b/scripts/add-coins-dialog.js @@ -0,0 +1,78 @@ +Hooks.on("renderDialog", (dialog, html, data) => { + // Check if this dialog contains currency inputs + if ( + html.find('[name="pp"], [name="gp"], [name="sp"], [name="cp"]').length > 0 + ) { + console.log("Add Coins dialog detected, hiding currencies"); + hideCurrenciesInAddCoinsDialog(html); + } +}); + +// Also hook into the specific PF2e currency dialog +Hooks.on("renderCurrencyExchange", (dialog, html, data) => { + console.log("CurrencyExchange dialog detected"); + hideCurrenciesInAddCoinsDialog(html); +}); + +// Fallback: use a MutationObserver to catch the dialog when it appears +const dialogObserver = new MutationObserver((mutations) => { + mutations.forEach((mutation) => { + if (mutation.addedNodes.length) { + mutation.addedNodes.forEach((node) => { + if (node.nodeType === 1) { + // Element node + const $node = $(node); + + // Check if this node or its children contain currency inputs + if ( + $node.find('[name="pp"], [name="gp"], [name="sp"], [name="cp"]') + .length > 0 || + $node.is('[name="pp"], [name="gp"], [name="sp"], [name="cp"]') + ) { + console.log("Currency dialog detected via MutationObserver"); + hideCurrenciesInAddCoinsDialog($node); + } + } + }); + } + }); +}); + +dialogObserver.observe(document.body, { + childList: true, + subtree: true, +}); + +function hideCurrenciesInAddCoinsDialog(html) { + console.log("Attempting to hide currencies"); + + // Find all form groups + const formGroups = html.find(".form-group"); + console.log("Found form groups:", formGroups.length); + + formGroups.each(function () { + const $group = $(this); + const input = $group.find("input"); + const inputName = input.attr("name"); + const label = $group.find("label").text(); + + console.log( + "Processing form group with input name:", + inputName, + "label:", + label, + ); + + // Hide Platinum, Gold, and Copper form groups + if (inputName === "pp" || inputName === "gp" || inputName === "cp") { + console.log("Hiding:", inputName); + $group.css("display", "none"); + } + + // Rename Silver to Credits + if (inputName === "sp") { + console.log("Renaming Silver to Credits"); + $group.find("label").text("Credits"); + } + }); +} diff --git a/scripts/convert-prices-to-credits.js b/scripts/convert-prices-to-credits.js new file mode 100644 index 0000000..339a00a --- /dev/null +++ b/scripts/convert-prices-to-credits.js @@ -0,0 +1,83 @@ +Hooks.on("renderActorSheet", (app, html, data) => { + convertPricesToCredits(html); +}); + +Hooks.on("updateActor", (actor, data) => { + const sheets = actor.apps; + sheets.forEach((sheet) => { + if (sheet.element) { + convertPricesToCredits(sheet.element); + } + }); +}); + +function convertPricesToCredits(html) { + // Find all price displays in the sheet + const priceElements = html.find("[data-price]"); + + priceElements.each(function () { + const $element = $(this); + let priceText = $element.text(); + + // Convert pp to cr (1 pp = 100 cr) + priceText = priceText.replace(/(\d+(?:\.\d+)?)\s*pp/gi, (match, amount) => { + const creditAmount = parseFloat(amount) * 100; + return `${creditAmount} cr`; + }); + + // Convert gp to cr (1 gp = 10 cr) + priceText = priceText.replace(/(\d+(?:\.\d+)?)\s*gp/gi, (match, amount) => { + const creditAmount = parseFloat(amount) * 10; + return `${creditAmount} cr`; + }); + + // Convert cp to cr (1 cp = 0.1 cr) + priceText = priceText.replace(/(\d+(?:\.\d+)?)\s*cp/gi, (match, amount) => { + const creditAmount = parseFloat(amount) / 10; + return `${creditAmount} cr`; + }); + + // Replace sp with cr (no conversion needed, 1 sp = 1 cr) + priceText = priceText.replace(/(\d+(?:\.\d+)?)\s*sp/gi, "$1 cr"); + + $element.text(priceText); + }); + + // Also handle price text in tooltips and other text content + const allElements = html.find("*"); + allElements.each(function () { + const $element = $(this); + + // Skip elements that have child elements (to avoid processing parent containers) + if ($element.children().length === 0) { + let text = $element.text(); + const originalText = text; + + // Convert pp to cr (1 pp = 100 cr) + text = text.replace(/(\d+(?:\.\d+)?)\s*pp/gi, (match, amount) => { + const creditAmount = parseFloat(amount) * 100; + return `${creditAmount} cr`; + }); + + // Convert gp to cr (1 gp = 10 cr) + text = text.replace(/(\d+(?:\.\d+)?)\s*gp/gi, (match, amount) => { + const creditAmount = parseFloat(amount) * 10; + return `${creditAmount} cr`; + }); + + // Convert cp to cr (1 cp = 0.1 cr) + text = text.replace(/(\d+(?:\.\d+)?)\s*cp/gi, (match, amount) => { + const creditAmount = parseFloat(amount) / 10; + return `${creditAmount} cr`; + }); + + // Replace sp with cr (no conversion needed, 1 sp = 1 cr) + text = text.replace(/(\d+(?:\.\d+)?)\s*sp/gi, "$1 cr"); + + // Only update if text changed + if (text !== originalText) { + $element.text(text); + } + } + }); +} diff --git a/scripts/party-sheet-prices.js b/scripts/party-sheet-prices.js new file mode 100644 index 0000000..f2b4c51 --- /dev/null +++ b/scripts/party-sheet-prices.js @@ -0,0 +1,81 @@ +Hooks.on("renderPartySheet", (app, html, data) => { + convertPartySheetPricesToCredits(html); +}); + +Hooks.on("updateActor", (actor, data) => { + // Also update when actors are updated + const sheets = actor.apps; + sheets.forEach((sheet) => { + if ( + sheet.constructor.name === "PartySheet" || + sheet.element?.closest(".party-sheet") + ) { + convertPartySheetPricesToCredits(sheet.element); + } + }); +}); + +// Fallback: use MutationObserver to catch party sheet updates +const partySheetObserver = new MutationObserver((mutations) => { + mutations.forEach((mutation) => { + if (mutation.addedNodes.length || mutation.type === "characterData") { + mutation.addedNodes.forEach((node) => { + if (node.nodeType === 1) { + // Element node + const $node = $(node); + + // Check if this contains party sheet value spans + if ($node.find(".value").length > 0 || $node.hasClass("value")) { + convertPartySheetPricesToCredits($node); + } + } + }); + } + }); +}); + +partySheetObserver.observe(document.body, { + childList: true, + subtree: true, + characterData: true, +}); + +function convertPartySheetPricesToCredits(html) { + console.log("Converting party sheet prices to credits"); + + // Target the .value spans specifically + const valueSpans = html.find(".value"); + + valueSpans.each(function () { + const $span = $(this); + let text = $span.text(); + const originalText = text; + + // Convert pp to cr (1 pp = 100 cr) + text = text.replace(/(\d+(?:\.\d+)?)\s*pp/gi, (match, amount) => { + const creditAmount = parseFloat(amount) * 100; + return `${creditAmount} cr`; + }); + + // Convert gp to cr (1 gp = 10 cr) + text = text.replace(/(\d+(?:\.\d+)?)\s*gp/gi, (match, amount) => { + const creditAmount = parseFloat(amount) * 10; + return `${creditAmount} cr`; + }); + + // Convert cp to cr (1 cp = 0.1 cr) + text = text.replace(/(\d+(?:\.\d+)?)\s*cp/gi, (match, amount) => { + const creditAmount = parseFloat(amount) / 10; + return `${creditAmount} cr`; + }); + + // Replace sp with cr (no conversion needed, 1 sp = 1 cr) + text = text.replace(/(\d+(?:\.\d+)?)\s*sp/gi, "$1 cr"); + + // Only update if text changed + if (text !== originalText) { + console.log("Converting:", originalText, "to:", text); + $span.text(text); + } + }); +} diff --git a/scripts/sf2e-silver-to-credits.js b/scripts/sf2e-silver-to-credits.js new file mode 100644 index 0000000..a23b000 --- /dev/null +++ b/scripts/sf2e-silver-to-credits.js @@ -0,0 +1,56 @@ +Hooks.on("renderActorSheet", (app, html, data) => { + replaceSilverWithCredits(html); +}); + +Hooks.on("updateActor", (actor, data) => { + const sheets = actor.apps; + sheets.forEach((sheet) => { + if (sheet.element) { + replaceSilverWithCredits(sheet.element); + } + }); +}); + +function replaceSilverWithCredits(html) { + // Update tooltip for silver currency + const silverContainers = html.find('[data-unit="sp"]'); + silverContainers.each(function () { + $(this).attr("data-tooltip", "Credits"); + $(this).find(".currency-image").attr("data-tooltip", "Credits"); + }); + + updateTooltips(); +} + +function updateTooltips() { + const tooltipObserver = new MutationObserver(() => { + const allTooltips = document.querySelectorAll(".tooltip, [role='tooltip']"); + allTooltips.forEach((tooltip) => { + if (tooltip.textContent.trim() === "Silver") { + tooltip.textContent = "Credits"; + } + }); + }); + + tooltipObserver.observe(document.body, { + childList: true, + subtree: true, + }); +} + +// Inject CSS rules with high specificity to override the PF2e system CSS +const styleSheet = document.createElement("style"); +styleSheet.id = "sf2e-silver-to-credits-override"; +styleSheet.textContent = ` + /* Override silver currency background image globally */ + [data-unit="sp"] .currency-image { + background-image: url('modules/sf2e-silver-to-credits/icons/equipment/treasure/currency/credits.webp') !important; + } + + /* Catch it in any context */ + .currency-image[data-tooltip*="Silver"], + [style*="silver-pieces.webp"] { + background-image: url('modules/sf2e-silver-to-credits/icons/equipment/treasure/currency/credits.webp') !important; + } +`; +document.head.appendChild(styleSheet); diff --git a/styles/sf2e-silver-to-credits.css b/styles/sf2e-silver-to-credits.css new file mode 100644 index 0000000..0f29380 --- /dev/null +++ b/styles/sf2e-silver-to-credits.css @@ -0,0 +1,34 @@ +/* Hide Platinum, Gold, and Copper currencies on PF2e character sheet */ +.pf2e.sheet.actor.character [data-unit="pp"], +.pf2e.sheet.actor.character [data-unit="gp"], +.pf2e.sheet.actor.character [data-unit="cp"], +.sheet.actor.character [data-unit="pp"], +.sheet.actor.character [data-unit="gp"], +.sheet.actor.character [data-unit="cp"], +.currency-pp, +.currency-gp, +.currency-cp, +.currency li.pp, +.currency li.gp, +.currency li.cp, +.coinage [data-unit="pp"], +.coinage [data-unit="gp"], +.coinage [data-unit="cp"], +.fa-coins.pp, +.fa-coins.gp, +.fa-coins.cp { + display: none !important; +} + +/* Override silver currency icon with EXTREME specificity */ +[data-unit="sp"] .currency-image { + background-image: url("../icons/equipment/treasure/currency/credits.webp") !important; + background-size: cover !important; + background-repeat: no-repeat !important; + background-position: center !important; +} + +/* Extra fallback for any silver piece reference */ +[style*="silver-pieces.webp"] { + background-image: url("../icons/equipment/treasure/currency/credits.webp") !important; +}