Improved registration and unregistration of triggers

This commit is contained in:
WBHarry 2026-01-15 21:52:22 +01:00
parent 9393bab6cf
commit 9fb9ae1b91
5 changed files with 129 additions and 27 deletions

View file

@ -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) {

View file

@ -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) {

View file

@ -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) {

View file

@ -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();
}
}

View file

@ -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);
}
}
}