import { TRIGGER_TYPES } from "../constants.js"; import { handleTrigger } from "../engine/reaction-engine.js"; import { getBestTokenDocumentForActor } from "../utils/token-utils.js"; export function registerDnd5eDamageTrigger() { Hooks.on("dnd5e.applyDamage", async (actor, amount, options = {}) => { if (!game.user.isGM) return; if (!actor) return; const hookOptions = normalizeDamageHookOptions(options); const tokenDocument = resolveDamageTokenDocument(actor, hookOptions); const damageTypes = extractDamageTypes(hookOptions); const numericAmount = Number(amount) || 0; await handleTrigger(TRIGGER_TYPES.DAMAGE_RECEIVED, { actor: tokenDocument?.actor ?? actor, targetActor: tokenDocument?.actor ?? actor, tokenDocument, targetTokenDocument: tokenDocument, targetToken: tokenDocument?.object ?? null, amount: numericAmount, damageAmount: numericAmount, damageTypes, options: hookOptions }); }); } function normalizeDamageHookOptions(options) { return options && typeof options === "object" ? options : {}; } function resolveDamageTokenDocument(actor, options) { return options.tokenDocument ?? options.token?.document ?? options.token ?? options.targetTokenDocument ?? options.targetToken?.document ?? actor.token ?? getBestTokenDocumentForActor(actor); } function extractDamageTypes(options) { const knownTypes = getKnownDamageTypes(); const collected = new Set(); collectDamageTypes(options, knownTypes, collected); return Array.from(collected); } function collectDamageTypes(value, knownTypes, collected, seen = new WeakSet(), depth = 0) { if (value == null || depth > 5) return; if (typeof value === "string") { if (knownTypes.has(value)) collected.add(value); return; } if (typeof value !== "object") return; if (seen.has(value)) return; seen.add(value); if (Array.isArray(value) || value instanceof Set) { for (const entry of value) collectDamageTypes(entry, knownTypes, collected, seen, depth + 1); return; } if (value instanceof Map) { for (const entry of value.values()) collectDamageTypes(entry, knownTypes, collected, seen, depth + 1); return; } for (const key of ["type", "damageType", "damageTypeId", "types", "damageTypes", "damage", "damages", "damageDetail", "parts"]) { collectDamageTypes(value[key], knownTypes, collected, seen, depth + 1); } } function getKnownDamageTypes() { const damageTypes = CONFIG.DND5E?.damageTypes ?? {}; if (damageTypes instanceof Map) return new Set(damageTypes.keys()); return new Set(Object.keys(damageTypes)); }