From 9fb9ae1b918affd490b7cfeee64409dade3fa9a2 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Thu, 15 Jan 2026 21:52:22 +0100 Subject: [PATCH] Improved registration and unregistration of triggers --- daggerheart.mjs | 111 ++++++++++++++++++++++++++++++++++--- module/data/item/base.mjs | 21 +------ module/documents/actor.mjs | 4 ++ module/documents/item.mjs | 14 +++++ module/documents/token.mjs | 6 ++ 5 files changed, 129 insertions(+), 27 deletions(-) diff --git a/daggerheart.mjs b/daggerheart.mjs index 4e88c148..d1b09028 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -389,16 +389,107 @@ Hooks.on('refreshToken', (_, options) => { Hooks.on('renderCompendiumDirectory', (app, html) => applications.ui.ItemBrowser.injectSidebarButton(html)); Hooks.on('renderDocumentDirectory', (app, html) => applications.ui.ItemBrowser.injectSidebarButton(html)); +/* Non actor-linked Actors should unregister the triggers of their tokens if a scene's token layer is torn down */ +Hooks.on('canvasTearDown', canvas => { + game.system.registeredTriggers.unregisterSceneTriggers(canvas.scene); +}); + +Hooks.on('canvasReady', canas => { + game.system.registeredTriggers.registerSceneTriggers(canvas.scene); +}); + class RegisteredTriggers extends Map { constructor() { super(); } - async registerTriggers(trigger, actor, triggeringActorType, uuid, commands) { - const existingTrigger = this.get(trigger); - if (!existingTrigger) this.set(trigger, new Map()); + registerTriggers(triggers, actor, uuid) { + for (const triggerKey of Object.keys(CONFIG.DH.TRIGGER.triggers)) { + const match = triggers[triggerKey]; + const existingTrigger = this.get(triggerKey); - this.get(trigger).set(uuid, { actor, triggeringActorType, commands }); + if (!match) { + if (existingTrigger?.get(uuid)) this.get(triggerKey).delete(uuid); + } else { + const { trigger, triggeringActorType, commands } = match; + + if (!existingTrigger) this.set(trigger, new Map()); + this.get(trigger).set(uuid, { actor, triggeringActorType, commands }); + } + } + } + + registerItemTriggers(item) { + for (const action of item.system.actions ?? []) { + if (!action.actor) continue; + + /* Non actor-linked should only prep synthetic actors so they're not registering triggers unless they're on the canvas */ + if ( + !action.actor.prototypeToken.actorLink && + (!(action.actor.parent instanceof game.system.api.documents.DhToken) || !action.actor.parent?.uuid) + ) + continue; + + const triggers = {}; + for (const trigger of action.triggers) { + const { args } = CONFIG.DH.TRIGGER.triggers[trigger.trigger]; + const fn = new foundry.utils.AsyncFunction(...args, `{${trigger.command}\n}`); + + if (!triggers[trigger.trigger]) + triggers[trigger.trigger] = { + trigger: trigger.trigger, + triggeringActorType: trigger.triggeringActorType, + commands: [] + }; + triggers[trigger.trigger].commands.push(fn.bind(action)); + } + + this.registerTriggers(triggers, action.actor?.uuid, item.uuid); + } + } + + unregisterTriggers(triggerKeys, uuid) { + for (const triggerKey of triggerKeys) { + const existingTrigger = this.get(triggerKey); + if (!existingTrigger) return; + + existingTrigger.delete(uuid); + } + } + + unregisterItemTriggers(items) { + for (const item of items) { + if (!item.system.actions.size) continue; + + const triggers = (item.system.actions ?? []).reduce((acc, action) => { + acc.push(...action.triggers.map(x => x.trigger)); + return acc; + }, []); + + this.unregisterTriggers(triggers, item.uuid); + } + } + + unregisterSceneTriggers(scene) { + for (const triggerKey of Object.keys(CONFIG.DH.TRIGGER.triggers)) { + const existingTrigger = this.get(triggerKey); + if (!existingTrigger) continue; + const filtered = new Map(); + for (const [uuid, data] of existingTrigger.entries()) { + if (!uuid.startsWith(scene.uuid)) filtered.set(uuid, data); + } + this.set(triggerKey, filtered); + } + } + + registerSceneTriggers(scene) { + for (const actor of scene.tokens.filter(x => x.actor).map(x => x.actor)) { + if (actor.prototypeToken.actorLink) continue; + + for (const item of actor.items) { + this.registerItemTriggers(item); + } + } } async runTrigger(trigger, currentActor, ...args) { @@ -408,11 +499,17 @@ class RegisteredTriggers extends Map { const dualityTrigger = this.get(trigger); if (dualityTrigger) { - for (let { actor, triggeringActorType, commands } of dualityTrigger.values()) { + const tokenBoundActors = ['adversary', 'environment']; + const triggerActors = ['character', ...tokenBoundActors]; + for (let { actor: actorUuid, triggeringActorType, commands } of dualityTrigger.values()) { + const actor = await foundry.utils.fromUuid(actorUuid); + if (!actor || !triggerActors.includes(actor.type)) continue; + if (tokenBoundActors.includes(actor.type) && !actor.getActiveTokens().length) continue; + const triggerData = CONFIG.DH.TRIGGER.triggers[trigger]; if (triggerData.usesActor && triggeringActorType !== 'any') { - if (triggeringActorType === 'self' && currentActor?.uuid !== actor) continue; - else if (triggeringActorType === 'other' && currentActor?.uuid === actor) continue; + if (triggeringActorType === 'self' && currentActor?.uuid !== actorUuid) continue; + else if (triggeringActorType === 'other' && currentActor?.uuid === actorUuid) continue; } for (let command of commands) { diff --git a/module/data/item/base.mjs b/module/data/item/base.mjs index 415fc8d4..65617830 100644 --- a/module/data/item/base.mjs +++ b/module/data/item/base.mjs @@ -164,26 +164,7 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel { prepareBaseData() { super.prepareBaseData(); - - for (const action of this.actions ?? []) { - if (!action.actor) continue; - - const actionsToRegister = []; - for (let i = 0; i < action.triggers.length; i++) { - const trigger = action.triggers[i]; - const { args } = CONFIG.DH.TRIGGER.triggers[trigger.trigger]; - const fn = new foundry.utils.AsyncFunction(...args, `{${trigger.command}\n}`); - actionsToRegister.push(fn.bind(action)); - if (i === action.triggers.length - 1) - game.system.registeredTriggers.registerTriggers( - trigger.trigger, - action.actor?.uuid, - trigger.triggeringActorType, - this.parent.uuid, - actionsToRegister - ); - } - } + game.system.registeredTriggers.registerItemTriggers(this.parent); } async _preCreate(data, options, user) { diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index 1a4153ad..baca3326 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -104,6 +104,10 @@ export default class DhpActor extends Actor { } } + async _preDelete() { + game.system.registeredTriggers.unregisterItemTriggers(this.items); + } + _onDelete(options, userId) { super._onDelete(options, userId); for (const party of this.parties) { diff --git a/module/documents/item.mjs b/module/documents/item.mjs index 7607658c..e795c5dd 100644 --- a/module/documents/item.mjs +++ b/module/documents/item.mjs @@ -208,4 +208,18 @@ export default class DHItem extends foundry.documents.Item { cls.create(msg); } + + deleteTriggers() { + const actions = Array.from(this.system.actions ?? []); + if (!actions.length) return; + + game.system.registeredTriggers.unregisterTriggers( + actions.flatMap(action => action.triggers.map(x => x.trigger)), + this.uuid + ); + } + + async _preDelete() { + this.deleteTriggers(); + } } diff --git a/module/documents/token.mjs b/module/documents/token.mjs index 4ac29264..317f3acf 100644 --- a/module/documents/token.mjs +++ b/module/documents/token.mjs @@ -536,4 +536,10 @@ export default class DHToken extends CONFIG.Token.documentClass { }; } //#endregion + + async _preDelete() { + if (this.actor && !this.actor.prototypeToken?.actorLink) { + game.system.registeredTriggers.unregisterItemTriggers(this.actor.items); + } + } }