From f69e5704e4f47051fd681db81f768e53f5a6fca0 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Mon, 18 Aug 2025 03:59:57 +0200 Subject: [PATCH] Added a simple ViewMode for a character's levelup progression (#997) --- lang/en.json | 6 +- module/applications/levelup/_module.mjs | 1 + .../applications/levelup/levelupViewMode.mjs | 95 +++++++++++++++++++ .../applications/sheets/actors/character.mjs | 23 ++++- templates/levelup/tabs/viewMode.hbs | 24 +++++ 5 files changed, 143 insertions(+), 6 deletions(-) create mode 100644 module/applications/levelup/levelupViewMode.mjs create mode 100644 templates/levelup/tabs/viewMode.hbs diff --git a/lang/en.json b/lang/en.json index aeb63449..a7a615f5 100755 --- a/lang/en.json +++ b/lang/en.json @@ -193,7 +193,8 @@ "companionLevelup": { "confirmTitle": "Companion Levelup", "confirmText": "Would you like to level up your companion {name} by {levelChange} levels at this time? (You can do it manually later)" - } + }, + "viewLevelups": "View Levelups" }, "Companion": { "FIELDS": { @@ -493,7 +494,8 @@ "pretext": "When you level up, record it on your character sheet, then choose two from the list below or any unmarked from the previous tier.", "posttext": "Take an additional domain card of your level or lower from a domain you have access to." }, - "title": "{actor} Level Up" + "title": "{actor} Level Up", + "viewModeTitle": "{actor} Level Up (View Mode)" }, "MulticlassChoice": { "title": "Multiclassing - {actor}", diff --git a/module/applications/levelup/_module.mjs b/module/applications/levelup/_module.mjs index dd4135b4..24944feb 100644 --- a/module/applications/levelup/_module.mjs +++ b/module/applications/levelup/_module.mjs @@ -1,3 +1,4 @@ export { default as CharacterLevelup } from './characterLevelup.mjs'; export { default as CompanionLevelup } from './companionLevelup.mjs'; export { default as Levelup } from './levelup.mjs'; +export { default as LevelupViewMode } from './levelupViewMode.mjs'; diff --git a/module/applications/levelup/levelupViewMode.mjs b/module/applications/levelup/levelupViewMode.mjs new file mode 100644 index 00000000..b3d7c30f --- /dev/null +++ b/module/applications/levelup/levelupViewMode.mjs @@ -0,0 +1,95 @@ +import { chunkify } from '../../helpers/utils.mjs'; + +const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; + +export default class DhlevelUpViewMode extends HandlebarsApplicationMixin(ApplicationV2) { + constructor(actor) { + super({}); + + this.actor = actor; + } + + get title() { + return game.i18n.format('DAGGERHEART.APPLICATIONS.Levelup.viewModeTitle', { actor: this.actor.name }); + } + + static DEFAULT_OPTIONS = { + classes: ['daggerheart', 'dialog', 'dh-style', 'levelup'], + position: { width: 1000, height: 'auto' }, + window: { + resizable: true, + icon: 'fa-solid fa-arrow-turn-up' + } + }; + + static PARTS = { + main: { template: 'systems/daggerheart/templates/levelup/tabs/viewMode.hbs' } + }; + + async _prepareContext(_options) { + const context = await super._prepareContext(_options); + + const { tiers } = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers); + const tierKeys = Object.keys(tiers); + const selections = Object.keys(this.actor.system.levelData.levelups).reduce( + (acc, key) => { + const level = this.actor.system.levelData.levelups[key]; + Object.keys(level.selections).forEach(optionKey => { + const choice = level.selections[optionKey]; + if (!acc[choice.tier][choice.optionKey]) acc[choice.tier][choice.optionKey] = {}; + acc[choice.tier][choice.optionKey][choice.checkboxNr] = choice; + }); + + return acc; + }, + tierKeys.reduce((acc, key) => { + acc[key] = {}; + return acc; + }, {}) + ); + + context.tiers = tierKeys.map((tierKey, tierIndex) => { + const tier = tiers[tierKey]; + + return { + name: tier.name, + active: true, + groups: Object.keys(tier.options).map(optionKey => { + const option = tier.options[optionKey]; + + const checkboxes = [...Array(option.checkboxSelections).keys()].flatMap(index => { + const checkboxNr = index + 1; + const checkboxData = selections[tierKey]?.[optionKey]?.[checkboxNr]; + const checkbox = { ...option, checkboxNr, tier: tierKey, disabled: true }; + + if (checkboxData) { + checkbox.level = checkboxData.level; + checkbox.selected = true; + } + + return checkbox; + }); + + let label = game.i18n.localize(option.label); + return { + label: label, + checkboxGroups: chunkify(checkboxes, option.minCost, chunkedBoxes => { + const anySelected = chunkedBoxes.some(x => x.selected); + const anyDisabled = chunkedBoxes.some(x => x.disabled); + return { + multi: option.minCost > 1, + checkboxes: chunkedBoxes.map(x => ({ + ...x, + selected: anySelected, + disabled: anyDisabled + })) + }; + }) + }; + }) + }; + }); + + return context; + } +} diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index 481c745e..78a77406 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -1,7 +1,7 @@ import DHBaseActorSheet from '../api/base-actor.mjs'; import DhpDeathMove from '../../dialogs/deathMove.mjs'; import { abilities } from '../../../config/actorConfig.mjs'; -import DhCharacterlevelUp from '../../levelup/characterLevelup.mjs'; +import { CharacterLevelup, LevelupViewMode } from '../../levelup/_module.mjs'; import DhCharacterCreation from '../../characterCreation/characterCreation.mjs'; import FilterMenu from '../../ux/filter-menu.mjs'; import { getDocFromElement, getDocFromElementSync } from '../../../helpers/utils.mjs'; @@ -23,6 +23,7 @@ export default class CharacterSheet extends DHBaseActorSheet { openPack: CharacterSheet.#openPack, makeDeathMove: CharacterSheet.#makeDeathMove, levelManagement: CharacterSheet.#levelManagement, + viewLevelups: CharacterSheet.#viewLevelups, toggleEquipItem: CharacterSheet.#toggleEquipItem, toggleResourceDice: CharacterSheet.#toggleResourceDice, handleResourceDice: CharacterSheet.#handleResourceDice, @@ -30,7 +31,14 @@ export default class CharacterSheet extends DHBaseActorSheet { tempBrowser: CharacterSheet.#tempBrowser }, window: { - resizable: true + resizable: true, + controls: [ + { + icon: 'fa-solid fa-angles-up', + label: 'DAGGERHEART.ACTORS.Character.viewLevelups', + action: 'viewLevelups' + } + ] }, dragDrop: [ { @@ -585,7 +593,14 @@ export default class CharacterSheet extends DHBaseActorSheet { if (!value || !subclass) return ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.missingClassOrSubclass')); - new DhCharacterlevelUp(this.document).render({ force: true }); + new CharacterLevelup(this.document).render({ force: true }); + } + + /** + * Opens the charater level management window in viewMode. + */ + static #viewLevelups() { + new LevelupViewMode(this.document).render({ force: true }); } /** @@ -638,7 +653,7 @@ export default class CharacterSheet extends DHBaseActorSheet { ability: abilityLabel }) }); - + this.consumeResource(result?.costs); } diff --git a/templates/levelup/tabs/viewMode.hbs b/templates/levelup/tabs/viewMode.hbs new file mode 100644 index 00000000..12e7cbcd --- /dev/null +++ b/templates/levelup/tabs/viewMode.hbs @@ -0,0 +1,24 @@ +