mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-07-22 02:19:54 +02:00
[Rework] ChatMessage Damage (#2079)
Some checks are pending
Project CI / build (24.x) (push) Waiting to run
Some checks are pending
Project CI / build (24.x) (push) Waiting to run
* Initial rework * Removed unneeded method * Removed outcommented code * Added migration * Fixed DamageActions * Semi-corrected reroll of one of multiple results on a die * Code improvement * Added aseDie.rerollResult method to specifically reroll a specific result die in a grouping * TagTeamDialog somewhat working * Added migration for TagTeamData * Fix for mean tagTeamDialog.finish * . * Improved migration * Internalised ChatDamageData.prepareRolls to its own constructor * Moved DamageTypes to roll.options * TagTeamDialog fixes to accomodate new flat damage.types structure * Changed so all default rolls become BaseRolls to access convenience functions and getters * . * Fixed so that party.tagTeam now uses a declared damageRollData(ChatDamageData) field. rollData.options.damage is retired. * Critical damage fixes * Corrected TagTeamDialog rerender logic when selecting a roll * Fixed so that DamageTypes are retained and joined together throughout TagTeamDialog * Removed some remaining types.<>.roll references * [Rework] Damage and Damage Resource split (#2094) * Changed the label of ActionSheet damage.resources to Mark Resources * Fix assigning includeBase * Localize add resource * Maybe simplify API * Fix onRollSimple * Handle erroring action update differently --------- Co-authored-by: Carlos Fernandez <cfern1990@gmail.com>
This commit is contained in:
parent
4b651836ff
commit
effab8db0c
51 changed files with 1007 additions and 788 deletions
|
|
@ -622,6 +622,11 @@ export default class DhpActor extends Actor {
|
|||
return rollData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if damage can be reduced in one way or another.
|
||||
* @param {number} hpDamage the amount of marked hp that will be marked
|
||||
* @param {Set<string>} types a list of damage types
|
||||
*/
|
||||
#canReduceDamage(hpDamage, types) {
|
||||
const { stressDamageReduction, disabledArmor, reduceSeverity, thresholdImmunities } =
|
||||
this.system.rules.damageReduction;
|
||||
|
|
@ -648,35 +653,32 @@ export default class DhpActor extends Actor {
|
|||
return canUseArmor || canUseStress || hasReduceSeverity || hasThresholdImmunity;
|
||||
}
|
||||
|
||||
async takeDamage(damages, isDirect = false) {
|
||||
if (Hooks.call(`${CONFIG.DH.id}.preTakeDamage`, this, damages) === false) return null;
|
||||
async takeDamage(args, isDirect = false) {
|
||||
args = this.#parseDamageArgs(args);
|
||||
if (Hooks.call(`${CONFIG.DH.id}.preTakeDamage`, this, args) === false) return null;
|
||||
|
||||
if (this.type === 'companion') {
|
||||
await this.modifyResource([{ value: 1, key: 'stress' }]);
|
||||
return;
|
||||
}
|
||||
|
||||
const updates = [];
|
||||
const updates = args.resourceUpdates;
|
||||
if (args.main) {
|
||||
// todo: avoid side effects, but hook currently requires it
|
||||
args.main.value = this.calculateDamage(args.main.value, args.main.damageTypes);
|
||||
}
|
||||
|
||||
Object.entries(damages).forEach(([key, damage]) => {
|
||||
damage.parts.forEach(part => {
|
||||
if (part.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id)
|
||||
part.total = this.calculateDamage(part.total, part.damageTypes);
|
||||
const update = updates.find(u => u.key === key);
|
||||
if (update) {
|
||||
update.value += part.total;
|
||||
update.damageTypes.add(...new Set(part.damageTypes));
|
||||
} else updates.push({ value: part.total, key, damageTypes: new Set(part.damageTypes) });
|
||||
});
|
||||
});
|
||||
if (Hooks.call(`${CONFIG.DH.id}.postCalculateDamage`, this, args) === false) return null;
|
||||
|
||||
if (Hooks.call(`${CONFIG.DH.id}.postCalculateDamage`, this, damages) === false) return null;
|
||||
// Convert deducted resources to a record of updates. Return if nothing to do.
|
||||
if (!updates.some(u => u.value) && !args.main) return;
|
||||
|
||||
if (!updates.length) return;
|
||||
|
||||
const hpDamage = updates.find(u => u.key === CONFIG.DH.GENERAL.healingTypes.hitPoints.id);
|
||||
if (hpDamage?.value) {
|
||||
hpDamage.value = this.convertDamageToThreshold(hpDamage.value);
|
||||
if (args.main) {
|
||||
const hpDamage = {
|
||||
value: this.convertDamageToThreshold(args.main.value),
|
||||
damageTypes: new Set(args.main.damageTypes),
|
||||
key: CONFIG.DH.GENERAL.healingTypes.hitPoints.id
|
||||
};
|
||||
if (this.type === 'character' && !isDirect && this.#canReduceDamage(hpDamage.value, hpDamage.damageTypes)) {
|
||||
const armorSlotResult = await this.owner.query(
|
||||
'armorSlot',
|
||||
|
|
@ -691,7 +693,7 @@ export default class DhpActor extends Actor {
|
|||
);
|
||||
if (armorSlotResult) {
|
||||
const { modifiedDamage, armorChanges, stressSpent } = armorSlotResult;
|
||||
updates.find(u => u.key === 'hitPoints').value = modifiedDamage;
|
||||
hpDamage.value = modifiedDamage;
|
||||
for (const armorChange of armorChanges) {
|
||||
updates.push({ value: armorChange.amount, key: 'armor', uuid: armorChange.uuid });
|
||||
}
|
||||
|
|
@ -701,20 +703,24 @@ export default class DhpActor extends Actor {
|
|||
else updates.push({ value: stressSpent, key: 'stress' });
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.type === 'adversary') {
|
||||
} else if (this.type === 'adversary') {
|
||||
const reducedSeverity = hpDamage.damageTypes.reduce((value, curr) => {
|
||||
return Math.max(this.system.rules.damageReduction.reduceSeverity[curr], value);
|
||||
}, 0);
|
||||
hpDamage.value = Math.max(hpDamage.value - reducedSeverity, 0);
|
||||
|
||||
if (
|
||||
hpDamage.value &&
|
||||
this.system.rules.damageReduction.thresholdImmunities[getDamageKey(hpDamage.value)]
|
||||
) {
|
||||
hpDamage.value -= 1;
|
||||
if (this.system.rules.damageReduction.thresholdImmunities[getDamageKey(hpDamage.value)]) {
|
||||
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(
|
||||
|
|
@ -729,12 +735,10 @@ export default class DhpActor extends Actor {
|
|||
for (var result of results) resourceMap.addResources(result.updates);
|
||||
resourceMap.updateResources();
|
||||
}
|
||||
|
||||
updates.forEach(
|
||||
u =>
|
||||
(u.value =
|
||||
u.key === 'fear' || this.system?.resources?.[u.key]?.isReversed === false ? u.value * -1 : u.value)
|
||||
);
|
||||
|
||||
for (const u of updates) {
|
||||
u.value = u.key === 'fear' || this.system?.resources?.[u.key]?.isReversed === false ? u.value * -1 : u.value;
|
||||
}
|
||||
|
||||
await this.modifyResource(updates);
|
||||
|
||||
|
|
@ -743,6 +747,36 @@ export default class DhpActor extends Actor {
|
|||
return updates;
|
||||
}
|
||||
|
||||
async takeHealing(args) {
|
||||
args = this.#parseDamageArgs({ resources: 'resources' in args ? args.resources : args });
|
||||
if (Hooks.call(`${CONFIG.DH.id}.preTakeHealing`, this, args) === false) return null;
|
||||
|
||||
const updates = args.resourceUpdates;
|
||||
for (const u of updates) {
|
||||
const shouldFlip = !(u.key === 'fear' || this.system?.resources?.[u.key]?.isReversed === false);
|
||||
u.value = shouldFlip ? u.value * -1 : u.value;
|
||||
}
|
||||
await this.modifyResource(updates);
|
||||
|
||||
if (Hooks.call(`${CONFIG.DH.id}.postTakeHealing`, this, updates) === false) return null;
|
||||
|
||||
return updates;
|
||||
}
|
||||
|
||||
/** Parse damage args that may be coming from takeHealing or takeDamage. Used to simplify macro usage and roll vs non-roll usage */
|
||||
#parseDamageArgs(args = {}) {
|
||||
const damageRoll = 'total' in args ? args : (args.main ?? args.damage);
|
||||
const damageValue = typeof damageRoll === 'number' ? damageRoll : damageRoll?.total;
|
||||
const damageTypes = Array.from(damageRoll?.options?.damageTypes ?? damageRoll?.damageTypes ?? []);
|
||||
const main = typeof damageValue === 'number' ? { key: 'damage', value: damageValue, damageTypes } : null;
|
||||
const resourceUpdates = Object.entries(args.resources ?? {}).map(([key, damage]) => ({
|
||||
key,
|
||||
value: typeof damage === 'number' ? damage : damage?.total ?? 0
|
||||
}));
|
||||
|
||||
return { main, resourceUpdates };
|
||||
}
|
||||
|
||||
calculateDamage(baseDamage, type) {
|
||||
if (this.canResist(type, 'immunity')) return 0;
|
||||
if (this.canResist(type, 'resistance')) baseDamage = Math.ceil(baseDamage / 2);
|
||||
|
|
@ -767,32 +801,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]) => {
|
||||
healing.parts.forEach(part => {
|
||||
const update = updates.find(u => u.key === key);
|
||||
if (update) update.value += part.total;
|
||||
else updates.push({ value: part.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.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue