This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
import { MODULE_ID, SETTINGS } from "../constants.js";
|
||||
|
||||
const ASSIGNED_REACTIONS_FLAG = "assignedReactions";
|
||||
const MANAGED_FEATURE_FLAG = "managedAssignmentFeature";
|
||||
const FEATURE_TYPE = "feat";
|
||||
|
||||
export async function assignReactionToSelectedTokens(reactionId) {
|
||||
if (!game.user.isGM) {
|
||||
ui.notifications.warn(game.i18n.localize("CONFIGURABLE_REACTIONS.Errors.GmOnly"));
|
||||
@@ -18,7 +22,7 @@ export async function assignReactionToSelectedTokens(reactionId) {
|
||||
}
|
||||
|
||||
const reactions = game.settings.get(MODULE_ID, SETTINGS.REACTIONS) ?? [];
|
||||
const reaction = reactions.find(r => r.id === reactionId);
|
||||
const reaction = reactions.find(candidate => candidate.id === reactionId);
|
||||
if (!reaction) {
|
||||
ui.notifications.error(game.i18n.localize("CONFIGURABLE_REACTIONS.Errors.ReactionNotFound"));
|
||||
return;
|
||||
@@ -29,65 +33,70 @@ export async function assignReactionToSelectedTokens(reactionId) {
|
||||
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);
|
||||
await ensureManagedReactionEffectOnActor(actor, reaction, tokenDocument.uuid);
|
||||
}
|
||||
|
||||
assignedCount++;
|
||||
const result = await assignReactionToToken({ token, reaction, reactionId, assignments });
|
||||
if (result === "assigned") assignedCount++;
|
||||
else skippedCount++;
|
||||
}
|
||||
|
||||
await game.settings.set(MODULE_ID, SETTINGS.ASSIGNMENTS, assignments);
|
||||
ui.notifications.info(game.i18n.format("CONFIGURABLE_REACTIONS.Assignments.AssignedResult", { assigned: assignedCount, skipped: skippedCount }));
|
||||
}
|
||||
|
||||
async function assignReactionToToken({ token, reaction, reactionId, assignments }) {
|
||||
const tokenDocument = token?.document;
|
||||
const actor = token?.actor;
|
||||
if (!tokenDocument || !actor) return "skipped";
|
||||
|
||||
const assignment = buildAssignmentForToken(tokenDocument, actor, reactionId);
|
||||
if (hasAssignment(assignments, assignment)) return "skipped";
|
||||
|
||||
assignments.push(assignment);
|
||||
|
||||
if (assignment.mode === "actor") {
|
||||
await addReactionFlag(actor, reactionId);
|
||||
await ensureManagedReactionFeature(actor, reaction);
|
||||
return "assigned";
|
||||
}
|
||||
|
||||
await addReactionFlag(tokenDocument, reactionId);
|
||||
await ensureManagedReactionFeature(actor, reaction, tokenDocument.uuid);
|
||||
return "assigned";
|
||||
}
|
||||
|
||||
function hasAssignment(assignments, assignment) {
|
||||
return assignments.some(existing =>
|
||||
existing.reactionId === assignment.reactionId &&
|
||||
existing.mode === assignment.mode &&
|
||||
existing.actorUuid === assignment.actorUuid &&
|
||||
existing.tokenUuid === assignment.tokenUuid
|
||||
);
|
||||
}
|
||||
|
||||
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);
|
||||
const assignment = assignments.find(candidate => candidate.id === assignmentId);
|
||||
if (!assignment) return;
|
||||
|
||||
await game.settings.set(MODULE_ID, SETTINGS.ASSIGNMENTS, assignments.filter(a => a.id !== assignmentId));
|
||||
await game.settings.set(MODULE_ID, SETTINGS.ASSIGNMENTS, assignments.filter(candidate => candidate.id !== assignmentId));
|
||||
await removeAssignmentMarkers(assignment);
|
||||
}
|
||||
|
||||
async function removeAssignmentMarkers(assignment) {
|
||||
if (assignment.mode === "actor" && assignment.actorUuid) {
|
||||
const actor = await fromUuid(assignment.actorUuid);
|
||||
if (actor) await removeReactionFromDocument(actor, assignment.reactionId, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (assignment.mode === "token" && assignment.tokenUuid) {
|
||||
const tokenDocument = await fromUuid(assignment.tokenUuid);
|
||||
if (tokenDocument) {
|
||||
await removeReactionFromDocument(tokenDocument, assignment.reactionId, false);
|
||||
if (tokenDocument.actor) await removeReactionFromDocument(tokenDocument.actor, assignment.reactionId, true, tokenDocument.uuid);
|
||||
}
|
||||
}
|
||||
if (assignment.mode !== "token" || !assignment.tokenUuid) return;
|
||||
|
||||
const tokenDocument = await fromUuid(assignment.tokenUuid);
|
||||
if (!tokenDocument) return;
|
||||
|
||||
await removeReactionFromDocument(tokenDocument, assignment.reactionId, false);
|
||||
if (tokenDocument.actor) await removeReactionFromDocument(tokenDocument.actor, assignment.reactionId, true, tokenDocument.uuid);
|
||||
}
|
||||
|
||||
function buildAssignmentForToken(tokenDocument, actor, reactionId) {
|
||||
@@ -115,57 +124,71 @@ function buildAssignmentForToken(tokenDocument, actor, reactionId) {
|
||||
}
|
||||
|
||||
async function addReactionFlag(document, reactionId) {
|
||||
const assigned = foundry.utils.deepClone(document.getFlag(MODULE_ID, "assignedReactions") ?? []);
|
||||
const assigned = foundry.utils.deepClone(document.getFlag(MODULE_ID, ASSIGNED_REACTIONS_FLAG) ?? []);
|
||||
if (!assigned.some(entry => entry.reactionId === reactionId)) {
|
||||
assigned.push({ reactionId, assignedAt: Date.now(), assignedBy: game.user.id });
|
||||
}
|
||||
await document.setFlag(MODULE_ID, "assignedReactions", assigned);
|
||||
await document.setFlag(MODULE_ID, ASSIGNED_REACTIONS_FLAG, assigned);
|
||||
}
|
||||
|
||||
async function ensureManagedReactionEffectOnActor(actor, reaction, tokenUuid = null) {
|
||||
async function ensureManagedReactionFeature(actor, reaction, tokenUuid = null) {
|
||||
if (!game.settings.get(MODULE_ID, SETTINGS.CREATE_MANAGED_EFFECTS_ON_ASSIGNMENT)) return;
|
||||
if (!actor?.createEmbeddedDocuments) return;
|
||||
if (findManagedReactionFeature(actor, reaction.id, tokenUuid)) return;
|
||||
|
||||
const existing = actor.effects.find(effect =>
|
||||
effect.getFlag(MODULE_ID, "reactionId") === reaction.id &&
|
||||
effect.getFlag(MODULE_ID, "managed") === true &&
|
||||
(tokenUuid === null || effect.getFlag(MODULE_ID, "tokenUuid") === tokenUuid)
|
||||
await actor.createEmbeddedDocuments("Item", [buildManagedReactionFeatureData(reaction, tokenUuid)]);
|
||||
}
|
||||
|
||||
function findManagedReactionFeature(actor, reactionId, tokenUuid = null) {
|
||||
return actor.items?.find(item =>
|
||||
item.type === FEATURE_TYPE &&
|
||||
item.getFlag(MODULE_ID, "reactionId") === reactionId &&
|
||||
item.getFlag(MODULE_ID, MANAGED_FEATURE_FLAG) === true &&
|
||||
(tokenUuid === null || item.getFlag(MODULE_ID, "tokenUuid") === tokenUuid)
|
||||
);
|
||||
}
|
||||
|
||||
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: [],
|
||||
function buildManagedReactionFeatureData(reaction, tokenUuid = null) {
|
||||
const name = game.i18n.format("CONFIGURABLE_REACTIONS.Effects.ManagedReaction", { name: reaction.name });
|
||||
return {
|
||||
name,
|
||||
type: FEATURE_TYPE,
|
||||
img: reaction.icon ?? "icons/svg/aura.svg",
|
||||
system: {
|
||||
description: {
|
||||
value: `<p>${name}</p>`,
|
||||
chat: ""
|
||||
}
|
||||
},
|
||||
flags: {
|
||||
[MODULE_ID]: {
|
||||
reactionId: reaction.id,
|
||||
managed: true,
|
||||
[MANAGED_FEATURE_FLAG]: true,
|
||||
tokenUuid
|
||||
}
|
||||
}
|
||||
}]);
|
||||
};
|
||||
}
|
||||
|
||||
async function removeReactionFromDocument(document, reactionId, removeManagedEffects, tokenUuid = null) {
|
||||
const assigned = foundry.utils.deepClone(document.getFlag(MODULE_ID, "assignedReactions") ?? [])
|
||||
async function removeReactionFromDocument(document, reactionId, removeManagedFeature, tokenUuid = null) {
|
||||
const assigned = foundry.utils.deepClone(document.getFlag(MODULE_ID, ASSIGNED_REACTIONS_FLAG) ?? [])
|
||||
.filter(entry => entry.reactionId !== reactionId);
|
||||
|
||||
if (assigned.length) await document.setFlag(MODULE_ID, "assignedReactions", assigned);
|
||||
else await document.unsetFlag(MODULE_ID, "assignedReactions");
|
||||
if (assigned.length) await document.setFlag(MODULE_ID, ASSIGNED_REACTIONS_FLAG, assigned);
|
||||
else await document.unsetFlag(MODULE_ID, ASSIGNED_REACTIONS_FLAG);
|
||||
|
||||
if (!removeManagedEffects || !document.effects) return;
|
||||
if (removeManagedFeature) await removeManagedReactionFeatures(document, reactionId, tokenUuid);
|
||||
}
|
||||
|
||||
const effectIds = document.effects
|
||||
.filter(effect =>
|
||||
effect.getFlag(MODULE_ID, "reactionId") === reactionId &&
|
||||
effect.getFlag(MODULE_ID, "managed") === true &&
|
||||
(tokenUuid === null || effect.getFlag(MODULE_ID, "tokenUuid") === tokenUuid)
|
||||
async function removeManagedReactionFeatures(actor, reactionId, tokenUuid = null) {
|
||||
const itemIds = actor.items
|
||||
?.filter(item =>
|
||||
item.getFlag(MODULE_ID, "reactionId") === reactionId &&
|
||||
item.getFlag(MODULE_ID, MANAGED_FEATURE_FLAG) === true &&
|
||||
(tokenUuid === null || item.getFlag(MODULE_ID, "tokenUuid") === tokenUuid)
|
||||
)
|
||||
.map(effect => effect.id);
|
||||
.map(item => item.id) ?? [];
|
||||
|
||||
if (effectIds.length) await document.deleteEmbeddedDocuments("ActiveEffect", effectIds);
|
||||
if (itemIds.length) await actor.deleteEmbeddedDocuments("Item", itemIds);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user