mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-07-21 18:09:54 +02:00
Compare commits
7 commits
907bb8be50
...
88754c5a80
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88754c5a80 | ||
|
|
6273394db8 | ||
|
|
21daa7f3c0 | ||
|
|
b2bf102443 | ||
|
|
b7e92cfe0e | ||
|
|
4bffc27403 | ||
|
|
ce37bd9c60 |
14 changed files with 232 additions and 181 deletions
|
|
@ -24,7 +24,7 @@ import TokenManager from './module/documents/tokenManager.mjs';
|
|||
CONFIG.DH = SYSTEM;
|
||||
CONFIG.TextEditor.enrichers.push(...enricherConfig);
|
||||
|
||||
CONFIG.Dice.rolls = [Roll = BaseRoll, DHRoll, DualityRoll, D20Roll, DamageRoll, FateRoll];
|
||||
CONFIG.Dice.rolls = [BaseRoll, Roll, DHRoll, DualityRoll, D20Roll, DamageRoll, FateRoll];
|
||||
CONFIG.Dice.daggerheart = {
|
||||
DHRoll: DHRoll,
|
||||
DualityRoll: DualityRoll,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -553,8 +553,10 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
const { memberKey } = button.dataset;
|
||||
this.updatePartyData(
|
||||
{
|
||||
[`system.tagTeam.members.${memberKey}.damageRollData.types`]:
|
||||
_replace({})
|
||||
[`system.tagTeam.members.${memberKey}.damageRollData`]: {
|
||||
main: null,
|
||||
resources: _replace({})
|
||||
}
|
||||
},
|
||||
this.getUpdatingParts(button)
|
||||
);
|
||||
|
|
@ -577,19 +579,19 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
|
||||
async getCriticalDamage(origDamage) {
|
||||
const newDamage = origDamage ? ChatDamageData.fromJSON(JSON.stringify(origDamage)) : null;
|
||||
for (let key in newDamage?.types ?? {}) {
|
||||
const criticalDamage = await getCritDamageBonus(newDamage.types[key].formula);
|
||||
if (!criticalDamage) continue;
|
||||
|
||||
const criticalTerm = new foundry.dice.terms.NumericTerm({ number: criticalDamage, evaluated: true });
|
||||
criticalTerm.evaluate();
|
||||
newDamage.types[key] = await Roll.fromTerms([
|
||||
...origDamage.types[key].terms,
|
||||
new foundry.dice.terms.OperatorTerm({ operator: '+' }),
|
||||
criticalTerm
|
||||
]);
|
||||
newDamage.types[key].options = foundry.utils.deepClone(origDamage.types[key].options);
|
||||
}
|
||||
if (newDamage?.main) {
|
||||
const criticalDamage = await getCritDamageBonus(newDamage.main.formula);
|
||||
if (criticalDamage) {
|
||||
const criticalTerm = new foundry.dice.terms.NumericTerm({ number: criticalDamage, evaluated: true });
|
||||
criticalTerm.evaluate();
|
||||
newDamage.main = await Roll.fromTerms([
|
||||
...origDamage.main.terms,
|
||||
new foundry.dice.terms.OperatorTerm({ operator: '+' }),
|
||||
criticalTerm
|
||||
]);
|
||||
newDamage.main.options = foundry.utils.deepClone(origDamage.main.options);
|
||||
}
|
||||
}
|
||||
|
||||
return newDamage;
|
||||
}
|
||||
|
|
@ -644,25 +646,47 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
? await this.getCriticalDamage(secondaryRoll.damageRollData)
|
||||
: secondaryRoll.damageRollData;
|
||||
if (mainRoll.damageRollData) {
|
||||
for (const [key, damage] of Object.entries(secondaryDamage.types ?? {})) {
|
||||
if (key in mainRoll.damageRollData.types) {
|
||||
mainRoll.damageRollData.types[key] = Roll.fromTerms([
|
||||
...baseMainRoll.damageRollData.types[key].terms,
|
||||
if (secondaryDamage.main) {
|
||||
if (mainRoll.damageRollData.main) {
|
||||
mainRoll.damageRollData.main = Roll.fromTerms([
|
||||
...baseMainRoll.damageRollData.main.terms,
|
||||
new foundry.dice.terms.OperatorTerm({ operator: '+' }),
|
||||
...baseSecondaryRoll.damageRollData.types[key].terms
|
||||
...baseSecondaryRoll.damageRollData.main.terms
|
||||
]);
|
||||
|
||||
/* Joining the roll.options of both rolls */
|
||||
const joinedDamageTypes = new Set([
|
||||
...baseMainRoll.damageRollData.types[key].options.damageTypes,
|
||||
...baseSecondaryRoll.damageRollData.types[key].options.damageTypes
|
||||
...baseMainRoll.damageRollData.main.options.damageTypes,
|
||||
...baseSecondaryRoll.damageRollData.main.options.damageTypes
|
||||
]);
|
||||
mainRoll.damageRollData.types[key].options = {
|
||||
...baseMainRoll.damageRollData.types[key].options,
|
||||
mainRoll.damageRollData.main.options = {
|
||||
...baseMainRoll.damageRollData.main.options,
|
||||
damageTypes: [...joinedDamageTypes]
|
||||
};
|
||||
} else {
|
||||
mainRoll.damageRollData.types[key] = damage;
|
||||
mainRoll.damageRollData.main = secondaryDamage.main;
|
||||
}
|
||||
}
|
||||
|
||||
for (const [key, damage] of Object.entries(secondaryDamage.resources ?? {})) {
|
||||
if (key in mainRoll.damageRollData.resources) {
|
||||
mainRoll.damageRollData.resources[key] = Roll.fromTerms([
|
||||
...baseMainRoll.damageRollData.resources[key].terms,
|
||||
new foundry.dice.terms.OperatorTerm({ operator: '+' }),
|
||||
...baseSecondaryRoll.damageRollData.resources[key].terms
|
||||
]);
|
||||
|
||||
/* Joining the roll.options of both rolls */
|
||||
const joinedDamageTypes = new Set([
|
||||
...baseMainRoll.damageRollData.resources[key].options.damageTypes,
|
||||
...baseSecondaryRoll.damageRollData.resources[key].options.damageTypes
|
||||
]);
|
||||
mainRoll.damageRollData.resources[key].options = {
|
||||
...baseMainRoll.damageRollData.resources[key].options,
|
||||
damageTypes: [...joinedDamageTypes]
|
||||
};
|
||||
} else {
|
||||
mainRoll.damageRollData.resources[key] = damage;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -727,8 +751,12 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
...mainRoll.options,
|
||||
damage: joinedRoll.damageRollData?.toJSON()
|
||||
};
|
||||
for (const type of Object.keys(joinedRoll.damageRollData?.types ?? {})) {
|
||||
systemData.damage.types[type] = joinedRoll.damageRollData.types[type].toJSON();
|
||||
|
||||
if (joinedRoll.damageRollData.main) {
|
||||
systemData.damage.main = joinedRoll.damageRollData.toJSON();
|
||||
}
|
||||
for (const type of Object.keys(joinedRoll.damageRollData?.resources ?? {})) {
|
||||
systemData.damage.resources[type] = joinedRoll.damageRollData.resources[type].toJSON();
|
||||
}
|
||||
|
||||
const cls = getDocumentClass('ChatMessage'),
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ export default class DHAdversarySettings extends DHBaseActorSettings {
|
|||
classes: ['adversary-settings'],
|
||||
position: { width: 455, height: 'auto' },
|
||||
actions: {
|
||||
addExperience: DHAdversarySettings.#onAddExperience,
|
||||
removeExperience: DHAdversarySettings.#onRemoveExperience,
|
||||
addExperience: this.#onAddExperience,
|
||||
removeExperience: this.#onRemoveExperience,
|
||||
addDamage: this.#onAddDamage,
|
||||
removeDamage: this.#onRemoveDamage
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,18 +13,19 @@ export default class DHAttackAction extends DHDamageAction {
|
|||
if (this.damage.includeBase) {
|
||||
const baseDamage = this.getParentHitPointDamage();
|
||||
if (baseDamage) {
|
||||
if (!this.damage.parts.hitPoints) {
|
||||
this.damage.parts.hitPoints = baseDamage;
|
||||
if (!this.damage.main) {
|
||||
this.damage.main = baseDamage;
|
||||
} else {
|
||||
for (const type of baseDamage.type) this.damage.parts.hitPoints.type.add(type);
|
||||
for (const type of baseDamage.type) this.damage.main.type.add(type);
|
||||
|
||||
this.damage.parts.hitPoints.value.custom = {
|
||||
this.damage.main.value.custom = {
|
||||
enabled: true,
|
||||
formula: `${baseDamage.value.getFormula()} + ${this.damage.parts.hitPoints.value.getFormula()}`
|
||||
formula: `${baseDamage.value.getFormula()} + ${this.damage.main.value.getFormula()}`
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.roll.useDefault) {
|
||||
this.roll.trait = this.item.system.attack.roll.trait;
|
||||
this.roll.type = 'attack';
|
||||
|
|
@ -33,18 +34,18 @@ export default class DHAttackAction extends DHDamageAction {
|
|||
}
|
||||
|
||||
getParentHitPointDamage() {
|
||||
return this.item?.system?.attack.damage.parts.hitPoints;
|
||||
return this.item?.system?.attack.damage.main;
|
||||
}
|
||||
|
||||
get damageFormula() {
|
||||
const hitPointsPart = this.damage.parts.hitPoints;
|
||||
const hitPointsPart = this.damage.main;
|
||||
if (!hitPointsPart) return '0';
|
||||
|
||||
return hitPointsPart.value.getFormula();
|
||||
}
|
||||
|
||||
get altDamageFormula() {
|
||||
const hitPointsPart = this.damage.parts.hitPoints;
|
||||
const hitPointsPart = this.damage.main;
|
||||
if (!hitPointsPart) return '0';
|
||||
|
||||
return hitPointsPart.valueAlt.getFormula();
|
||||
|
|
|
|||
|
|
@ -288,7 +288,6 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
hasEffect: this.hasEffect,
|
||||
hasSave: this.hasSave,
|
||||
onSave: this.save?.damageMod,
|
||||
isDirect: !!this.damage?.direct,
|
||||
selectedMessageMode: game.settings.get('core', 'messageMode'),
|
||||
data: this.getRollData(),
|
||||
evaluate: this.hasRoll,
|
||||
|
|
@ -306,20 +305,20 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
};
|
||||
|
||||
if (this.damage) {
|
||||
config.isDirect = this.damage.direct;
|
||||
config.isDirect = !!this.damage.main?.direct;
|
||||
|
||||
const groupAttackTokens = this.damage.groupAttack
|
||||
const groupAttackTokens = this.damage.main?.groupAttack
|
||||
? game.system.api.fields.ActionFields.DamageField.getGroupAttackTokens(
|
||||
this.actor.id,
|
||||
this.damage.groupAttack
|
||||
this.damage.main.groupAttack
|
||||
)
|
||||
: null;
|
||||
|
||||
config.damageOptions = {
|
||||
groupAttack: this.damage.groupAttack
|
||||
groupAttack: this.damage.main?.groupAttack
|
||||
? {
|
||||
numAttackers: Math.max(groupAttackTokens.length, 1),
|
||||
range: this.damage.groupAttack
|
||||
range: this.damage.main.groupAttack
|
||||
}
|
||||
: null
|
||||
};
|
||||
|
|
@ -429,11 +428,11 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
|
|||
}
|
||||
|
||||
get hasDamage() {
|
||||
return this.type !== 'healing' && Boolean(this.damage.main) || Boolean(this.damage.resources.length);
|
||||
return this.type !== 'healing' && (Boolean(this.damage.main) || !foundry.utils.isEmpty(this.damage.resources));
|
||||
}
|
||||
|
||||
get hasHealing() {
|
||||
return this.type === 'healing' && Boolean(this.damage.main) || Boolean(this.damage.resources.length);
|
||||
return this.type === 'healing' && !foundry.utils.isEmpty(this.damage.resources);
|
||||
}
|
||||
|
||||
get hasSave() {
|
||||
|
|
|
|||
|
|
@ -40,17 +40,17 @@ export function getTierAdjustedAdversary(source, tier) {
|
|||
|
||||
// Store initial attack damage for abilities that have you deal a "standard attack"
|
||||
const initialAttack = {
|
||||
type: source.system.attack.damage?.parts.hitPoints?.type?.toSorted(),
|
||||
value: getFormula(source.system.attack.damage?.parts.hitPoints?.value)
|
||||
type: source.system.attack.damage?.main?.type?.toSorted(),
|
||||
value: getFormula(source.system.attack.damage?.main?.value)
|
||||
};
|
||||
|
||||
// Update damage of base attack.
|
||||
try {
|
||||
const damage = source.system.attack.damage;
|
||||
if (!damage?.parts.hitPoints) throw new Error('Unexpected missing attack in adversary');
|
||||
if (!damage?.main) throw new Error('Unexpected missing damage in adversary');
|
||||
|
||||
for (const property of ['value', 'valueAlt']) {
|
||||
const data = damage.parts.hitPoints[property];
|
||||
const data = damage.main[property];
|
||||
const previousFormula = getFormula(data);
|
||||
const value = calculateAdjustedDamage(previousFormula, 'attack', damageMeta);
|
||||
applyAdjustedDamage(data, value);
|
||||
|
|
@ -82,12 +82,12 @@ export function getTierAdjustedAdversary(source, tier) {
|
|||
|
||||
// Update damage in item actions and convert all formula matches in the descriptions to the new damage
|
||||
for (const action of Object.values(item.system.actions)) {
|
||||
if (!action.damage?.parts.hitPoints) continue;
|
||||
if (!action.damage?.main) continue;
|
||||
try {
|
||||
// Apply conversions and save a record. If it matches attack damage *and* Its not in the description, use attack conversion instead
|
||||
const result = [];
|
||||
for (const property of ['value', 'valueAlt']) {
|
||||
const { [property]: data, type: damageType } = action.damage.parts.hitPoints;
|
||||
const { [property]: data, type: damageType } = action.damage.main;
|
||||
const previousFormula = getFormula(data);
|
||||
const isActuallyAttack =
|
||||
previousFormula === initialAttack.value &&
|
||||
|
|
@ -199,7 +199,7 @@ function calculateAdjustedDamage(formula, type, { currentDamageRange, newDamageR
|
|||
}
|
||||
|
||||
/**
|
||||
* Get formula from either damage parts *or* a simple formula object.
|
||||
* Get formula from either damage data *or* a simple formula object.
|
||||
* @returns {string} the new formula data
|
||||
*/
|
||||
function getFormula(data) {
|
||||
|
|
|
|||
|
|
@ -188,31 +188,36 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
|
|||
}
|
||||
|
||||
static migrateData(source) {
|
||||
if (source.hasDamage && !source.damage.resources === undefined) {
|
||||
const { main, resources, ...flatDamageKeys } = source.damage;
|
||||
if (!main && !resources) {
|
||||
source.damage.main = null;
|
||||
source.damage.resources = {};
|
||||
|
||||
const getRoll = key => {
|
||||
const damageData = source.damage[key];
|
||||
const oldRoll = damageData.parts[0]?.roll;
|
||||
return oldRoll ? {
|
||||
return oldRoll ? JSON.stringify({
|
||||
...oldRoll,
|
||||
class: 'BaseRoll',
|
||||
options: {
|
||||
...oldRoll.options,
|
||||
damageTypes: damageData.parts[0].damageTypes ?? []
|
||||
}
|
||||
} : null;
|
||||
}) : null;
|
||||
};
|
||||
|
||||
source.damage = {
|
||||
main: source.damage.hitPoints ? getRoll('hitPoints') : null,
|
||||
resources: Object.keys(source.damage).reduce((acc, key) => {
|
||||
if (key === 'hitPoints') return acc;
|
||||
for (const key of Object.keys(flatDamageKeys)) {
|
||||
if (key === 'hitPoints' && source.hasDamage && !source.hasHealing) {
|
||||
source.damage.main = getRoll('hitPoints');
|
||||
}
|
||||
else {
|
||||
source.damage.resources[key] = getRoll(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const roll = getRoll(key);
|
||||
if (!roll) return acc;
|
||||
|
||||
acc[key] = roll;
|
||||
return acc;
|
||||
}, {})
|
||||
};
|
||||
for (const key of Object.keys(flatDamageKeys)) {
|
||||
delete source.damage[key];
|
||||
}
|
||||
|
||||
return source;
|
||||
|
|
|
|||
|
|
@ -28,10 +28,7 @@ export class ChatDamageData extends foundry.abstract.DataModel {
|
|||
}
|
||||
|
||||
_prepareRolls() {
|
||||
if (this.main) {
|
||||
this.main = Roll.fromData(this.main);
|
||||
}
|
||||
|
||||
this.main &&= Roll.fromData(this.main);
|
||||
for (const key of Object.keys(this.resources)) {
|
||||
this.resources[key] = Roll.fromData(this.resources[key]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ export default class DamageField extends fields.SchemaField {
|
|||
|
||||
const targetDamage = [];
|
||||
const damagePromises = [];
|
||||
for (let target of targets) {
|
||||
for (const target of targets) {
|
||||
const actor = foundry.utils.fromUuidSync(target.actorId);
|
||||
if (!actor) continue;
|
||||
if (!config.hasHealing && config.onSave && target.saved?.success === true) {
|
||||
|
|
@ -99,17 +99,15 @@ 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 = foundry.utils.deepClone(config.damage.types);
|
||||
const hpDamageMultiplier = config.actionActor?.system.rules?.attack?.damage?.hpDamageMultiplier ?? 1;
|
||||
const hpDamageTakenMultiplier = actor.system.rules?.attack?.damage?.hpDamageTakenMultiplier;
|
||||
if (configDamage.hitPoints) {
|
||||
configDamage.hitPoints = configDamage.hitPoints.toJSON();
|
||||
configDamage.hitPoints.total = Math.ceil(
|
||||
configDamage.hitPoints.total * hpDamageMultiplier * hpDamageTakenMultiplier
|
||||
);
|
||||
const configDamage = config.damage.clone();
|
||||
configDamage.main &&= configDamage.main.toJSON();
|
||||
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);
|
||||
}
|
||||
|
||||
damagePromises.push(
|
||||
|
|
@ -179,12 +177,12 @@ export default class DamageField extends fields.SchemaField {
|
|||
static formatFormulas(damageData, data) {
|
||||
const formulas = damageData.map(x => ({
|
||||
formula: DamageField.getFormulaValue.call(this, x, data).getFormula(this.actor),
|
||||
damageTypes: x.applyTo === 'hitPoints' && !x.type.size ? new Set(['physical']) : x.type,
|
||||
damageTypes: x.type ?? new Set(),
|
||||
applyTo: x.applyTo
|
||||
}));
|
||||
|
||||
const formattedFormulas = [];
|
||||
formulas.forEach(formula => {
|
||||
for (const formula of formulas) {
|
||||
if (isNaN(formula.formula))
|
||||
formula.formula = Roll.replaceFormulaData(formula.formula, this.getRollData(data));
|
||||
const same = formattedFormulas.find(
|
||||
|
|
@ -192,7 +190,7 @@ export default class DamageField extends fields.SchemaField {
|
|||
);
|
||||
if (same) same.formula += ` + ${formula.formula}`;
|
||||
else formattedFormulas.push(formula);
|
||||
});
|
||||
}
|
||||
|
||||
return formattedFormulas;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
@ -116,12 +120,10 @@ export default class DamageRoll extends DHRoll {
|
|||
const type = this.options.messageType ?? (this.options.hasHealing ? 'healing' : 'damage');
|
||||
const changeKeys = [];
|
||||
|
||||
for (const roll of this.options.roll) {
|
||||
for (const damageType of roll.damageTypes?.values?.() ?? []) {
|
||||
changeKeys.push(`system.bonuses.${type}.${damageType}`);
|
||||
}
|
||||
for (const damageType of this.options.damageFormula?.damageTypes?.values?.() ?? []) {
|
||||
changeKeys.push(`system.bonuses.${type}.${damageType}`);
|
||||
}
|
||||
|
||||
|
||||
const item = this.data.parent?.items?.get(this.options.source.item);
|
||||
if (item) {
|
||||
switch (item.type) {
|
||||
|
|
@ -145,6 +147,7 @@ export default class DamageRoll extends DHRoll {
|
|||
}
|
||||
|
||||
constructFormula(formulaData, config, isDamage) {
|
||||
if (!formulaData) return null;
|
||||
this.options.isCritical = config.isCritical;
|
||||
|
||||
const isHitpointPart = formulaData.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id;
|
||||
|
|
|
|||
|
|
@ -656,26 +656,23 @@ export default class DhpActor extends Actor {
|
|||
return;
|
||||
}
|
||||
|
||||
const updates = [];
|
||||
|
||||
Object.entries(damages).forEach(([key, damage]) => {
|
||||
if (key === CONFIG.DH.GENERAL.healingTypes.hitPoints.id)
|
||||
damage.total = this.calculateDamage(damage.total, damage.damageTypes);
|
||||
const update = updates.find(u => u.key === key);
|
||||
if (update) {
|
||||
update.value += damage.total;
|
||||
update.damageTypes.add(...new Set(damage.damageTypes));
|
||||
} else updates.push({ value: damage.total, key, damageTypes: new Set(damage.damageTypes) });
|
||||
});
|
||||
if (damages.main) {
|
||||
damages.main.total = this.calculateDamage(damages.main.total, damages.main.damageTypes);
|
||||
}
|
||||
|
||||
if (Hooks.call(`${CONFIG.DH.id}.postCalculateDamage`, this, damages) === false) return null;
|
||||
|
||||
if (!updates.length) return;
|
||||
// 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 (!updates.some(u => u.value) && !damages.main) return;
|
||||
|
||||
const hpDamage = updates.find(u => u.key === CONFIG.DH.GENERAL.healingTypes.hitPoints.id);
|
||||
if (hpDamage?.value) {
|
||||
hpDamage.value = this.convertDamageToThreshold(hpDamage.value);
|
||||
if (this.type === 'character' && !isDirect && this.#canReduceDamage(hpDamage.value, hpDamage.damageTypes)) {
|
||||
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',
|
||||
{
|
||||
|
|
@ -689,7 +686,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 });
|
||||
}
|
||||
|
|
@ -699,20 +696,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(
|
||||
|
|
@ -727,12 +728,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);
|
||||
|
||||
|
|
@ -741,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);
|
||||
|
|
@ -765,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.
|
||||
|
|
|
|||
|
|
@ -17,6 +17,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
.section-header {
|
||||
font-size: var(--font-size-20);
|
||||
color: light-dark(@dark, @beige);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.bonuses {
|
||||
gap: 4px;
|
||||
.critical-chip {
|
||||
|
|
|
|||
|
|
@ -15,12 +15,53 @@
|
|||
{{/each}}
|
||||
</fieldset>
|
||||
{{/if}}
|
||||
|
||||
{{#if @root.damageFormula}}
|
||||
{{#with @root.damageFormula}}
|
||||
<div class="damage-formula">
|
||||
<span class="damage-resource"><b>{{localize "DAGGERHEART.GENERAL.formula"}}:</b> {{roll.formula}}</span>
|
||||
<span class="damage-details">
|
||||
{{#with (lookup @root.config.GENERAL.healingTypes applyTo)}}
|
||||
{{localize label}}
|
||||
{{/with}}
|
||||
{{#if damageTypes}}
|
||||
{{#each damageTypes as | type | }}
|
||||
{{#with (lookup @root.config.GENERAL.damageTypes type)}}
|
||||
<i class="fa-solid {{icon}}"></i>
|
||||
{{/with}}
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="bonuses form-group flexrow">
|
||||
<input type="text" value="{{extraFormula}}" name="damageFormula.extraFormula" placeholder="{{localize "DAGGERHEART.GENERAL.situationalBonus"}}">
|
||||
<button class="critical-chip" data-action="toggleCritical">
|
||||
<span><i class="{{#if @root.isCritical}}fa-solid{{else}}fa-regular{{/if}} fa-circle"></i></span>
|
||||
<span class="label">{{localize "DAGGERHEART.GENERAL.criticalShort"}}</span>
|
||||
</button>
|
||||
</div>
|
||||
{{/with}}
|
||||
{{/if}}
|
||||
|
||||
{{> formula @root.damageFormula path="damageFormula"}}
|
||||
<div class="flexcol">
|
||||
{{#unless (empty @root.resourceFormulas)}}
|
||||
<div class="section-header">{{localize "Resource Drain"}}</div>
|
||||
{{/unless}}
|
||||
|
||||
{{#each @root.resourceFormulas}}
|
||||
{{> formula path=(concat "resourceFormulas." @key)}}
|
||||
{{/each}}
|
||||
{{#each @root.resourceFormulas}}
|
||||
<div class="damage-formula">
|
||||
<span class="damage-resource"><b>{{localize "DAGGERHEART.GENERAL.formula"}}:</b> {{roll.formula}}</span>
|
||||
<span class="damage-details">
|
||||
{{#with (lookup @root.config.GENERAL.healingTypes applyTo)}}
|
||||
{{localize label}}
|
||||
{{/with}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="bonuses form-group flexrow">
|
||||
<input type="text" value="{{extraFormula}}" name={{concat "resourceFormulas." @key ".extraFormula"}} placeholder="{{localize "DAGGERHEART.GENERAL.situationalBonus"}}">
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
|
||||
{{#if damageOptions.groupAttack}}
|
||||
<fieldset class="group-attack-container">
|
||||
|
|
@ -66,31 +107,4 @@
|
|||
<span class="label">{{localize "DAGGERHEART.GENERAL.roll"}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{{#*inline "formula"}}
|
||||
<div class="damage-formula">
|
||||
<span class="damage-resource"><b>{{localize "DAGGERHEART.GENERAL.formula"}}:</b> {{roll.formula}}</span>
|
||||
<span class="damage-details">
|
||||
{{#with (lookup @root.config.GENERAL.healingTypes applyTo)}}
|
||||
{{localize label}}
|
||||
{{/with}}
|
||||
{{#unless @root.hasHealing}}
|
||||
{{#if damageTypes}}
|
||||
{{#each damageTypes as | type | }}
|
||||
{{#with (lookup @root.config.GENERAL.damageTypes type)}}
|
||||
<i class="fa-solid {{icon}}"></i>
|
||||
{{/with}}
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="bonuses form-group flexrow">
|
||||
<input type="text" value="{{extraFormula}}" name={{concat path ".extraFormula"}} placeholder="{{localize "DAGGERHEART.GENERAL.situationalBonus"}}">
|
||||
<button class="critical-chip" data-action="toggleCritical">
|
||||
<span><i class="{{#if @root.isCritical}}fa-solid{{else}}fa-regular{{/if}} fa-circle"></i></span>
|
||||
<span class="label">{{localize "DAGGERHEART.GENERAL.criticalShort"}}</span>
|
||||
</button>
|
||||
</div>
|
||||
{{/inline}}
|
||||
</section>
|
||||
Loading…
Add table
Add a link
Reference in a new issue