handle marker as feature
All checks were successful
Sonarqube Scanner / Build and analyze (push) Successful in 50s
All checks were successful
Sonarqube Scanner / Build and analyze (push) Successful in 50s
This commit is contained in:
parent
eb43af49b2
commit
95e8b7b994
4
.gitignore
vendored
4
.gitignore
vendored
@ -3,5 +3,5 @@ Thumbs.db
|
|||||||
node_modules/
|
node_modules/
|
||||||
dist/
|
dist/
|
||||||
*.log
|
*.log
|
||||||
patches/
|
patches/**
|
||||||
.gitea/
|
.gitea/**
|
||||||
|
|||||||
@ -7,35 +7,35 @@ export async function executeApplyStatusAction(action, context) {
|
|||||||
const statuses = action.statuses ?? [];
|
const statuses = action.statuses ?? [];
|
||||||
if (!statuses.length) return { success: false, consumed: false, reason: "NO_STATUSES" };
|
if (!statuses.length) return { success: false, consumed: false, reason: "NO_STATUSES" };
|
||||||
|
|
||||||
const effects = statuses.map(statusId => {
|
const effects = statuses.map(statusId => buildStatusEffectData(statusId, action, context));
|
||||||
const statusConfig = getStatusEffectConfig(statusId);
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: statusConfig?.name ? game.i18n.localize(statusConfig.name) : statusId,
|
|
||||||
icon: statusConfig?.img ?? statusConfig?.icon ?? "icons/svg/aura.svg",
|
|
||||||
statuses: [statusId],
|
|
||||||
origin: context.reactionOrigin,
|
|
||||||
duration: buildEffectDuration(action.duration),
|
|
||||||
changes: [],
|
|
||||||
flags: {
|
|
||||||
[MODULE_ID]: {
|
|
||||||
managed: true,
|
|
||||||
reactionId: context.reaction.id,
|
|
||||||
actionId: action.id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
await actor.createEmbeddedDocuments("ActiveEffect", effects);
|
await actor.createEmbeddedDocuments("ActiveEffect", effects);
|
||||||
return { success: true, consumed: true };
|
return { success: true, consumed: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildStatusEffectData(statusId, action, context) {
|
||||||
|
const statusConfig = getStatusEffectConfig(statusId);
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: statusConfig?.name ? game.i18n.localize(statusConfig.name) : statusId,
|
||||||
|
icon: statusConfig?.img ?? "icons/svg/aura.svg",
|
||||||
|
statuses: [statusId],
|
||||||
|
origin: context.reactionOrigin,
|
||||||
|
duration: buildEffectDuration(action.duration),
|
||||||
|
changes: [],
|
||||||
|
flags: {
|
||||||
|
[MODULE_ID]: {
|
||||||
|
managed: true,
|
||||||
|
reactionId: context.reaction.id,
|
||||||
|
actionId: action.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function getStatusEffectConfig(statusId) {
|
function getStatusEffectConfig(statusId) {
|
||||||
const statusEffects = CONFIG.statusEffects ?? [];
|
const statusEffects = CONFIG.statusEffects ?? [];
|
||||||
if (Array.isArray(statusEffects)) return statusEffects.find(status => status.id === statusId);
|
if (statusEffects instanceof Map) return statusEffects.get(statusId);
|
||||||
if (statusEffects instanceof Map) return statusEffects.get(statusId) ?? null;
|
return statusEffects.find(status => status.id === statusId);
|
||||||
return Object.values(statusEffects).find(status => status.id === statusId) ?? null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildEffectDuration(duration) {
|
function buildEffectDuration(duration) {
|
||||||
|
|||||||
@ -1,5 +1,9 @@
|
|||||||
import { MODULE_ID, SETTINGS } from "../constants.js";
|
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) {
|
export async function assignReactionToSelectedTokens(reactionId) {
|
||||||
if (!game.user.isGM) {
|
if (!game.user.isGM) {
|
||||||
ui.notifications.warn(game.i18n.localize("CONFIGURABLE_REACTIONS.Errors.GmOnly"));
|
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 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) {
|
if (!reaction) {
|
||||||
ui.notifications.error(game.i18n.localize("CONFIGURABLE_REACTIONS.Errors.ReactionNotFound"));
|
ui.notifications.error(game.i18n.localize("CONFIGURABLE_REACTIONS.Errors.ReactionNotFound"));
|
||||||
return;
|
return;
|
||||||
@ -29,65 +33,70 @@ export async function assignReactionToSelectedTokens(reactionId) {
|
|||||||
let skippedCount = 0;
|
let skippedCount = 0;
|
||||||
|
|
||||||
for (const token of selectedTokens) {
|
for (const token of selectedTokens) {
|
||||||
const tokenDocument = token.document;
|
const result = await assignReactionToToken({ token, reaction, reactionId, assignments });
|
||||||
const actor = token.actor;
|
if (result === "assigned") assignedCount++;
|
||||||
|
else skippedCount++;
|
||||||
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++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await game.settings.set(MODULE_ID, SETTINGS.ASSIGNMENTS, assignments);
|
await game.settings.set(MODULE_ID, SETTINGS.ASSIGNMENTS, assignments);
|
||||||
ui.notifications.info(game.i18n.format("CONFIGURABLE_REACTIONS.Assignments.AssignedResult", { assigned: assignedCount, skipped: skippedCount }));
|
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) {
|
export async function removeAssignment(assignmentId) {
|
||||||
if (!game.user.isGM) return;
|
if (!game.user.isGM) return;
|
||||||
|
|
||||||
const assignments = foundry.utils.deepClone(game.settings.get(MODULE_ID, SETTINGS.ASSIGNMENTS) ?? []);
|
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;
|
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) {
|
if (assignment.mode === "actor" && assignment.actorUuid) {
|
||||||
const actor = await fromUuid(assignment.actorUuid);
|
const actor = await fromUuid(assignment.actorUuid);
|
||||||
if (actor) await removeReactionFromDocument(actor, assignment.reactionId, true);
|
if (actor) await removeReactionFromDocument(actor, assignment.reactionId, true);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (assignment.mode === "token" && assignment.tokenUuid) {
|
if (assignment.mode !== "token" || !assignment.tokenUuid) return;
|
||||||
const tokenDocument = await fromUuid(assignment.tokenUuid);
|
|
||||||
if (tokenDocument) {
|
const tokenDocument = await fromUuid(assignment.tokenUuid);
|
||||||
await removeReactionFromDocument(tokenDocument, assignment.reactionId, false);
|
if (!tokenDocument) return;
|
||||||
if (tokenDocument.actor) await removeReactionFromDocument(tokenDocument.actor, assignment.reactionId, true, tokenDocument.uuid);
|
|
||||||
}
|
await removeReactionFromDocument(tokenDocument, assignment.reactionId, false);
|
||||||
}
|
if (tokenDocument.actor) await removeReactionFromDocument(tokenDocument.actor, assignment.reactionId, true, tokenDocument.uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildAssignmentForToken(tokenDocument, actor, reactionId) {
|
function buildAssignmentForToken(tokenDocument, actor, reactionId) {
|
||||||
@ -115,57 +124,71 @@ function buildAssignmentForToken(tokenDocument, actor, reactionId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function addReactionFlag(document, 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)) {
|
if (!assigned.some(entry => entry.reactionId === reactionId)) {
|
||||||
assigned.push({ reactionId, assignedAt: Date.now(), assignedBy: game.user.id });
|
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 (!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 =>
|
await actor.createEmbeddedDocuments("Item", [buildManagedReactionFeatureData(reaction, tokenUuid)]);
|
||||||
effect.getFlag(MODULE_ID, "reactionId") === reaction.id &&
|
}
|
||||||
effect.getFlag(MODULE_ID, "managed") === true &&
|
|
||||||
(tokenUuid === null || effect.getFlag(MODULE_ID, "tokenUuid") === 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;
|
function buildManagedReactionFeatureData(reaction, tokenUuid = null) {
|
||||||
|
const name = game.i18n.format("CONFIGURABLE_REACTIONS.Effects.ManagedReaction", { name: reaction.name });
|
||||||
await actor.createEmbeddedDocuments("ActiveEffect", [{
|
return {
|
||||||
name: game.i18n.format("CONFIGURABLE_REACTIONS.Effects.ManagedReaction", { name: reaction.name }),
|
name,
|
||||||
icon: reaction.icon ?? "icons/svg/aura.svg",
|
type: FEATURE_TYPE,
|
||||||
origin: `Module.${MODULE_ID}`,
|
img: reaction.icon ?? "icons/svg/aura.svg",
|
||||||
disabled: false,
|
system: {
|
||||||
transfer: false,
|
description: {
|
||||||
changes: [],
|
value: `<p>${name}</p>`,
|
||||||
|
chat: ""
|
||||||
|
}
|
||||||
|
},
|
||||||
flags: {
|
flags: {
|
||||||
[MODULE_ID]: {
|
[MODULE_ID]: {
|
||||||
reactionId: reaction.id,
|
reactionId: reaction.id,
|
||||||
managed: true,
|
managed: true,
|
||||||
|
[MANAGED_FEATURE_FLAG]: true,
|
||||||
tokenUuid
|
tokenUuid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}]);
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function removeReactionFromDocument(document, reactionId, removeManagedEffects, tokenUuid = null) {
|
async function removeReactionFromDocument(document, reactionId, removeManagedFeature, tokenUuid = null) {
|
||||||
const assigned = foundry.utils.deepClone(document.getFlag(MODULE_ID, "assignedReactions") ?? [])
|
const assigned = foundry.utils.deepClone(document.getFlag(MODULE_ID, ASSIGNED_REACTIONS_FLAG) ?? [])
|
||||||
.filter(entry => entry.reactionId !== reactionId);
|
.filter(entry => entry.reactionId !== reactionId);
|
||||||
|
|
||||||
if (assigned.length) await document.setFlag(MODULE_ID, "assignedReactions", assigned);
|
if (assigned.length) await document.setFlag(MODULE_ID, ASSIGNED_REACTIONS_FLAG, assigned);
|
||||||
else await document.unsetFlag(MODULE_ID, "assignedReactions");
|
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 =>
|
async function removeManagedReactionFeatures(actor, reactionId, tokenUuid = null) {
|
||||||
effect.getFlag(MODULE_ID, "reactionId") === reactionId &&
|
const itemIds = actor.items
|
||||||
effect.getFlag(MODULE_ID, "managed") === true &&
|
?.filter(item =>
|
||||||
(tokenUuid === null || effect.getFlag(MODULE_ID, "tokenUuid") === tokenUuid)
|
item.getFlag(MODULE_ID, "reactionId") === reactionId &&
|
||||||
)
|
item.getFlag(MODULE_ID, MANAGED_FEATURE_FLAG) === true &&
|
||||||
.map(effect => effect.id);
|
(tokenUuid === null || item.getFlag(MODULE_ID, "tokenUuid") === tokenUuid)
|
||||||
|
)
|
||||||
if (effectIds.length) await document.deleteEmbeddedDocuments("ActiveEffect", effectIds);
|
.map(item => item.id) ?? [];
|
||||||
|
|
||||||
|
if (itemIds.length) await actor.deleteEmbeddedDocuments("Item", itemIds);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,10 +3,8 @@ export async function checkDamageCondition(config, context) {
|
|||||||
const absoluteAmount = Math.abs(amount);
|
const absoluteAmount = Math.abs(amount);
|
||||||
|
|
||||||
if (absoluteAmount < (Number(config.minAmount) || 0)) return false;
|
if (absoluteAmount < (Number(config.minAmount) || 0)) return false;
|
||||||
|
|
||||||
if (config.amountMode === "damageOnly" && amount <= 0) return false;
|
if (config.amountMode === "damageOnly" && amount <= 0) return false;
|
||||||
if (config.amountMode === "healingOnly" && amount >= 0) return false;
|
if (config.amountMode === "healingOnly" && amount >= 0) return false;
|
||||||
|
|
||||||
if (config.amountMode === "healingOnly") return true;
|
if (config.amountMode === "healingOnly") return true;
|
||||||
|
|
||||||
const configuredTypes = config.types ?? [];
|
const configuredTypes = config.types ?? [];
|
||||||
|
|||||||
@ -4,25 +4,33 @@ import { getBestTokenDocumentForActor } from "../utils/token-utils.js";
|
|||||||
|
|
||||||
export function registerDnd5eDamageTrigger() {
|
export function registerDnd5eDamageTrigger() {
|
||||||
Hooks.on("dnd5e.applyDamage", async (actor, amount, options = {}) => {
|
Hooks.on("dnd5e.applyDamage", async (actor, amount, options = {}) => {
|
||||||
if (!game.user.isGM) return;
|
await handleDamageReceivedHook(actor, amount, options);
|
||||||
if (!actor) return;
|
});
|
||||||
|
|
||||||
const hookOptions = normalizeDamageHookOptions(options);
|
Hooks.on("dnd5e.damageActor", async (actor, changes = {}, update = {}, userId = null) => {
|
||||||
const tokenDocument = resolveDamageTokenDocument(actor, hookOptions);
|
const amount = normalizeDamageActorAmount(changes, update);
|
||||||
const damageTypes = extractDamageTypes(hookOptions);
|
await handleDamageReceivedHook(actor, amount, { changes, update, userId });
|
||||||
const numericAmount = Number(amount) || 0;
|
});
|
||||||
|
}
|
||||||
|
|
||||||
await handleTrigger(TRIGGER_TYPES.DAMAGE_RECEIVED, {
|
async function handleDamageReceivedHook(actor, amount, options = {}) {
|
||||||
actor: tokenDocument?.actor ?? actor,
|
if (!game.user.isGM || !actor) return;
|
||||||
targetActor: tokenDocument?.actor ?? actor,
|
|
||||||
tokenDocument,
|
const hookOptions = normalizeDamageHookOptions(options);
|
||||||
targetTokenDocument: tokenDocument,
|
const tokenDocument = resolveDamageTokenDocument(actor, hookOptions);
|
||||||
targetToken: tokenDocument?.object ?? null,
|
const targetActor = tokenDocument?.actor ?? actor;
|
||||||
amount: numericAmount,
|
const numericAmount = Number(amount) || 0;
|
||||||
damageAmount: numericAmount,
|
|
||||||
damageTypes,
|
await handleTrigger(TRIGGER_TYPES.DAMAGE_RECEIVED, {
|
||||||
options: hookOptions
|
actor: targetActor,
|
||||||
});
|
targetActor,
|
||||||
|
tokenDocument,
|
||||||
|
targetTokenDocument: tokenDocument,
|
||||||
|
targetToken: tokenDocument?.object ?? null,
|
||||||
|
amount: numericAmount,
|
||||||
|
damageAmount: numericAmount,
|
||||||
|
damageTypes: extractDamageTypes(hookOptions),
|
||||||
|
options: hookOptions
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,16 +38,28 @@ function normalizeDamageHookOptions(options) {
|
|||||||
return options && typeof options === "object" ? options : {};
|
return options && typeof options === "object" ? options : {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeDamageActorAmount(changes, update) {
|
||||||
|
const candidates = [changes?.total, changes?.amount, changes?.value, update?.total, update?.amount, update?.value];
|
||||||
|
const value = candidates.find(candidate => Number.isFinite(Number(candidate)));
|
||||||
|
return Number(value) || 0;
|
||||||
|
}
|
||||||
|
|
||||||
function resolveDamageTokenDocument(actor, options) {
|
function resolveDamageTokenDocument(actor, options) {
|
||||||
return options.tokenDocument ??
|
return asTokenDocument(options.tokenDocument) ??
|
||||||
options.token?.document ??
|
asTokenDocument(options.token) ??
|
||||||
options.token ??
|
asTokenDocument(options.targetTokenDocument) ??
|
||||||
options.targetTokenDocument ??
|
asTokenDocument(options.targetToken) ??
|
||||||
options.targetToken?.document ??
|
asTokenDocument(actor.token) ??
|
||||||
actor.token ??
|
|
||||||
getBestTokenDocumentForActor(actor);
|
getBestTokenDocumentForActor(actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function asTokenDocument(value) {
|
||||||
|
if (!value) return null;
|
||||||
|
if (value.documentName === "Token") return value;
|
||||||
|
if (value.document?.documentName === "Token") return value.document;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
function extractDamageTypes(options) {
|
function extractDamageTypes(options) {
|
||||||
const knownTypes = getKnownDamageTypes();
|
const knownTypes = getKnownDamageTypes();
|
||||||
const collected = new Set();
|
const collected = new Set();
|
||||||
@ -55,8 +75,7 @@ function collectDamageTypes(value, knownTypes, collected, seen = new WeakSet(),
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof value !== "object") return;
|
if (typeof value !== "object" || seen.has(value)) return;
|
||||||
if (seen.has(value)) return;
|
|
||||||
seen.add(value);
|
seen.add(value);
|
||||||
|
|
||||||
if (Array.isArray(value) || value instanceof Set) {
|
if (Array.isArray(value) || value instanceof Set) {
|
||||||
@ -69,7 +88,7 @@ function collectDamageTypes(value, knownTypes, collected, seen = new WeakSet(),
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const key of ["type", "damageType", "damageTypeId", "types", "damageTypes", "damage", "damages", "damageDetail", "parts"]) {
|
for (const key of ["type", "damageType", "damageTypeId", "types", "damageTypes", "damage", "damages", "damageDetail", "parts", "workflow"]) {
|
||||||
collectDamageTypes(value[key], knownTypes, collected, seen, depth + 1);
|
collectDamageTypes(value[key], knownTypes, collected, seen, depth + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,24 +4,27 @@ export function getBestTokenDocumentForActor(actor) {
|
|||||||
const syntheticToken = getSyntheticTokenDocument(actor);
|
const syntheticToken = getSyntheticTokenDocument(actor);
|
||||||
if (syntheticToken) return syntheticToken;
|
if (syntheticToken) return syntheticToken;
|
||||||
|
|
||||||
const controlled = canvas?.tokens?.controlled?.find(token => token.actor?.uuid === actor.uuid || token.actor?.id === actor.id);
|
const controlled = canvas?.tokens?.controlled?.find(token => sameActor(token.actor, actor));
|
||||||
if (controlled) return controlled.document;
|
if (controlled) return controlled.document;
|
||||||
|
|
||||||
const combatant = game.combat?.combatants?.find(c => c.actor?.uuid === actor.uuid || c.actor?.id === actor.id);
|
const combatant = game.combat?.combatants?.find(candidate => sameActor(candidate.actor, actor));
|
||||||
if (combatant?.token) return combatant.token;
|
if (combatant?.token) return combatant.token;
|
||||||
|
|
||||||
const activeTokens = actor.getActiveTokens?.(true, true) ?? [];
|
const activeTokens = actor.getActiveTokens?.(true, true) ?? [];
|
||||||
if (activeTokens.length === 1) return activeTokens[0].document;
|
if (activeTokens.length === 1) return activeTokens[0].document;
|
||||||
|
|
||||||
return activeTokens[0]?.document ?? null;
|
return activeTokens[0]?.document ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSyntheticTokenDocument(actor) {
|
function getSyntheticTokenDocument(actor) {
|
||||||
const tokenDocument = actor.token ?? actor.prototypeToken ?? null;
|
const tokenDocument = actor.token ?? null;
|
||||||
if (tokenDocument?.documentName === "Token") return tokenDocument;
|
if (tokenDocument?.documentName === "Token") return tokenDocument;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sameActor(candidate, actor) {
|
||||||
|
return candidate?.uuid === actor.uuid || candidate?.id === actor.id;
|
||||||
|
}
|
||||||
|
|
||||||
export function getPrimaryOwner(actor) {
|
export function getPrimaryOwner(actor) {
|
||||||
if (!actor) return null;
|
if (!actor) return null;
|
||||||
return game.users.find(user => user.active && !user.isGM && actor.testUserPermission(user, "OWNER")) ?? null;
|
return game.users.find(user => user.active && !user.isGM && actor.testUserPermission(user, "OWNER")) ?? null;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user