From cf55659097047a257374b4c47fc94385e3737b5b Mon Sep 17 00:00:00 2001 From: Dapoolp Date: Fri, 22 Aug 2025 23:10:45 +0200 Subject: [PATCH] Update Beastform action --- lang/en.json | 15 +-- .../settings/automationSettings.mjs | 2 +- .../applications/sheets/actors/character.mjs | 26 +---- .../sheets/api/application-mixin.mjs | 10 ++ module/data/action/baseAction.mjs | 94 +++++++++++++--- module/data/action/beastformAction.mjs | 5 +- module/data/fields/action/beastformField.mjs | 99 +++++++++++++++++ module/data/fields/action/costField.mjs | 24 ++-- module/data/fields/action/damageField.mjs | 24 ++-- module/data/fields/action/effectsField.mjs | 20 ++-- module/data/fields/action/macroField.mjs | 8 +- module/data/fields/action/rollField.mjs | 9 +- module/data/fields/action/saveField.mjs | 6 +- module/data/fields/action/usesField.mjs | 10 +- styles/less/ui/settings/settings.less | 104 ++++++++++++------ 15 files changed, 324 insertions(+), 132 deletions(-) diff --git a/lang/en.json b/lang/en.json index a34b187d..23150432 100755 --- a/lang/en.json +++ b/lang/en.json @@ -302,7 +302,8 @@ "toLoadout": "Send to Loadout", "toVault": "Send to Vault", "unequip": "Unequip", - "useItem": "Use Item" + "useItem": "Use Item", + "cancelBeastform": "Cancel Beastform" }, "Countdown": { "addCountdown": "Add Countdown", @@ -2199,23 +2200,23 @@ "roll": { "roll": { "label": "Roll", - "hint": "Roll Hint" + "hint": "Auto behavior for rolls like Attack, Spellcast, etc." }, "damage": { "label": "Damage/Healing Roll", - "hint": "Damage/Healing Roll Hint" + "hint": "Auto behavior for Damage & Healing rolls after the Attack/Spellcast." }, "save": { "label": "Reaction Roll", - "hint": "Reaction Roll Hint" + "hint": "Auto behavior if a Reaction Roll is needed. Targets must be selected before the action is made" }, "damageApply": { "label": "Apply Damage/Healing", - "hint": "Apply Damage/Healing Hint" + "hint": "Automatically apply damages & healings. Targets must be selected before the action is made. Bypass users permissions." }, "effect": { "label": "Apply Effects", - "hint": "Apply Effects Hint" + "hint": "Automatically apply effects. Targets must be selected before the action is made. Bypass users permissions." } } }, @@ -2223,7 +2224,7 @@ "title": "Defeated Handling" }, "roll": { - "title": "Roll" + "title": "Actions" } }, "Homebrew": { diff --git a/module/applications/settings/automationSettings.mjs b/module/applications/settings/automationSettings.mjs index 1a9b6c36..4407897d 100644 --- a/module/applications/settings/automationSettings.mjs +++ b/module/applications/settings/automationSettings.mjs @@ -42,7 +42,7 @@ export default class DhAutomationSettings extends HandlebarsApplicationMixin(App /** @inheritdoc */ static TABS = { main: { - tabs: [{ id: 'general' }, { id: 'rules' }, { id: 'roll', cssClass: "with-hint" }], + tabs: [{ id: 'general' }, { id: 'rules' }, { id: 'roll' }], initial: 'general', labelPrefix: 'DAGGERHEART.GENERAL.Tabs' } diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index 0328f0dd..35d02992 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -661,31 +661,7 @@ export default class CharacterSheet extends DHBaseActorSheet { }) }); - this.consumeResource(result?.costs); - } - - // Remove when Action Refactor part #2 done - async consumeResource(costs) { - if (!costs?.length) return; - const usefulResources = { - ...foundry.utils.deepClone(this.actor.system.resources), - fear: { - value: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear), - max: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxFear, - reversed: false - } - }; - const resources = game.system.api.fields.ActionFields.CostField.getRealCosts(costs).map(c => { - const resource = usefulResources[c.key]; - return { - key: c.key, - value: (c.total ?? c.value) * (resource.isReversed ? 1 : -1), - target: resource.target, - keyIsID: resource.keyIsID - }; - }); - - await this.actor.modifyResource(resources); + game.system.api.fields.ActionFields.CostField.execute.call(this, result); } //TODO: redo toggleEquipItem method diff --git a/module/applications/sheets/api/application-mixin.mjs b/module/applications/sheets/api/application-mixin.mjs index 29ff6a2e..7f338ac1 100644 --- a/module/applications/sheets/api/application-mixin.mjs +++ b/module/applications/sheets/api/application-mixin.mjs @@ -412,6 +412,16 @@ export default function DHApplicationMixin(Base) { ]; if (usable) { + options.unshift({ + name: 'DAGGERHEART.APPLICATIONS.ContextMenu.cancelBeastform', + icon: 'fa-solid fa-ban', + condition: target => { + const doc = getDocFromElementSync(target); + return doc && doc.system?.actions?.some(a => a.type === "beastform"); + }, + callback: async target => game.system.api.fields.ActionFields.BeastformField.handleActiveTransformations.call(await getDocFromElement(target)) + }); + options.unshift({ name: 'DAGGERHEART.GENERAL.damage', icon: 'fa-solid fa-explosion', diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index 914697df..8af4ec04 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -4,10 +4,6 @@ import { ActionMixin } from '../fields/actionField.mjs'; const fields = foundry.data.fields; -/* - !!! I'm currently refactoring the whole Action thing, it's a WIP !!! -*/ - /* ToDo - Target Check / Target Picker @@ -19,6 +15,7 @@ const fields = foundry.data.fields; export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel) { static extraSchemas = ['cost', 'uses', 'range']; + /** @inheritDoc */ static defineSchema() { const schemaFields = { _id: new fields.DocumentIdField({ initial: () => foundry.utils.randomID() }), @@ -36,23 +33,33 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel }; this.extraSchemas.forEach(s => { - let clsField; - if ((clsField = this.getActionField(s))) + let clsField = this.getActionField(s); + if (clsField) schemaFields[s] = new clsField(); }); return schemaFields; } + /** + * Create a Map containing each Action step based on fields define in schema. Ordered by Fields order property. + * @returns {Map} + */ defineWorkflow() { const workflow = new Map(); - Object.entries(this.schema.fields).forEach(([k,s]) => { - if(s.execute) workflow.set(k, { order: s.order, execute: s.execute.bind(this) } ); + this.constructor.extraSchemas.forEach(s => { + let clsField = this.constructor.getActionField(s); + if (clsField?.execute) { + workflow.set(s, { order: clsField.order, execute: clsField.execute.bind(this) } ); + if( s === "damage" ) workflow.set("applyDamage", { order: 75, execute: clsField.applyDamage.bind(this) } ); + } }); - if(this.schema.fields.damage) workflow.set("applyDamage", { order: 75, execute: game.system.api.fields.ActionFields.DamageField.applyDamage.bind(this) } ); return new Map([...workflow.entries()].sort(([aKey, aValue], [bKey, bValue]) => aValue.order - bValue.order)); } + /** + * Getter returning the workflow property or creating it the first time the property is called + */ get workflow() { if ( this.hasOwnProperty("_workflow") ) return this._workflow; const workflow = Object.freeze(this.defineWorkflow()); @@ -60,24 +67,39 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel return workflow; } + /** + * Get the Field class from ActionFields global config + * @param {string} name Field short name, equal to Action property + * @returns Action Field + */ static getActionField(name) { const field = game.system.api.fields.ActionFields[`${name.capitalize()}Field`]; return fields.DataField.isPrototypeOf(field) && field; } + /** @inheritDoc */ prepareData() { this.name = this.name || game.i18n.localize(CONFIG.DH.ACTIONS.actionTypes[this.type].name); this.img = this.img ?? this.parent?.parent?.img; } + /** + * Get Action ID + */ get id() { return this._id; } + /** + * Return Item the action is attached too. + */ get item() { return this.parent.parent; } + /** + * Return the first Actor parent found. + */ get actor() { return this.item instanceof DhpActor ? this.item @@ -90,6 +112,11 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel return 'trait'; } + /** + * Prepare base data based on Action Type & Parent Type + * @param {object} parent + * @returns {object} + */ static getSourceConfig(parent) { const updateSource = {}; if (parent?.parent?.type === 'weapon' && this === game.system.api.models.actions.actionsTypes.attack) { @@ -112,6 +139,11 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel return updateSource; } + /** + * Obtain a data object used to evaluate any dice rolls associated with this particular Action + * @param {object} [data ={}] Optional data object from previous configuration/rolls + * @returns {object} + */ getRollData(data = {}) { if (!this.actor) return null; const actorData = this.actor.getRollData(false); @@ -127,6 +159,10 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel return actorData; } + /** + * Execute each part of the Action workflow in order, calling a specific event before and after each part. + * @param {object} config Config object usually created from prepareConfig method + */ async executeWorkflow(config) { for(const [key, part] of this.workflow) { if (Hooks.call(`${CONFIG.DH.id}.pre${key.capitalize()}Action`, this, config) === false) return; @@ -135,7 +171,12 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel } } - async use(event, options = {}) { + /** + * Main method to use the Action + * @param {Event} event Event from the button used to trigger the Action + * @returns {object} + */ + async use(event) { if (!this.actor) throw new Error("An Action can't be used outside of an Actor context."); if (this.chatDisplay) await this.toChat(); @@ -159,7 +200,11 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel return config; } - /* */ + /** + * Create the basic config common to every action type + * @param {Event} event Event from the button used to trigger the Action + * @returns {object} + */ prepareBaseConfig(event) { const config = { event, @@ -174,7 +219,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel hasRoll: this.hasRoll, hasDamage: this.hasDamage, hasHealing: this.hasHealing, - hasEffect: !!this.effects?.length, + hasEffect: this.hasEffect, hasSave: this.hasSave, isDirect: !!this.damage?.direct, selectedRollMode: game.settings.get('core', 'rollMode'), @@ -185,6 +230,11 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel return config; } + /** + * Create the config for that action used for its workflow + * @param {Event} event Event from the button used to trigger the Action + * @returns {object} + */ prepareConfig(event) { const config = this.prepareBaseConfig(event); for(const clsField of Object.values(this.schema.fields)) { @@ -194,10 +244,20 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel return config; } + /** + * Method used to know if a configuration dialog must be shown or not when there is no roll. + * @param {*} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. + * @returns {boolean} + */ requireConfigurationDialog(config) { return !config.event.shiftKey && !config.hasRoll && (config.costs?.length || config.uses); } + /** + * + * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. + * @param {boolean} successCost + */ async consume(config, successCost = false) { await this.workflow.get("cost")?.execute(config, successCost); await this.workflow.get("uses")?.execute(config, successCost); @@ -209,12 +269,16 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel } } + /** + * Set if a configuration dialog must be shown or not if a special keyboard key is pressed. + * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. + */ static applyKeybindings(config) { config.dialog.configure ??= !(config.event.shiftKey || config.event.altKey || config.event.ctrlKey); } /** - * Getters to know which parts the action of composed of. A field can exist but configured to not be used. + * Getters to know which parts the action is composed of. A field can exist but configured to not be used. * @returns {boolean} If that part is in the action. */ @@ -229,10 +293,6 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel get hasHealing() { return this.damage?.parts?.length && this.type === 'healing' } - - get hasDamagePart() { - return this.damage?.parts?.length; - } get hasSave() { return !!this.save?.trait; diff --git a/module/data/action/beastformAction.mjs b/module/data/action/beastformAction.mjs index 8c2dd31e..657cfde2 100644 --- a/module/data/action/beastformAction.mjs +++ b/module/data/action/beastformAction.mjs @@ -1,10 +1,9 @@ -import BeastformDialog from '../../applications/dialogs/beastformDialog.mjs'; import DHBaseAction from './baseAction.mjs'; export default class DhBeastformAction extends DHBaseAction { static extraSchemas = [...super.extraSchemas, 'beastform']; - async use(event, options) { + /* async use(event, options) { const beastformConfig = this.prepareBeastformConfig(); const abort = await this.handleActiveTransformations(); @@ -82,5 +81,5 @@ export default class DhBeastformAction extends DHBaseAction { beastformEffects.map(x => x.id) ); return existingEffects; - } + } */ } diff --git a/module/data/fields/action/beastformField.mjs b/module/data/fields/action/beastformField.mjs index 832bd9f6..62c735d0 100644 --- a/module/data/fields/action/beastformField.mjs +++ b/module/data/fields/action/beastformField.mjs @@ -1,6 +1,13 @@ +import BeastformDialog from "../../../applications/dialogs/beastformDialog.mjs"; + const fields = foundry.data.fields; export default class BeastformField extends fields.SchemaField { + /** + * Action Workflow order + */ + static order = 90; + constructor(options = {}, context = {}) { const beastformFields = { tierAccess: new fields.SchemaField({ @@ -27,4 +34,96 @@ export default class BeastformField extends fields.SchemaField { }; super(beastformFields, options, context); } + + /** + * Beastform Transformation Action Workflow part. + * Must be called within Action context or similar. + * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. + */ + static async execute(config) { + // Should not be useful anymore here + await BeastformField.handleActiveTransformations.call(this); + + const { selected, evolved, hybrid } = await BeastformDialog.configure(config, this.item); + if (!selected) return false; + + return await BeastformField.transform.call(this, selected, evolved, hybrid); + } + + /** + * Update Action Workflow config object. + * Must be called within Action context. + * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. + */ + prepareConfig(config) { + if(this.actor.effects.find(x => x.type === 'beastform')) { + ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.beastformAlreadyApplied')); + return false; + } + + const settingsTiers = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers).tiers; + const actorLevel = this.actor.system.levelData.level.current; + const actorTier = + Object.values(settingsTiers).find( + tier => actorLevel >= tier.levels.start && actorLevel <= tier.levels.end + ) ?? 1; + + config.tierLimit = this.beastform.tierAccess.exact ?? actorTier; + } + + /** + * TODO by Harry + * @param {*} selectedForm + * @param {*} evolvedData + * @param {*} hybridData + * @returns + */ + static async transform(selectedForm, evolvedData, hybridData) { + const formData = evolvedData?.form ? evolvedData.form.toObject() : selectedForm.toObject(); + const beastformEffect = formData.effects.find(x => x.type === 'beastform'); + if (!beastformEffect) { + ui.notifications.error('DAGGERHEART.UI.Notifications.beastformMissingEffect'); + return false; + } + + if (evolvedData?.form) { + const evolvedForm = selectedForm.effects.find(x => x.type === 'beastform'); + if (!evolvedForm) { + ui.notifications.error('DAGGERHEART.UI.Notifications.beastformMissingEffect'); + return false; + } + + beastformEffect.changes = [...beastformEffect.changes, ...evolvedForm.changes]; + formData.system.features = [...formData.system.features, ...selectedForm.system.features.map(x => x.uuid)]; + } + + if (selectedForm.system.beastformType === CONFIG.DH.ITEM.beastformTypes.hybrid.id) { + formData.system.advantageOn = Object.values(hybridData.advantages).reduce((advantages, formCategory) => { + Object.keys(formCategory).forEach(advantageKey => { + advantages[advantageKey] = formCategory[advantageKey]; + }); + return advantages; + }, {}); + formData.system.features = [ + ...formData.system.features, + ...Object.values(hybridData.features).flatMap(x => Object.keys(x)) + ]; + } + + this.actor.createEmbeddedDocuments('Item', [formData]); + } + + /** + * Remove existing beastform effect and return true if there was one + * @returns {boolean} + */ + static async handleActiveTransformations() { + const beastformEffects = this.actor.effects.filter(x => x.type === 'beastform'); + const existingEffects = beastformEffects.length > 0; + await this.actor.deleteEmbeddedDocuments( + 'ActiveEffect', + beastformEffects.map(x => x.id) + ); + return existingEffects; + } } diff --git a/module/data/fields/action/costField.mjs b/module/data/fields/action/costField.mjs index 1f2ee630..72325d73 100644 --- a/module/data/fields/action/costField.mjs +++ b/module/data/fields/action/costField.mjs @@ -4,7 +4,7 @@ export default class CostField extends fields.ArrayField { /** * Action Workflow order */ - order = 150; + static order = 150; /** @inheritDoc */ constructor(options = {}, context = {}) { @@ -29,11 +29,11 @@ export default class CostField extends fields.ArrayField { /** * Cost Consumption Action Workflow part. * Consume configured action resources. - * Must be called within Action context. + * Must be called within Action context or similar. * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. * @param {boolean} [successCost=false] Consume only resources configured as "On Success only" if not already consumed. */ - async execute(config, successCost = false) { + static async execute(config, successCost = false) { const actor= this.actor.system.partner ?? this.actor, usefulResources = { ...foundry.utils.deepClone(actor.system.resources), @@ -44,13 +44,15 @@ export default class CostField extends fields.ArrayField { } }; - for (var cost of config.costs) { - if (cost.keyIsID) { - usefulResources[cost.key] = { - value: cost.value, - target: this.parent.parent, - keyIsID: true - }; + if(this.parent?.parent) { + for (var cost of config.costs) { + if (cost.keyIsID) { + usefulResources[cost.key] = { + value: cost.value, + target: this.parent.parent, + keyIsID: true + }; + } } } @@ -78,7 +80,7 @@ export default class CostField extends fields.ArrayField { /** * Update Action Workflow config object. - * Must be called within Action context. + * Must be called within Action context or similar. * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. * @returns {boolean} Return false if fast-forwarded and no more uses. */ diff --git a/module/data/fields/action/damageField.mjs b/module/data/fields/action/damageField.mjs index acaedbca..631171aa 100644 --- a/module/data/fields/action/damageField.mjs +++ b/module/data/fields/action/damageField.mjs @@ -7,7 +7,7 @@ export default class DamageField extends fields.SchemaField { /** * Action Workflow order */ - order = 20; + static order = 20; /** @inheritDoc */ constructor(options, context = {}) { @@ -24,12 +24,12 @@ export default class DamageField extends fields.SchemaField { /** * Roll Damage/Healing Action Workflow part. - * Must be called within Action context. + * Must be called within Action context or similar. * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. * @param {string} [messageId=null] ChatMessage Id where the clicked button belong. * @param {boolean} [force=false] If the method should be executed outside of Action workflow, for ChatMessage button for example. */ - async execute(config, messageId = null, force = false) { + static async execute(config, messageId = null, force = false) { if((this.hasRoll && DamageField.getAutomation() === CONFIG.DH.SETTINGS.actionAutomationChoices.never.id) && !force) return; let formulas = this.damage.parts.map(p => ({ @@ -97,11 +97,11 @@ export default class DamageField extends fields.SchemaField { } /** - * - * Must be called within Action context. - * @param {*} part - * @param {*} data - * @returns + * Return value or valueAlt from damage part + * Must be called within Action context or similar. + * @param {object} part Damage Part + * @param {object} data Action getRollData + * @returns Formula value object */ static getFormulaValue(part, data) { let formulaValue = part.value; @@ -118,10 +118,10 @@ export default class DamageField extends fields.SchemaField { } /** - * - * Must be called within Action context. - * @param {*} formulas - * @param {*} systemData + * Prepare formulas for Damage Roll + * Must be called within Action context or similar. + * @param {object[]} formulas Array of formatted formulas object + * @param {object} data Action getRollData * @returns */ static formatFormulas(formulas, data) { diff --git a/module/data/fields/action/effectsField.mjs b/module/data/fields/action/effectsField.mjs index f0bd4e1f..0d189f18 100644 --- a/module/data/fields/action/effectsField.mjs +++ b/module/data/fields/action/effectsField.mjs @@ -4,7 +4,7 @@ export default class EffectsField extends fields.ArrayField { /** * Action Workflow order */ - order = 100; + static order = 100; /** @inheritDoc */ constructor(options = {}, context = {}) { @@ -17,12 +17,12 @@ export default class EffectsField extends fields.ArrayField { /** * Apply Effects Action Workflow part. - * Must be called within Action context. + * Must be called within Action context or similar. * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. * @param {object[]} [targets=null] Array of targets to override pre-selected ones. * @param {boolean} [force=false] If the method should be executed outside of Action workflow, for ChatMessage button for example. */ - async execute(config, targets = null, force = false) { + static async execute(config, targets = null, force = false) { if(!config.hasEffect) return; let message = config.message ?? ui.chat.collection.get(config.parent?._id); if(!message) { @@ -37,10 +37,9 @@ export default class EffectsField extends fields.ArrayField { } /** - * - * Must be called within Action context. - * @param {*} targets - * @returns + * Apply Action Effects to a list of Targets + * Must be called within Action context or similar. + * @param {object[]} targets Array of formatted targets */ static async applyEffects(targets) { if (!this.effects?.length || !targets?.length) return; @@ -59,10 +58,9 @@ export default class EffectsField extends fields.ArrayField { } /** - * - * @param {*} effect - * @param {*} actor - * @returns + * Apply an Effect to a target or enable it if already on it + * @param {object} effect Effect object containing ActiveEffect UUID + * @param {object} actor Actor Document */ static async applyEffect(effect, actor) { const existingEffect = actor.effects.find(e => e.origin === effect.uuid); diff --git a/module/data/fields/action/macroField.mjs b/module/data/fields/action/macroField.mjs index 7ae0746f..222feb2a 100644 --- a/module/data/fields/action/macroField.mjs +++ b/module/data/fields/action/macroField.mjs @@ -1,7 +1,10 @@ const fields = foundry.data.fields; export default class MacroField extends fields.DocumentUUIDField { - order = 70; + /** + * Action Workflow order + */ + static order = 70; /** @inheritDoc */ constructor(context = {}) { @@ -10,9 +13,10 @@ export default class MacroField extends fields.DocumentUUIDField { /** * Macro Action Workflow part. + * Must be called within Action context or similar or similar. * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. Currently not used. */ - async execute(config) { + static async execute(config) { const fixUUID = !this.macro.includes('Macro.') ? `Macro.${this.macro}` : this.macro, macro = await fromUuid(fixUUID); try { diff --git a/module/data/fields/action/rollField.mjs b/module/data/fields/action/rollField.mjs index a6fceb39..d4b82e5e 100644 --- a/module/data/fields/action/rollField.mjs +++ b/module/data/fields/action/rollField.mjs @@ -110,7 +110,10 @@ export class DHActionRollData extends foundry.abstract.DataModel { } export default class RollField extends fields.EmbeddedDataField { - order = 10; + /** + * Action Workflow order + */ + static order = 10; /** @inheritDoc */ constructor(options, context = {}) { @@ -119,10 +122,10 @@ export default class RollField extends fields.EmbeddedDataField { /** * Roll Action Workflow part. - * Must be called within Action context. + * Must be called within Action context or similar. * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. */ - async execute(config) { + static async execute(config) { if(!config.hasRoll) return; config = await this.actor.diceRoll(config); if(!config) return false; diff --git a/module/data/fields/action/saveField.mjs b/module/data/fields/action/saveField.mjs index 96fcc267..5103523b 100644 --- a/module/data/fields/action/saveField.mjs +++ b/module/data/fields/action/saveField.mjs @@ -6,7 +6,7 @@ export default class SaveField extends fields.SchemaField { /** * Action Workflow order */ - order = 50; + static order = 50; /** @inheritDoc */ constructor(options = {}, context = {}) { @@ -27,12 +27,12 @@ export default class SaveField extends fields.SchemaField { /** * Reaction Roll Action Workflow part. - * Must be called within Action context. + * Must be called within Action context or similar. * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. * @param {object[]} [targets=null] Array of targets to override pre-selected ones. * @param {boolean} [force=false] If the method should be executed outside of Action workflow, for ChatMessage button for example. */ - async execute(config, targets = null, force = false) { + 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) { diff --git a/module/data/fields/action/usesField.mjs b/module/data/fields/action/usesField.mjs index a990f77c..d180ddf8 100644 --- a/module/data/fields/action/usesField.mjs +++ b/module/data/fields/action/usesField.mjs @@ -6,7 +6,7 @@ export default class UsesField extends fields.SchemaField { /** * Action Workflow order */ - order = 160; + static order = 160; /** @inheritDoc */ constructor(options = {}, context = {}) { @@ -29,11 +29,11 @@ export default class UsesField extends fields.SchemaField { /** * Uses Consumption Action Workflow part. * Increment Action spent uses by 1. - * Must be called within Action context. + * Must be called within Action context or similar or similar. * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. * @param {boolean} [successCost=false] Consume only resources configured as "On Success only" if not already consumed. */ - async execute(config, successCost = false) { + static async execute(config, successCost = false) { if ( config.uses?.enabled && ((!successCost && (!config.uses?.consumeOnSuccess || config.roll?.success)) || @@ -60,9 +60,9 @@ export default class UsesField extends fields.SchemaField { } /** - * + * Prepare Uses object for Action Workflow * Must be called within Action context. - * @param {*} uses + * @param {object} uses * @returns {object} */ static calcUses(uses) { diff --git a/styles/less/ui/settings/settings.less b/styles/less/ui/settings/settings.less index faddb3e6..11142e19 100644 --- a/styles/less/ui/settings/settings.less +++ b/styles/less/ui/settings/settings.less @@ -19,6 +19,41 @@ &.start-align { align-self: flex-start; } + + // .form-group { + // flex-wrap: wrap; + + // .hint { + // flex: 0 0 100%; + // margin: 0; + // font-size: var(--font-size-12); + // color: var(--color-form-hint); + // } + + // &:hover { + // .hint { + // color: var(--color-form-hint-hover); + // } + // } + + // &.setting-two-values { + // display: grid; + // grid-template-columns: repeat(3, 1fr); + // gap: .25rem .5rem; + + // label, .form-fields { + // flex: 1; + // } + + // .form-group label { + // text-align: right; + // } + + // .hint { + // grid-column: 1 / -1; + // } + // } + // } } .setting-group-field { @@ -140,44 +175,49 @@ } } - .tab.with-hint { - fieldset { - gap: .5rem; + // .tab.with-hint { + // fieldset { + // gap: .5rem; - .form-group { + // .form-group { // flex-wrap: wrap; - gap: .25rem; + // gap: .25rem .5rem; - label { - flex: 1; - } + // label, .form-fields { + // flex: 1; + // } - .hint { - flex: 0 0 100%; - margin: 0; - font-size: var(--font-size-14); - color: var(--color-form-hint); - } + // .hint { + // flex: 0 0 100%; + // margin: 0; + // font-size: var(--font-size-12); + // color: var(--color-form-hint); + // } - &:hover { - .hint { - color: var(--color-form-hint-hover); - } - } - } + // &:hover { + // .hint { + // color: var(--color-form-hint-hover); + // } + // } + // } - .form-group.setting-two-values { - display: grid; - grid-template-columns: repeat(3, 1fr); + // .form-group.setting-two-values { + // display: grid; + // grid-template-columns: repeat(3, 1fr); + // gap: .25rem .5rem; - .form-group label { - text-align: right; - } + // label, .form-fields { + // flex: 1; + // } - .hint { - grid-column: 1 / -1; - } - } - } - } + // .form-group label { + // text-align: right; + // } + + // .hint { + // grid-column: 1 / -1; + // } + // } + // } + // } }