From 180c4d2a5348233bb8d1511d6c6bef105327efac Mon Sep 17 00:00:00 2001 From: Dapoolp Date: Sat, 16 Aug 2025 01:22:23 +0200 Subject: [PATCH] Action Worflow #1 --- module/applications/ui/chatLog.mjs | 4 +- module/data/action/baseAction.mjs | 89 +++++++++++++++-------- module/data/action/damageAction.mjs | 4 +- module/data/fields/action/damageField.mjs | 63 ++++++++++++++++ module/data/fields/action/macroField.mjs | 13 ++++ module/data/fields/action/rollField.mjs | 25 +++++++ 6 files changed, 164 insertions(+), 34 deletions(-) diff --git a/module/applications/ui/chatLog.mjs b/module/applications/ui/chatLog.mjs index 8b4b12d3..4775d425 100644 --- a/module/applications/ui/chatLog.mjs +++ b/module/applications/ui/chatLog.mjs @@ -111,8 +111,8 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo if (game.user.character?.id !== actor.id && !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?.rollDamage) return; - await action.rollDamage(event, message); + if (!action || !action?.hasDamagePart) return; + await game.system.api.fields.ActionFields.DamageField.execute.call(action, message, true); } } diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index 43850196..0b7ef036 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -111,6 +111,26 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel return actorData; } + prepareWorkflow(workflow) { + for (let i = 0; i < this.constructor.extraSchemas.length; i++) { + let clsField = this.constructor.getActionField(this.constructor.extraSchemas[i]); + if (clsField?.execute) { + workflow.push({ order: clsField.order, exec: clsField.execute}); + // const keep = clsField.prepareConfig.call(this, config); + // if (config.isFastForward && !keep) return; + } + } + workflow.sort((a, b) => a.order - b.order); + } + + async executeWorkflow(workflow, config) { + console.log(workflow) + for(const part of workflow) { + console.log(part) + if(await part.exec.call(this, config) === false) return; + } + } + async use(event, options = {}) { if (!this.actor) throw new Error("An Action can't be used outside of an Actor context."); @@ -133,25 +153,30 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel if (!config) return; } - if (config.hasRoll) { - const rollConfig = this.prepareRoll(config); - config.roll = rollConfig; - config = await this.actor.diceRoll(config); - if (!config) return; - } + const workflow = []; - if (this.doFollowUp(config)) { - if (this.rollDamage && this.damage.parts.length) await this.rollDamage(event, config); - else if (this.trigger) await this.trigger(event, config); - else if (this.hasSave || this.hasEffect) { - const roll = new CONFIG.Dice.daggerheart.DHRoll(''); - roll._evaluated = true; - await CONFIG.Dice.daggerheart.DHRoll.toMessage(roll, config); - } - } + this.prepareWorkflow(workflow); + await this.executeWorkflow(workflow, config); + + // if (config.hasRoll) { + // const rollConfig = this.prepareRoll(config); + // config.roll = rollConfig; + // config = await this.actor.diceRoll(config); + // if (!config) return; + // } + + // if (this.doFollowUp(config)) { + // if (this.rollDamage && this.damage.parts.length) await this.rollDamage(event, config); + // else if (this.trigger) await this.trigger(event, config); + // else if (this.hasSave || this.hasEffect) { + // const roll = new CONFIG.Dice.daggerheart.DHRoll(''); + // roll._evaluated = true; + // await CONFIG.Dice.daggerheart.DHRoll.toMessage(roll, config); + // } + // } // Consume resources - await this.consume(config); + // await this.consume(config); if (Hooks.call(`${CONFIG.DH.id}.postUseAction`, this, config) === false) return; @@ -174,8 +199,8 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel }, type: this.type, hasRoll: hasRoll, - hasDamage: this.damage?.parts?.length && this.type !== 'healing', - hasHealing: this.damage?.parts?.length && this.type === 'healing', + hasDamage: this.hasDamagePart && this.type !== 'healing', + hasHealing: this.hasDamagePart && this.type === 'healing', hasEffect: !!this.effects?.length, isDirect: !!this.damage?.direct, hasSave: this.hasSave, @@ -190,19 +215,19 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel return !config.event.shiftKey && !config.hasRoll && (config.costs?.length || config.uses); } - prepareRoll() { - const roll = { - baseModifiers: this.roll.getModifier(), - label: 'Attack', - type: this.actionType, - difficulty: this.roll?.difficulty, - formula: this.roll.getFormula(), - advantage: CONFIG.DH.ACTIONS.advantageState[this.roll.advState].value - }; - if (this.roll?.type === 'diceSet' || !this.hasRoll) roll.lite = true; + // prepareRoll() { + // const roll = { + // baseModifiers: this.roll.getModifier(), + // label: 'Attack', + // type: this.actionType, + // difficulty: this.roll?.difficulty, + // formula: this.roll.getFormula(), + // advantage: CONFIG.DH.ACTIONS.advantageState[this.roll.advState].value + // }; + // if (this.roll?.type === 'diceSet' || !this.hasRoll) roll.lite = true; - return roll; - } + // return roll; + // } doFollowUp(config) { return !config.hasRoll; @@ -272,6 +297,10 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel return !!this.roll?.type; } + get hasDamagePart() { + return this.damage?.parts?.length; + } + get modifiers() { if (!this.actor) return []; const modifiers = []; diff --git a/module/data/action/damageAction.mjs b/module/data/action/damageAction.mjs index 7deeb006..38d8852a 100644 --- a/module/data/action/damageAction.mjs +++ b/module/data/action/damageAction.mjs @@ -4,7 +4,7 @@ import DHBaseAction from './baseAction.mjs'; export default class DHDamageAction extends DHBaseAction { static extraSchemas = [...super.extraSchemas, 'damage', 'target', 'effects']; - getFormulaValue(part, data) { + /* getFormulaValue(part, data) { let formulaValue = part.value; if (data.hasRoll && part.resultBased && data.roll.result.duality === -1) return part.valueAlt; @@ -61,5 +61,5 @@ export default class DHDamageAction extends DHBaseAction { } return CONFIG.Dice.daggerheart.DamageRoll.build(config); - } + } */ } diff --git a/module/data/fields/action/damageField.mjs b/module/data/fields/action/damageField.mjs index cf327204..b6092c8e 100644 --- a/module/data/fields/action/damageField.mjs +++ b/module/data/fields/action/damageField.mjs @@ -3,6 +3,8 @@ import FormulaField from '../formulaField.mjs'; const fields = foundry.data.fields; export default class DamageField extends fields.SchemaField { + static order = 50; + constructor(options, context = {}) { const damageFields = { parts: new fields.ArrayField(new fields.EmbeddedDataField(DHDamageData)), @@ -14,6 +16,67 @@ export default class DamageField extends fields.SchemaField { }; super(damageFields, options, context); } + + static async execute(data, force = false) { + if(this.hasRoll && !force) return; + + const systemData = data.system ?? data; + + let formulas = this.damage.parts.map(p => ({ + formula: DamageField.getFormulaValue.call(this, p, systemData).getFormula(this.actor), + damageTypes: p.applyTo === 'hitPoints' && !p.type.size ? new Set(['physical']) : p.type, + applyTo: p.applyTo + })); + + if (!formulas.length) return false; + + formulas = DamageField.formatFormulas.call(this, formulas, systemData); + + delete systemData.evaluate; + const config = { + ...systemData, + roll: formulas, + dialog: {}, + data: this.getRollData() + }; + if (this.hasSave) config.onSave = this.save.damageMod; + if (data.system) { + config.source.message = data._id; + config.directDamage = false; + } else { + config.directDamage = true; + } + + if(!CONFIG.Dice.daggerheart.DamageRoll.build(config)) return false; + } + + static getFormulaValue(part, data) { + let formulaValue = part.value; + + if (data.hasRoll && part.resultBased && data.roll.result.duality === -1) return part.valueAlt; + + const isAdversary = this.actor.type === 'adversary'; + if (isAdversary && this.actor.system.type === CONFIG.DH.ACTOR.adversaryTypes.horde.id) { + const hasHordeDamage = this.actor.effects.find(x => x.type === 'horde'); + if (hasHordeDamage && !hasHordeDamage.disabled) return part.valueAlt; + } + + return formulaValue; + } + + static formatFormulas(formulas, systemData) { + const formattedFormulas = []; + formulas.forEach(formula => { + if (isNaN(formula.formula)) + formula.formula = Roll.replaceFormulaData(formula.formula, this.getRollData(systemData)); + const same = formattedFormulas.find( + f => setsEqual(f.damageTypes, formula.damageTypes) && f.applyTo === formula.applyTo + ); + if (same) same.formula += ` + ${formula.formula}`; + else formattedFormulas.push(formula); + }); + return formattedFormulas; + } } export class DHActionDiceData extends foundry.abstract.DataModel { diff --git a/module/data/fields/action/macroField.mjs b/module/data/fields/action/macroField.mjs index 62da0da0..57c13050 100644 --- a/module/data/fields/action/macroField.mjs +++ b/module/data/fields/action/macroField.mjs @@ -1,7 +1,20 @@ const fields = foundry.data.fields; export default class MacroField extends fields.DocumentUUIDField { + static order = 100; + constructor(context = {}) { super({ type: "Macro" }, context); } + + static async execute(config) { + const fixUUID = !this.macro.includes('Macro.') ? `Macro.${this.macro}` : this.macro, + macro = await fromUuid(fixUUID); + try { + if (!macro) throw new Error(`No macro found for the UUID: ${this.macro}.`); + macro.execute(); + } catch (error) { + ui.notifications.error(error); + } + } } diff --git a/module/data/fields/action/rollField.mjs b/module/data/fields/action/rollField.mjs index a4df2a9e..10c868f4 100644 --- a/module/data/fields/action/rollField.mjs +++ b/module/data/fields/action/rollField.mjs @@ -110,7 +110,32 @@ export class DHActionRollData extends foundry.abstract.DataModel { } export default class RollField extends fields.EmbeddedDataField { + static order = 10; + constructor(options, context = {}) { super(DHActionRollData, options, context); } + + static async execute(config) { + if(!config.hasRoll) return; + config = await this.actor.diceRoll(config); + if(!config) return false; + } + + static prepareConfig(config) { + if(!config.hasRoll) return true; + + const roll = { + baseModifiers: this.roll.getModifier(), + label: 'Attack', + type: this.actionType, + difficulty: this.roll?.difficulty, + formula: this.roll.getFormula(), + advantage: CONFIG.DH.ACTIONS.advantageState[this.roll.advState].value + }; + if (this.roll.type === 'diceSet' || !this.hasRoll) roll.lite = true; + + config.roll = roll; + return true; + } }