From c0fe127df4d70d5c029969d0c297667de03b3d83 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sun, 19 Jul 2026 17:24:53 +0200 Subject: [PATCH 1/5] Added overrides to modifierTotal and total getters in DamageRoll to addin critical damage when applicable --- module/data/chat-message/chatDamageData.mjs | 7 ++++++- module/data/fields/action/damageField.mjs | 2 +- module/dice/damageRoll.mjs | 7 ++++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/module/data/chat-message/chatDamageData.mjs b/module/data/chat-message/chatDamageData.mjs index 404cda43..231c03f2 100644 --- a/module/data/chat-message/chatDamageData.mjs +++ b/module/data/chat-message/chatDamageData.mjs @@ -7,6 +7,11 @@ export class ChatDamageData extends foundry.abstract.DataModel { this._prepareRolls(); } + get isCritical() { + const roll = Roll.fromJSON(this.parent.parent._source.rolls[0]); + return roll.isCritical; + } + static defineSchema() { const fields = foundry.data.fields; @@ -28,7 +33,7 @@ export class ChatDamageData extends foundry.abstract.DataModel { } _prepareRolls() { - this.main &&= Roll.fromData(this.main); + this.main &&= Roll.fromData({ ...this.main, options: { ...this.main.options, isCritical: this.isCritical } }); for (const key of Object.keys(this.resources)) { this.resources[key] = Roll.fromData(this.resources[key]); } diff --git a/module/data/fields/action/damageField.mjs b/module/data/fields/action/damageField.mjs index 25b3ba97..05d9f664 100644 --- a/module/data/fields/action/damageField.mjs +++ b/module/data/fields/action/damageField.mjs @@ -107,7 +107,7 @@ export default class DamageField extends fields.SchemaField { if (configDamage.main) { const multiplier = config.actionActor?.system.rules?.attack?.damage?.hpDamageMultiplier ?? 1; const takenMultiplier = actor.system.rules?.attack?.damage?.hpDamageTakenMultiplier; - configDamage.main.total = Math.ceil(configDamage.main.total * multiplier * takenMultiplier); + configDamage.main.total = Math.ceil(config.damage.main.total * multiplier * takenMultiplier); } damagePromises.push( diff --git a/module/dice/damageRoll.mjs b/module/dice/damageRoll.mjs index 2ca04a06..3e99865d 100644 --- a/module/dice/damageRoll.mjs +++ b/module/dice/damageRoll.mjs @@ -8,7 +8,7 @@ export default class DamageRoll extends DHRoll { } get isCritical() { - return true; + return this.options.isCritical; } get modifierTotal() { @@ -16,6 +16,11 @@ export default class DamageRoll extends DHRoll { return super.modifierTotal + criticalDamageBonus; } + get total() { + const criticalDamageBonus = this.isCritical ? getCritDamageBonus(this.terms) : 0; + return super.total + criticalDamageBonus; + } + static DefaultDialog = DamageDialog; static createRollInstance(config) { From cfb6bc247968b34799ae36a83cdd6037b8cd1c53 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sun, 19 Jul 2026 18:22:30 +0200 Subject: [PATCH 2/5] Fixed so that the forced critical toggle in the DamageDialog sets damageRoll.options.isCritical --- module/applications/dialogs/damageDialog.mjs | 7 +++++++ module/data/chat-message/chatDamageData.mjs | 11 ++++++++++- module/dice/damageRoll.mjs | 8 +++++--- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/module/applications/dialogs/damageDialog.mjs b/module/applications/dialogs/damageDialog.mjs index 21b2c0ad..46bfde76 100644 --- a/module/applications/dialogs/damageDialog.mjs +++ b/module/applications/dialogs/damageDialog.mjs @@ -121,7 +121,14 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application } static async submitRoll() { + const isCritical = this.config.isCritical; const { damageFormula, resourceFormulas } = this.roll.constructFormulas({ ...this.config, isCritical: false }); + /* Sideeffect occuring in constructFormulas that sets this.config.isCritical to the false value. Can remove the below if it can be prevented */ + this.config.isCritical = isCritical; + damageFormula.roll.options.isCritical = isCritical; + for (const formula of resourceFormulas) + formula.roll.options.isCritical = isCritical; + this.config.damageFormula = damageFormula; this.config.resourceFormulas = resourceFormulas; await this.close({ submitted: true }); diff --git a/module/data/chat-message/chatDamageData.mjs b/module/data/chat-message/chatDamageData.mjs index 231c03f2..f2bb8285 100644 --- a/module/data/chat-message/chatDamageData.mjs +++ b/module/data/chat-message/chatDamageData.mjs @@ -8,6 +8,8 @@ export class ChatDamageData extends foundry.abstract.DataModel { } get isCritical() { + if (!this.parent?.parent) return false; + const roll = Roll.fromJSON(this.parent.parent._source.rolls[0]); return roll.isCritical; } @@ -33,7 +35,14 @@ export class ChatDamageData extends foundry.abstract.DataModel { } _prepareRolls() { - this.main &&= Roll.fromData({ ...this.main, options: { ...this.main.options, isCritical: this.isCritical } }); + this.main &&= Roll.fromData({ + ...this.main, + options: { + ...this.main.options, + isCritical: this.main.options.isCritical || this.isCritical + } + }); + for (const key of Object.keys(this.resources)) { this.resources[key] = Roll.fromData(this.resources[key]); } diff --git a/module/dice/damageRoll.mjs b/module/dice/damageRoll.mjs index 3e99865d..722b05f6 100644 --- a/module/dice/damageRoll.mjs +++ b/module/dice/damageRoll.mjs @@ -33,7 +33,7 @@ export default class DamageRoll extends DHRoll { const evaluateRoll = async roll => { await roll.roll.evaluate(); - roll.roll.options = { damageTypes: roll.damageTypes ? [...roll.damageTypes] : [] }; + roll.roll.options = { ...roll.roll.options, damageTypes: roll.damageTypes ? [...roll.damageTypes] : [] }; return roll.roll; } @@ -41,8 +41,10 @@ export default class DamageRoll extends DHRoll { if (config.damageFormula) { config.damage.main = await evaluateRoll(config.damageFormula); - config.damage.main.options = { damageTypes: - config.damageFormula.damageTypes ? [...config.damageFormula.damageTypes] : [] + config.damage.main.options = { + ...config.damage.main.options, + damageTypes: + config.damageFormula.damageTypes ? [...config.damageFormula.damageTypes] : [] }; } From d33277f2be9189a5f8bb52c1968452dfb572939c Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sun, 19 Jul 2026 20:30:59 +0200 Subject: [PATCH 3/5] Correted chatDamageData isCritical so it works for both TagTeam and normal chatMessage damage --- module/data/chat-message/chatDamageData.mjs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/module/data/chat-message/chatDamageData.mjs b/module/data/chat-message/chatDamageData.mjs index f2bb8285..a31a9ef1 100644 --- a/module/data/chat-message/chatDamageData.mjs +++ b/module/data/chat-message/chatDamageData.mjs @@ -1,4 +1,6 @@ import { triggerChatRollFx } from '../../helpers/utils.mjs'; +import { MemberData } from '../tagTeamData.mjs'; +import DHActorRoll from './actorRoll.mjs'; export class ChatDamageData extends foundry.abstract.DataModel { constructor(data = {}, options = {}) { @@ -8,10 +10,14 @@ export class ChatDamageData extends foundry.abstract.DataModel { } get isCritical() { - if (!this.parent?.parent) return false; + if (this.parent && this.parent instanceof MemberData) { + return this.parent.roll.isCritical; + } + if (this.parent && this.parent instanceof DHActorRoll && this.parent.parent) { + return Roll.fromJSON(this.parent.parent._source.rolls[0]).isCritical; + } - const roll = Roll.fromJSON(this.parent.parent._source.rolls[0]); - return roll.isCritical; + return false; } static defineSchema() { From bbf27e5b4d6b62c8a6abb7a321862c8879b38e6e Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sun, 19 Jul 2026 22:13:37 +0200 Subject: [PATCH 4/5] Fixed TagTeamDialog --- module/applications/dialogs/damageDialog.mjs | 14 +++++--- module/applications/dialogs/tagTeamDialog.mjs | 35 ++++++++++++------- module/data/chat-message/chatDamageData.mjs | 5 +-- templates/dialogs/tagTeamDialog/result.hbs | 19 ++++++---- 4 files changed, 48 insertions(+), 25 deletions(-) diff --git a/module/applications/dialogs/damageDialog.mjs b/module/applications/dialogs/damageDialog.mjs index 46bfde76..0d98ce24 100644 --- a/module/applications/dialogs/damageDialog.mjs +++ b/module/applications/dialogs/damageDialog.mjs @@ -6,6 +6,7 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application this.roll = roll; this.config = config; + this.originalIsCritical = config.isCritical; this.selectedEffects = this.config.bonusEffects; } @@ -121,13 +122,16 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application } static async submitRoll() { - const isCritical = this.config.isCritical; const { damageFormula, resourceFormulas } = this.roll.constructFormulas({ ...this.config, isCritical: false }); /* Sideeffect occuring in constructFormulas that sets this.config.isCritical to the false value. Can remove the below if it can be prevented */ - this.config.isCritical = isCritical; - damageFormula.roll.options.isCritical = isCritical; - for (const formula of resourceFormulas) - formula.roll.options.isCritical = isCritical; + this.config.isCritical = this.originalIsCritical; + + /* If a critical has been forced in the Dialog, save that forced state to the damage roll */ + if (this.config.isCritical && !this.originalIsCritical) { + damageFormula.roll.options.isCritical = true; + for (const formula of resourceFormulas) + formula.roll.options.isCritical = true; + } this.config.damageFormula = damageFormula; this.config.resourceFormulas = resourceFormulas; diff --git a/module/applications/dialogs/tagTeamDialog.mjs b/module/applications/dialogs/tagTeamDialog.mjs index 5779bd00..e03d6658 100644 --- a/module/applications/dialogs/tagTeamDialog.mjs +++ b/module/applications/dialogs/tagTeamDialog.mjs @@ -1,6 +1,7 @@ import { ResourceUpdateMap } from '../../data/action/baseAction.mjs'; import { ChatDamageData } from '../../data/chat-message/chatDamageData.mjs'; import { MemberData } from '../../data/tagTeamData.mjs'; +import DamageRoll from '../../dice/damageRoll.mjs'; import { getCritDamageBonus, shouldUseHopeFearAutomation } from '../../helpers/utils.mjs'; import { emitGMUpdate, GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs'; import PartySheet from '../sheets/actors/party.mjs'; @@ -235,8 +236,12 @@ 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?.roll?.isCritical ?? false); + if (data.damageRollData.main) { + const selectedRoll = Object.values(this.party.system.tagTeam.members).find(member => member.selected); + const critSelected = !selectedRoll ? undefined : (selectedRoll?.roll?.isCritical ?? false); + const useCritDamage = critSelected || (critSelected === undefined && data.roll?.isCritical); + data.damageRollData.main.options.isCritical = useCritDamage; + } return { ...data, @@ -249,7 +254,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio damageRollOptions, damage: data.damageRollData, critDamage: this.getCriticalDamage(data.damageRollData), - useCritDamage: critSelected || (critSelected === undefined && data.roll?.isCritical) + useCritDamage: false }; } @@ -640,17 +645,13 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio secondaryRoll.damageRollData = baseSecondaryRoll.damageRollData ? ChatDamageData.fromJSON(JSON.stringify(baseSecondaryRoll.damageRollData)) : null; - const isCritical = overrideIsCritical ?? mainRoll.roll.isCritical; - if (isCritical) mainRoll.damageRollData = this.getCriticalDamage(mainRoll.damageRollData); - if (secondaryRoll.damageRollData) { - const secondaryDamage = (displayVersion ? overrideIsCritical : isCritical) - ? this.getCriticalDamage(secondaryRoll.damageRollData) - : secondaryRoll.damageRollData; + const secondaryDamage = secondaryRoll.damageRollData; + if (mainRoll.damageRollData) { if (secondaryDamage.main) { if (mainRoll.damageRollData.main) { - mainRoll.damageRollData.main = Roll.fromTerms([ + mainRoll.damageRollData.main = DamageRoll.fromTerms([ ...baseMainRoll.damageRollData.main.terms, new foundry.dice.terms.OperatorTerm({ operator: '+' }), ...baseSecondaryRoll.damageRollData.main.terms @@ -672,7 +673,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio for (const [key, damage] of Object.entries(secondaryDamage.resources ?? {})) { if (key in mainRoll.damageRollData.resources) { - mainRoll.damageRollData.resources[key] = Roll.fromTerms([ + mainRoll.damageRollData.resources[key] = DamageRoll.fromTerms([ ...baseMainRoll.damageRollData.resources[key].terms, new foundry.dice.terms.OperatorTerm({ operator: '+' }), ...baseSecondaryRoll.damageRollData.resources[key].terms @@ -696,6 +697,12 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio } } + if (mainRoll.damageRollData.main) { + const isCritical = overrideIsCritical ?? mainRoll.roll.isCritical; + mainRoll.damageRollData.main.options.isCritical = isCritical; + } + + return mainRoll; } catch (err) { console.error(err); @@ -755,7 +762,11 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio }; if (joinedRoll.damageRollData.main) { - systemData.damage.main = joinedRoll.damageRollData.toJSON(); + systemData.damage.main = joinedRoll.damageRollData.main.toJSON(); + // isCritical is used internally in TagTeamDialog to force-flip damage from normal to critical and vice versa. + // It's deleted here to avoid interupting normal critical damage logic in the chatMessage. + // If someone explicitly set their own damage roll to be a forced critical, then I think it's fine that isn't transmitted to the final joined roll. + delete systemData.damage.main.options.isCritical; } for (const type of Object.keys(joinedRoll.damageRollData?.resources ?? {})) { systemData.damage.resources[type] = joinedRoll.damageRollData.resources[type].toJSON(); diff --git a/module/data/chat-message/chatDamageData.mjs b/module/data/chat-message/chatDamageData.mjs index a31a9ef1..da28061f 100644 --- a/module/data/chat-message/chatDamageData.mjs +++ b/module/data/chat-message/chatDamageData.mjs @@ -41,11 +41,12 @@ export class ChatDamageData extends foundry.abstract.DataModel { } _prepareRolls() { - this.main &&= Roll.fromData({ + this.main &&= Roll.fromData({ ...this.main, options: { ...this.main.options, - isCritical: this.main.options.isCritical || this.isCritical + isCritical: + this.main.options.isCritical === false ? false : (this.main.options.isCritical || this.isCritical) } }); diff --git a/templates/dialogs/tagTeamDialog/result.hbs b/templates/dialogs/tagTeamDialog/result.hbs index 88f579a6..ffeeefd9 100644 --- a/templates/dialogs/tagTeamDialog/result.hbs +++ b/templates/dialogs/tagTeamDialog/result.hbs @@ -18,11 +18,11 @@ {{#if joinedRoll.damageRollData}}
- {{#each joinedRoll.damageRollData.types as |damage key|}} -
-
{{localize (concat "DAGGERHEART.CONFIG.HealingType." key ".name")}}
-
{{damage.total}}
-
+ {{#if joinedRoll.damageRollData.main}} + {{> damageSummary roll=joinedRoll.damageRollData.main label=(localize "DAGGERHEART.GENERAL.damage")}} + {{/if}} + {{#each joinedRoll.damageRollData.resources as |roll key|}} + {{> damageSummary roll=roll label=(localize (concat "DAGGERHEART.CONFIG.HealingType." key ".name"))}} {{/each}}
{{/if}} @@ -38,4 +38,11 @@ - \ No newline at end of file + + +{{#*inline "damageSummary"}} +
+
{{label}}
+
{{roll.total}}
+
+{{/inline}} \ No newline at end of file From 76aabb235dab721257be6a6ded5c2d8bf7dd21b0 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sun, 19 Jul 2026 22:59:45 +0200 Subject: [PATCH 5/5] Improved TagTeamDialog Results section --- module/applications/dialogs/tagTeamDialog.mjs | 11 +++++------ styles/less/dialog/tag-team-dialog/sheet.less | 7 ++----- .../tagTeamDialog/parts/tagTeamDamageParts.hbs | 2 +- templates/dialogs/tagTeamDialog/result.hbs | 16 ++-------------- .../dialogs/tagTeamDialog/tagTeamMember.hbs | 6 +----- 5 files changed, 11 insertions(+), 31 deletions(-) diff --git a/module/applications/dialogs/tagTeamDialog.mjs b/module/applications/dialogs/tagTeamDialog.mjs index e03d6658..e0517abe 100644 --- a/module/applications/dialogs/tagTeamDialog.mjs +++ b/module/applications/dialogs/tagTeamDialog.mjs @@ -236,11 +236,11 @@ 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?.roll?.isCritical ?? false); + const isCritical = critSelected || (critSelected === undefined && data.roll?.isCritical); if (data.damageRollData.main) { - const selectedRoll = Object.values(this.party.system.tagTeam.members).find(member => member.selected); - const critSelected = !selectedRoll ? undefined : (selectedRoll?.roll?.isCritical ?? false); - const useCritDamage = critSelected || (critSelected === undefined && data.roll?.isCritical); - data.damageRollData.main.options.isCritical = useCritDamage; + data.damageRollData.main.options.isCritical = isCritical; } return { @@ -253,8 +253,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio rollOptions, damageRollOptions, damage: data.damageRollData, - critDamage: this.getCriticalDamage(data.damageRollData), - useCritDamage: false + isCritical }; } diff --git a/styles/less/dialog/tag-team-dialog/sheet.less b/styles/less/dialog/tag-team-dialog/sheet.less index 82bc0270..3a6778d8 100644 --- a/styles/less/dialog/tag-team-dialog/sheet.less +++ b/styles/less/dialog/tag-team-dialog/sheet.less @@ -252,11 +252,8 @@ width: 100%; text-align: center; - .result-info { - display: flex; - gap: 4px; - align-items: center; - justify-content: center; + .damage-info { + text-decoration: underline; } } } diff --git a/templates/dialogs/tagTeamDialog/parts/tagTeamDamageParts.hbs b/templates/dialogs/tagTeamDialog/parts/tagTeamDamageParts.hbs index e8bec150..4727d7e8 100644 --- a/templates/dialogs/tagTeamDialog/parts/tagTeamDamageParts.hbs +++ b/templates/dialogs/tagTeamDialog/parts/tagTeamDamageParts.hbs @@ -1,5 +1,5 @@ {{#if damage.main}} - {{> damage roll=damage.main label=(localize "DAGGERHEART.GENERAL.damage") memberKey=key}} + {{> damage roll=damage.main label=(localize "DAGGERHEART.GENERAL.damage") memberKey=key isCritical=isCritical}} {{/if}} {{#each damage.resources as |roll key|}} diff --git a/templates/dialogs/tagTeamDialog/result.hbs b/templates/dialogs/tagTeamDialog/result.hbs index ffeeefd9..4351823e 100644 --- a/templates/dialogs/tagTeamDialog/result.hbs +++ b/templates/dialogs/tagTeamDialog/result.hbs @@ -6,18 +6,9 @@ {{#if hintText}}
{{localize hintText}}
{{else}} - {{#if joinedRoll.rollData}} -
- -
-
{{joinedRoll.rollData.total}}
-
{{localize "DAGGERHEART.GENERAL.withThing" thing=joinedRoll.roll.totalLabel}}
-
-
- {{/if}} {{#if joinedRoll.damageRollData}}
- +
{{joinedRoll.rollData.total}} {{localize "DAGGERHEART.GENERAL.withThing" thing=joinedRoll.roll.totalLabel}}
{{#if joinedRoll.damageRollData.main}} {{> damageSummary roll=joinedRoll.damageRollData.main label=(localize "DAGGERHEART.GENERAL.damage")}} {{/if}} @@ -41,8 +32,5 @@ {{#*inline "damageSummary"}} -
-
{{label}}
-
{{roll.total}}
-
+
{{label}} {{roll.total}}
{{/inline}} \ No newline at end of file diff --git a/templates/dialogs/tagTeamDialog/tagTeamMember.hbs b/templates/dialogs/tagTeamDialog/tagTeamMember.hbs index 7758979b..079a2a6f 100644 --- a/templates/dialogs/tagTeamDialog/tagTeamMember.hbs +++ b/templates/dialogs/tagTeamDialog/tagTeamMember.hbs @@ -109,11 +109,7 @@
{{#if damage.active}} - {{#if useCritDamage}} - {{> "systems/daggerheart/templates/dialogs/tagTeamDialog/parts/tagTeamDamageParts.hbs" damage=critDamage isCritical=true }} - {{else}} - {{> "systems/daggerheart/templates/dialogs/tagTeamDialog/parts/tagTeamDamageParts.hbs" damage=damage }} - {{/if}} + {{> "systems/daggerheart/templates/dialogs/tagTeamDialog/parts/tagTeamDamageParts.hbs" damage=damage isCritical=isCritical }} {{else}} {{localize "DAGGERHEART.APPLICATIONS.TagTeamSelect.makeYourRoll"}} {{/if}}