add spell item dropin for spell based triggers and actions
This commit is contained in:
Executable → Regular
Executable → Regular
+16
-3
@@ -6,8 +6,13 @@ export async function executeCastSpellAction(action, context) {
|
||||
const item = await resolveItem(actor, action.spellSelection);
|
||||
if (!item) return { success: false, consumed: false, reason: "NO_SPELL_ITEM" };
|
||||
|
||||
if (action.activationMode === "autoUse" && typeof item.use === "function") {
|
||||
await item.use({}, { configureDialog: true });
|
||||
return { success: true, consumed: true };
|
||||
}
|
||||
|
||||
item.sheet?.render(true);
|
||||
ui.notifications.info(`${item.name} wurde für ${actor.name} geöffnet. Vollautomatische Zauberauslösung folgt in einer späteren Version.`);
|
||||
ui.notifications.info(`${item.name} wurde für ${actor.name} geöffnet. Der Zauber wird aus dem Inventar des Characters über dessen Spellslots genutzt.`);
|
||||
|
||||
return { success: true, consumed: true };
|
||||
}
|
||||
@@ -22,7 +27,15 @@ function resolveCasterToken(action, context) {
|
||||
}
|
||||
|
||||
async function resolveItem(actor, selection = {}) {
|
||||
if (selection.mode === "itemUuid" && selection.itemUuid) return fromUuid(selection.itemUuid);
|
||||
if (selection.mode === "itemName" && selection.itemName) return actor.items.find(item => item.name === selection.itemName);
|
||||
if (selection.mode === "itemUuid" && selection.itemUuid) {
|
||||
const item = await fromUuid(selection.itemUuid);
|
||||
return item?.type === "spell" ? item : null;
|
||||
}
|
||||
|
||||
if (selection.mode === "itemName" && selection.itemName) {
|
||||
const expected = selection.itemName.toLocaleLowerCase();
|
||||
return actor.items.find(item => item.type === "spell" && item.name.toLocaleLowerCase() === expected) ?? null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
+98
-4
@@ -31,7 +31,9 @@ export class ConfigurableReactionsConfigApp extends HandlebarsApplicationMixin(A
|
||||
assignSelectedTokens: ConfigurableReactionsConfigApp.#onAssignSelectedTokens,
|
||||
removeAssignment: ConfigurableReactionsConfigApp.#onRemoveAssignment,
|
||||
removeAction: ConfigurableReactionsConfigApp.#onRemoveAction,
|
||||
saveReactionBasics: ConfigurableReactionsConfigApp.#onSaveReactionBasics
|
||||
saveReactionBasics: ConfigurableReactionsConfigApp.#onSaveReactionBasics,
|
||||
clearTriggerSpell: ConfigurableReactionsConfigApp.#onClearTriggerSpell,
|
||||
clearActionSpell: ConfigurableReactionsConfigApp.#onClearActionSpell
|
||||
}
|
||||
};
|
||||
|
||||
@@ -62,6 +64,8 @@ export class ConfigurableReactionsConfigApp extends HandlebarsApplicationMixin(A
|
||||
}));
|
||||
|
||||
const activeTrigger = triggerPalette.find(t => t.type === selectedReaction?.trigger?.type) ?? null;
|
||||
const triggerSpellName = selectedReaction?.trigger?.spell?.itemName || selectedReaction?.trigger?.spell?.itemUuid || "";
|
||||
const activeTriggerAcceptsSpell = isSpellTrigger(selectedReaction?.trigger?.type);
|
||||
const visualActions = (selectedReaction?.actions ?? []).map((action, index) => {
|
||||
const paletteEntry = actionPalette.find(p => p.type === action.type);
|
||||
return {
|
||||
@@ -69,7 +73,9 @@ export class ConfigurableReactionsConfigApp extends HandlebarsApplicationMixin(A
|
||||
index,
|
||||
label: paletteEntry?.label ?? action.type,
|
||||
icon: paletteEntry?.icon ?? "fa-solid fa-puzzle-piece",
|
||||
summary: summarizeAction(action)
|
||||
summary: summarizeAction(action),
|
||||
isSpellAction: action.type === ACTION_TYPES.CAST_SPELL_FROM_TOKEN,
|
||||
spellName: action.spellSelection?.itemName || action.spellSelection?.itemUuid || ""
|
||||
};
|
||||
});
|
||||
|
||||
@@ -86,6 +92,8 @@ export class ConfigurableReactionsConfigApp extends HandlebarsApplicationMixin(A
|
||||
triggerPalette,
|
||||
actionPalette,
|
||||
activeTrigger,
|
||||
activeTriggerAcceptsSpell,
|
||||
triggerSpellName,
|
||||
visualActions,
|
||||
statusEffects: CONFIG.statusEffects.map(s => ({ id: s.id, name: game.i18n.localize(s.name ?? s.id) }))
|
||||
};
|
||||
@@ -130,18 +138,37 @@ export class ConfigurableReactionsConfigApp extends HandlebarsApplicationMixin(A
|
||||
event.preventDefault();
|
||||
dropZone.classList.remove("cr-drop-active");
|
||||
|
||||
event.stopPropagation();
|
||||
|
||||
const payload = readDragPayload(event);
|
||||
if (!payload) return;
|
||||
|
||||
const droppedItem = await resolveDroppedItem(payload);
|
||||
|
||||
if (dropZone.dataset.crDrop === "trigger" && payload.kind === "trigger") {
|
||||
await this.#setTrigger(payload.type);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dropZone.dataset.crDrop === "trigger" && isSpellItem(droppedItem)) {
|
||||
await this.#setTriggerSpell(droppedItem);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dropZone.dataset.crDrop === "actions" && payload.kind === "action") {
|
||||
await this.#addAction(createDefaultAction(payload.type));
|
||||
return;
|
||||
}
|
||||
|
||||
if (dropZone.dataset.crDrop === "actions" && isSpellItem(droppedItem)) {
|
||||
await this.#addAction(createDefaultAction(ACTION_TYPES.CAST_SPELL_FROM_TOKEN, droppedItem));
|
||||
return;
|
||||
}
|
||||
|
||||
if (dropZone.dataset.crDrop === "action-spell" && isSpellItem(droppedItem)) {
|
||||
await this.#setActionSpell(Number(dropZone.dataset.actionIndex), droppedItem);
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -230,6 +257,24 @@ export class ConfigurableReactionsConfigApp extends HandlebarsApplicationMixin(A
|
||||
});
|
||||
}
|
||||
|
||||
static async #onClearTriggerSpell(event, target) {
|
||||
await this.#mutateSelectedReaction(reaction => {
|
||||
reaction.trigger ??= {};
|
||||
delete reaction.trigger.spell;
|
||||
});
|
||||
}
|
||||
|
||||
static async #onClearActionSpell(event, target) {
|
||||
const actionIndex = Number(target.dataset.actionIndex);
|
||||
if (!Number.isInteger(actionIndex)) return;
|
||||
|
||||
await this.#mutateSelectedReaction(reaction => {
|
||||
const action = reaction.actions?.[actionIndex];
|
||||
if (!action || action.type !== ACTION_TYPES.CAST_SPELL_FROM_TOKEN) return;
|
||||
action.spellSelection = { mode: "itemName", itemName: "" };
|
||||
});
|
||||
}
|
||||
|
||||
static async #onSaveReactionBasics(event, target) {
|
||||
const name = this.element.querySelector("[name='reactionName']")?.value?.trim();
|
||||
const enabled = this.element.querySelector("[name='reactionEnabled']")?.checked === true;
|
||||
@@ -244,6 +289,25 @@ export class ConfigurableReactionsConfigApp extends HandlebarsApplicationMixin(A
|
||||
await this.#mutateSelectedReaction(reaction => {
|
||||
reaction.trigger ??= {};
|
||||
reaction.trigger.type = triggerType;
|
||||
if (!isSpellTrigger(triggerType)) delete reaction.trigger.spell;
|
||||
});
|
||||
}
|
||||
|
||||
async #setTriggerSpell(item) {
|
||||
await this.#mutateSelectedReaction(reaction => {
|
||||
reaction.trigger ??= {};
|
||||
if (!isSpellTrigger(reaction.trigger.type)) reaction.trigger.type = TRIGGER_TYPES.SPELL_SEEN;
|
||||
reaction.trigger.spell = buildSpellSelection(item);
|
||||
});
|
||||
}
|
||||
|
||||
async #setActionSpell(actionIndex, item) {
|
||||
if (!Number.isInteger(actionIndex)) return;
|
||||
|
||||
await this.#mutateSelectedReaction(reaction => {
|
||||
const action = reaction.actions?.[actionIndex];
|
||||
if (!action || action.type !== ACTION_TYPES.CAST_SPELL_FROM_TOKEN) return;
|
||||
action.spellSelection = buildSpellSelection(item);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -277,7 +341,37 @@ function readDragPayload(event) {
|
||||
}
|
||||
}
|
||||
|
||||
function createDefaultAction(type) {
|
||||
async function resolveDroppedItem(data) {
|
||||
if (!data) return null;
|
||||
|
||||
if (data.uuid) {
|
||||
const document = await fromUuid(data.uuid);
|
||||
if (document?.documentName === "Item") return document;
|
||||
}
|
||||
|
||||
if (data.type === "Item" && data.id) {
|
||||
return game.items.get(data.id) ?? null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function isSpellItem(item) {
|
||||
return item?.documentName === "Item" && item.type === "spell";
|
||||
}
|
||||
|
||||
function isSpellTrigger(type) {
|
||||
return [TRIGGER_TYPES.SPELL_SEEN, TRIGGER_TYPES.SPELL_CAST_START, TRIGGER_TYPES.SPELL_CAST_COMPLETE].includes(type);
|
||||
}
|
||||
|
||||
function buildSpellSelection(item) {
|
||||
return {
|
||||
mode: "itemName",
|
||||
itemName: item.name
|
||||
};
|
||||
}
|
||||
|
||||
function createDefaultAction(type, item = null) {
|
||||
switch (type) {
|
||||
case ACTION_TYPES.TELEPORT:
|
||||
return {
|
||||
@@ -309,7 +403,7 @@ function createDefaultAction(type) {
|
||||
type: ACTION_TYPES.CAST_SPELL_FROM_TOKEN,
|
||||
enabled: true,
|
||||
casterMode: "affectedToken",
|
||||
spellSelection: {
|
||||
spellSelection: item ? buildSpellSelection(item) : {
|
||||
mode: "itemName",
|
||||
itemName: ""
|
||||
},
|
||||
|
||||
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
+11
@@ -13,6 +13,7 @@ export async function handleTrigger(triggerType, context) {
|
||||
try {
|
||||
if (!reaction?.enabled) continue;
|
||||
if (reaction.trigger?.type !== triggerType) continue;
|
||||
if (!matchesTriggerSpellFilter(reaction.trigger, context)) continue;
|
||||
if (!await checkConsumptionAvailable(reaction, context)) continue;
|
||||
if (!await checkConditions(reaction, context)) continue;
|
||||
|
||||
@@ -23,3 +24,13 @@ export async function handleTrigger(triggerType, context) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function matchesTriggerSpellFilter(trigger, context) {
|
||||
const expectedName = trigger?.spell?.itemName;
|
||||
if (!expectedName) return true;
|
||||
|
||||
const actualName = context?.spellItem?.name ?? context?.item?.name ?? context?.itemName ?? context?.spellName ?? null;
|
||||
if (!actualName) return false;
|
||||
|
||||
return actualName.toLocaleLowerCase() === expectedName.toLocaleLowerCase();
|
||||
}
|
||||
|
||||
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Executable → Regular
Reference in New Issue
Block a user