From fdb6412c8c7caf33cef9b8d490d11798450e9a93 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Sat, 24 Jan 2026 20:24:26 +0100 Subject: [PATCH] [Feature] HP DamageMultiplier (#1567) * Added a hpDamageMultiplier rule active effects can modify to multiply the total damage an actor deals * Added a hpDamageTkenMultiplier rule active effects can modify to multiply the total damage an actor takes from others * . --- module/data/actor/base.mjs | 20 ++++++- module/data/actor/character.mjs | 54 +++++++++---------- module/data/fields/action/damageField.mjs | 14 ++++- ...ary_Demon_of_Despair_kE4dfhqmIQpNd44e.json | 46 +++++++++++++++- ...ry_Failed_Experiment_ChwwVqowFw8hJQwT.json | 46 +++++++++++++++- ...sary_Hallowed_Archer_kabueAo6BALApWqp.json | 46 +++++++++++++++- ...ed_Knife_Kneebreaker_CBKixLH3yhivZZuL.json | 13 +++-- ...sary_Skeleton_Archer_7X5q7a6ueeHs5oA9.json | 46 +++++++++++++++- 8 files changed, 247 insertions(+), 38 deletions(-) diff --git a/module/data/actor/base.mjs b/module/data/actor/base.mjs index b90361e2..08308eab 100644 --- a/module/data/actor/base.mjs +++ b/module/data/actor/base.mjs @@ -27,7 +27,7 @@ const resistanceField = (resistanceLabel, immunityLabel, reductionLabel) => }); /* Common rules applying to Characters and Adversaries */ -export const commonActorRules = (extendedData = { damageReduction: {} }) => ({ +export const commonActorRules = (extendedData = { damageReduction: {}, attack: { damage: {} } }) => ({ conditionImmunities: new fields.SchemaField({ hidden: new fields.BooleanField({ initial: false }), restrained: new fields.BooleanField({ initial: false }), @@ -41,7 +41,23 @@ export const commonActorRules = (extendedData = { damageReduction: {} }) => ({ magical: new fields.NumberField({ initial: 0, min: 0 }), physical: new fields.NumberField({ initial: 0, min: 0 }) }), - ...extendedData.damageReduction + ...(extendedData.damageReduction ?? {}) + }), + attack: new fields.SchemaField({ + ...extendedData.attack, + damage: new fields.SchemaField({ + hpDamageMultiplier: new fields.NumberField({ + required: true, + nullable: false, + initial: 1 + }), + hpDamageTakenMultiplier: new fields.NumberField({ + required: true, + nullable: false, + initial: 1 + }), + ...(extendedData.attack?.damage ?? {}) + }) }) }); diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index 92e1e978..594f078c 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -253,35 +253,35 @@ export default class DhCharacter extends BaseDataActor { hint: 'DAGGERHEART.GENERAL.Rules.damageReduction.increasePerArmorMark.hint' }), disabledArmor: new fields.BooleanField({ intial: false }) + }, + attack: { + damage: { + diceIndex: new fields.NumberField({ + integer: true, + min: 0, + max: 5, + initial: 0, + label: 'DAGGERHEART.GENERAL.Rules.attack.damage.dice.label', + hint: 'DAGGERHEART.GENERAL.Rules.attack.damage.dice.hint' + }), + bonus: new fields.NumberField({ + required: true, + initial: 0, + min: 0, + label: 'DAGGERHEART.GENERAL.Rules.attack.damage.bonus.label' + }) + }, + roll: new fields.SchemaField({ + trait: new fields.StringField({ + required: true, + choices: CONFIG.DH.ACTOR.abilities, + nullable: true, + initial: null, + label: 'DAGGERHEART.GENERAL.Rules.attack.roll.trait.label' + }) + }) } }), - attack: new fields.SchemaField({ - damage: new fields.SchemaField({ - diceIndex: new fields.NumberField({ - integer: true, - min: 0, - max: 5, - initial: 0, - label: 'DAGGERHEART.GENERAL.Rules.attack.damage.dice.label', - hint: 'DAGGERHEART.GENERAL.Rules.attack.damage.dice.hint' - }), - bonus: new fields.NumberField({ - required: true, - initial: 0, - min: 0, - label: 'DAGGERHEART.GENERAL.Rules.attack.damage.bonus.label' - }) - }), - roll: new fields.SchemaField({ - trait: new fields.StringField({ - required: true, - choices: CONFIG.DH.ACTOR.abilities, - nullable: true, - initial: null, - label: 'DAGGERHEART.GENERAL.Rules.attack.roll.trait.label' - }) - }) - }), dualityRoll: new fields.SchemaField({ defaultHopeDice: new fields.NumberField({ nullable: false, diff --git a/module/data/fields/action/damageField.mjs b/module/data/fields/action/damageField.mjs index bb81c702..ef91c64e 100644 --- a/module/data/fields/action/damageField.mjs +++ b/module/data/fields/action/damageField.mjs @@ -105,12 +105,22 @@ export default class DamageField extends fields.SchemaField { damagePromises.push( actor.takeHealing(config.damage).then(updates => targetDamage.push({ token, updates })) ); - else + else { + const configDamage = foundry.utils.deepClone(config.damage); + const hpDamageMultiplier = config.actionActor?.system.rules.attack.damage.hpDamageMultiplier ?? 1; + const hpDamageTakenMultiplier = actor.system.rules.attack.damage.hpDamageTakenMultiplier; + if (configDamage.hitPoints) { + for (const part of configDamage.hitPoints.parts) { + part.total = Math.ceil(part.total * hpDamageMultiplier * hpDamageTakenMultiplier); + } + } + damagePromises.push( actor - .takeDamage(config.damage, config.isDirect) + .takeDamage(configDamage, config.isDirect) .then(updates => targetDamage.push({ token, updates })) ); + } } Promise.all(damagePromises).then(async _ => { diff --git a/src/packs/adversaries/adversary_Demon_of_Despair_kE4dfhqmIQpNd44e.json b/src/packs/adversaries/adversary_Demon_of_Despair_kE4dfhqmIQpNd44e.json index 188b2687..830848c3 100644 --- a/src/packs/adversaries/adversary_Demon_of_Despair_kE4dfhqmIQpNd44e.json +++ b/src/packs/adversaries/adversary_Demon_of_Despair_kE4dfhqmIQpNd44e.json @@ -235,7 +235,51 @@ }, "_id": "2ESeh4tPhr6DI5ty", "img": "icons/magic/death/skull-horned-worn-fire-blue.webp", - "effects": [], + "effects": [ + { + "name": "Depths Of Despair", + "type": "base", + "system": { + "rangeDependence": { + "enabled": false, + "type": "withinRange", + "target": "hostile", + "range": "melee" + } + }, + "_id": "nofxm1vGZ2TmceA2", + "img": "icons/magic/death/skull-horned-worn-fire-blue.webp", + "changes": [ + { + "key": "system.rules.attack.damage.hpDamageMultiplier", + "mode": 5, + "value": "2", + "priority": null + } + ], + "disabled": true, + "duration": { + "startTime": null, + "combat": null, + "seconds": null, + "rounds": null, + "turns": null, + "startRound": null, + "startTurn": null + }, + "description": "

The Demon of Despair deals double damage to PCs with 0 Hope.

", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!actors.items.effects!kE4dfhqmIQpNd44e.2ESeh4tPhr6DI5ty.nofxm1vGZ2TmceA2" + } + ], "folder": null, "sort": 0, "ownership": { diff --git a/src/packs/adversaries/adversary_Failed_Experiment_ChwwVqowFw8hJQwT.json b/src/packs/adversaries/adversary_Failed_Experiment_ChwwVqowFw8hJQwT.json index 39800002..408d5102 100644 --- a/src/packs/adversaries/adversary_Failed_Experiment_ChwwVqowFw8hJQwT.json +++ b/src/packs/adversaries/adversary_Failed_Experiment_ChwwVqowFw8hJQwT.json @@ -304,7 +304,51 @@ }, "_id": "1fE6xo8yIOmZkGNE", "img": "icons/skills/melee/strike-slashes-orange.webp", - "effects": [], + "effects": [ + { + "name": "Overwhelm", + "type": "base", + "system": { + "rangeDependence": { + "enabled": false, + "type": "withinRange", + "target": "hostile", + "range": "melee" + } + }, + "_id": "eGB9G0ljYCcdGbOx", + "img": "icons/skills/melee/strike-slashes-orange.webp", + "changes": [ + { + "key": "system.rules.attack.damage.hpDamageMultiplier", + "mode": 5, + "value": "2", + "priority": null + } + ], + "disabled": true, + "duration": { + "startTime": null, + "combat": null, + "seconds": null, + "rounds": null, + "turns": null, + "startRound": null, + "startTurn": null + }, + "description": "

When a target the Failed Experiment attacks has other adversaries within Very Close range, the Failed Experiment deals double damage.

", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!actors.items.effects!ChwwVqowFw8hJQwT.1fE6xo8yIOmZkGNE.eGB9G0ljYCcdGbOx" + } + ], "folder": null, "sort": 0, "ownership": { diff --git a/src/packs/adversaries/adversary_Hallowed_Archer_kabueAo6BALApWqp.json b/src/packs/adversaries/adversary_Hallowed_Archer_kabueAo6BALApWqp.json index 0abf1661..8cce1b94 100644 --- a/src/packs/adversaries/adversary_Hallowed_Archer_kabueAo6BALApWqp.json +++ b/src/packs/adversaries/adversary_Hallowed_Archer_kabueAo6BALApWqp.json @@ -229,7 +229,51 @@ }, "_id": "FGJTAeL38zTVd4fA", "img": "icons/magic/control/buff-flight-wings-runes-red-yellow.webp", - "effects": [], + "effects": [ + { + "name": "Punish the Guilty", + "type": "base", + "system": { + "rangeDependence": { + "enabled": false, + "type": "withinRange", + "target": "hostile", + "range": "melee" + } + }, + "_id": "ID85zoIa5GfhNMti", + "img": "icons/magic/control/buff-flight-wings-runes-red-yellow.webp", + "changes": [ + { + "key": "system.rules.attack.damage.hpDamageMultiplier", + "mode": 5, + "value": "2", + "priority": null + } + ], + "disabled": true, + "duration": { + "startTime": null, + "combat": null, + "seconds": null, + "rounds": null, + "turns": null, + "startRound": null, + "startTurn": null + }, + "description": "

The Hallowed Archer deals double damage to targets marked Guilty by a High Seraph.

", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!actors.items.effects!kabueAo6BALApWqp.FGJTAeL38zTVd4fA.ID85zoIa5GfhNMti" + } + ], "folder": null, "sort": 0, "ownership": { diff --git a/src/packs/adversaries/adversary_Jagged_Knife_Kneebreaker_CBKixLH3yhivZZuL.json b/src/packs/adversaries/adversary_Jagged_Knife_Kneebreaker_CBKixLH3yhivZZuL.json index fc644604..c38260e9 100644 --- a/src/packs/adversaries/adversary_Jagged_Knife_Kneebreaker_CBKixLH3yhivZZuL.json +++ b/src/packs/adversaries/adversary_Jagged_Knife_Kneebreaker_CBKixLH3yhivZZuL.json @@ -336,7 +336,14 @@ "range": "melee" } }, - "changes": [], + "changes": [ + { + "key": "system.rules.attack.damage.hpDamageTakenMultiplier", + "mode": 5, + "value": "2", + "priority": null + } + ], "disabled": false, "duration": { "startTime": null, @@ -350,8 +357,8 @@ "description": "", "tint": "#ffffff", "statuses": [ - "restrained", - "vulnerable" + "vulnerable", + "restrained" ], "sort": 0, "flags": {}, diff --git a/src/packs/adversaries/adversary_Skeleton_Archer_7X5q7a6ueeHs5oA9.json b/src/packs/adversaries/adversary_Skeleton_Archer_7X5q7a6ueeHs5oA9.json index e5381f6f..9d837ac0 100644 --- a/src/packs/adversaries/adversary_Skeleton_Archer_7X5q7a6ueeHs5oA9.json +++ b/src/packs/adversaries/adversary_Skeleton_Archer_7X5q7a6ueeHs5oA9.json @@ -230,7 +230,51 @@ "subType": null, "originId": null }, - "effects": [], + "effects": [ + { + "name": "Opportunist", + "type": "base", + "system": { + "rangeDependence": { + "enabled": false, + "type": "withinRange", + "target": "hostile", + "range": "melee" + } + }, + "_id": "O03vYbyNLO3YPZGo", + "img": "icons/skills/targeting/crosshair-triple-strike-orange.webp", + "changes": [ + { + "key": "system.rules.attack.damage.hpDamageMultiplier", + "mode": 5, + "value": "2", + "priority": null + } + ], + "disabled": true, + "duration": { + "startTime": null, + "combat": null, + "seconds": null, + "rounds": null, + "turns": null, + "startRound": null, + "startTurn": null + }, + "description": "

When two or more adversaries are within Very Close range of a creature, all damage the Skeleton Archer deals to that creature is doubled.

", + "origin": null, + "tint": "#ffffff", + "transfer": true, + "statuses": [], + "sort": 0, + "flags": {}, + "_stats": { + "compendiumSource": null + }, + "_key": "!actors.items.effects!7X5q7a6ueeHs5oA9.6mL2FQ9pQdfoDNzG.O03vYbyNLO3YPZGo" + } + ], "folder": null, "sort": 0, "ownership": {