From d7f0747e5ca0b05d6c9b71efc3d683192b2808ab Mon Sep 17 00:00:00 2001 From: WBHarry Date: Fri, 17 Jul 2026 16:28:37 +0200 Subject: [PATCH 1/7] Changed so all default rolls become BaseRolls to access convenience functions and getters --- daggerheart.mjs | 2 +- module/dice/baseRoll.mjs | 4 ++++ templates/dialogs/tagTeamDialog/parts/tagTeamDamageParts.hbs | 2 +- templates/ui/chat/parts/damage-part.hbs | 3 ++- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/daggerheart.mjs b/daggerheart.mjs index 31c9f63d..4641c34a 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -24,7 +24,7 @@ import TokenManager from './module/documents/tokenManager.mjs'; CONFIG.DH = SYSTEM; CONFIG.TextEditor.enrichers.push(...enricherConfig); -CONFIG.Dice.rolls = [BaseRoll, DHRoll, DualityRoll, D20Roll, DamageRoll, FateRoll]; +CONFIG.Dice.rolls = [Roll = BaseRoll, DHRoll, DualityRoll, D20Roll, DamageRoll, FateRoll]; CONFIG.Dice.daggerheart = { DHRoll: DHRoll, DualityRoll: DualityRoll, diff --git a/module/dice/baseRoll.mjs b/module/dice/baseRoll.mjs index 4d065fff..2c42d38a 100644 --- a/module/dice/baseRoll.mjs +++ b/module/dice/baseRoll.mjs @@ -4,4 +4,8 @@ export default class BaseRoll extends Roll { /** @inheritdoc */ static TOOLTIP_TEMPLATE = 'systems/daggerheart/templates/ui/chat/foundryRollTooltip.hbs'; + + get modifierTotal() { + return this.total - this.dice.reduce((acc, dice) => acc + dice.total, 0); + } } diff --git a/templates/dialogs/tagTeamDialog/parts/tagTeamDamageParts.hbs b/templates/dialogs/tagTeamDialog/parts/tagTeamDamageParts.hbs index 5f37c6d1..1d516cd8 100644 --- a/templates/dialogs/tagTeamDialog/parts/tagTeamDamageParts.hbs +++ b/templates/dialogs/tagTeamDialog/parts/tagTeamDamageParts.hbs @@ -2,7 +2,7 @@
{{localize (concat "DAGGERHEART.CONFIG.HealingType." key ".name")}}: - {{damage.roll.total}} + {{roll.total}}
diff --git a/templates/ui/chat/parts/damage-part.hbs b/templates/ui/chat/parts/damage-part.hbs index 43fc3a6f..d4d1c9fe 100644 --- a/templates/ui/chat/parts/damage-part.hbs +++ b/templates/ui/chat/parts/damage-part.hbs @@ -39,7 +39,7 @@ {{#if active}}
{{#if hasRerolls}}{{/if}} @@ -49,6 +49,7 @@ {{/if}} {{/each}} {{/each}} + {{#if roll.modifierTotal}}
{{roll.modifierTotal}}
From e087b379fcfc6eba0e0d6d46c0abe75e369446de Mon Sep 17 00:00:00 2001 From: WBHarry Date: Fri, 17 Jul 2026 16:44:33 +0200 Subject: [PATCH 2/7] . --- .../tagTeamDialog/parts/tagTeamDamageParts.hbs | 12 ++++++++---- templates/dialogs/tagTeamDialog/result.hbs | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/templates/dialogs/tagTeamDialog/parts/tagTeamDamageParts.hbs b/templates/dialogs/tagTeamDialog/parts/tagTeamDamageParts.hbs index 1d516cd8..4145b706 100644 --- a/templates/dialogs/tagTeamDialog/parts/tagTeamDamageParts.hbs +++ b/templates/dialogs/tagTeamDialog/parts/tagTeamDamageParts.hbs @@ -7,10 +7,14 @@
{{#each roll.dice as |dice index|}} - - {{dice.total}} - - + {{#each results}} + {{#if active}} + + {{result}} + + + {{/if}} + {{/each}} {{#unless @last}} + {{/unless}} diff --git a/templates/dialogs/tagTeamDialog/result.hbs b/templates/dialogs/tagTeamDialog/result.hbs index ccce9504..88f579a6 100644 --- a/templates/dialogs/tagTeamDialog/result.hbs +++ b/templates/dialogs/tagTeamDialog/result.hbs @@ -21,7 +21,7 @@ {{#each joinedRoll.damageRollData.types as |damage key|}}
{{localize (concat "DAGGERHEART.CONFIG.HealingType." key ".name")}}
-
{{damage.roll.total}}
+
{{damage.total}}
{{/each}}
From ab1772264a8b6f0dcce8a9506d5621d917aa5327 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Fri, 17 Jul 2026 17:41:32 +0200 Subject: [PATCH 3/7] Fixed so that party.tagTeam now uses a declared damageRollData(ChatDamageData) field. rollData.options.damage is retired. --- module/applications/dialogs/tagTeamDialog.mjs | 56 ++++--------------- module/data/actor/party.mjs | 9 --- module/data/tagTeamData.mjs | 3 + module/dice/damageRoll.mjs | 2 +- .../parts/tagTeamDamageParts.hbs | 12 ++-- .../dialogs/tagTeamDialog/tagTeamMember.hbs | 11 ++-- 6 files changed, 25 insertions(+), 68 deletions(-) diff --git a/module/applications/dialogs/tagTeamDialog.mjs b/module/applications/dialogs/tagTeamDialog.mjs index 2b865304..417dd511 100644 --- a/module/applications/dialogs/tagTeamDialog.mjs +++ b/module/applications/dialogs/tagTeamDialog.mjs @@ -141,7 +141,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio const hasRolled = Boolean(data.rollData); if (!hasRolled) return false; - return !data.rollData.options.hasDamage || Boolean(data.rollData.options.damage); + return !data.rollData.options.hasDamage || data.damageRollData.active; }); return context; @@ -379,7 +379,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio let rollIsSelected = false; for (const member of Object.values(members)) { const rollFinished = Boolean(member.rollData); - const damageFinished = member.rollData?.options?.hasDamage ? Boolean(member.rollData.options.damage) : true; + const damageFinished = member.rollData?.options?.hasDamage ? member.damageRollData.active : true; rollsAreFinished = rollsAreFinished && rollFinished && damageFinished; rollIsSelected = rollIsSelected || member.selected; @@ -540,17 +540,10 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio await action.workflow.get('damage').execute(config, null, true); if (!config.damage) return; - - const current = this.party.system.tagTeam.members[memberKey].rollData; + await this.updatePartyData( { - [`system.tagTeam.members.${memberKey}.rollData`]: { - ...current, - options: { - ...current.options, - damage: config.damage - } - } + [`system.tagTeam.members.${memberKey}.damageRollData`]: config.damage }, this.getUpdatingParts(button) ); @@ -558,52 +551,25 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio static async #removeDamageRoll(_, button) { const { memberKey } = button.dataset; - const current = this.party.system.tagTeam.members[memberKey].rollData; this.updatePartyData( { - [`system.tagTeam.members.${memberKey}.rollData`]: { - ...current, - options: { - ...current.options, - damage: null - } - } + [`system.tagTeam.members.${memberKey}.damageRollData.types`]: + _replace({}) }, this.getUpdatingParts(button) ); } static async #rerollDamageDice(_, button) { - const { memberKey, damageKey, part, dice } = button.dataset; + const { memberKey, damageKey, diceIndex, resultIndex } = button.dataset; const memberData = this.party.system.tagTeam.members[memberKey]; - const partData = memberData.rollData.options.damage[damageKey].parts[part]; - const activeDiceResultKey = Object.keys(partData.dice[dice].results).find( - index => partData.dice[dice].results[index].active - ); - const { parsedRoll, rerolledDice } = await game.system.api.dice.DamageRoll.reroll( - partData, - dice, - activeDiceResultKey - ); - - const rollData = this.party.system.tagTeam.members[memberKey].rollData; - rollData.options.damage[damageKey].parts = rollData.options.damage[damageKey].parts.map((damagePart, index) => { - if (index !== Number.parseInt(part)) return damagePart; - - return { - ...damagePart, - total: parsedRoll.total, - dice: rerolledDice - }; - }); - rollData.options.damage[damageKey].total = rollData.options.damage[damageKey].parts.reduce((acc, part) => { - acc += part.total; - return acc; - }, 0); + await memberData.damageRollData.rerollDamageDie(damageKey, diceIndex, resultIndex); this.updatePartyData( { - [`system.tagTeam.members.${memberKey}.rollData`]: rollData + [`system.tagTeam.members.${memberKey}.damageRollData.types`]: { + [damageKey]: memberData.damageRollData.types[damageKey].toJSON() + } }, this.getUpdatingParts(button) ); diff --git a/module/data/actor/party.mjs b/module/data/actor/party.mjs index b735c65a..5b9cccab 100644 --- a/module/data/actor/party.mjs +++ b/module/data/actor/party.mjs @@ -3,7 +3,6 @@ import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayFie import TagTeamData from '../tagTeamData.mjs'; import GroupRollData from '../groupRollData.mjs'; import { GoldField } from '../fields/actorField.mjs'; -import { ChatDamageData } from '../chat-message/chatDamageData.mjs'; export default class DhParty extends BaseDataActor { /** @inheritdoc */ @@ -49,14 +48,6 @@ export default class DhParty extends BaseDataActor { } } - prepareDerivedData() { - for (const memberKey in this.tagTeam.members) { - const member = this.tagTeam.members[memberKey]; - member.damageRollData = member.rollData?.options.damage ? - ChatDamageData.fromJSON(JSON.stringify(member.rollData.options.damage)) : null; - } - } - _onCreate(data, options, userId) { super._onCreate(data, options, userId); diff --git a/module/data/tagTeamData.mjs b/module/data/tagTeamData.mjs index 640c2f6c..ef6536f1 100644 --- a/module/data/tagTeamData.mjs +++ b/module/data/tagTeamData.mjs @@ -1,3 +1,5 @@ +import { ChatDamageData } from './chat-message/chatDamageData.mjs'; + export default class TagTeamData extends foundry.abstract.DataModel { static defineSchema() { const fields = foundry.data.fields; @@ -37,6 +39,7 @@ export class MemberData extends foundry.abstract.DataModel { }), rollChoice: new fields.StringField({ nullable: true, initial: null }), rollData: new fields.JSONField({ nullable: true, initial: null }), + damageRollData: new fields.EmbeddedDataField(ChatDamageData), selected: new fields.BooleanField({ initial: false }) }; } diff --git a/module/dice/damageRoll.mjs b/module/dice/damageRoll.mjs index 56712337..a191a8b4 100644 --- a/module/dice/damageRoll.mjs +++ b/module/dice/damageRoll.mjs @@ -37,7 +37,7 @@ export default class DamageRoll extends DHRoll { if (game.modules.get('dice-so-nice')?.active) { config.mute = true; const pool = foundry.dice.terms.PoolTerm.fromRolls( - Object.values(config.damage.types).map(x => x.roll) + Object.values(config.damage.types) ); diceRolls.push(Roll.fromTerms([pool])); } diff --git a/templates/dialogs/tagTeamDialog/parts/tagTeamDamageParts.hbs b/templates/dialogs/tagTeamDialog/parts/tagTeamDamageParts.hbs index 4145b706..3f7f457b 100644 --- a/templates/dialogs/tagTeamDialog/parts/tagTeamDamageParts.hbs +++ b/templates/dialogs/tagTeamDialog/parts/tagTeamDamageParts.hbs @@ -6,12 +6,12 @@
- {{#each roll.dice as |dice index|}} - {{#each results}} - {{#if active}} - - {{result}} - + {{#each roll.dice}} + {{#each results as |result index|}} + {{#if result.active}} + + {{result.result}} + {{/if}} {{/each}} diff --git a/templates/dialogs/tagTeamDialog/tagTeamMember.hbs b/templates/dialogs/tagTeamDialog/tagTeamMember.hbs index 7510422f..b608c8f0 100644 --- a/templates/dialogs/tagTeamDialog/tagTeamMember.hbs +++ b/templates/dialogs/tagTeamDialog/tagTeamMember.hbs @@ -100,15 +100,12 @@ - - {{#if damage}} - - - - {{/if}} + + +
- {{#if damage}} + {{#if damage.active}} {{#if useCritDamage}} {{> "systems/daggerheart/templates/dialogs/tagTeamDialog/parts/tagTeamDamageParts.hbs" damage=critDamage isCritical=true }} {{else}} From 0fe2865e79e3acc429ac0cc5d9ff04a1c58084b3 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Fri, 17 Jul 2026 18:10:08 +0200 Subject: [PATCH 4/7] Critical damage fixes --- module/applications/dialogs/tagTeamDialog.mjs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/module/applications/dialogs/tagTeamDialog.mjs b/module/applications/dialogs/tagTeamDialog.mjs index 417dd511..5a88264d 100644 --- a/module/applications/dialogs/tagTeamDialog.mjs +++ b/module/applications/dialogs/tagTeamDialog.mjs @@ -182,7 +182,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio const selectedRoll = Object.values(this.party.system.tagTeam.members).find(member => member.selected); const critSelected = !selectedRoll ? undefined - : (selectedRoll?.rollData?.options?.roll?.isCritical ?? false); + : (selectedRoll?.roll?.isCritical ?? false); partContext.hintText = await this.getInfoTexts(this.party.system.tagTeam.members); partContext.joinedRoll = await this.getJoinedRoll({ @@ -236,7 +236,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio } const selectedRoll = Object.values(this.party.system.tagTeam.members).find(member => member.selected); - const critSelected = !selectedRoll ? undefined : (selectedRoll?.rollData?.options?.roll?.isCritical ?? false); + const critSelected = !selectedRoll ? undefined : (selectedRoll?.roll?.isCritical ?? false); return { ...data, @@ -249,7 +249,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio damageRollOptions, damage: data.damageRollData, critDamage: await this.getCriticalDamage(data.damageRollData), - useCritDamage: critSelected || (critSelected === undefined && data.rollData?.options?.roll?.isCritical) + useCritDamage: critSelected || (critSelected === undefined && data.roll?.isCritical) }; } @@ -629,9 +629,8 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio secondaryRoll.damageRollData = baseSecondaryRoll.damageRollData ? ChatDamageData.fromJSON(JSON.stringify(baseSecondaryRoll.damageRollData)) : null; - const systemData = mainRoll.rollData.options; - const isCritical = overrideIsCritical ?? systemData.roll.isCritical; - if (isCritical) mainRoll.damageRollData = await this.getCriticalDamage(systemData.damageRollData); + const isCritical = overrideIsCritical ?? mainRoll.roll.isCritical; + if (isCritical) mainRoll.damageRollData = await this.getCriticalDamage(mainRoll.damageRollData); if (secondaryRoll.damageRollData) { const secondaryDamage = (displayVersion ? overrideIsCritical : isCritical) From 7afc98ed936ccdf08fb0d7330401fe3c2c6090b6 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Fri, 17 Jul 2026 18:35:25 +0200 Subject: [PATCH 5/7] Corrected TagTeamDialog rerender logic when selecting a roll --- module/applications/dialogs/tagTeamDialog.mjs | 8 +++++++- templates/dialogs/tagTeamDialog/tagTeamMember.hbs | 5 ++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/module/applications/dialogs/tagTeamDialog.mjs b/module/applications/dialogs/tagTeamDialog.mjs index 5a88264d..fc5541b5 100644 --- a/module/applications/dialogs/tagTeamDialog.mjs +++ b/module/applications/dialogs/tagTeamDialog.mjs @@ -595,6 +595,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio static async #selectRoll(_, button) { const { memberKey } = button.dataset; + this.updatePartyData( { [`system.tagTeam.members`]: Object.entries(this.party.system.tagTeam.members).reduce( @@ -605,7 +606,12 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio {} ) }, - this.getUpdatingParts(button) + /* Selecting a roll must update all member sections hbs to display the correct damage information incase of a critical */ + [ + ...Object.keys(this.party.system.tagTeam.members), + this.constructor.PARTS.rollSelection.id, + this.constructor.PARTS.result.id + ] ); } diff --git a/templates/dialogs/tagTeamDialog/tagTeamMember.hbs b/templates/dialogs/tagTeamDialog/tagTeamMember.hbs index b608c8f0..7758979b 100644 --- a/templates/dialogs/tagTeamDialog/tagTeamMember.hbs +++ b/templates/dialogs/tagTeamDialog/tagTeamMember.hbs @@ -64,7 +64,10 @@ {{#if roll}}
-
{{roll.total}} {{localize "DAGGERHEART.GENERAL.withThing" thing=roll.totalLabel}}
+
+ {{roll.total}} + {{#if roll.isCritical}}{{roll.totalLabel}}{{else}}{{localize "DAGGERHEART.GENERAL.withThing" thing=roll.totalLabel}}{{/if}} +
{{roll.dHope.total}} From e6dcb0f9a5068e2d970fed048fd3080342053bc2 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Fri, 17 Jul 2026 22:23:53 +0200 Subject: [PATCH 6/7] Fixed so that DamageTypes are retained and joined together throughout TagTeamDialog --- module/applications/dialogs/tagTeamDialog.mjs | 11 +++++++++++ module/dice/damageRoll.mjs | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/module/applications/dialogs/tagTeamDialog.mjs b/module/applications/dialogs/tagTeamDialog.mjs index fc5541b5..c54720f4 100644 --- a/module/applications/dialogs/tagTeamDialog.mjs +++ b/module/applications/dialogs/tagTeamDialog.mjs @@ -588,6 +588,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio new foundry.dice.terms.OperatorTerm({ operator: '+' }), criticalTerm ]); + newDamage.types[key].options = foundry.utils.deepClone(origDamage.types[key].options); } return newDamage; @@ -650,6 +651,16 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio new foundry.dice.terms.OperatorTerm({ operator: '+' }), ...baseSecondaryRoll.damageRollData.types[key].terms ]); + + /* Joining the roll.options of both rolls */ + const joinedDamageTypes = new Set([ + ...baseMainRoll.damageRollData.types[key].options.damageTypes, + ...baseSecondaryRoll.damageRollData.types[key].options.damageTypes + ]); + mainRoll.damageRollData.types[key].options = { + ...baseMainRoll.damageRollData.types[key].options, + damageTypes: [...joinedDamageTypes] + }; } else { mainRoll.damageRollData.types[key] = damage; } diff --git a/module/dice/damageRoll.mjs b/module/dice/damageRoll.mjs index a191a8b4..8667d89f 100644 --- a/module/dice/damageRoll.mjs +++ b/module/dice/damageRoll.mjs @@ -19,7 +19,7 @@ export default class DamageRoll extends DHRoll { for (const roll of config.roll) { await roll.roll.evaluate(); - roll.roll.options = { damageTypes: roll.damageTypes ?? [] }; + roll.roll.options = { damageTypes: roll.damageTypes ? [...roll.damageTypes] : [] }; if (!config.damage?.types) config.damage = { types: {} }; config.damage.types[roll.applyTo] = roll.roll; From 3828337472246f630c517e947f2bc0a608c63eee Mon Sep 17 00:00:00 2001 From: WBHarry Date: Fri, 17 Jul 2026 22:46:32 +0200 Subject: [PATCH 7/7] Removed some remaining types.<>.roll references --- module/data/fields/action/damageField.mjs | 6 +++--- module/documents/actor.mjs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/module/data/fields/action/damageField.mjs b/module/data/fields/action/damageField.mjs index 6efa1f51..c4295e0d 100644 --- a/module/data/fields/action/damageField.mjs +++ b/module/data/fields/action/damageField.mjs @@ -118,9 +118,9 @@ export default class DamageField extends fields.SchemaField { const hpDamageMultiplier = config.actionActor?.system.rules?.attack?.damage?.hpDamageMultiplier ?? 1; const hpDamageTakenMultiplier = actor.system.rules?.attack?.damage?.hpDamageTakenMultiplier; if (configDamage.hitPoints) { - configDamage.hitPoints.roll = configDamage.hitPoints.roll.toJSON(); - configDamage.hitPoints.roll.total = Math.ceil( - configDamage.hitPoints.roll.total * hpDamageMultiplier * hpDamageTakenMultiplier + configDamage.hitPoints = configDamage.hitPoints.toJSON(); + configDamage.hitPoints.total = Math.ceil( + configDamage.hitPoints.total * hpDamageMultiplier * hpDamageTakenMultiplier ); } diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index b85c13d5..d7733dd5 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -660,12 +660,12 @@ export default class DhpActor extends Actor { Object.entries(damages).forEach(([key, damage]) => { if (key === CONFIG.DH.GENERAL.healingTypes.hitPoints.id) - damage.roll.total = this.calculateDamage(damage.roll.total, damage.damageTypes); + damage.total = this.calculateDamage(damage.total, damage.damageTypes); const update = updates.find(u => u.key === key); if (update) { - update.value += damage.roll.total; + update.value += damage.total; update.damageTypes.add(...new Set(damage.damageTypes)); - } else updates.push({ value: damage.roll.total, key, damageTypes: new Set(damage.damageTypes) }); + } else updates.push({ value: damage.total, key, damageTypes: new Set(damage.damageTypes) }); }); if (Hooks.call(`${CONFIG.DH.id}.postCalculateDamage`, this, damages) === false) return null;