Healing updates

This commit is contained in:
Dapoolp 2025-07-27 23:31:38 +02:00
parent 8cb4b7663e
commit 9d0d35d1a7
21 changed files with 146 additions and 86 deletions

View file

@ -38,17 +38,15 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application
};
get title() {
return game.i18n.localize('DAGGERHEART.EFFECTS.ApplyLocations.damageRoll.name');
return game.i18n.localize(`DAGGERHEART.EFFECTS.ApplyLocations.${this.config.isHealing ? 'healing' : 'damage'}Roll.name`);
}
async _prepareContext(_options) {
const context = await super._prepareContext(_options);
context.config = CONFIG.DH;
context.title = this.config.title
? this.config.title
: game.i18n.localize('DAGGERHEART.EFFECTS.ApplyLocations.damageRoll.name');
// context.extraFormula = this.config.extraFormula;
context.title = this.config.title ?? this.title;
context.formula = this.roll.constructFormula(this.config);
context.isHealing = this.config.isHealing;
context.directDamage = this.config.directDamage;
context.selectedRollMode = this.config.selectedRollMode;
context.rollModes = Object.entries(CONFIG.Dice.rollModes).map(([action, { label, icon }]) => ({

View file

@ -1,3 +1,5 @@
// TO DELETE ?
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
export default class DamageSelectionDialog extends HandlebarsApplicationMixin(ApplicationV2) {

View file

@ -118,7 +118,6 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
{ key: 1, label: game.i18n.localize('DAGGERHEART.GENERAL.Tiers.1') },
...Object.values(settingsTiers).map(x => ({ key: x.tier, label: x.name }))
];
return context;
}

View file

@ -17,9 +17,9 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
html.querySelectorAll('.duality-action-damage').forEach(element =>
element.addEventListener('click', event => this.onRollDamage(event, data.message))
);
html.querySelectorAll('.duality-action-healing').forEach(element =>
element.addEventListener('click', event => this.onRollHealing(event, data.message))
);
// html.querySelectorAll('.duality-action-healing').forEach(element =>
// element.addEventListener('click', event => this.onRollDamage(event, data.message))
// );
html.querySelectorAll('.target-save-container').forEach(element =>
element.addEventListener('click', event => this.onRollSave(event, data.message))
);
@ -92,16 +92,16 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
}
}
async onRollHealing(event, message) {
event.stopPropagation();
const actor = await this.getActor(message.system.source.actor);
if (!actor || !game.user.isGM) return true;
if (message.system.source.item && message.system.source.action) {
const action = this.getAction(actor, message.system.source.item, message.system.source.action);
if (!action || !action?.rollHealing) return;
await action.rollHealing(event, message);
}
}
// async onRollHealing(event, message) {
// event.stopPropagation();
// const actor = await this.getActor(message.system.source.actor);
// if (!actor || !game.user.isGM) return true;
// if (message.system.source.item && message.system.source.action) {
// const action = this.getAction(actor, message.system.source.item, message.system.source.action);
// if (!action || !action?.rollHealing) return;
// await action.rollHealing(event, message);
// }
// }
async onRollSave(event, message) {
event.stopPropagation();
@ -160,7 +160,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
return {
isHit,
targets: isHit
? message.system.targets.filter(t => t.hit === true).map(target => game.canvas.tokens.get(target.id))
? message.system.targets.filter(t => t.hit === true).map(target => game.canvas.tokens.documentCollection.find(t => t.actor.uuid === target.actorId))
: Array.from(game.user.targets)
};
}
@ -222,21 +222,24 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
});
}
target.actor.takeDamage(damages);
if(message.system.hasHealing)
target.actor.takeHealing(damages);
else
target.actor.takeDamage(damages);
}
}
async onHealing(event, message) {
event.stopPropagation();
const targets = Array.from(game.user.targets);
// async onHealing(event, message) {
// event.stopPropagation();
// const targets = Array.from(game.user.targets);
if (targets.length === 0)
return ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelected'));
// if (targets.length === 0)
// return ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelected'));
for (var target of targets) {
target.actor.takeHealing(message.system.roll);
}
}
// for (var target of targets) {
// target.actor.takeHealing(message.system.roll);
// }
// }
/**
* Toggle visibility of target containers.

View file

@ -439,7 +439,7 @@ export const abilityCosts = {
},
armor: {
id: 'armor',
label: 'Armor Stack',
label: 'Armor Slot',
group: 'TYPES.Actor.character'
},
fear: {

View file

@ -168,8 +168,8 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
},
dialog: {},
type: this.type,
hasDamage: !!this.damage?.parts?.length,
hasHealing: !!this.healing,
hasDamage: this.damage?.parts?.length && this.type !== 'healing',
hasHealing: this.damage?.parts?.length && this.type === 'healing',
hasEffect: !!this.effects?.length,
hasSave: this.hasSave,
selectedRollMode: game.settings.get('core', 'rollMode'),

View file

@ -47,11 +47,12 @@ export default class DHDamageAction extends DHBaseAction {
formulas = this.formatFormulas(formulas, systemData);
const config = {
title: game.i18n.format('DAGGERHEART.UI.Chat.damageRoll.title', { damage: game.i18n.localize(this.name) }),
title: game.i18n.format(`DAGGERHEART.UI.Chat.${ this.type === 'healing' ? 'healing' : 'damage'}Roll.title`, { damage: game.i18n.localize(this.name) }),
roll: formulas,
targets: systemData.targets?.filter(t => t.hit) ?? data.targets,
hasSave: this.hasSave,
isCritical: systemData.roll?.isCritical ?? false,
isHealing: this.type === 'healing',
source: systemData.source,
data: this.getRollData(),
event

View file

@ -1,7 +1,14 @@
import DHBaseAction from './baseAction.mjs';
import DHDamageAction from './damageAction.mjs';
export default class DHHealingAction extends DHBaseAction {
static extraSchemas = [...super.extraSchemas, 'target', 'effects', 'healing', 'roll'];
export default class DHHealingAction extends DHDamageAction {
static extraSchemas = [...super.extraSchemas, 'roll'];
static getRollType(parent) {
return 'spellcast';
}
/* static extraSchemas = [...super.extraSchemas, 'target', 'effects', 'healing', 'roll'];
static getRollType(parent) {
return 'spellcast';
@ -44,5 +51,5 @@ export default class DHHealingAction extends DHBaseAction {
get modifiers() {
return [];
}
} */
}

View file

@ -21,6 +21,7 @@ export default class DHDamageRoll extends foundry.abstract.TypeDataModel {
),
targetSelection: new fields.BooleanField({ initial: true }),
hasSave: new fields.BooleanField({ initial: false }),
isHealing: new fields.BooleanField({ initial: false }),
onSave: new fields.StringField(),
source: new fields.SchemaField({
actor: new fields.StringField(),

View file

@ -2,8 +2,11 @@ import { DHDamageData } from './damageField.mjs';
const fields = foundry.data.fields;
export default class HealingField extends fields.EmbeddedDataField {
export default class HealingField extends fields.SchemaField {
constructor(options, context = {}) {
super(DHDamageData, options, context);
const healingFields = {
parts: new fields.ArrayField(new fields.EmbeddedDataField(DHDamageData))
};
super(healingFields, options, context);
}
}

View file

@ -17,11 +17,13 @@ export default class TargetField extends fields.SchemaField {
if (!this.target?.type) return [];
let targets;
if (this.target?.type === CONFIG.DH.ACTIONS.targetTypes.self.id)
targets = TargetField.formatTarget.call(this, this.actor.token ?? this.actor.prototypeToken);
targets = Array.from(game.user.targets);
if (this.target.type !== CONFIG.DH.ACTIONS.targetTypes.any.id) {
targets = targets.filter(t => TargetField.isTargetFriendly.call(this, t));
if (this.target.amount && targets.length > this.target.amount) targets = [];
targets = [this.actor.token ?? this.actor.prototypeToken];
else {
targets = Array.from(game.user.targets);
if (this.target.type !== CONFIG.DH.ACTIONS.targetTypes.any.id) {
targets = targets.filter(t => TargetField.isTargetFriendly.call(this, t));
if (this.target.amount && targets.length > this.target.amount) targets = [];
}
}
config.targets = targets.map(t => TargetField.formatTarget.call(this, t));
const hasTargets = TargetField.checkTargets.call(this, this.target.amount, config.targets);

View file

@ -200,7 +200,7 @@ export function ActionMixin(Base) {
}
);
const created = await parent.parent.update({ [`system.actions.${action.id}`]: action.toObject() });
const newAction = parent.actions.get(action.id);
const newAction = created.system.actions.get(action.id);
if (!newAction) return null;
if (renderSheet) newAction.sheet.render({ force: true });
return newAction;
@ -215,10 +215,7 @@ export function ActionMixin(Base) {
await this.parent.updateSource({ [path]: updates }, options);
result = this.parent;
} else {
/* Fix me - For some reason updating the "healing" section in particular doesn't work without this */
await this.item.update({ [path]: updates }, options);
await this.item.updateSource({ [path]: updates }, options);
result = this.item;
result = await this.item.update({ [path]: updates }, options);
}
return this.inCollection

View file

@ -83,18 +83,20 @@ export default class DamageRoll extends DHRoll {
applyBaseBonus(part) {
const modifiers = [],
type = this.options.messageType ?? 'damage',
type = this.options.messageType ?? (this.options.isHealing ? 'healing' : 'damage'),
options = part ?? this.options;
modifiers.push(...this.getBonus(`${type}`, `${type.capitalize()} Bonus`));
options.damageTypes?.forEach(t => {
modifiers.push(...this.getBonus(`${type}.${t}`, `${t.capitalize()} ${type.capitalize()} Bonus`));
});
const weapons = ['primaryWeapon', 'secondaryWeapon'];
weapons.forEach(w => {
if (this.options.source.item && this.options.source.item === this.data[w]?.id)
modifiers.push(...this.getBonus(`${type}.${w}`, 'Weapon Bonus'));
});
if(!this.options.isHealing) {
options.damageTypes?.forEach(t => {
modifiers.push(...this.getBonus(`${type}.${t}`, `${t.capitalize()} ${type.capitalize()} Bonus`));
});
const weapons = ['primaryWeapon', 'secondaryWeapon'];
weapons.forEach(w => {
if (this.options.source.item && this.options.source.item === this.data[w]?.id)
modifiers.push(...this.getBonus(`${type}.${w}`, 'Weapon Bonus'));
});
}
return modifiers;
}

View file

@ -471,7 +471,7 @@ export default class DhpActor extends Actor {
await this.modifyResource(updates);
if (Hooks.call(`${CONFIG.DH.id}.postTakeDamage`, this, damages) === false) return null;
if (Hooks.call(`${CONFIG.DH.id}.postTakeDamage`, this, updates) === false) return null;
}
calculateDamage(baseDamage, type) {
@ -498,14 +498,28 @@ export default class DhpActor extends Actor {
return reduction === Infinity ? 0 : reduction;
}
async takeHealing(resources) {
const updates = Object.entries(resources).map(([key, value]) => ({
key: key,
value: !(key === 'fear' || this.system?.resources?.[key]?.isReversed === false)
? value.total * -1
: value.total
}));
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;
}
async modifyResource(resources) {
@ -547,6 +561,7 @@ export default class DhpActor extends Actor {
}
}
});
Object.keys(updates).forEach(async key => {
const u = updates[key];
if (key === 'items') {

View file

@ -53,6 +53,7 @@ export const getCommandTarget = () => {
};
export const setDiceSoNiceForDualityRoll = async (rollResult, advantageState, hopeFaces, fearFaces, advantageFaces) => {
if (!game.modules.get('dice-so-nice')?.active) return;
const diceSoNicePresets = await getDiceSoNicePresets(hopeFaces, fearFaces, advantageFaces, advantageFaces);
rollResult.dice[0].options = diceSoNicePresets.hope;
rollResult.dice[1].options = diceSoNicePresets.fear;