From fb9240e130d93be0fb8ddc5d7cd11504733adecf Mon Sep 17 00:00:00 2001 From: Dapoolp Date: Tue, 26 Aug 2025 13:30:38 +0200 Subject: [PATCH] Some fixes --- lang/en.json | 4 +- .../applications/sheets/actors/adversary.mjs | 4 +- module/data/action/baseAction.mjs | 2 +- module/data/chat-message/actorRoll.mjs | 2 +- module/data/fields/action/costField.mjs | 2 +- module/data/fields/action/damageField.mjs | 3 +- module/data/fields/action/effectsField.mjs | 12 +++- module/data/fields/action/rollField.mjs | 23 ------- module/data/fields/action/saveField.mjs | 65 +++++++++++-------- module/dice/dhRoll.mjs | 1 + module/dice/dualityRoll.mjs | 2 +- module/documents/actor.mjs | 2 +- module/documents/chatMessage.mjs | 22 +++++-- module/systemRegistration/socket.mjs | 8 ++- templates/actionTypes/damage.hbs | 8 +-- templates/dialogs/dice-roll/rollSelection.hbs | 10 +-- templates/ui/chat/parts/damage-part.hbs | 4 +- templates/ui/chat/parts/target-part.hbs | 2 +- 18 files changed, 94 insertions(+), 82 deletions(-) diff --git a/lang/en.json b/lang/en.json index 5d06dc41..7799cf84 100755 --- a/lang/en.json +++ b/lang/en.json @@ -2228,11 +2228,11 @@ }, "damageApply": { "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": { "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." } } }, diff --git a/module/applications/sheets/actors/adversary.mjs b/module/applications/sheets/actors/adversary.mjs index f575a2f2..64f48d02 100644 --- a/module/applications/sheets/actors/adversary.mjs +++ b/module/applications/sheets/actors/adversary.mjs @@ -146,9 +146,9 @@ export default class AdversarySheet extends DHBaseActorSheet { title: `Reaction Roll: ${this.actor.name}`, headerTitle: 'Adversary Reaction Roll', roll: { - type: 'reaction' + type: 'trait' }, - type: 'trait', + actionType: 'reaction', hasRoll: true, data: this.actor.getRollData() }; diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index f5d0da7c..60540619 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -208,7 +208,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel prepareBaseConfig(event) { const config = { 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: { item: this.item._id, action: this._id, diff --git a/module/data/chat-message/actorRoll.mjs b/module/data/chat-message/actorRoll.mjs index d87bab6a..b6512fbd 100644 --- a/module/data/chat-message/actorRoll.mjs +++ b/module/data/chat-message/actorRoll.mjs @@ -95,7 +95,7 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel { } registerTargetHook() { - if (!this.parent.isAuthor) return; + if (!this.parent.isAuthor || !this.hasTarget) return; if (this.targetMode && this.parent.targetHook !== null) { Hooks.off('targetToken', this.parent.targetHook); return (this.parent.targetHook = null); diff --git a/module/data/fields/action/costField.mjs b/module/data/fields/action/costField.mjs index 72325d73..ca942909 100644 --- a/module/data/fields/action/costField.mjs +++ b/module/data/fields/action/costField.mjs @@ -111,7 +111,7 @@ export default class CostField extends fields.ArrayField { c.key === 'fear' ? game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear) : resources[c.key].isReversed - ? resources[c.key].max + ? resources[c.key].max - resources[c.key].value : resources[c.key].value; if (c.scalable) c.maxStep = Math.floor((c.max - c.value) / c.step); return c; diff --git a/module/data/fields/action/damageField.mjs b/module/data/fields/action/damageField.mjs index 9f842146..82b9758c 100644 --- a/module/data/fields/action/damageField.mjs +++ b/module/data/fields/action/damageField.mjs @@ -62,6 +62,7 @@ export default class DamageField extends fields.SchemaField { const damageResult = await CONFIG.Dice.daggerheart.DamageRoll.build(damageConfig); if(!damageResult) return false; 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. */ 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; for (let target of targets) { const actor = fromUuidSync(target.actorId); diff --git a/module/data/fields/action/effectsField.mjs b/module/data/fields/action/effectsField.mjs index 0d189f18..3b8c5e43 100644 --- a/module/data/fields/action/effectsField.mjs +++ b/module/data/fields/action/effectsField.mjs @@ -1,3 +1,5 @@ +import { emitAsGM, GMUpdateEvent } from "../../../systemRegistration/socket.mjs"; + const fields = foundry.data.fields; 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); } if(EffectsField.getAutomation() || force) { - targets ??= config.targets.filter(t => !config.hasRoll || t.hit); - EffectsField.applyEffects.call(this, config.targets.filter(t => !config.hasRoll || t.hit)); + targets ??= (message.system?.targets ?? 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)); } } diff --git a/module/data/fields/action/rollField.mjs b/module/data/fields/action/rollField.mjs index 4cc50777..cfcfb56b 100644 --- a/module/data/fields/action/rollField.mjs +++ b/module/data/fields/action/rollField.mjs @@ -71,29 +71,6 @@ export class DHActionRollData extends foundry.abstract.DataModel { const modifiers = []; if (!this.parent?.actor) return modifiers; 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 'adversary': if (this.type === CONFIG.DH.GENERAL.rollTypes.attack.id) diff --git a/module/data/fields/action/saveField.mjs b/module/data/fields/action/saveField.mjs index 5103523b..0003a4d5 100644 --- a/module/data/fields/action/saveField.mjs +++ b/module/data/fields/action/saveField.mjs @@ -35,6 +35,7 @@ export default class SaveField extends fields.SchemaField { static async execute(config, targets = null, force = false) { if(!config.hasSave) return; let message = config.message ?? ui.chat.collection.get(config.parent?._id); + if(!message) { const roll = new CONFIG.Dice.daggerheart.DHRoll(''); roll._evaluated = true; @@ -42,8 +43,8 @@ export default class SaveField extends fields.SchemaField { } if(SaveField.getAutomation() !== CONFIG.DH.SETTINGS.actionAutomationChoices.never.id || force) { 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. */ static async rollAllSave(targets, event, message) { - if(!targets || !game.user.isGM) return; - targets.forEach(target => { - const actor = fromUuidSync(target.actorId); - if(actor) { - const rollSave = game.user === actor.owner ? - SaveField.rollSave.call(this, actor, event, message) - : actor.owner - .query('reactionRoll', { - actionId: this.uuid, - actorId: actor.uuid, - event, - message - }); - rollSave.then(result => SaveField.updateSaveMessage.call(this, result, message, target.id)); - } - }); + if(!targets) return; + return new Promise(resolve => { + const aPromise = []; + targets.forEach(target => { + aPromise.push( + new Promise(async subResolve => { + const actor = fromUuidSync(target.actorId); + if(actor) { + const rollSave = game.user === actor.owner ? + SaveField.rollSave.call(this, actor, event) + : actor.owner + .query('reactionRoll', { + actionId: this.uuid, + 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: { trait: this.save.trait, difficulty: this.save.difficulty ?? this.actor?.baseSaveDifficulty, - type: 'reaction' + type: 'trait' }, - type: 'trait', + actionType: 'reaction', hasRoll: true, 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); } @@ -108,10 +119,10 @@ export default class SaveField extends fields.SchemaField { * @param {object} message ChatMessage to update * @param {string} targetId Token ID */ - static updateSaveMessage(result, message, targetId) { + static async updateSaveMessage(result, message, targetId) { if (!result) return; - const updateMsg = function(message, targetId, result) { - setTimeout(async () => { + const updateMsg = async function(message, targetId, result) { + // setTimeout(async () => { const chatMessage = ui.chat.collection.get(message._id), changes = { flags: { @@ -127,11 +138,11 @@ export default class SaveField extends fields.SchemaField { } }; await chatMessage.update(changes); - }, 100); + // }, 100); }; if (game.modules.get('dice-so-nice')?.active) - game.dice3d.waitFor3DAnimationByMessageID(result.message.id ?? result.message._id).then(() => updateMsg(message, targetId, result)); - else updateMsg(message, targetId, result); + game.dice3d.waitFor3DAnimationByMessageID(result.message.id ?? result.message._id).then(async () => await updateMsg(message, targetId, result)); + else await updateMsg(message, targetId, result); } /** diff --git a/module/dice/dhRoll.mjs b/module/dice/dhRoll.mjs index 3c32b8c9..e203bb0c 100644 --- a/module/dice/dhRoll.mjs +++ b/module/dice/dhRoll.mjs @@ -28,6 +28,7 @@ export default class DHRoll extends Roll { static async buildConfigure(config = {}, message = {}) { config.hooks = [...this.getHooks(), '']; config.dialog ??= {}; + for (const hook of config.hooks) { if (Hooks.call(`${CONFIG.DH.id}.preRoll${hook.capitalize()}`, config, message) === false) return null; } diff --git a/module/dice/dualityRoll.mjs b/module/dice/dualityRoll.mjs index 9e185779..93ac231e 100644 --- a/module/dice/dualityRoll.mjs +++ b/module/dice/dualityRoll.mjs @@ -154,7 +154,7 @@ export default class DualityRoll extends D20Roll { 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({ 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 diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index 08735a45..950ed621 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -14,7 +14,7 @@ export default class DhpActor extends Actor { get owner() { const user = 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; } diff --git a/module/documents/chatMessage.mjs b/module/documents/chatMessage.mjs index 0b760192..1a619a9c 100644 --- a/module/documents/chatMessage.mjs +++ b/module/documents/chatMessage.mjs @@ -147,12 +147,16 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { async onRollDamage(event) { 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) { 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) { 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')); 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) { @@ -198,17 +202,21 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { async onRollAllSave(event) { event.stopPropagation(); if (!game.user.isGM) return; - const targets = this.system.hitTargets; - this.system.action?.workflow.get("save")?.execute(this.system, targets, true); + const targets = this.system.hitTargets, + config = foundry.utils.deepClone(this.system); + config.event = event; + this.system.action?.workflow.get("save")?.execute(config, targets, true); } async onApplyEffect(event) { 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) ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelectedOrPerm')); 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) { diff --git a/module/systemRegistration/socket.mjs b/module/systemRegistration/socket.mjs index 4f91362c..f3f9629b 100644 --- a/module/systemRegistration/socket.mjs +++ b/module/systemRegistration/socket.mjs @@ -22,6 +22,7 @@ export const socketEvent = { export const GMUpdateEvent = { UpdateDocument: 'DhGMUpdateDocument', + UpdateEffect: 'DhGMUpdateEffect', UpdateSetting: 'DhGMUpdateSetting', UpdateFear: 'DhGMUpdateFear', UpdateSaveMessage: 'DhGMUpdateSaveMessage' @@ -37,9 +38,12 @@ export const registerSocketHooks = () => { const document = data.uuid ? await fromUuid(data.uuid) : null; switch (data.action) { case GMUpdateEvent.UpdateDocument: - if (document && data.update) { + if (document && 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; case GMUpdateEvent.UpdateSetting: await game.settings.set(CONFIG.DH.id, data.uuid, data.update); diff --git a/templates/actionTypes/damage.hbs b/templates/actionTypes/damage.hbs index 6a94752b..96bb361c 100644 --- a/templates/actionTypes/damage.hbs +++ b/templates/actionTypes/damage.hbs @@ -56,7 +56,7 @@
{{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"}}
@@ -70,7 +70,7 @@ {{#*inline "formula"}} {{#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}} {{#if source.custom.enabled}} {{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}} {{formField fields.multiplier value=source.multiplier name=(concat path "damage.parts." realIndex "." target ".multiplier") localize=true}} {{/unless}} - {{#if (eq source.multiplier 'flat')}}{{formField fields.flatMultiplier value=source.flatMultiplier name=(concat path "damage.parts." realIndex "." target ".flatMultiplier") }}{{/if}} - {{formField fields.dice value=source.dice name=(concat path "damage.parts." realIndex "." target ".dice")}} + {{#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") localize=true}} {{formField fields.bonus value=source.bonus name=(concat path "damage.parts." realIndex "." target ".bonus") localize=true}} {{/if}} diff --git a/templates/dialogs/dice-roll/rollSelection.hbs b/templates/dialogs/dice-roll/rollSelection.hbs index 61934530..18bb1429 100644 --- a/templates/dialogs/dice-roll/rollSelection.hbs +++ b/templates/dialogs/dice-roll/rollSelection.hbs @@ -126,10 +126,12 @@ {{selectOptions diceOptions selected=@root.roll.dAdvantage.denomination}} - {{localize "DAGGERHEART.GENERAL.traitModifier"}} - + {{#if abilities}} + {{localize "DAGGERHEART.GENERAL.traitModifier"}} + + {{/if}} {{/unless}} {{#if @root.rallyDie.length}} {{localize "DAGGERHEART.CLASS.Feature.rallyDice"}} diff --git a/templates/ui/chat/parts/damage-part.hbs b/templates/ui/chat/parts/damage-part.hbs index 05b6b825..97828bd2 100644 --- a/templates/ui/chat/parts/damage-part.hbs +++ b/templates/ui/chat/parts/damage-part.hbs @@ -13,10 +13,10 @@ {{#each damage as | roll index | }}
- {{localize (concat 'DAGGERHEART.CONFIG.HealingType.' index '.inChatRoll')}}
{{localize "DAGGERHEART.GENERAL.total"}}: {{roll.total}}
{{#if (and (eq index "hitPoints")../isDirect)}}
{{localize "DAGGERHEART.CONFIG.DamageType.direct.short"}}
{{/if}} + {{#if ../hasHealing}}{{localize (concat 'DAGGERHEART.CONFIG.HealingType.' index '.name')}}{{else}}{{localize (concat 'DAGGERHEART.CONFIG.HealingType.' index '.inChatRoll')}}{{/if}}
{{localize "DAGGERHEART.GENERAL.total"}}: {{roll.total}}
{{#if (and (eq index "hitPoints") ../isDirect)}}
{{localize "DAGGERHEART.CONFIG.DamageType.direct.short"}}
{{/if}}
{{#each roll.parts}} - {{#if damageTypes.length}} + {{#if (and (not @root.hasHealing) damageTypes.length)}}