-
-
{{name}} +{{value}}
+ {{#each selectedEffects as |effect id|}}
+
+
+ {{effect.name}}
- {{/if}}
- {{/each}}
-
- {{/if}}
+ {{/each}}
+
+ {{/if}}
-
+ {{/if}}
{{/unless}}
{{#if (or costs uses)}}
{{> 'systems/daggerheart/templates/dialogs/dice-roll/costSelection.hbs'}}
{{/if}}
-
{{localize "DAGGERHEART.GENERAL.formula"}}: {{@root.formula}}
+ {{#if (ne @root.rollType 'FateRoll')}}
+
{{localize "DAGGERHEART.GENERAL.formula"}}: {{@root.formula}}
+ {{/if}}
- {{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}}
diff --git a/templates/sheets-settings/character-settings/details.hbs b/templates/sheets-settings/character-settings/details.hbs
index 42e17a9b..3f9247e0 100644
--- a/templates/sheets-settings/character-settings/details.hbs
+++ b/templates/sheets-settings/character-settings/details.hbs
@@ -31,7 +31,7 @@
{{formGroup systemFields.resources.fields.stress.fields.max value=document._source.system.resources.stress.max localize=true}}
{{formGroup systemFields.resources.fields.hope.fields.value value=document._source.system.resources.hope.value localize=true}}
- {{formGroup systemFields.resources.fields.hope.fields.max value=document._source.system.resources.hope.max localize=true}}
+ {{formGroup systemFields.scars value=document._source.system.scars localize=true}}
{{formGroup systemFields.proficiency value=document._source.system.proficiency localize=true}}
diff --git a/templates/sheets/actors/character/header.hbs b/templates/sheets/actors/character/header.hbs
index 87319dbb..d2c01f3c 100644
--- a/templates/sheets/actors/character/header.hbs
+++ b/templates/sheets/actors/character/header.hbs
@@ -76,6 +76,11 @@
{{/if}}
{{/times}}
+ {{#times document.system.scars}}
+
+
+
+ {{/times}}
{{#if document.system.class.value}}
diff --git a/templates/ui/chat/deathMove.hbs b/templates/ui/chat/deathMove.hbs
index 9940376e..7c677fe3 100644
--- a/templates/ui/chat/deathMove.hbs
+++ b/templates/ui/chat/deathMove.hbs
@@ -1,17 +1,32 @@
-
-
-
-
-
+
+
+
+
+
-
+
{{{this.description}}}
+
+ {{{this.result}}}
+
+ {{#if this.showRiskItAllButton}}
+
+
+
+ {{/if}}
+
+
+
\ No newline at end of file
diff --git a/templates/ui/chat/parts/roll-part.hbs b/templates/ui/chat/parts/roll-part.hbs
index 8e88015f..ea962f9e 100644
--- a/templates/ui/chat/parts/roll-part.hbs
+++ b/templates/ui/chat/parts/roll-part.hbs
@@ -29,64 +29,86 @@
{{/unless}}
-
\ No newline at end of file
+
From 77bac647a86f886e811bd71126cc51289be75d33 Mon Sep 17 00:00:00 2001
From: WBHarry <89362246+WBHarry@users.noreply.github.com>
Date: Mon, 19 Jan 2026 11:31:54 +0100
Subject: [PATCH 03/88] Fixed typo in levelupViewMode domain card option
(#1558)
---
module/applications/levelup/levelupViewMode.mjs | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/module/applications/levelup/levelupViewMode.mjs b/module/applications/levelup/levelupViewMode.mjs
index b3d7c30f..afd7dbc4 100644
--- a/module/applications/levelup/levelupViewMode.mjs
+++ b/module/applications/levelup/levelupViewMode.mjs
@@ -70,7 +70,10 @@ export default class DhlevelUpViewMode extends HandlebarsApplicationMixin(Applic
return checkbox;
});
- let label = game.i18n.localize(option.label);
+ let label =
+ optionKey === 'domainCard'
+ ? game.i18n.format(option.label, { maxLevel: tier.levels.end })
+ : game.i18n.localize(option.label);
return {
label: label,
checkboxGroups: chunkify(checkboxes, option.minCost, chunkedBoxes => {
From cc998bffa7ca04bdf203f6969ebde17cf8c751cb Mon Sep 17 00:00:00 2001
From: WBHarry <89362246+WBHarry@users.noreply.github.com>
Date: Tue, 20 Jan 2026 11:03:52 +0100
Subject: [PATCH 04/88] [Feature] DeathMove Condition Improvement (#1562)
* Added DeathMove condition and automated changing to the correct condition depending on the result of death moves
* .
* Update module/data/settings/Automation.mjs
Co-authored-by: Chris Ryan <73275196+chrisryan10@users.noreply.github.com>
* Update lang/en.json
Co-authored-by: Chris Ryan <73275196+chrisryan10@users.noreply.github.com>
* Fixed DefeatedCondition localizations
---------
Co-authored-by: Chris Ryan <73275196+chrisryan10@users.noreply.github.com>
---
lang/en.json | 10 +++++++++-
module/applications/dialogs/deathMove.mjs | 11 +++++++----
module/config/generalConfig.mjs | 6 +++++-
module/data/actor/character.mjs | 13 ++++++++++++-
module/data/settings/Automation.mjs | 16 +++++++++++-----
module/documents/actor.mjs | 16 ++++++++++++++--
templates/settings/automation-settings/rules.hbs | 3 ++-
7 files changed, 60 insertions(+), 15 deletions(-)
diff --git a/lang/en.json b/lang/en.json
index 69965b9e..dda78410 100755
--- a/lang/en.json
+++ b/lang/en.json
@@ -973,6 +973,10 @@
"outsideRange": "Outside Range"
},
"Condition": {
+ "deathMove": {
+ "name": "Death Move",
+ "description": "The character is about to make a Death Move"
+ },
"dead": {
"name": "Dead",
"description": "The character is dead"
@@ -2435,7 +2439,11 @@
"overlay": { "label": "Overlay Effect" },
"characterDefault": { "label": "Character Default Defeated Status" },
"adversaryDefault": { "label": "Adversary Default Defeated Status" },
- "companionDefault": { "label": "Companion Default Defeated Status" }
+ "companionDefault": { "label": "Companion Default Defeated Status" },
+ "deathMove": { "label": "Death Move" },
+ "dead": { "label": "Dead" },
+ "defeated": { "label": "Defeated" },
+ "unconscious": { "label": "Unconscious" }
},
"hopeFear": {
"label": "Hope & Fear",
diff --git a/module/applications/dialogs/deathMove.mjs b/module/applications/dialogs/deathMove.mjs
index 01df6057..d1b9379b 100644
--- a/module/applications/dialogs/deathMove.mjs
+++ b/module/applications/dialogs/deathMove.mjs
@@ -54,10 +54,9 @@ export default class DhDeathMove extends HandlebarsApplicationMixin(ApplicationV
if (!config.roll.fate) return;
+ let returnMessage = game.i18n.localize('DAGGERHEART.UI.Chat.deathMove.avoidScar');
if (config.roll.fate.value <= this.actor.system.levelData.level.current) {
- // apply scarring - for now directly apply - later add a button.
const newScarAmount = this.actor.system.scars + 1;
-
await this.actor.update({
system: {
scars: newScarAmount
@@ -65,13 +64,15 @@ export default class DhDeathMove extends HandlebarsApplicationMixin(ApplicationV
});
if (newScarAmount >= this.actor.system.resources.hope.max) {
+ await this.actor.setDeathMoveDefeated(CONFIG.DH.GENERAL.defeatedConditionChoices.dead.id);
return game.i18n.format('DAGGERHEART.UI.Chat.deathMove.journeysEnd', { scars: newScarAmount });
}
- return game.i18n.localize('DAGGERHEART.UI.Chat.deathMove.gainScar');
+ returnMessage = game.i18n.localize('DAGGERHEART.UI.Chat.deathMove.gainScar');
}
- return game.i18n.localize('DAGGERHEART.UI.Chat.deathMove.avoidScar');
+ await this.actor.setDeathMoveDefeated(CONFIG.DH.GENERAL.defeatedConditionChoices.unconscious.id);
+ return returnMessage;
}
async handleRiskItAll() {
@@ -118,6 +119,7 @@ export default class DhDeathMove extends HandlebarsApplicationMixin(ApplicationV
}
if (config.roll.result.duality == -1) {
+ await this.actor.setDeathMoveDefeated(CONFIG.DH.GENERAL.defeatedConditionChoices.dead.id);
chatMessage = game.i18n.localize('DAGGERHEART.UI.Chat.deathMove.riskItAllFailure');
}
@@ -141,6 +143,7 @@ export default class DhDeathMove extends HandlebarsApplicationMixin(ApplicationV
}
]);
+ await this.actor.setDeathMoveDefeated(CONFIG.DH.GENERAL.defeatedConditionChoices.dead.id);
return game.i18n.localize('DAGGERHEART.UI.Chat.deathMove.blazeOfGlory');
}
diff --git a/module/config/generalConfig.mjs b/module/config/generalConfig.mjs
index 37894644..be1dfce1 100644
--- a/module/config/generalConfig.mjs
+++ b/module/config/generalConfig.mjs
@@ -171,7 +171,7 @@ export const defeatedConditions = () => {
acc[key] = {
...choice,
img: defeated[`${choice.id}Icon`],
- description: `DAGGERHEART.CONFIG.Condition.${choice.id}.description`
+ description: game.i18n.localize(`DAGGERHEART.CONFIG.Condition.${choice.id}.description`)
};
return acc;
@@ -179,6 +179,10 @@ export const defeatedConditions = () => {
};
export const defeatedConditionChoices = {
+ deathMove: {
+ id: 'deathMove',
+ name: 'DAGGERHEART.CONFIG.Condition.deathMove.name'
+ },
defeated: {
id: 'defeated',
name: 'DAGGERHEART.CONFIG.Condition.defeated.name'
diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs
index a7f99ca8..12396384 100644
--- a/module/data/actor/character.mjs
+++ b/module/data/actor/character.mjs
@@ -549,7 +549,18 @@ export default class DhCharacter extends BaseDataActor {
}
get deathMoveViable() {
- return this.resources.hitPoints.max > 0 && this.resources.hitPoints.value >= this.resources.hitPoints.max;
+ const { characterDefault } = game.settings.get(
+ CONFIG.DH.id,
+ CONFIG.DH.SETTINGS.gameSettings.Automation
+ ).defeated;
+ const deathMoveOutcomeStatuses = Object.keys(CONFIG.DH.GENERAL.defeatedConditionChoices).filter(
+ key => key !== characterDefault
+ );
+ const deathMoveNotResolved = this.parent.statuses.every(status => !deathMoveOutcomeStatuses.includes(status));
+
+ const allHitPointsMarked =
+ this.resources.hitPoints.max > 0 && this.resources.hitPoints.value >= this.resources.hitPoints.max;
+ return deathMoveNotResolved && allHitPointsMarked;
}
get armorApplicableDamageTypes() {
diff --git a/module/data/settings/Automation.mjs b/module/data/settings/Automation.mjs
index bff0bae9..436f0eb7 100644
--- a/module/data/settings/Automation.mjs
+++ b/module/data/settings/Automation.mjs
@@ -58,7 +58,7 @@ export default class DhAutomation extends foundry.abstract.DataModel {
defeated: new fields.SchemaField({
enabled: new fields.BooleanField({
required: true,
- initial: false,
+ initial: true,
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.defeated.enabled.label'
}),
overlay: new fields.BooleanField({
@@ -69,7 +69,7 @@ export default class DhAutomation extends foundry.abstract.DataModel {
characterDefault: new fields.StringField({
required: true,
choices: CONFIG.DH.GENERAL.defeatedConditionChoices,
- initial: CONFIG.DH.GENERAL.defeatedConditionChoices.unconscious.id,
+ initial: CONFIG.DH.GENERAL.defeatedConditionChoices.deathMove.id,
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.defeated.characterDefault.label'
}),
adversaryDefault: new fields.StringField({
@@ -84,23 +84,29 @@ export default class DhAutomation extends foundry.abstract.DataModel {
initial: CONFIG.DH.GENERAL.defeatedConditionChoices.defeated.id,
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.defeated.companionDefault.label'
}),
+ deathMoveIcon: new fields.FilePathField({
+ initial: 'icons/magic/life/heart-cross-purple-orange.webp',
+ categories: ['IMAGE'],
+ base64: false,
+ label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.defeated.deathMove.label'
+ }),
deadIcon: new fields.FilePathField({
initial: 'icons/magic/death/grave-tombstone-glow-teal.webp',
categories: ['IMAGE'],
base64: false,
- label: 'Dead'
+ label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.defeated.dead.label'
}),
defeatedIcon: new fields.FilePathField({
initial: 'icons/magic/control/fear-fright-mask-orange.webp',
categories: ['IMAGE'],
base64: false,
- label: 'Defeated'
+ label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.defeated.defeated.label'
}),
unconsciousIcon: new fields.FilePathField({
initial: 'icons/magic/control/sleep-bubble-purple.webp',
categories: ['IMAGE'],
base64: false,
- label: 'Unconcious'
+ label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.defeated.unconscious.label'
})
}),
roll: new fields.SchemaField({
diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs
index cec1a24d..d76d7447 100644
--- a/module/documents/actor.mjs
+++ b/module/documents/actor.mjs
@@ -849,8 +849,8 @@ export default class DhpActor extends Actor {
async toggleDefeated(defeatedState) {
const settings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).defeated;
- const { unconscious, defeated, dead } = CONFIG.DH.GENERAL.conditions();
- const defeatedConditions = new Set([unconscious.id, defeated.id, dead.id]);
+ const { deathMove, unconscious, defeated, dead } = CONFIG.DH.GENERAL.conditions();
+ const defeatedConditions = new Set([deathMove.id, unconscious.id, defeated.id, dead.id]);
if (!defeatedState) {
for (let defeatedId of defeatedConditions) {
await this.toggleStatusEffect(defeatedId, { overlay: settings.overlay, active: defeatedState });
@@ -864,6 +864,18 @@ export default class DhpActor extends Actor {
}
}
+ async setDeathMoveDefeated(defeatedIconId) {
+ const settings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).defeated;
+ const actorDefault = settings[`${this.type}Default`];
+ if (!settings.enabled || !settings.enabled || !actorDefault || actorDefault === defeatedIconId) return;
+
+ for (let defeatedId of Object.keys(CONFIG.DH.GENERAL.defeatedConditionChoices)) {
+ await this.toggleStatusEffect(defeatedId, { overlay: settings.overlay, active: false });
+ }
+
+ if (defeatedIconId) await this.toggleStatusEffect(defeatedIconId, { overlay: settings.overlay, active: true });
+ }
+
queueScrollText(scrollingTextData) {
this.#scrollTextQueue.push(...scrollingTextData.map(data => () => createScrollText(this, data)));
if (!this.#scrollTextInterval) {
diff --git a/templates/settings/automation-settings/rules.hbs b/templates/settings/automation-settings/rules.hbs
index a12c2999..24f0b262 100644
--- a/templates/settings/automation-settings/rules.hbs
+++ b/templates/settings/automation-settings/rules.hbs
@@ -9,11 +9,12 @@
{{formGroup settingFields.schema.fields.defeated.fields.enabled value=settingFields._source.defeated.enabled localize=true}}
- {{formGroup settingFields.schema.fields.defeated.fields.overlay value=settingFields._source.defeated.overlay localize=true}}
+ {{formGroup settingFields.schema.fields.defeated.fields.overlay value=settingFields._source.defeated.overlay localize=true}}
{{formGroup settingFields.schema.fields.defeated.fields.characterDefault value=settingFields._source.defeated.characterDefault labelAttr="name" localize=true}}
{{formGroup settingFields.schema.fields.defeated.fields.adversaryDefault value=settingFields._source.defeated.adversaryDefault labelAttr="name" localize=true}}
{{formGroup settingFields.schema.fields.defeated.fields.companionDefault value=settingFields._source.defeated.companionDefault labelAttr="name" localize=true}}
+ {{formGroup settingFields.schema.fields.defeated.fields.deathMoveIcon value=settingFields._source.defeated.deathMoveIcon localize=true}}
{{formGroup settingFields.schema.fields.defeated.fields.deadIcon value=settingFields._source.defeated.deadIcon localize=true}}
{{formGroup settingFields.schema.fields.defeated.fields.defeatedIcon value=settingFields._source.defeated.defeatedIcon localize=true}}
{{formGroup settingFields.schema.fields.defeated.fields.unconsciousIcon value=settingFields._source.defeated.unconsciousIcon localize=true}}
From 3725fc29ef8c8d23f364c03d6d070a4e13415614 Mon Sep 17 00:00:00 2001
From: WBHarry <89362246+WBHarry@users.noreply.github.com>
Date: Wed, 21 Jan 2026 02:52:07 +0100
Subject: [PATCH 05/88] [Feature] Seaborne Improvement (#1553)
* Added a max to KnowTheTide. Added a onFear trigger to increase the resource
* .
* Added a notification message when KnowTheTide gains a token
---
lang/en.json | 3 +-
module/data/registeredTriggers.mjs | 1 +
...eature_Know_the_Tide_07x6Qe6qMzDw2xN4.json | 42 +++++++++++++++++--
3 files changed, 41 insertions(+), 5 deletions(-)
diff --git a/lang/en.json b/lang/en.json
index dda78410..37cd627e 100755
--- a/lang/en.json
+++ b/lang/en.json
@@ -2876,7 +2876,8 @@
"documentIsMissing": "The {documentType} is missing from the world.",
"tokenActorMissing": "{name} is missing an Actor",
"tokenActorsMissing": "[{names}] missing Actors",
- "domainTouchRequirement": "This domain card requires {nr} {domain} cards in the loadout to be used"
+ "domainTouchRequirement": "This domain card requires {nr} {domain} cards in the loadout to be used",
+ "knowTheTide": "Know The Tide gained a token"
},
"Sidebar": {
"actorDirectory": {
diff --git a/module/data/registeredTriggers.mjs b/module/data/registeredTriggers.mjs
index 8a100585..3fd9f82c 100644
--- a/module/data/registeredTriggers.mjs
+++ b/module/data/registeredTriggers.mjs
@@ -20,6 +20,7 @@ export default class RegisteredTriggers extends Map {
}
registerItemTriggers(item, registerOverride) {
+ if (!item.actor || !item._stats.createdTime) return;
for (const action of item.system.actions ?? []) {
if (!action.actor) continue;
diff --git a/src/packs/communities/feature_Know_the_Tide_07x6Qe6qMzDw2xN4.json b/src/packs/communities/feature_Know_the_Tide_07x6Qe6qMzDw2xN4.json
index 069fe6ba..41f11a74 100644
--- a/src/packs/communities/feature_Know_the_Tide_07x6Qe6qMzDw2xN4.json
+++ b/src/packs/communities/feature_Know_the_Tide_07x6Qe6qMzDw2xN4.json
@@ -9,13 +9,47 @@
"resource": {
"type": "simple",
"value": 0,
- "max": "",
- "icon": "",
- "recovery": null,
+ "max": "@system.levelData.level.current",
+ "icon": "fa-solid fa-water",
+ "recovery": "session",
"diceStates": {},
"dieFaces": "d4"
},
- "actions": {},
+ "actions": {
+ "tFlus34KotJjHfTe": {
+ "type": "effect",
+ "_id": "tFlus34KotJjHfTe",
+ "systemPath": "actions",
+ "baseAction": false,
+ "description": "",
+ "chatDisplay": true,
+ "originItem": {
+ "type": "itemCollection"
+ },
+ "actionType": "action",
+ "triggers": [
+ {
+ "trigger": "fearRoll",
+ "triggeringActorType": "self",
+ "command": "const { max, value } = this.item.system.resource;\nconst maxValue = actor.system.levelData.level.current;\nconst afterUpdate = value+1;\nif (afterUpdate > maxValue) return;\n\nui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.knowTheTide'));\nreturn { updates: [{\n key: 'resource',\n itemId: this.item.id,\n target: this.item,\n value: 1,\n}]};"
+ }
+ ],
+ "cost": [],
+ "uses": {
+ "value": null,
+ "max": "",
+ "recovery": null,
+ "consumeOnSuccess": false
+ },
+ "effects": [],
+ "target": {
+ "type": "any",
+ "amount": null
+ },
+ "name": "Know The Tide",
+ "range": ""
+ }
+ },
"originItemType": null,
"subType": null,
"originId": null,
From 2aba7cf9213e41e970c31e4b3c0305c3217b0d17 Mon Sep 17 00:00:00 2001
From: WBHarry <89362246+WBHarry@users.noreply.github.com>
Date: Wed, 21 Jan 2026 02:56:47 +0100
Subject: [PATCH 06/88] Changed to use a dialog to choose which parts are kept
when reseting (#1557)
---
lang/en.json | 7 ++
module/applications/dialogs/_module.mjs | 1 +
.../dialogs/characterResetDialog.mjs | 105 ++++++++++++++++++
.../applications/sheets/actors/character.mjs | 26 +----
styles/less/dialog/character-reset/sheet.less | 27 +++++
styles/less/dialog/index.less | 2 +
templates/dialogs/characterReset.hbs | 33 ++++++
7 files changed, 179 insertions(+), 22 deletions(-)
create mode 100644 module/applications/dialogs/characterResetDialog.mjs
create mode 100644 styles/less/dialog/character-reset/sheet.less
create mode 100644 templates/dialogs/characterReset.hbs
diff --git a/lang/en.json b/lang/en.json
index 37cd627e..720a08c5 100755
--- a/lang/en.json
+++ b/lang/en.json
@@ -330,6 +330,12 @@
"title": "{actor} - Character Setup",
"traitIncreases": "Trait Increases"
},
+ "CharacterReset": {
+ "title": "Reset Character",
+ "alwaysDeleteSection": "Deleted Data",
+ "optionalDeleteSection": "Optional Data",
+ "headerTitle": "Select which data you'd like to keep"
+ },
"CombatTracker": {
"combatStarted": "Active",
"giveSpotlight": "Give The Spotlight",
@@ -2214,6 +2220,7 @@
"single": "Player",
"plurial": "Players"
},
+ "portrait": "Portrait",
"proficiency": "Proficiency",
"quantity": "Quantity",
"range": "Range",
diff --git a/module/applications/dialogs/_module.mjs b/module/applications/dialogs/_module.mjs
index d43045e6..4eda8579 100644
--- a/module/applications/dialogs/_module.mjs
+++ b/module/applications/dialogs/_module.mjs
@@ -1,5 +1,6 @@
export { default as AttributionDialog } from './attributionDialog.mjs';
export { default as BeastformDialog } from './beastformDialog.mjs';
+export { default as CharacterResetDialog } from './characterResetDialog.mjs';
export { default as d20RollDialog } from './d20RollDialog.mjs';
export { default as DamageDialog } from './damageDialog.mjs';
export { default as DamageReductionDialog } from './damageReductionDialog.mjs';
diff --git a/module/applications/dialogs/characterResetDialog.mjs b/module/applications/dialogs/characterResetDialog.mjs
new file mode 100644
index 00000000..1f3f3d5a
--- /dev/null
+++ b/module/applications/dialogs/characterResetDialog.mjs
@@ -0,0 +1,105 @@
+const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
+
+export default class CharacterResetDialog extends HandlebarsApplicationMixin(ApplicationV2) {
+ constructor(actor, options = {}) {
+ super(options);
+
+ this.actor = actor;
+ this.data = {
+ delete: {
+ class: { keep: false, label: 'TYPES.Item.class' },
+ subclass: { keep: false, label: 'TYPES.Item.subclass' },
+ ancestry: { keep: false, label: 'TYPES.Item.ancestry' },
+ community: { keep: false, label: 'TYPES.Item.community' }
+ },
+ optional: {
+ portrait: { keep: true, label: 'DAGGERHEART.GENERAL.portrait' },
+ name: { keep: true, label: 'Name' },
+ biography: { keep: true, label: 'DAGGERHEART.GENERAL.Tabs.biography' },
+ inventory: { keep: false, label: 'DAGGERHEART.GENERAL.inventory' }
+ }
+ };
+ }
+
+ static DEFAULT_OPTIONS = {
+ tag: 'form',
+ classes: ['daggerheart', 'dialog', 'dh-style', 'views', 'character-reset'],
+ window: {
+ icon: 'fa-solid fa-arrow-rotate-left',
+ title: 'DAGGERHEART.APPLICATIONS.CharacterReset.title'
+ },
+ actions: {
+ finishSelection: this.#finishSelection
+ },
+ form: {
+ handler: this.updateData,
+ submitOnChange: true,
+ submitOnClose: false
+ }
+ };
+
+ /** @override */
+ static PARTS = {
+ resourceDice: {
+ id: 'resourceDice',
+ template: 'systems/daggerheart/templates/dialogs/characterReset.hbs'
+ }
+ };
+
+ async _prepareContext(_options) {
+ const context = await super._prepareContext(_options);
+ context.data = this.data;
+
+ return context;
+ }
+
+ static async updateData(event, _, formData) {
+ const { data } = foundry.utils.expandObject(formData.object);
+
+ this.data = foundry.utils.mergeObject(this.data, data);
+ this.render();
+ }
+
+ static getUpdateData() {
+ const update = {};
+ if (!this.data.optional.portrait) update.if(!this.data.optional.biography);
+
+ if (!this.data.optional.inventory) return update;
+ }
+
+ static async #finishSelection() {
+ const update = {};
+ if (!this.data.optional.name.keep) {
+ const defaultName = game.system.api.documents.DhpActor.defaultName({ type: 'character' });
+ foundry.utils.setProperty(update, 'name', defaultName);
+ foundry.utils.setProperty(update, 'prototypeToken.name', defaultName);
+ }
+
+ if (!this.data.optional.portrait.keep) {
+ foundry.utils.setProperty(update, 'img', this.actor.schema.fields.img.initial(this.actor));
+ foundry.utils.setProperty(update, 'prototypeToken.==texture', {});
+ foundry.utils.setProperty(update, 'prototypeToken.==ring', {});
+ }
+
+ if (this.data.optional.biography.keep)
+ foundry.utils.setProperty(update, 'system.biography', this.actor.system.biography);
+
+ if (this.data.optional.inventory.keep) foundry.utils.setProperty(update, 'system.gold', this.actor.system.gold);
+
+ const { system, ...rest } = update;
+ await this.actor.update({
+ ...rest,
+ '==system': system ?? {}
+ });
+
+ const inventoryItemTypes = ['weapon', 'armor', 'consumable', 'loot'];
+ await this.actor.deleteEmbeddedDocuments(
+ 'Item',
+ this.actor.items
+ .filter(x => !inventoryItemTypes.includes(x.type) || !this.data.optional.inventory.keep)
+ .map(x => x.id)
+ );
+
+ this.close();
+ }
+}
diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs
index 5c6bac3a..4ecaeb06 100644
--- a/module/applications/sheets/actors/character.mjs
+++ b/module/applications/sheets/actors/character.mjs
@@ -669,26 +669,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
* 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)
- );
+ new game.system.api.applications.dialogs.CharacterResetDialog(this.document).render({ force: true });
}
/**
@@ -753,8 +734,9 @@ export default class CharacterSheet extends DHBaseActorSheet {
if (!result) return;
/* This could be avoided by baking config.costs into config.resourceUpdates. Didn't feel like messing with it at the time */
- const costResources = result.costs?.filter(x => x.enabled)
- .map(cost => ({ ...cost, value: -cost.value, total: -cost.total })) || {};
+ const costResources =
+ result.costs?.filter(x => x.enabled).map(cost => ({ ...cost, value: -cost.value, total: -cost.total })) ||
+ {};
config.resourceUpdates.addResources(costResources);
await config.resourceUpdates.updateResources();
}
diff --git a/styles/less/dialog/character-reset/sheet.less b/styles/less/dialog/character-reset/sheet.less
new file mode 100644
index 00000000..44312a3e
--- /dev/null
+++ b/styles/less/dialog/character-reset/sheet.less
@@ -0,0 +1,27 @@
+.daggerheart.dh-style.dialog.views.character-reset {
+ .character-reset-container {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+
+ legend {
+ padding: 0 4px;
+ }
+
+ .character-reset-header {
+ font-size: var(--font-size-18);
+ text-align: center;
+ }
+
+ .reset-data-container {
+ display: grid;
+ grid-template-columns: 3fr 2fr;
+ align-items: center;
+ gap: 4px;
+
+ label {
+ font-weight: bold;
+ }
+ }
+ }
+}
diff --git a/styles/less/dialog/index.less b/styles/less/dialog/index.less
index 01a3f954..733cdd1c 100644
--- a/styles/less/dialog/index.less
+++ b/styles/less/dialog/index.less
@@ -41,3 +41,5 @@
@import './settings/change-currency-icon.less';
@import './risk-it-all/sheet.less';
+
+@import './character-reset/sheet.less';
diff --git a/templates/dialogs/characterReset.hbs b/templates/dialogs/characterReset.hbs
new file mode 100644
index 00000000..298826e5
--- /dev/null
+++ b/templates/dialogs/characterReset.hbs
@@ -0,0 +1,33 @@
+