From 67d4b0387ad8987ec43c3e0ce676329095e71e42 Mon Sep 17 00:00:00 2001 From: trotFunky Date: Sun, 6 Jul 2025 21:32:25 +0100 Subject: [PATCH] Fortune: Introduce fortune display Add a new sub-module that displays Fortune in character and party sheets. In the character sheet, this is an input field and is automatically tracked thanks to flags. Add an icon for the party sheet and the according CSS class. --- README.md | 10 ++- assets/fortune.png | Bin 0 -> 565 bytes lang/en.json | 9 +++ lang/fr.json | 9 +++ module.json | 10 ++- scripts/fortune_integration.mjs | 118 ++++++++++++++++++++++++++++++++ styles/mills_fabula.css | 3 + 7 files changed, 155 insertions(+), 4 deletions(-) create mode 100644 assets/fortune.png create mode 100644 scripts/fortune_integration.mjs create mode 100644 styles/mills_fabula.css diff --git a/README.md b/README.md index b9dbd08..f269d36 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# The Mill's Fabula - v0.3.3 +# The Mill's Fabula - v0.4.0 This little [FoundryVTT](https://foundryvtt.com/) module is a collection of compendiums and functionalities to power our Fabula Ultima campaigns. @@ -51,6 +51,14 @@ for NPCs this is only done for PC tokens. - The current implementation relies on monkey patching, which make it vulnerable to compatibility issues with other modules manipulating the same methods. +## Fortune display + +In our game, "Fortune" is a key currency in the world. +This adds an interactible display in the character sheets, allowing it to be tracked, and in the party sheet, +for a nice overview. + +The value is stored in flags, and will not be lost when disabling the module or option. + # Compendia - The only thing in the compendium pack is a macro automating the tinkerer's alchemy potions, diff --git a/assets/fortune.png b/assets/fortune.png new file mode 100644 index 0000000000000000000000000000000000000000..4d360b39a134559b23e83e3a02310c7848558c68 GIT binary patch literal 565 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%C&rs6b?Si}mUKs7M+SzC z{oH>NSwSk3J%W507^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10f-TA0 z-G$*l2rk&Wd@@jkv%n*=n1O*?7=#%aX3dcRTG!+0;usQfI5|Ng;ef}wV@|n?|DX8t zZ}oGDm(N~-IPvT5IUIb!wFa&1DwlJ=)RWDdIMpDtun^Wyz+`mk#?`=6%|4`mB(?8}JT%A*`E z_>|$9{(X*96QzZGu5jel@GY6e-0@MVPO<81y zhvxQ&eLJeURSFkzD=j)zy~1BoLPFwS)@s2J=P)S+EiH~8Vs{01ykeTuDX?03!w;Tx z4FO4^iekov6B=3%9cOkwQO0sGhohW@vDr?}qUo~Yjwbp1W)|i2DbpOKH15V~O#7tZ z%IIGvba?v8&p?wUB!0-|ba=Js99b5&gWv1)zv&gf1w;6`d*`j4{UI%P((1>cfU4e> zcSzv*Nxwq^$|;sd&wdihw-@P7Jm|mXX-MXTgw1nA4mjnWPQ2)xFjf#fsw`F>FVdQ&MBb@0EdFx#{d8T literal 0 HcmV?d00001 diff --git a/lang/en.json b/lang/en.json index a6a782c..377da2d 100644 --- a/lang/en.json +++ b/lang/en.json @@ -25,6 +25,15 @@ "Hint": "Moves both attribute bars below the token, the nameplate above and, for PCs, the status icons to the right." } } + }, + "Fortune": { + "Name": "Fortune", + "Settings": { + "DisplayEnabled": { + "Name": "Fortune display", + "Hint": "Display and track \"Fortune\" currency in the character and party sheets." + } + } } } } diff --git a/lang/fr.json b/lang/fr.json index 3630ef7..b8280a5 100644 --- a/lang/fr.json +++ b/lang/fr.json @@ -25,6 +25,15 @@ "Hint": "Déplace les barres d'attributs sous le token, le nom au dessus et, pour les PJs, les indicateurs de statut à droite." } } + }, + "Fortune": { + "Name": "Fortune", + "Settings": { + "DisplayEnabled": { + "Name": "Affichage de la Fortune", + "Hint": "Affiche et gère une monnaie, la \"Fortune\", dans les fiches de personnage et de groupe." + } + } } } } diff --git a/module.json b/module.json index 9531077..fbe6c83 100644 --- a/module.json +++ b/module.json @@ -2,7 +2,7 @@ "id": "the-mills-fabula", "title": "Fabula Moletrina", "description": "A module implementing our idiosyncratic ideas for the Mill's campaigns.", - "version": "0.3.3", + "version": "0.4.0", "compatibility": { "minimum": "12", "verified": "12", @@ -55,7 +55,11 @@ ], "esmodules": [ "scripts/token_combat_border.mjs", - "scripts/token_ui_adjust.mjs" + "scripts/token_ui_adjust.mjs", + "scripts/fortune_integration.mjs" + ], + "styles": [ + "styles/mills_fabula.css" ], "socket": true, @@ -86,5 +90,5 @@ "readme": "README.md", "url": "https://git.tfk-astrodome.net/trotFunky/TheMillsFabula/src/branch/release", "manifest": "https://git.tfk-astrodome.net/trotFunky/TheMillsFabula/raw/branch/release/module.json", - "download": "https://git.tfk-astrodome.net/trotFunky/TheMillsFabula/archive/v0.3.3.zip" + "download": "https://git.tfk-astrodome.net/trotFunky/TheMillsFabula/archive/v0.4.0.zip" } diff --git a/scripts/fortune_integration.mjs b/scripts/fortune_integration.mjs new file mode 100644 index 0000000..dc92409 --- /dev/null +++ b/scripts/fortune_integration.mjs @@ -0,0 +1,118 @@ +import * as MillsFabula from "./mills_fabula.mjs"; + +const fortune_flag = "char_fortune" + +const FortuneSettings = Object.freeze({ + EnableFortuneDisplay: "enableFortuneDisplay", +}) + +/** + * Find all the characters in the group sheet and insert their Fortune amount + * within the resources, copying the system's style. + * @param {DocumentSheet|foundry.applications.api.DocumentSheetV2} app The source Application + * @param {HTMLElement} html The rendered HTML + * @returns {Promise} Does not return + */ +async function party_sheet_fortune(app, html) { + if (!(app instanceof foundry.applications.api.DocumentSheetV2)) + html = html[0] /* Retrieve the internal DOM element */ + else + console.warn("AppV2 is not properly tested yet") + + let character_divs = html.querySelectorAll(".section-container.plate.character") + for (let char_div of character_divs) { + /** @type{FUActor} */ + let actor = await fromUuid(char_div.getAttribute("data-uuid")) + let resources = char_div.getElementsByClassName("resources")[0] + let icon = document.createElement("i"); + icon.classList.add("fuk", "icon-aff", "mf-fortune") + icon.setAttribute("data-tooltip", game.i18n.localize('MF.Fortune.Name')) + + /* Insert the Fortune elements before Fabula points, so find it */ + let fp_icon = resources.getElementsByClassName("fu-fp")[0] + resources.insertBefore(icon, fp_icon) + /* The system also has whitespace for spacing */ + resources.insertBefore( + document.createTextNode(` ${actor.getFlag(MillsFabula.id, fortune_flag) ?? 0} `), + fp_icon) + } +} + +/** + * Insert a Fortune input at the top of the character sheet, + * following the same styling as the system for Fabula points. + * @param {DocumentSheet|foundry.applications.api.DocumentSheetV2} app The source Application + * @param {HTMLElement} html The rendered HTML + */ +function character_sheet_fortune(app, html) { + /* Only player characters have Fortune. */ + if (app.actor.type !== "character") + return + + if (!(app instanceof foundry.applications.api.DocumentSheetV2)) + html = html[0] /* Retrieve the internal DOM element */ + else + console.warn("AppV2 is not properly tested yet") + + let resources_div = html.getElementsByClassName("header-center")[0].lastElementChild + resources_div.classList.replace("grid-3col", "grid-4col") + + /* + * We want the Fortune to have the same importance/weight as Fabula points, + * so we'll copy the style and bundle them in a 2 column div. + */ + /* TODO: This all should probably be some form of template ? */ + let fortune_fabula_div = document.createElement("div") + fortune_fabula_div.classList.add("grid", "grid-2col") + + let fortune_div = document.createElement("div") + fortune_div.classList.add("flex-group-center", "resource-content") + let fortune_label = document.createElement("span") + fortune_label.classList.add("resource-label-l") + let fortune_icon = document.createElement("i") + fortune_icon.classList.add("fas", "fa-leaf", "icon") + fortune_label.appendChild(fortune_icon) + fortune_label.appendChild(document.createTextNode(game.i18n.localize('MF.Fortune.Name'))) + fortune_div.appendChild(fortune_label) + let fortune_data_div = document.createElement("div") + fortune_data_div.classList.add("buttons-inc") + let fortune_input = foundry.applications.fields.createNumberInput({ + /* + * By setting the name of the field using this format, the backing + * flag will automatically be picked up by Foundry's code and updated + * when the field is changed on the sheet. + * This works even if it wasn't set previously ! + */ + name: `flags.${MillsFabula.id}.${fortune_flag}`, + value: app.document.getFlag(MillsFabula.id, fortune_flag) ?? 0, + step: 1, + min: 0, + type: "number" + }) + fortune_input.classList.add("fp-resource-inputs") + fortune_data_div.appendChild(fortune_input) + fortune_div.appendChild(fortune_data_div) + + fortune_fabula_div.appendChild(fortune_div) + /* The last element should always be the Fabula Points div. */ + fortune_fabula_div.appendChild(resources_div.lastElementChild) + + resources_div.appendChild(fortune_fabula_div) +} + +Hooks.once("init", () => { + game.settings.register(MillsFabula.id, FortuneSettings.EnableFortuneDisplay, { + name: game.i18n.localize('MF.Fortune.Settings.DisplayEnabled.Name'), + hint: game.i18n.localize('MF.Fortune.Settings.DisplayEnabled.Hint'), + type: Boolean, + config: true, + scope: 'world', + requiresReload: true, + default: true + }); + + if (game.settings.get(MillsFabula.id, FortuneSettings.EnableFortuneDisplay)) { + Hooks.on("renderFUPartySheet", party_sheet_fortune) + Hooks.on("renderFUStandardActorSheet", character_sheet_fortune) + } +}) diff --git a/styles/mills_fabula.css b/styles/mills_fabula.css new file mode 100644 index 0000000..6ea8d11 --- /dev/null +++ b/styles/mills_fabula.css @@ -0,0 +1,3 @@ +.mf-fortune { + background-image: url("/modules/the-mills-fabula/assets/fortune.png"); +}