From dccb0bfa2de4e4e3f068fa596f010fb764379e3d Mon Sep 17 00:00:00 2001 From: WBHarry Date: Mon, 17 Nov 2025 23:02:22 +0100 Subject: [PATCH] Added countdown actions --- lang/en.json | 7 ++ .../sheets-configs/action-config.mjs | 3 +- module/applications/ui/countdownEdit.mjs | 2 +- module/config/actionConfig.mjs | 6 ++ module/config/generalConfig.mjs | 4 +- module/data/_module.mjs | 1 + module/data/action/_module.mjs | 2 + module/data/action/baseAction.mjs | 7 ++ module/data/action/countdownAction.mjs | 15 ++++ module/data/fields/action/_module.mjs | 1 + module/data/fields/action/countdownField.mjs | 89 +++++++++++++++++++ module/systemRegistration/handlebars.mjs | 1 + styles/less/ui/countdown/countdown-edit.less | 2 +- templates/actionTypes/countdown.hbs | 21 +++++ .../action-settings/effect.hbs | 3 +- templates/ui/countdown-edit.hbs | 2 +- 16 files changed, 159 insertions(+), 7 deletions(-) create mode 100644 module/data/action/countdownAction.mjs create mode 100644 module/data/fields/action/countdownField.mjs create mode 100644 templates/actionTypes/countdown.hbs diff --git a/lang/en.json b/lang/en.json index 30878907..eb691050 100755 --- a/lang/en.json +++ b/lang/en.json @@ -46,6 +46,10 @@ "name": "Beastform", "tooltip": "Shapeshift the user into another form." }, + "countdown": { + "name": "Countdown", + "tooltip": "Start a countdown" + }, "damage": { "name": "Damage", "tooltip": "Direct damage without a roll." @@ -73,6 +77,9 @@ "exactHint": "The Character's Tier is used if empty", "label": "Beastform" }, + "countdown": { + "defaultOwnership": "Default Ownership" + }, "damage": { "multiplier": "Multiplier", "flatMultiplier": "Flat Multiplier" diff --git a/module/applications/sheets-configs/action-config.mjs b/module/applications/sheets-configs/action-config.mjs index 43753f3b..247e8345 100644 --- a/module/applications/sheets-configs/action-config.mjs +++ b/module/applications/sheets-configs/action-config.mjs @@ -200,7 +200,8 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) { const data = this.action.toObject(), key = event.target.closest('[data-key]').dataset.key; if (!this.action[key]) return; - data[key].push({}); + + data[key].push(this.action.defaultValues[key] ?? {}); this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) }); } diff --git a/module/applications/ui/countdownEdit.mjs b/module/applications/ui/countdownEdit.mjs index 2098fb10..abaafb7e 100644 --- a/module/applications/ui/countdownEdit.mjs +++ b/module/applications/ui/countdownEdit.mjs @@ -50,7 +50,7 @@ export default class CountdownEdit extends HandlebarsApplicationMixin(Applicatio const countdown = this.data.countdowns[key]; acc[key] = { ...countdown, - typeName: game.i18n.localize(CONFIG.DH.GENERAL.countdownBaseTypes[countdown.type].name), + typeName: game.i18n.localize(CONFIG.DH.GENERAL.countdownBaseTypes[countdown.type].label), progress: { ...countdown.progress, typeName: game.i18n.localize(CONFIG.DH.GENERAL.countdownTypes[countdown.progress.type].label) diff --git a/module/config/actionConfig.mjs b/module/config/actionConfig.mjs index 3c669a7b..d64fd9e9 100644 --- a/module/config/actionConfig.mjs +++ b/module/config/actionConfig.mjs @@ -5,6 +5,12 @@ export const actionTypes = { icon: 'fa-khanda', tooltip: 'DAGGERHEART.ACTIONS.TYPES.attack.tooltip' }, + countdown: { + id: 'countdown', + name: 'DAGGERHEART.ACTIONS.TYPES.countdown.name', + icon: 'fa-hourglass-half', + tooltip: 'DAGGERHEART.ACTIONS.TYPES.countdown.tooltip' + }, healing: { id: 'healing', name: 'DAGGERHEART.ACTIONS.TYPES.healing.name', diff --git a/module/config/generalConfig.mjs b/module/config/generalConfig.mjs index 6ecc76e6..9286c610 100644 --- a/module/config/generalConfig.mjs +++ b/module/config/generalConfig.mjs @@ -673,11 +673,11 @@ export const simpleOwnershiplevels = { export const countdownBaseTypes = { narrative: { id: 'narrative', - name: 'DAGGERHEART.APPLICATIONS.Countdown.types.narrative' + label: 'DAGGERHEART.APPLICATIONS.Countdown.types.narrative' }, encounter: { id: 'encounter', - name: 'DAGGERHEART.APPLICATIONS.Countdown.types.encounter' + label: 'DAGGERHEART.APPLICATIONS.Countdown.types.encounter' } }; diff --git a/module/data/_module.mjs b/module/data/_module.mjs index 2749bfce..0a476ee9 100644 --- a/module/data/_module.mjs +++ b/module/data/_module.mjs @@ -2,6 +2,7 @@ export { default as DhCombat } from './combat.mjs'; export { default as DhCombatant } from './combatant.mjs'; export { default as DhTagTeamRoll } from './tagTeamRoll.mjs'; +export * as countdowns from './countdowns.mjs'; export * as actions from './action/_module.mjs'; export * as activeEffects from './activeEffect/_module.mjs'; export * as actors from './actor/_module.mjs'; diff --git a/module/data/action/_module.mjs b/module/data/action/_module.mjs index 7f7a2c82..9cfc48cb 100644 --- a/module/data/action/_module.mjs +++ b/module/data/action/_module.mjs @@ -1,6 +1,7 @@ import AttackAction from './attackAction.mjs'; import BaseAction from './baseAction.mjs'; import BeastformAction from './beastformAction.mjs'; +import CountdownAction from './countdownAction.mjs'; import DamageAction from './damageAction.mjs'; import EffectAction from './effectAction.mjs'; import HealingAction from './healingAction.mjs'; @@ -10,6 +11,7 @@ import SummonAction from './summonAction.mjs'; export const actionsTypes = { base: BaseAction, attack: AttackAction, + countdown: CountdownAction, damage: DamageAction, healing: HealingAction, summon: SummonAction, diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index ba401ae9..6960de11 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -43,6 +43,13 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel return schemaFields; } + /** + * The default values to supply to schema fields when they are created in the actionConfig. Defined by implementing classes. + */ + get defaultValues() { + return {}; + } + /** * Create a Map containing each Action step based on fields define in schema. Ordered by Fields order property. * diff --git a/module/data/action/countdownAction.mjs b/module/data/action/countdownAction.mjs new file mode 100644 index 00000000..3b5a80c6 --- /dev/null +++ b/module/data/action/countdownAction.mjs @@ -0,0 +1,15 @@ +import DHBaseAction from './baseAction.mjs'; + +export default class DhCountdownAction extends DHBaseAction { + static extraSchemas = [...super.extraSchemas, 'countdown']; + + get defaultValues() { + return { + ...super.defaultValues, + countdown: { + name: this.parent.parent.name, + img: this.img + } + }; + } +} diff --git a/module/data/fields/action/_module.mjs b/module/data/fields/action/_module.mjs index 7a33e147..ef69394a 100644 --- a/module/data/fields/action/_module.mjs +++ b/module/data/fields/action/_module.mjs @@ -1,4 +1,5 @@ export { default as CostField } from './costField.mjs'; +export { default as CountdownField } from './countdownField.mjs'; export { default as UsesField } from './usesField.mjs'; export { default as RangeField } from './rangeField.mjs'; export { default as TargetField } from './targetField.mjs'; diff --git a/module/data/fields/action/countdownField.mjs b/module/data/fields/action/countdownField.mjs new file mode 100644 index 00000000..9a8f3544 --- /dev/null +++ b/module/data/fields/action/countdownField.mjs @@ -0,0 +1,89 @@ +import { emitAsGM, GMUpdateEvent, RefreshType, socketEvent } from '../../../systemRegistration/socket.mjs'; + +const fields = foundry.data.fields; + +export default class CountdownField extends fields.ArrayField { + constructor(options = {}, context = {}) { + const element = new fields.SchemaField({ + ...game.system.api.data.countdowns.DhCountdown.defineSchema(), + type: new fields.StringField({ + required: true, + choices: CONFIG.DH.GENERAL.countdownBaseTypes, + initial: CONFIG.DH.GENERAL.countdownBaseTypes.encounter.id, + label: 'DAGGERHEART.GENERAL.type' + }), + name: new fields.StringField({ + required: true, + initial: game.i18n.localize('DAGGERHEART.APPLICATIONS.Countdown.newCountdown'), + label: 'DAGGERHEART.APPLICATIONS.Countdown.FIELDS.countdowns.element.name.label' + }), + defaultOwnership: new fields.NumberField({ + required: true, + choices: CONFIG.DH.GENERAL.simpleOwnershiplevels, + initial: CONST.DOCUMENT_OWNERSHIP_LEVELS.INHERIT, + label: 'DAGGERHEART.ACTIONS.Config.countdown.defaultOwnership' + }) + }); + super(element, options, context); + } + + /** + * Countdown Action Workflow part. + * Must be called within Action context or similar. Requires a GM online to edit the game setting for countdowns. + * @param {object} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. + */ + static async execute(config) { + const noGM = !game.users.find(x => x.isGM && x.active); + if (noGM) { + ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.gmRequired')); + return; + } + + const data = config.countdowns.reduce( + (acc, curr) => { + acc.countdowns[foundry.utils.randomID()] = { + ...curr, + progress: { + ...curr.progress, + current: curr.progress.max + } + }; + return acc; + }, + { countdowns: {} } + ); + + await emitAsGM( + GMUpdateEvent.UpdateCountdowns, + async () => { + const countdownSetting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns); + await countdownSetting.updateSource(data); + await game.settings.set( + CONFIG.DH.id, + CONFIG.DH.SETTINGS.gameSettings.Countdowns, + countdownSetting.toObject() + ), + game.socket.emit(`system.${CONFIG.DH.id}`, { + action: socketEvent.Refresh, + data: { refreshType: RefreshType.Countdown } + }); + Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.Countdown }); + }, + data, + null, + { + refreshType: RefreshType.Countdown + } + ); + } + + /** + * 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) { + config.countdowns = this.countdown; + return config; + } +} diff --git a/module/systemRegistration/handlebars.mjs b/module/systemRegistration/handlebars.mjs index 2bf820c1..2b87dda1 100644 --- a/module/systemRegistration/handlebars.mjs +++ b/module/systemRegistration/handlebars.mjs @@ -28,6 +28,7 @@ export const preloadHandlebarsTemplates = async function () { 'systems/daggerheart/templates/actionTypes/range-target.hbs', 'systems/daggerheart/templates/actionTypes/effect.hbs', 'systems/daggerheart/templates/actionTypes/beastform.hbs', + 'systems/daggerheart/templates/actionTypes/countdown.hbs', 'systems/daggerheart/templates/settings/components/settings-item-line.hbs', 'systems/daggerheart/templates/ui/tooltip/parts/tooltipChips.hbs', 'systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs', diff --git a/styles/less/ui/countdown/countdown-edit.less b/styles/less/ui/countdown/countdown-edit.less index 9051cccb..323ed5af 100644 --- a/styles/less/ui/countdown/countdown-edit.less +++ b/styles/less/ui/countdown/countdown-edit.less @@ -65,7 +65,7 @@ .countdown-edit-container { display: grid; - grid-template-columns: 48px 1fr 64px; + grid-template-columns: 48px 1fr 72px; align-items: center; gap: 8px; diff --git a/templates/actionTypes/countdown.hbs b/templates/actionTypes/countdown.hbs new file mode 100644 index 00000000..1c2669b4 --- /dev/null +++ b/templates/actionTypes/countdown.hbs @@ -0,0 +1,21 @@ +
+ + {{localize "DAGGERHEART.ACTIONS.TYPES.countdown.name"}} + + + {{#each source as |countdown index|}} +
+ {{formField ../fields.name value=countdown.name name=(concat "countdown." index ".name") localize=true}} + +
+
+ {{formField ../fields.type value=countdown.type name=(concat "countdown." index ".type") localize=true}} + {{formField ../fields.defaultOwnership value=countdown.defaultOwnership name=(concat "countdown." index ".defaultOwnership") localize=true}} +
+ {{formField ../fields.img value=countdown.img name=(concat "countdown." index ".img") label="DAGGERHEART.GENERAL.imagePath" localize=true}} +
+ {{formField ../fields.progress.fields.type value=countdown.progress.type name=(concat "countdown." index ".progress.type") localize=true}} + {{formField ../fields.progress.fields.max value=countdown.progress.max name=(concat "countdown." index ".progress.max") localize=true}} +
+ {{/each}} +
\ No newline at end of file diff --git a/templates/sheets-settings/action-settings/effect.hbs b/templates/sheets-settings/action-settings/effect.hbs index 51c15aae..bf2f3aa1 100644 --- a/templates/sheets-settings/action-settings/effect.hbs +++ b/templates/sheets-settings/action-settings/effect.hbs @@ -1,4 +1,4 @@ -
'systems/daggerheart/templates/actionTypes/macro.hbs' fields=fields.macro source=source.macro}}{{/if}} {{#if fields.effects}}{{> 'systems/daggerheart/templates/actionTypes/effect.hbs' fields=fields.effects.element.fields source=source.effects}}{{/if}} {{#if fields.beastform}}{{> 'systems/daggerheart/templates/actionTypes/beastform.hbs' fields=fields.beastform.fields source=source.beastform}}{{/if}} + {{#if fields.countdown}}{{> 'systems/daggerheart/templates/actionTypes/countdown.hbs' fields=fields.countdown.element.fields source=source.countdown}}{{/if}}
\ No newline at end of file diff --git a/templates/ui/countdown-edit.hbs b/templates/ui/countdown-edit.hbs index b95a8471..e6668873 100644 --- a/templates/ui/countdown-edit.hbs +++ b/templates/ui/countdown-edit.hbs @@ -59,7 +59,7 @@