From c3ff9b88b0c41edba9977077dd87152f26cab66a Mon Sep 17 00:00:00 2001 From: Matt Smith Date: Sun, 18 Jan 2026 16:03:47 -0600 Subject: [PATCH] Add support for Token Initiative variant - Added new 'Token Actions' setting to enable the token initiative variant rule. If this setting is enabled, the action counter in the combat sidebar will instead represent the two actions each character has each round. Resetting the actor's action count or advancing to the next round will reset this back up to 2. - Added tokenActions field to actors that can be used to track remaining actions each round. This can be added as a resource to the actor's token, displaying it as a progress bar on the actor. - Updated combat tracker to work with the harm pools house rule. --- src/lang/en.yml | 4 ++++ src/module/data/actor-character.mjs | 17 +++++++++++++++++ src/module/documents/combat.mjs | 28 ++++++++++++++++++++++++---- src/module/grimwild.mjs | 10 ++++++++++ src/templates/combat/tracker.hbs | 10 +++++++--- 5 files changed, 62 insertions(+), 7 deletions(-) diff --git a/src/lang/en.yml b/src/lang/en.yml index 50e0303..1dd78ee 100644 --- a/src/lang/en.yml +++ b/src/lang/en.yml @@ -28,6 +28,9 @@ GRIMWILD: slowXp: name: "Slow XP" hint: Enable to use half-rate XP where it takes two pips per XP checkbox to be filled. + tokenActions: + name: "[Variant] Token Initiative" + hint: Enable the variant Token Initiative rule that gives each players 2 tokens per "round" of combat/scenes. Otherwise, a simple action counter will be used to count how many actions have been taken. Stat: bra: long: Brawn @@ -248,6 +251,7 @@ GRIMWILD: ActionCount: 'Rolls' ResetActionCount: Reset Action Count SpotlightActor: Spotlight + TokenActions: 'Actions' TYPES: Actor: character: Character diff --git a/src/module/data/actor-character.mjs b/src/module/data/actor-character.mjs index 7ac3167..e705535 100644 --- a/src/module/data/actor-character.mjs +++ b/src/module/data/actor-character.mjs @@ -128,6 +128,20 @@ export default class GrimwildCharacter extends GrimwildActorBase { }) ); + schema.tokenActions = new fields.SchemaField({ + value: new fields.NumberField({ + ...requiredInteger, + max: 2, + initial: 2, + min: 0 + }), + max: new fields.NumberField({ + ...requiredInteger, + initial: 2, + min: 0 + }) + }); + return schema; } @@ -354,6 +368,9 @@ export default class GrimwildCharacter extends GrimwildActorBase { const actionCount = Number(combatant.flags?.grimwild?.actionCount ?? 0); await combatant.setFlag("grimwild", "actionCount", actionCount + 1); + const actor = combatant.actor; + await actor.update({"system.tokenActions.value": Math.max(actor.system.tokenActions.value - 1, 0)}); + // Update the active turn. const combatantTurn = combat.turns.findIndex((c) => c.id === combatant.id); if (combatantTurn !== undefined) { diff --git a/src/module/documents/combat.mjs b/src/module/documents/combat.mjs index 6746e5e..f4baf6c 100644 --- a/src/module/documents/combat.mjs +++ b/src/module/documents/combat.mjs @@ -40,11 +40,15 @@ export class GrimwildCombat extends foundry.documents.Combat { /** @inheritdoc */ async _onStartRound(context) { + if (game.settings.get("grimwild", "tokenActions")) { + this.resetActions(); + } } resetActions() { for (let combatant of this.combatants) { combatant.setFlag("grimwild", "actionCount", 0); + combatant.actor.update({"system.tokenActions.value": 2}); } } @@ -111,6 +115,9 @@ export class GrimwildCombatTracker extends foundry.applications.sidebar.tabs.Com } } + context.tokenActions = game.settings.get("grimwild", "tokenActions"); + context.enableHarmPools = game.settings.get("grimwild", "enableHarmPools"); + if (!turns.character.length) delete turns.character; if (!turns.monster.length) delete turns.monster; if (!turns.other.length) delete turns.other; @@ -131,7 +138,9 @@ export class GrimwildCombatTracker extends foundry.applications.sidebar.tabs.Com turn.spark = combatant.actor.system.spark; turn.bloodied = combatant.actor.system.bloodied; turn.rattled = combatant.actor.system.rattled; - turn.actionCount = combatant.flags?.grimwild?.actionCount ?? 0; + turn.actionCount = game.settings.get("grimwild", "tokenActions") + ? combatant.actor.system.tokenActions.value ?? 0 + : combatant.flags?.grimwild?.actionCount ?? 0; } return turn; @@ -173,7 +182,13 @@ export class GrimwildCombatTracker extends foundry.applications.sidebar.tabs.Com name: "GRIMWILD.Combat.ResetActionCount", icon: '', condition: (li) => game.user.isGM, - callback: (li) => getCombatant(li)?.setFlag("grimwild", "actionCount", 0) + callback: (li) => { + const combatant = getCombatant(li); + if (combatant) { + combatant?.setFlag("grimwild", "actionCount", 0) + combatant?.actor.update({"system.tokenActions.value": 2}); + } + } }, { name: "COMBAT.CombatantRemove", icon: '', @@ -200,11 +215,16 @@ export class GrimwildCombatTracker extends foundry.applications.sidebar.tabs.Com const { combatantId } = event.target.closest(".combatant[data-combatant-id]")?.dataset ?? {}; const { harm } = target.dataset ?? false; const combatant = this.viewed.combatants.get(combatantId); + const update = {}; if (!combatant || !harm) return; const actor = combatant.actor; if (!actor.isOwner) return; - const harmValue = actor.system?.[harm].marked; - actor.update({ [`system.${harm}.marked`]: !harmValue }); + let harmValue = actor.system?.[harm].marked; + update[`system.${harm}.marked`] = !harmValue; + if (game.settings.get("grimwild", "enableHarmPools")) { + update[`system.${harm}.pool.diceNum`] = actor.system?.[harm].pool.diceNum > 0 ? 0 : 1; + } + actor.update(update); } static #onToggleSpark(...args) { diff --git a/src/module/grimwild.mjs b/src/module/grimwild.mjs index f7afb4b..1c5578a 100644 --- a/src/module/grimwild.mjs +++ b/src/module/grimwild.mjs @@ -146,6 +146,16 @@ Hooks.once("init", function () { requiresReload: true }); + game.settings.register("grimwild", "tokenActions", { + name: game.i18n.localize("GRIMWILD.Settings.tokenActions.name"), + hint: game.i18n.localize("GRIMWILD.Settings.tokenActions.hint"), + scope: "world", + config: true, + type: Boolean, + default: false, + requiresReload: true + }); + // Override 3d dice. if (game.modules.get("dice-so-nice")) { game.settings.register("grimwild", "diceSoNiceOverride", { diff --git a/src/templates/combat/tracker.hbs b/src/templates/combat/tracker.hbs index 957ccd4..55a70ed 100644 --- a/src/templates/combat/tracker.hbs +++ b/src/templates/combat/tracker.hbs @@ -5,7 +5,11 @@
{{localize "Name"}}
-
{{localize "GRIMWILD.Combat.ActionCount"}}
+ {{#if @root.tokenActions}} +
{{localize "GRIMWILD.Combat.TokenActions"}}
+ {{else}} +
{{localize "GRIMWILD.Combat.ActionCount"}}
+ {{/if}}
{{/if}} {{#each turnsOfType}} @@ -47,8 +51,8 @@ {{#if (eq type "character")}}
{{spark.value}} - - + {{#if @root.enableHarmPools}}{{bloodied.pool.diceNum}}{{/if}} + {{#if @root.enableHarmPools}}{{rattled.pool.diceNum}}{{/if}}
{{/if}}