diff --git a/module/applications/dialogs/damageDialog.mjs b/module/applications/dialogs/damageDialog.mjs index 8b37cf2d..ce613ade 100644 --- a/module/applications/dialogs/damageDialog.mjs +++ b/module/applications/dialogs/damageDialog.mjs @@ -77,7 +77,10 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application static updateRollConfiguration(_event, _, formData) { const data = foundry.utils.expandObject(formData.object); - foundry.utils.mergeObject(this.config.damageFormula, data.damageFormula); + + if (this.config.damageFormula) + foundry.utils.mergeObject(this.config.damageFormula, data.damageFormula); + foundry.utils.mergeObject(this.config.resourceFormulas, data.resourceFormulas); foundry.utils.mergeObject(this.config.modifiers, data.modifiers); this.config.selectedMessageMode = data.selectedMessageMode; diff --git a/module/data/fields/action/damageField.mjs b/module/data/fields/action/damageField.mjs index 4fe38aca..c2db4b51 100644 --- a/module/data/fields/action/damageField.mjs +++ b/module/data/fields/action/damageField.mjs @@ -99,7 +99,7 @@ export default class DamageField extends fields.SchemaField { : actor.prototypeToken; if (config.hasHealing) damagePromises.push( - actor.takeHealing(config.damage.types).then(updates => targetDamage.push({ token, updates })) + actor.takeHealing(config.damage).then(updates => targetDamage.push({ token, updates })) ); else { const configDamage = config.damage.clone(); diff --git a/module/dice/damageRoll.mjs b/module/dice/damageRoll.mjs index b5e8b29e..79b9f653 100644 --- a/module/dice/damageRoll.mjs +++ b/module/dice/damageRoll.mjs @@ -27,10 +27,14 @@ export default class DamageRoll extends DHRoll { return roll.roll; } - config.damage.main = await evaluateRoll(config.damageFormula); - config.damage.main.options = { damageTypes: - config.damageFormula.damageTypes ? [...config.damageFormula.damageTypes] : [] - }; + if (!config.damage) config.damage = { main: null, resources: {} }; + + if (config.damageFormula) { + config.damage.main = await evaluateRoll(config.damageFormula); + config.damage.main.options = { damageTypes: + config.damageFormula.damageTypes ? [...config.damageFormula.damageTypes] : [] + }; + } for (const roll of config.resourceFormulas) { config.damage.resources[roll.applyTo] = await evaluateRoll(roll); diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index ec2d8348..de55b4b7 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -662,23 +662,16 @@ export default class DhpActor extends Actor { if (Hooks.call(`${CONFIG.DH.id}.postCalculateDamage`, this, damages) === false) return null; - // Convert deducted resources and damage to a record of updates, merging damage to hp with hp marked + // Convert deducted resources to a record of updates. Return if nothing to do. const updates = Object.entries(damages.resources).map(([key, damage]) => ({ key, value: damage.total })); - if (damages.main) { - const existing = updates.find(u => u.key === CONFIG.DH.GENERAL.healingTypes.hitPoints.id); - const value = this.convertDamageToThreshold(damages.main.total); - const damageTypes = new Set(damages.main.options.damageTypes); - if (existing) { - existing.value += value; - existing.damageTypes = damageTypes; - } else { - updates.push({ value, damageTypes, key: CONFIG.DH.GENERAL.healingTypes.hitPoints.id }); - } - } - if (!updates.some(u => u.value)) return; // early return if nothing to do + if (!updates.some(u => u.value) && !damages.main) return; - const hpDamage = updates.find(u => u.key === CONFIG.DH.GENERAL.healingTypes.hitPoints.id); - if (hpDamage?.value) { + if (damages.main) { + const hpDamage = { + value: this.convertDamageToThreshold(damages.main.total), + damageTypes: new Set(damages.main.options.damageTypes), + key: CONFIG.DH.GENERAL.healingTypes.hitPoints.id + }; if (this.type === 'character' && !isDirect && this.#canReduceDamage(hpDamage.total, hpDamage.damageTypes)) { const armorSlotResult = await this.owner.query( 'armorSlot', @@ -712,6 +705,15 @@ export default class DhpActor extends Actor { hpDamage.value = Math.max(0, hpDamage.value - 1); } } + + // Merge existing hitPoint deduction with finalised damage deduction + const existing = updates.find(u => u.key === CONFIG.DH.GENERAL.healingTypes.hitPoints.id); + if (existing) { + existing.value += hpDamage.value; + existing.damageTypes = hpDamage.damageTypes; + } else { + updates.push(hpDamage); + } } const results = await game.system.registeredTriggers.runTrigger( @@ -738,6 +740,28 @@ export default class DhpActor extends Actor { return updates; } + async takeHealing(healings) { + if (Hooks.call(`${CONFIG.DH.id}.preTakeHealing`, this, healings) === false) return null; + + const updates = Object.entries(healings.resources).map(([key, damage]) => ({ + key, + value: damage.total + })); + + updates.forEach( + u => + (u.value = !(u.key === 'fear' || this.system?.resources?.[u.key]?.isReversed === false) + ? u.value * -1 + : u.value) + ); + + await this.modifyResource(updates); + + if (Hooks.call(`${CONFIG.DH.id}.postTakeHealing`, this, updates) === false) return null; + + return updates; + } + calculateDamage(baseDamage, type) { if (this.canResist(type, 'immunity')) return 0; if (this.canResist(type, 'resistance')) baseDamage = Math.ceil(baseDamage / 2); @@ -762,30 +786,6 @@ export default class DhpActor extends Actor { return reduction === Infinity ? 0 : reduction; } - async takeHealing(healings) { - if (Hooks.call(`${CONFIG.DH.id}.preTakeHealing`, this, healings) === false) return null; - - const updates = []; - Object.entries(healings).forEach(([key, healing]) => { - const update = updates.find(u => u.key === key); - if (update) update.value += healing.roll.total; - else updates.push({ value: healing.roll.total, key }); - }); - - updates.forEach( - u => - (u.value = !(u.key === 'fear' || this.system?.resources?.[u.key]?.isReversed === false) - ? u.value * -1 - : u.value) - ); - - await this.modifyResource(updates); - - if (Hooks.call(`${CONFIG.DH.id}.postTakeHealing`, this, updates) === false) return null; - - return updates; - } - /** * Resources are modified asynchronously, so be careful not to update the same resource in * quick succession.