From 98cf6fa6de1d2bb61f0a9b9c4a4d2df385120b70 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Fri, 16 Jan 2026 13:13:26 +0100 Subject: [PATCH 1/3] [Feature] Character Creation Confirmations (#1533) * Added confirmation on ignoring character setup. Added reset option to character sheet. * Removed the system setting for playerCanEdit. It's always available now. --- lang/en.json | 11 ++-- .../applications/sheets/actors/character.mjs | 51 ++++++++++++++++--- module/data/settings/Automation.mjs | 5 -- .../settings/automation-settings/general.hbs | 1 - templates/sheets/actors/character/header.hbs | 6 +-- 5 files changed, 52 insertions(+), 22 deletions(-) diff --git a/lang/en.json b/lang/en.json index 8e64ab7d..870e1b2b 100755 --- a/lang/en.json +++ b/lang/en.json @@ -237,10 +237,13 @@ "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", + "resetCharacter": "Reset Character", "viewParty": "View Party", "InvalidOldCharacterImportTitle": "Old Character Import", "InvalidOldCharacterImportText": "Character data exported prior to system version 1.1 will not generate a complete character. Do you wish to continue?", - "cancelBeastform": "Cancel Beastform" + "cancelBeastform": "Cancel Beastform", + "resetCharacterConfirmationTitle": "Reset Character", + "resetCharacterConfirmationContent": "You are reseting all character data except name and portrait. Are you sure?" }, "Companion": { "FIELDS": { @@ -314,6 +317,8 @@ "selectPrimaryWeapon": "Select Primary Weapon", "selectSecondaryWeapon": "Select Secondary Weapon", "selectSubclass": "Select Subclass", + "setupSkipTitle": "Skipping Character Setup", + "setupSkipContent": "You are skipping the Character Setup by adding this manually. The character setup is the blinking arrows in the top-right. Are you sure you want to continue?", "startingItems": "Starting Items", "story": "Story", "storyExplanation": "Select which background and connection prompts you want to copy into your character's background.", @@ -2450,10 +2455,6 @@ "label": "Show Resource Change Scrolltexts", "hint": "When a character is damaged, uses armor etc, a scrolling text will briefly appear by the token to signify this." }, - "playerCanEditSheet": { - "label": "Players Can Manually Edit Character Settings", - "hint": "Players are allowed to access the manual Character Settings and change their statistics beyond the rules." - }, "roll": { "roll": { "label": "Roll", diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index e11fee05..d691c129 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -27,6 +27,7 @@ export default class CharacterSheet extends DHBaseActorSheet { makeDeathMove: CharacterSheet.#makeDeathMove, levelManagement: CharacterSheet.#levelManagement, viewLevelups: CharacterSheet.#viewLevelups, + resetCharacter: CharacterSheet.#resetCharacter, toggleEquipItem: CharacterSheet.#toggleEquipItem, toggleResourceDice: CharacterSheet.#toggleResourceDice, handleResourceDice: CharacterSheet.#handleResourceDice, @@ -42,6 +43,11 @@ export default class CharacterSheet extends DHBaseActorSheet { icon: 'fa-solid fa-angles-up', label: 'DAGGERHEART.ACTORS.Character.viewLevelups', action: 'viewLevelups' + }, + { + icon: 'fa-solid fa-arrow-rotate-left', + label: 'DAGGERHEART.ACTORS.Character.resetCharacter', + action: 'resetCharacter' } ] }, @@ -220,13 +226,6 @@ export default class CharacterSheet extends DHBaseActorSheet { async _preparePartContext(partId, context, options) { context = await super._preparePartContext(partId, context, options); switch (partId) { - case 'header': - const { playerCanEditSheet, levelupAuto } = game.settings.get( - CONFIG.DH.id, - CONFIG.DH.SETTINGS.gameSettings.Automation - ); - context.showSettings = game.user.isGM || !levelupAuto || (levelupAuto && playerCanEditSheet); - break; case 'loadout': await this._prepareLoadoutContext(context, options); break; @@ -666,6 +665,32 @@ export default class CharacterSheet extends DHBaseActorSheet { new LevelupViewMode(this.document).render({ force: true }); } + /** + * Resets the character data and removes all embedded documents. + */ + static async #resetCharacter() { + const confirmed = await foundry.applications.api.DialogV2.confirm({ + window: { + title: game.i18n.localize('DAGGERHEART.ACTORS.Character.resetCharacterConfirmationTitle') + }, + content: game.i18n.localize('DAGGERHEART.ACTORS.Character.resetCharacterConfirmationContent') + }); + + if (!confirmed) return; + + await this.document.update({ + '==system': {} + }); + await this.document.deleteEmbeddedDocuments( + 'Item', + this.document.items.map(x => x.id) + ); + await this.document.deleteEmbeddedDocuments( + 'ActiveEffect', + this.document.effects.map(x => x.id) + ); + } + /** * Opens the Death Move interface for the character. * @type {ApplicationClickAction} @@ -956,6 +981,18 @@ export default class CharacterSheet extends DHBaseActorSheet { } async _onDropItem(event, item) { + const setupCriticalItemTypes = ['class', 'subclass', 'ancestry', 'community']; + if (this.document.system.needsCharacterSetup && setupCriticalItemTypes.includes(item.type)) { + const confirmed = await foundry.applications.api.DialogV2.confirm({ + window: { + title: game.i18n.localize('DAGGERHEART.APPLICATIONS.CharacterCreation.setupSkipTitle') + }, + content: game.i18n.localize('DAGGERHEART.APPLICATIONS.CharacterCreation.setupSkipContent') + }); + + if (!confirmed) return; + } + if (this.document.uuid === item.parent?.uuid) { return super._onDropItem(event, item); } diff --git a/module/data/settings/Automation.mjs b/module/data/settings/Automation.mjs index 3376b153..bff0bae9 100644 --- a/module/data/settings/Automation.mjs +++ b/module/data/settings/Automation.mjs @@ -55,11 +55,6 @@ export default class DhAutomation extends foundry.abstract.DataModel { initial: true, label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.resourceScrollTexts.label' }), - playerCanEditSheet: new fields.BooleanField({ - required: true, - initial: false, - label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.playerCanEditSheet.label' - }), defeated: new fields.SchemaField({ enabled: new fields.BooleanField({ required: true, diff --git a/templates/settings/automation-settings/general.hbs b/templates/settings/automation-settings/general.hbs index d49ef9b8..bd91b2b1 100644 --- a/templates/settings/automation-settings/general.hbs +++ b/templates/settings/automation-settings/general.hbs @@ -18,7 +18,6 @@ {{formGroup settingFields.schema.fields.hordeDamage value=settingFields._source.hordeDamage localize=true}} {{formGroup settingFields.schema.fields.effects.fields.rangeDependent value=settingFields._source.effects.rangeDependent localize=true}} {{formGroup settingFields.schema.fields.levelupAuto value=settingFields._source.levelupAuto localize=true}} - {{formGroup settingFields.schema.fields.playerCanEditSheet value=settingFields._source.playerCanEditSheet localize=true}} {{formGroup settingFields.schema.fields.damageReductionRulesDefault value=settingFields._source.damageReductionRulesDefault localize=true}} {{formGroup settingFields.schema.fields.resourceScrollTexts value=settingFields._source.resourceScrollTexts localize=true}} diff --git a/templates/sheets/actors/character/header.hbs b/templates/sheets/actors/character/header.hbs index 1459e10b..87319dbb 100644 --- a/templates/sheets/actors/character/header.hbs +++ b/templates/sheets/actors/character/header.hbs @@ -123,9 +123,7 @@ {{/each}} - {{#> 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' showSettings=showSettings }} - {{#if ../showSettings}} - - {{/if}} + {{#> 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' }} + {{/'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs'}} \ No newline at end of file From 8d718879242903e6a9112b7785073bb8101871c5 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Fri, 16 Jan 2026 15:24:29 +0100 Subject: [PATCH 2/3] Dialog templating and logic --- lang/en.json | 14 +-- module/applications/dialogs/_module.mjs | 1 + module/applications/dialogs/deathMove.mjs | 8 +- .../applications/dialogs/riskItAllDialog.mjs | 85 ++++++++++++------- module/applications/ui/chatLog.mjs | 19 ++--- styles/less/dialog/index.less | 4 +- styles/less/dialog/risk-it-all/sheet.less | 60 +++++++++++++ templates/dialogs/riskItAllDialog.hbs | 58 +++++++------ templates/ui/chat/deathMove.hbs | 2 +- 9 files changed, 171 insertions(+), 80 deletions(-) create mode 100644 styles/less/dialog/risk-it-all/sheet.less diff --git a/lang/en.json b/lang/en.json index 82129bac..b5f6d612 100755 --- a/lang/en.json +++ b/lang/en.json @@ -615,9 +615,11 @@ "rerollDice": "Reroll Dice" }, "RiskItAllDialog": { - "title": "Character: {name} - Risk It All - Clear Stress and/or Hit Points", - "subtitle": "Clear {hope} Stress and/or Hit Points", - "submit": "Submit" + "title": "{name} - Risk It All", + "subtitle": "Clear Stress and Hit Points", + "remainingTitle": "Remaining Points", + "clearResource": "Clear {resource}", + "finalTitle": "Final Character Resources" }, "TagTeamSelect": { "title": "Tag Team Roll", @@ -2675,9 +2677,9 @@ "riskItAllCritical": "Critical Rolled, clearing all marked Stress and Hit Points.", "riskItAllFailure": "The fear die rolled higher. You have crossed through the veil of death.", "blazeOfGlory": "Blaze of Glory Effect Added!", - "riskItAllClearStressAndHitPoints": "Clear {hope} Stress Or Hit Points.", - "riskItAllSuccessWithEnoughHope": "Hope roll value is more than the marked Stress and Hit Points, clearing both.", - "riskItAllSuccess": "The hope die rolled higher, clear up to {hope} Stress Or Hit Points." + "riskItAllDialogButton": "Clear Stress And Hit Points.", + "riskItAllSuccessWithEnoughHope": "Hope roll value is more than the marked Stress and Hit Points. Both are cleared fully.", + "riskItAllSuccess": "The hope die rolled higher, clear up to {hope} Stress And Hit Points." }, "dicePool": { "title": "Dice Pool" diff --git a/module/applications/dialogs/_module.mjs b/module/applications/dialogs/_module.mjs index 92038c41..d43045e6 100644 --- a/module/applications/dialogs/_module.mjs +++ b/module/applications/dialogs/_module.mjs @@ -14,3 +14,4 @@ export { default as ResourceDiceDialog } from './resourceDiceDialog.mjs'; export { default as ActionSelectionDialog } from './actionSelectionDialog.mjs'; export { default as GroupRollDialog } from './group-roll-dialog.mjs'; export { default as TagTeamDialog } from './tagTeamDialog.mjs'; +export { default as RiskItAllDialog } from './riskItAllDialog.mjs'; diff --git a/module/applications/dialogs/deathMove.mjs b/module/applications/dialogs/deathMove.mjs index ea7a18eb..5cc78b5f 100644 --- a/module/applications/dialogs/deathMove.mjs +++ b/module/applications/dialogs/deathMove.mjs @@ -9,7 +9,7 @@ export default class DhDeathMove extends HandlebarsApplicationMixin(ApplicationV this.actor = actor; this.selectedMove = null; this.showRiskItAllButton = false; - this.riskItAllButtonLabel = ""; + this.riskItAllButtonLabel = ''; this.riskItAllHope = 0; } @@ -108,10 +108,12 @@ export default class DhDeathMove extends HandlebarsApplicationMixin(ApplicationV config.resourceUpdates.addResources(clearAllStressAndHitpointsUpdates); chatMessage = game.i18n.localize('DAGGERHEART.UI.Chat.deathMove.riskItAllSuccessWithEnoughHope'); } else { - chatMessage = game.i18n.format('DAGGERHEART.UI.Chat.deathMove.riskItAllSuccess', { hope: config.roll.hope.value }) + chatMessage = game.i18n.format('DAGGERHEART.UI.Chat.deathMove.riskItAllSuccess', { + hope: config.roll.hope.value + }); this.showRiskItAllButton = true; this.riskItAllHope = config.roll.hope.value; - this.riskItAllButtonLabel = game.i18n.format('DAGGERHEART.UI.Chat.deathMove.riskItAllClearStressAndHitPoints', { hope: config.roll.hope.value }) + this.riskItAllButtonLabel = game.i18n.format('DAGGERHEART.UI.Chat.deathMove.riskItAllDialogButton'); } } diff --git a/module/applications/dialogs/riskItAllDialog.mjs b/module/applications/dialogs/riskItAllDialog.mjs index fef76e66..9bdd5dc2 100644 --- a/module/applications/dialogs/riskItAllDialog.mjs +++ b/module/applications/dialogs/riskItAllDialog.mjs @@ -1,11 +1,14 @@ const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; export default class RiskItAllDialog extends HandlebarsApplicationMixin(ApplicationV2) { - constructor(actor, config) { + constructor(actor, resourceValue) { super({}); this.actor = actor; - this.validChoices = null; - this.config = config; + this.resourceValue = resourceValue; + this.choices = { + hitPoints: 0, + stress: 0 + }; } get title() { @@ -14,10 +17,10 @@ export default class RiskItAllDialog extends HandlebarsApplicationMixin(Applicat static DEFAULT_OPTIONS = { classes: ['daggerheart', 'dh-style', 'dialog', 'views', 'risk-it-all'], - position: { width: 'auto', height: 'auto' }, - window: { icon: 'fa-solid fa-skull' }, + position: { width: 280, height: 'auto' }, + window: { icon: 'fa-solid fa-dice fa-xl' }, actions: { - submit: this.submit + finish: RiskItAllDialog.#finish } }; @@ -28,42 +31,62 @@ export default class RiskItAllDialog extends HandlebarsApplicationMixin(Applicat } }; + _attachPartListeners(partId, htmlElement, options) { + super._attachPartListeners(partId, htmlElement, options); + + for (const input of htmlElement.querySelectorAll('.resource-container input')) + input.addEventListener('change', this.updateChoice.bind(this)); + } + async _prepareContext(_options) { const context = await super._prepareContext(_options); - context.RiskItAllDialog = this.RiskItAllDialog; - context.title = game.i18n.format('DAGGERHEART.APPLICATIONS.RiskItAllDialog.subtitle', { hope: this.config.hope }); - context.currentHitPointsLabel = "Current Marked Hit Points: " + this.actor.system.resources.hitPoints.value; - context.currentStressLabel = "Current Marked Stress: " + this.actor.system.resources.stress.value; + context.resourceValue = this.resourceValue; + context.remainingResource = this.resourceValue - this.choices.hitPoints - this.choices.stress; + context.unfinished = context.remainingResource !== 0; - context.newHitPoints = this.actor.system.resources.hitPoints.value; - context.newStress = this.actor.system.resources.stress.value; + context.choices = this.choices; + context.final = { + hitPoints: { + value: this.actor.system.resources.hitPoints.value - this.choices.hitPoints, + max: this.actor.system.resources.hitPoints.max + }, + stress: { + value: this.actor.system.resources.stress.value - this.choices.stress, + max: this.actor.system.resources.stress.max + } + }; + + context; return context; } - static checkForValidChoice() { - /* - TODO: + updateChoice(event) { + let value = Number.parseInt(event.target.value); + const choiceKey = event.target.dataset.choice; + const actorValue = this.actor.system.resources[choiceKey].value; + const remaining = this.resourceValue - this.choices.hitPoints - this.choices.stress; + const changeAmount = value - this.choices[choiceKey]; - return (this.config.hope == (this.actor.system.resources.hitPoints.value - newHitPointValue) + (this.actor.system.resources.stress.value - newStressValue)); - */ - return true; + /* If trying to increase beyond remaining resource points, just increase to max available */ + if (remaining - changeAmount < 0) value = this.choices[choiceKey] + remaining; + else if (actorValue - value < 0) value = actorValue; + + this.choices[choiceKey] = value; + this.render(); } - static async submit() { - this.close(); - // TODO: Update actor with changes. + static async #finish() { + const resourceUpdate = Object.keys(this.choices).reduce((acc, resourceKey) => { + const value = this.actor.system.resources[resourceKey].value - this.choices[resourceKey]; + acc[resourceKey] = { value }; + return acc; + }, {}); + await this.actor.update({ - system: { - resources: { - hitPoints: { - value: 0 // TODO put editted value here - }, - stress: { - value: 0 // TODO put editted value here - } - } - } + 'system.resources': resourceUpdate }); + + this.close(); } } diff --git a/module/applications/ui/chatLog.mjs b/module/applications/ui/chatLog.mjs index b4aa246a..f0c7288c 100644 --- a/module/applications/ui/chatLog.mjs +++ b/module/applications/ui/chatLog.mjs @@ -1,6 +1,5 @@ import { abilities } from '../../config/actorConfig.mjs'; import { emitAsGM, GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs'; -import RiskItAllDialog from '../dialogs/riskItAllDialog.mjs'; export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLog { constructor(options) { @@ -98,15 +97,17 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo /** Ensure the chat theme inherits the interface theme */ _replaceHTML(result, content, options) { - const themedElement = result.log?.querySelector(".chat-log"); - themedElement?.classList.remove("themed", "theme-light", "theme-dark"); + const themedElement = result.log?.querySelector('.chat-log'); + themedElement?.classList.remove('themed', 'theme-light', 'theme-dark'); super._replaceHTML(result, content, options); } /** Remove chat log theme from notifications area */ async _onFirstRender(result, content) { await super._onFirstRender(result, content); - document.querySelector("#chat-notifications .chat-log")?.classList.remove("themed", "theme-light", "theme-dark") + document + .querySelector('#chat-notifications .chat-log') + ?.classList.remove('themed', 'theme-light', 'theme-dark'); } async onRollSimple(event, message) { @@ -388,14 +389,8 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo event.target.closest('.group-roll-section').querySelector('.group-roll-content').classList.toggle('closed'); } - async riskItAllClearStressAndHitPoints(event, data) { - const hopeValue = event.target.dataset.hope; - const config = { - hope: hopeValue - } - await new RiskItAllDialog(data.actor, config).render({ force: true }); + const resourceValue = event.target.dataset.resourceValue; + new game.system.api.applications.dialogs.RiskItAllDialog(data.actor, resourceValue).render({ force: true }); } - - } diff --git a/styles/less/dialog/index.less b/styles/less/dialog/index.less index b5ed8764..01a3f954 100644 --- a/styles/less/dialog/index.less +++ b/styles/less/dialog/index.less @@ -38,4 +38,6 @@ @import './item-transfer/sheet.less'; -@import './settings/change-currency-icon.less'; \ No newline at end of file +@import './settings/change-currency-icon.less'; + +@import './risk-it-all/sheet.less'; diff --git a/styles/less/dialog/risk-it-all/sheet.less b/styles/less/dialog/risk-it-all/sheet.less new file mode 100644 index 00000000..db34a5c1 --- /dev/null +++ b/styles/less/dialog/risk-it-all/sheet.less @@ -0,0 +1,60 @@ +.daggerheart.dialog.dh-style.views.risk-it-all { + .risk-it-all-container { + display: flex; + align-items: center; + flex-direction: column; + gap: 8px; + text-align: center; + + header { + font-weight: bold; + font-size: var(--font-size-20); + } + + .section-label { + font-size: var(--font-size-18); + text-decoration: underline; + } + + .remaining-section { + display: flex; + flex-direction: column; + gap: 2px; + } + + .resource-section { + width: 100%; + display: flex; + gap: 8px; + } + + .final-section { + width: 100%; + display: flex; + flex-direction: column; + gap: 2px; + + .final-section-values-container { + width: 100%; + display: flex; + align-items: center; + justify-content: space-evenly; + + .final-section-value-container { + display: flex; + flex-direction: column; + gap: 2px; + } + } + } + } + + footer { + width: 100%; + display: flex; + + button { + flex: 1; + } + } +} diff --git a/templates/dialogs/riskItAllDialog.hbs b/templates/dialogs/riskItAllDialog.hbs index 9f494f58..edb2fbf6 100644 --- a/templates/dialogs/riskItAllDialog.hbs +++ b/templates/dialogs/riskItAllDialog.hbs @@ -1,33 +1,39 @@
-
-

{{title}}

-
-
- Remaining points to use here. -
-
- {{currentHitPointsLabel}} -
- New Hit Points Value - -
+
{{localize "DAGGERHEART.APPLICATIONS.RiskItAllDialog.subtitle"}}
- {{currentStressLabel}} -
- New Stress Value - +
+ +
{{this.remainingResource}}
+
+ +
+
+ + +
+
+ +
+ +
+ +
+
+ + {{this.final.hitPoints.value}}/{{this.final.hitPoints.max}} +
+
+ + {{this.final.stress.value}}/{{this.final.stress.max}} +
+
+
+ +
+ +
-
- - -
\ No newline at end of file diff --git a/templates/ui/chat/deathMove.hbs b/templates/ui/chat/deathMove.hbs index b36ee590..02691c7c 100644 --- a/templates/ui/chat/deathMove.hbs +++ b/templates/ui/chat/deathMove.hbs @@ -19,7 +19,7 @@
{{#if this.showRiskItAllButton}}
-
- {{formInput @root.fields.triggers.element.fields.command value=trigger.command elementType="code-mirror" name=(concat "triggers." index ".command") aria=(object label=(localize "Test")) }} + {{formInput @root.fields.triggers.element.fields.command value=trigger.command elementType="code-mirror" name=(concat "triggers." index ".command") }}
{{/each}}