import { MODULE_ID, SETTINGS } from "../constants.js"; export async function assignReactionToSelectedTokens(reactionId) { if (!game.user.isGM) { ui.notifications.warn(game.i18n.localize("CONFIGURABLE_REACTIONS.Errors.GmOnly")); return; } if (!reactionId) { ui.notifications.warn(game.i18n.localize("CONFIGURABLE_REACTIONS.Errors.NoReactionSelected")); return; } const selectedTokens = canvas?.tokens?.controlled ?? []; if (!selectedTokens.length) { ui.notifications.warn(game.i18n.localize("CONFIGURABLE_REACTIONS.Errors.NoTokensSelected")); return; } const reactions = game.settings.get(MODULE_ID, SETTINGS.REACTIONS) ?? []; const reaction = reactions.find(r => r.id === reactionId); if (!reaction) { ui.notifications.error(game.i18n.localize("CONFIGURABLE_REACTIONS.Errors.ReactionNotFound")); return; } const assignments = foundry.utils.deepClone(game.settings.get(MODULE_ID, SETTINGS.ASSIGNMENTS) ?? []); let assignedCount = 0; let skippedCount = 0; for (const token of selectedTokens) { const tokenDocument = token.document; const actor = token.actor; if (!actor) { skippedCount++; continue; } const assignment = buildAssignmentForToken(tokenDocument, actor, reactionId); const alreadyAssigned = assignments.some(existing => existing.reactionId === assignment.reactionId && existing.mode === assignment.mode && existing.actorUuid === assignment.actorUuid && existing.tokenUuid === assignment.tokenUuid ); if (alreadyAssigned) { skippedCount++; continue; } assignments.push(assignment); if (assignment.mode === "actor") { await addReactionFlag(actor, reactionId); await ensureManagedReactionEffectOnActor(actor, reaction); } else { await addReactionFlag(tokenDocument, reactionId); } assignedCount++; } await game.settings.set(MODULE_ID, SETTINGS.ASSIGNMENTS, assignments); ui.notifications.info(game.i18n.format("CONFIGURABLE_REACTIONS.Assignments.AssignedResult", { assigned: assignedCount, skipped: skippedCount })); } export async function removeAssignment(assignmentId) { if (!game.user.isGM) return; const assignments = foundry.utils.deepClone(game.settings.get(MODULE_ID, SETTINGS.ASSIGNMENTS) ?? []); const assignment = assignments.find(a => a.id === assignmentId); if (!assignment) return; await game.settings.set(MODULE_ID, SETTINGS.ASSIGNMENTS, assignments.filter(a => a.id !== assignmentId)); if (assignment.mode === "actor" && assignment.actorUuid) { const actor = await fromUuid(assignment.actorUuid); if (actor) await removeReactionFromDocument(actor, assignment.reactionId, true); } if (assignment.mode === "token" && assignment.tokenUuid) { const tokenDocument = await fromUuid(assignment.tokenUuid); if (tokenDocument) await removeReactionFromDocument(tokenDocument, assignment.reactionId, false); } } function buildAssignmentForToken(tokenDocument, actor, reactionId) { if (tokenDocument.actorLink === true) { return { id: foundry.utils.randomID(), reactionId, mode: "actor", actorUuid: actor.uuid, tokenUuid: null, sceneUuid: null, name: actor.name }; } return { id: foundry.utils.randomID(), reactionId, mode: "token", actorUuid: null, tokenUuid: tokenDocument.uuid, sceneUuid: tokenDocument.parent?.uuid ?? canvas.scene?.uuid ?? null, name: tokenDocument.name }; } async function addReactionFlag(document, reactionId) { const assigned = foundry.utils.deepClone(document.getFlag(MODULE_ID, "assignedReactions") ?? []); if (!assigned.some(entry => entry.reactionId === reactionId)) { assigned.push({ reactionId, assignedAt: Date.now(), assignedBy: game.user.id }); } await document.setFlag(MODULE_ID, "assignedReactions", assigned); } async function ensureManagedReactionEffectOnActor(actor, reaction) { if (!game.settings.get(MODULE_ID, SETTINGS.CREATE_MANAGED_EFFECTS_ON_ASSIGNMENT)) return; const existing = actor.effects.find(effect => effect.getFlag(MODULE_ID, "reactionId") === reaction.id && effect.getFlag(MODULE_ID, "managed") === true ); if (existing) return; await actor.createEmbeddedDocuments("ActiveEffect", [{ name: game.i18n.format("CONFIGURABLE_REACTIONS.Effects.ManagedReaction", { name: reaction.name }), icon: reaction.icon ?? "icons/svg/aura.svg", origin: `Module.${MODULE_ID}`, disabled: false, transfer: false, changes: [], flags: { [MODULE_ID]: { reactionId: reaction.id, managed: true } } }]); } async function removeReactionFromDocument(document, reactionId, removeManagedEffects) { const assigned = foundry.utils.deepClone(document.getFlag(MODULE_ID, "assignedReactions") ?? []) .filter(entry => entry.reactionId !== reactionId); if (assigned.length) await document.setFlag(MODULE_ID, "assignedReactions", assigned); else await document.unsetFlag(MODULE_ID, "assignedReactions"); if (!removeManagedEffects || !document.effects) return; const effectIds = document.effects .filter(effect => effect.getFlag(MODULE_ID, "reactionId") === reactionId && effect.getFlag(MODULE_ID, "managed") === true) .map(effect => effect.id); if (effectIds.length) await document.deleteEmbeddedDocuments("ActiveEffect", effectIds); }