Some fixes

This commit is contained in:
Dapoolp 2025-08-26 13:30:38 +02:00
parent 92a0883806
commit fb9240e130
18 changed files with 94 additions and 82 deletions

View file

@ -2228,11 +2228,11 @@
}, },
"damageApply": { "damageApply": {
"label": "Apply Damage/Healing", "label": "Apply Damage/Healing",
"hint": "Automatically apply damages & healings. Targets must be selected before the action is made. Bypass users permissions." "hint": "Automatically apply damages & healings. Targets must be selected before the action is made and Reaction Roll Automation must be different than Never. Bypass users permissions."
}, },
"effect": { "effect": {
"label": "Apply Effects", "label": "Apply Effects",
"hint": "Automatically apply effects. Targets must be selected before the action is made. Bypass users permissions." "hint": "Automatically apply effects. Targets must be selected before the action is made and Reaction Roll Automation must be different than Never. Bypass users permissions."
} }
} }
}, },

View file

@ -146,9 +146,9 @@ export default class AdversarySheet extends DHBaseActorSheet {
title: `Reaction Roll: ${this.actor.name}`, title: `Reaction Roll: ${this.actor.name}`,
headerTitle: 'Adversary Reaction Roll', headerTitle: 'Adversary Reaction Roll',
roll: { roll: {
type: 'reaction' type: 'trait'
}, },
type: 'trait', actionType: 'reaction',
hasRoll: true, hasRoll: true,
data: this.actor.getRollData() data: this.actor.getRollData()
}; };

View file

@ -208,7 +208,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
prepareBaseConfig(event) { prepareBaseConfig(event) {
const config = { const config = {
event, event,
title: `${this.item.name}: ${game.i18n.localize(this.name)}`, title: `${this.item instanceof CONFIG.Actor.documentClass ? "" : `${this.item.name}: `}${game.i18n.localize(this.name)}`,
source: { source: {
item: this.item._id, item: this.item._id,
action: this._id, action: this._id,

View file

@ -95,7 +95,7 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
} }
registerTargetHook() { registerTargetHook() {
if (!this.parent.isAuthor) return; if (!this.parent.isAuthor || !this.hasTarget) return;
if (this.targetMode && this.parent.targetHook !== null) { if (this.targetMode && this.parent.targetHook !== null) {
Hooks.off('targetToken', this.parent.targetHook); Hooks.off('targetToken', this.parent.targetHook);
return (this.parent.targetHook = null); return (this.parent.targetHook = null);

View file

@ -111,7 +111,7 @@ export default class CostField extends fields.ArrayField {
c.key === 'fear' c.key === 'fear'
? game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear) ? game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear)
: resources[c.key].isReversed : resources[c.key].isReversed
? resources[c.key].max ? resources[c.key].max - resources[c.key].value
: resources[c.key].value; : resources[c.key].value;
if (c.scalable) c.maxStep = Math.floor((c.max - c.value) / c.step); if (c.scalable) c.maxStep = Math.floor((c.max - c.value) / c.step);
return c; return c;

View file

@ -62,6 +62,7 @@ export default class DamageField extends fields.SchemaField {
const damageResult = await CONFIG.Dice.daggerheart.DamageRoll.build(damageConfig); const damageResult = await CONFIG.Dice.daggerheart.DamageRoll.build(damageConfig);
if(!damageResult) return false; if(!damageResult) return false;
config.damage = damageResult.damage; config.damage = damageResult.damage;
config.message ??= damageConfig.message;
} }
/** /**
@ -71,7 +72,7 @@ export default class DamageField extends fields.SchemaField {
* @param {boolean} force If the method should be executed outside of Action workflow, for ChatMessage button for example. * @param {boolean} force If the method should be executed outside of Action workflow, for ChatMessage button for example.
*/ */
static async applyDamage(config, targets = null, force = false) { static async applyDamage(config, targets = null, force = false) {
targets ??= config.targets; targets ??= config.targets.filter(target => target.hit);
if(!config.damage || !targets?.length || (!DamageField.getApplyAutomation() && !force)) return; if(!config.damage || !targets?.length || (!DamageField.getApplyAutomation() && !force)) return;
for (let target of targets) { for (let target of targets) {
const actor = fromUuidSync(target.actorId); const actor = fromUuidSync(target.actorId);

View file

@ -1,3 +1,5 @@
import { emitAsGM, GMUpdateEvent } from "../../../systemRegistration/socket.mjs";
const fields = foundry.data.fields; const fields = foundry.data.fields;
export default class EffectsField extends fields.ArrayField { export default class EffectsField extends fields.ArrayField {
@ -31,8 +33,14 @@ export default class EffectsField extends fields.ArrayField {
message = config.message = await CONFIG.Dice.daggerheart.DHRoll.toMessage(roll, config); message = config.message = await CONFIG.Dice.daggerheart.DHRoll.toMessage(roll, config);
} }
if(EffectsField.getAutomation() || force) { if(EffectsField.getAutomation() || force) {
targets ??= config.targets.filter(t => !config.hasRoll || t.hit); targets ??= (message.system?.targets ?? config.targets).filter(t => !config.hasRoll || t.hit);
EffectsField.applyEffects.call(this, config.targets.filter(t => !config.hasRoll || t.hit)); await emitAsGM(
GMUpdateEvent.UpdateEffect,
EffectsField.applyEffects.bind(this),
targets,
this.uuid
);
// EffectsField.applyEffects.call(this, config.targets.filter(t => !config.hasRoll || t.hit));
} }
} }

View file

@ -71,29 +71,6 @@ export class DHActionRollData extends foundry.abstract.DataModel {
const modifiers = []; const modifiers = [];
if (!this.parent?.actor) return modifiers; if (!this.parent?.actor) return modifiers;
switch (this.parent.actor.type) { switch (this.parent.actor.type) {
case 'character':
// const spellcastingTrait =
// this.type === 'spellcast'
// ? (this.parent.actor?.system?.spellcastModifierTrait?.key ?? 'agility')
// : null;
// const trait =
// this.useDefault || !this.trait
// ? (spellcastingTrait ?? this.parent.item.system.attack?.roll?.trait ?? 'agility')
// : this.trait;
// if (
// this.type === CONFIG.DH.GENERAL.rollTypes.attack.id ||
// this.type === CONFIG.DH.GENERAL.rollTypes.trait.id
// )
// modifiers.push({
// label: `DAGGERHEART.CONFIG.Traits.${trait}.name`,
// value: this.parent.actor.system.traits[trait].value
// });
// else if (this.type === CONFIG.DH.GENERAL.rollTypes.spellcast.id)
// modifiers.push({
// label: `DAGGERHEART.CONFIG.RollTypes.spellcast.name`,
// value: this.parent.actor.system.spellcastModifier
// });
break;
case 'companion': case 'companion':
case 'adversary': case 'adversary':
if (this.type === CONFIG.DH.GENERAL.rollTypes.attack.id) if (this.type === CONFIG.DH.GENERAL.rollTypes.attack.id)

View file

@ -35,6 +35,7 @@ export default class SaveField extends fields.SchemaField {
static async execute(config, targets = null, force = false) { static async execute(config, targets = null, force = false) {
if(!config.hasSave) return; if(!config.hasSave) return;
let message = config.message ?? ui.chat.collection.get(config.parent?._id); let message = config.message ?? ui.chat.collection.get(config.parent?._id);
if(!message) { if(!message) {
const roll = new CONFIG.Dice.daggerheart.DHRoll(''); const roll = new CONFIG.Dice.daggerheart.DHRoll('');
roll._evaluated = true; roll._evaluated = true;
@ -42,8 +43,8 @@ export default class SaveField extends fields.SchemaField {
} }
if(SaveField.getAutomation() !== CONFIG.DH.SETTINGS.actionAutomationChoices.never.id || force) { if(SaveField.getAutomation() !== CONFIG.DH.SETTINGS.actionAutomationChoices.never.id || force) {
targets ??= config.targets.filter(t => !config.hasRoll || t.hit); targets ??= config.targets.filter(t => !config.hasRoll || t.hit);
SaveField.rollAllSave.call(this, targets, config.event, message); await SaveField.rollAllSave.call(this, targets, config.event, message);
} } else return false;
} }
/** /**
@ -54,22 +55,32 @@ export default class SaveField extends fields.SchemaField {
* @param {ChatMessage} message The ChatMessage the triggered button comes from. * @param {ChatMessage} message The ChatMessage the triggered button comes from.
*/ */
static async rollAllSave(targets, event, message) { static async rollAllSave(targets, event, message) {
if(!targets || !game.user.isGM) return; if(!targets) return;
targets.forEach(target => { return new Promise(resolve => {
const actor = fromUuidSync(target.actorId); const aPromise = [];
if(actor) { targets.forEach(target => {
const rollSave = game.user === actor.owner ? aPromise.push(
SaveField.rollSave.call(this, actor, event, message) new Promise(async subResolve => {
: actor.owner const actor = fromUuidSync(target.actorId);
.query('reactionRoll', { if(actor) {
actionId: this.uuid, const rollSave = game.user === actor.owner ?
actorId: actor.uuid, SaveField.rollSave.call(this, actor, event)
event, : actor.owner
message .query('reactionRoll', {
}); actionId: this.uuid,
rollSave.then(result => SaveField.updateSaveMessage.call(this, result, message, target.id)); actorId: actor.uuid,
} event,
}); message
});
const result = await rollSave;
await SaveField.updateSaveMessage.call(this, result, message, target.id);
subResolve();
} else subResolve();
})
)
});
Promise.all(aPromise).then(result => resolve());
})
} }
/** /**
@ -92,13 +103,13 @@ export default class SaveField extends fields.SchemaField {
roll: { roll: {
trait: this.save.trait, trait: this.save.trait,
difficulty: this.save.difficulty ?? this.actor?.baseSaveDifficulty, difficulty: this.save.difficulty ?? this.actor?.baseSaveDifficulty,
type: 'reaction' type: 'trait'
}, },
type: 'trait', actionType: 'reaction',
hasRoll: true, hasRoll: true,
data: actor.getRollData() data: actor.getRollData()
}; };
if(SaveField.getAutomation() == CONFIG.DH.SETTINGS.actionAutomationChoices.always.id) rollConfig.dialog = { configure: false }; if(SaveField.getAutomation() === CONFIG.DH.SETTINGS.actionAutomationChoices.always.id) rollConfig.dialog = { configure: false };
return actor.diceRoll(rollConfig); return actor.diceRoll(rollConfig);
} }
@ -108,10 +119,10 @@ export default class SaveField extends fields.SchemaField {
* @param {object} message ChatMessage to update * @param {object} message ChatMessage to update
* @param {string} targetId Token ID * @param {string} targetId Token ID
*/ */
static updateSaveMessage(result, message, targetId) { static async updateSaveMessage(result, message, targetId) {
if (!result) return; if (!result) return;
const updateMsg = function(message, targetId, result) { const updateMsg = async function(message, targetId, result) {
setTimeout(async () => { // setTimeout(async () => {
const chatMessage = ui.chat.collection.get(message._id), const chatMessage = ui.chat.collection.get(message._id),
changes = { changes = {
flags: { flags: {
@ -127,11 +138,11 @@ export default class SaveField extends fields.SchemaField {
} }
}; };
await chatMessage.update(changes); await chatMessage.update(changes);
}, 100); // }, 100);
}; };
if (game.modules.get('dice-so-nice')?.active) if (game.modules.get('dice-so-nice')?.active)
game.dice3d.waitFor3DAnimationByMessageID(result.message.id ?? result.message._id).then(() => updateMsg(message, targetId, result)); game.dice3d.waitFor3DAnimationByMessageID(result.message.id ?? result.message._id).then(async () => await updateMsg(message, targetId, result));
else updateMsg(message, targetId, result); else await updateMsg(message, targetId, result);
} }
/** /**

View file

@ -28,6 +28,7 @@ export default class DHRoll extends Roll {
static async buildConfigure(config = {}, message = {}) { static async buildConfigure(config = {}, message = {}) {
config.hooks = [...this.getHooks(), '']; config.hooks = [...this.getHooks(), ''];
config.dialog ??= {}; config.dialog ??= {};
for (const hook of config.hooks) { for (const hook of config.hooks) {
if (Hooks.call(`${CONFIG.DH.id}.preRoll${hook.capitalize()}`, config, message) === false) return null; if (Hooks.call(`${CONFIG.DH.id}.preRoll${hook.capitalize()}`, config, message) === false) return null;
} }

View file

@ -154,7 +154,7 @@ export default class DualityRoll extends D20Roll {
applyBaseBonus() { applyBaseBonus() {
const modifiers = super.applyBaseBonus(); const modifiers = super.applyBaseBonus();
if (this.options.roll.trait && this.data.traits[this.options.roll.trait]) if (this.options.roll.trait && this.data.traits?.[this.options.roll.trait])
modifiers.unshift({ modifiers.unshift({
label: this.options.roll.type === CONFIG.DH.GENERAL.rollTypes.spellcast.id ? "DAGGERHEART.CONFIG.RollTypes.spellcast.name" : `DAGGERHEART.CONFIG.Traits.${this.options.roll.trait}.name`, label: this.options.roll.type === CONFIG.DH.GENERAL.rollTypes.spellcast.id ? "DAGGERHEART.CONFIG.RollTypes.spellcast.name" : `DAGGERHEART.CONFIG.Traits.${this.options.roll.trait}.name`,
value: this.data.traits[this.options.roll.trait].value value: this.data.traits[this.options.roll.trait].value

View file

@ -14,7 +14,7 @@ export default class DhpActor extends Actor {
get owner() { get owner() {
const user = const user =
this.hasPlayerOwner && game.users.players.find(u => this.testUserPermission(u, 'OWNER') && u.active); this.hasPlayerOwner && game.users.players.find(u => this.testUserPermission(u, 'OWNER') && u.active);
if (!user) return game.user.isGM ? game.user : null; if (!user) return game.users.activeGM;
return user; return user;
} }

View file

@ -147,12 +147,16 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
async onRollDamage(event) { async onRollDamage(event) {
event.stopPropagation(); event.stopPropagation();
this.system.action?.workflow.get("damage")?.execute(this.system, this._id, true); const config = foundry.utils.deepClone(this.system);
config.event = event;
this.system.action?.workflow.get("damage")?.execute(config, this._id, true);
} }
async onApplyDamage(event) { async onApplyDamage(event) {
event.stopPropagation(); event.stopPropagation();
const targets = this.filterPermTargets(this.system.hitTargets); const targets = this.filterPermTargets(this.system.hitTargets),
config = foundry.utils.deepClone(this.system);
config.event = event;
if (this.system.onSave) { if (this.system.onSave) {
const pendingingSaves = targets.filter(t => t.saved.success === null); const pendingingSaves = targets.filter(t => t.saved.success === null);
@ -169,7 +173,7 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
return ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelectedOrPerm')); return ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelectedOrPerm'));
this.consumeOnSuccess(); this.consumeOnSuccess();
this.system.action?.workflow.get("applyDamage")?.execute(this.system, targets, true); this.system.action?.workflow.get("applyDamage")?.execute(config, targets, true);
} }
async onRollSave(event) { async onRollSave(event) {
@ -198,17 +202,21 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
async onRollAllSave(event) { async onRollAllSave(event) {
event.stopPropagation(); event.stopPropagation();
if (!game.user.isGM) return; if (!game.user.isGM) return;
const targets = this.system.hitTargets; const targets = this.system.hitTargets,
this.system.action?.workflow.get("save")?.execute(this.system, targets, true); config = foundry.utils.deepClone(this.system);
config.event = event;
this.system.action?.workflow.get("save")?.execute(config, targets, true);
} }
async onApplyEffect(event) { async onApplyEffect(event) {
event.stopPropagation(); event.stopPropagation();
const targets = this.filterPermTargets(this.system.hitTargets); const targets = this.filterPermTargets(this.system.hitTargets),
config = foundry.utils.deepClone(this.system);
config.event = event;
if (targets.length === 0) if (targets.length === 0)
ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelectedOrPerm')); ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelectedOrPerm'));
this.consumeOnSuccess(); this.consumeOnSuccess();
this.system.action?.workflow.get("effects")?.execute(this.system, targets, true); this.system.action?.workflow.get("effects")?.execute(config, targets, true);
} }
filterPermTargets(targets) { filterPermTargets(targets) {

View file

@ -22,6 +22,7 @@ export const socketEvent = {
export const GMUpdateEvent = { export const GMUpdateEvent = {
UpdateDocument: 'DhGMUpdateDocument', UpdateDocument: 'DhGMUpdateDocument',
UpdateEffect: 'DhGMUpdateEffect',
UpdateSetting: 'DhGMUpdateSetting', UpdateSetting: 'DhGMUpdateSetting',
UpdateFear: 'DhGMUpdateFear', UpdateFear: 'DhGMUpdateFear',
UpdateSaveMessage: 'DhGMUpdateSaveMessage' UpdateSaveMessage: 'DhGMUpdateSaveMessage'
@ -37,9 +38,12 @@ export const registerSocketHooks = () => {
const document = data.uuid ? await fromUuid(data.uuid) : null; const document = data.uuid ? await fromUuid(data.uuid) : null;
switch (data.action) { switch (data.action) {
case GMUpdateEvent.UpdateDocument: case GMUpdateEvent.UpdateDocument:
if (document && data.update) { if (document && data.update)
await document.update(data.update); await document.update(data.update);
} break;
case GMUpdateEvent.UpdateEffect:
if (document && data.update)
await game.system.api.fields.ActionFields.EffectsField.applyEffects.call(document, data.update);
break; break;
case GMUpdateEvent.UpdateSetting: case GMUpdateEvent.UpdateSetting:
await game.settings.set(CONFIG.DH.id, data.uuid, data.update); await game.settings.set(CONFIG.DH.id, data.uuid, data.update);

View file

@ -56,7 +56,7 @@
<div class="nest-inputs"> <div class="nest-inputs">
<input type="hidden" name="{{../path}}damage.parts.{{realIndex}}.valueAlt.multiplier" value="flat"> <input type="hidden" name="{{../path}}damage.parts.{{realIndex}}.valueAlt.multiplier" value="flat">
{{formField ../fields.valueAlt.fields.flatMultiplier value=dmg.valueAlt.flatMultiplier name=(concat ../path "damage.parts." realIndex ".valueAlt.flatMultiplier") label="DAGGERHEART.ACTIONS.Settings.multiplier" classes="inline-child" localize=true }} {{formField ../fields.valueAlt.fields.flatMultiplier value=dmg.valueAlt.flatMultiplier name=(concat ../path "damage.parts." realIndex ".valueAlt.flatMultiplier") label="DAGGERHEART.ACTIONS.Settings.multiplier" classes="inline-child" localize=true }}
{{formField ../fields.valueAlt.fields.dice value=dmg.valueAlt.dice name=(concat ../path "damage.parts." realIndex ".valueAlt.dice") classes="inline-child"}} {{formField ../fields.valueAlt.fields.dice value=dmg.valueAlt.dice name=(concat ../path "damage.parts." realIndex ".valueAlt.dice") classes="inline-child" localize=true}}
{{formField ../fields.valueAlt.fields.bonus value=dmg.valueAlt.bonus name=(concat ../path "damage.parts." realIndex ".valueAlt.bonus") localize=true classes="inline-child"}} {{formField ../fields.valueAlt.fields.bonus value=dmg.valueAlt.bonus name=(concat ../path "damage.parts." realIndex ".valueAlt.bonus") localize=true classes="inline-child"}}
</div> </div>
</fieldset> </fieldset>
@ -70,7 +70,7 @@
{{#*inline "formula"}} {{#*inline "formula"}}
{{#unless dmg.base}} {{#unless dmg.base}}
{{formField fields.custom.fields.enabled value=source.custom.enabled name=(concat path "damage.parts." realIndex "." target ".custom.enabled") classes="checkbox"}} {{formField fields.custom.fields.enabled value=source.custom.enabled name=(concat path "damage.parts." realIndex "." target ".custom.enabled") classes="checkbox" localize=true}}
{{/unless}} {{/unless}}
{{#if source.custom.enabled}} {{#if source.custom.enabled}}
{{formField fields.custom.fields.formula value=source.custom.formula name=(concat path "damage.parts." realIndex "." target ".custom.formula") localize=true}} {{formField fields.custom.fields.formula value=source.custom.formula name=(concat path "damage.parts." realIndex "." target ".custom.formula") localize=true}}
@ -79,8 +79,8 @@
{{#unless @root.isNPC}} {{#unless @root.isNPC}}
{{formField fields.multiplier value=source.multiplier name=(concat path "damage.parts." realIndex "." target ".multiplier") localize=true}} {{formField fields.multiplier value=source.multiplier name=(concat path "damage.parts." realIndex "." target ".multiplier") localize=true}}
{{/unless}} {{/unless}}
{{#if (eq source.multiplier 'flat')}}{{formField fields.flatMultiplier value=source.flatMultiplier name=(concat path "damage.parts." realIndex "." target ".flatMultiplier") }}{{/if}} {{#if (eq source.multiplier 'flat')}}{{formField fields.flatMultiplier value=source.flatMultiplier name=(concat path "damage.parts." realIndex "." target ".flatMultiplier") localize=true }}{{/if}}
{{formField fields.dice value=source.dice name=(concat path "damage.parts." realIndex "." target ".dice")}} {{formField fields.dice value=source.dice name=(concat path "damage.parts." realIndex "." target ".dice") localize=true}}
{{formField fields.bonus value=source.bonus name=(concat path "damage.parts." realIndex "." target ".bonus") localize=true}} {{formField fields.bonus value=source.bonus name=(concat path "damage.parts." realIndex "." target ".bonus") localize=true}}
</div> </div>
{{/if}} {{/if}}

View file

@ -126,10 +126,12 @@
{{selectOptions diceOptions selected=@root.roll.dAdvantage.denomination}} {{selectOptions diceOptions selected=@root.roll.dAdvantage.denomination}}
</select> </select>
</div> </div>
<span>{{localize "DAGGERHEART.GENERAL.traitModifier"}}</span> {{#if abilities}}
<select name="trait"> <span>{{localize "DAGGERHEART.GENERAL.traitModifier"}}</span>
{{selectOptions abilities selected=@root.rollConfig.roll.trait valueAttr="id" labelAttr="label" localize=true}} <select name="trait">
</select> {{selectOptions abilities selected=@root.rollConfig.roll.trait valueAttr="id" labelAttr="label" localize=true}}
</select>
{{/if}}
{{/unless}} {{/unless}}
{{#if @root.rallyDie.length}} {{#if @root.rallyDie.length}}
<span class="formula-label">{{localize "DAGGERHEART.CLASS.Feature.rallyDice"}}</span> <span class="formula-label">{{localize "DAGGERHEART.CLASS.Feature.rallyDice"}}</span>

View file

@ -13,10 +13,10 @@
{{#each damage as | roll index | }} {{#each damage as | roll index | }}
<fieldset> <fieldset>
<legend> <legend>
{{localize (concat 'DAGGERHEART.CONFIG.HealingType.' index '.inChatRoll')}} <div class="roll-formula">{{localize "DAGGERHEART.GENERAL.total"}}: {{roll.total}}</div>{{#if (and (eq index "hitPoints")../isDirect)}} <div class="roll-formula">{{localize "DAGGERHEART.CONFIG.DamageType.direct.short"}}</div>{{/if}} {{#if ../hasHealing}}{{localize (concat 'DAGGERHEART.CONFIG.HealingType.' index '.name')}}{{else}}{{localize (concat 'DAGGERHEART.CONFIG.HealingType.' index '.inChatRoll')}}{{/if}} <div class="roll-formula">{{localize "DAGGERHEART.GENERAL.total"}}: {{roll.total}}</div>{{#if (and (eq index "hitPoints") ../isDirect)}} <div class="roll-formula">{{localize "DAGGERHEART.CONFIG.DamageType.direct.short"}}</div>{{/if}}
</legend> </legend>
{{#each roll.parts}} {{#each roll.parts}}
{{#if damageTypes.length}} {{#if (and (not @root.hasHealing) damageTypes.length)}}
<label class="roll-part-header"><span> <label class="roll-part-header"><span>
{{#each damageTypes}} {{#each damageTypes}}
{{localize (concat 'DAGGERHEART.CONFIG.ArmorFeature.' this '.name')}} {{localize (concat 'DAGGERHEART.CONFIG.ArmorFeature.' this '.name')}}

View file

@ -40,7 +40,7 @@
</div> </div>
{{/if}} {{/if}}
</div> </div>
{{#if (and ../hasSave (or hit (not @root.hasRoll)))}} {{#if (and ../hasSave (or hit (not @root.hasRoll) (not @root.targetMode)))}}
<div class="target-save{{#if saved.result includeZero=true}} is-rolled{{/if}}" data-perm-id="{{actorId}}"> <div class="target-save{{#if saved.result includeZero=true}} is-rolled{{/if}}" data-perm-id="{{actorId}}">
<i class="fa-solid {{#if saved.result includeZero=true}}{{#if saved.success}}fa-check{{else}}fa-xmark{{/if}}{{else}}fa-shield{{/if}} fa-lg"></i> <i class="fa-solid {{#if saved.result includeZero=true}}{{#if saved.success}}fa-check{{else}}fa-xmark{{/if}}{{else}}fa-shield{{/if}} fa-lg"></i>
</div> </div>