add spell item dropin for spell based triggers and actions
This commit is contained in:
parent
3c25080666
commit
c2ffa47ff2
1
.gitignore
vendored
Executable file → Normal file
1
.gitignore
vendored
Executable file → Normal file
@ -3,3 +3,4 @@ Thumbs.db
|
|||||||
node_modules/
|
node_modules/
|
||||||
dist/
|
dist/
|
||||||
*.log
|
*.log
|
||||||
|
patches/
|
||||||
|
|||||||
6
lang/de.json
Executable file → Normal file
6
lang/de.json
Executable file → Normal file
@ -79,5 +79,9 @@
|
|||||||
"CONFIGURABLE_REACTIONS.Actions.UseInventoryItem.Label": "Gegenstandseffekt auslösen",
|
"CONFIGURABLE_REACTIONS.Actions.UseInventoryItem.Label": "Gegenstandseffekt auslösen",
|
||||||
"CONFIGURABLE_REACTIONS.Actions.UseInventoryItem.Hint": "Öffnet oder nutzt einen Gegenstand aus dem Inventar eines Tokens.",
|
"CONFIGURABLE_REACTIONS.Actions.UseInventoryItem.Hint": "Öffnet oder nutzt einen Gegenstand aus dem Inventar eines Tokens.",
|
||||||
"CONFIGURABLE_REACTIONS.Actions.UseInventoryItem.Unconfigured": "Kein Gegenstand konfiguriert",
|
"CONFIGURABLE_REACTIONS.Actions.UseInventoryItem.Unconfigured": "Kein Gegenstand konfiguriert",
|
||||||
"CONFIGURABLE_REACTIONS.Duration.Unlimited": "Unbegrenzt"
|
"CONFIGURABLE_REACTIONS.Duration.Unlimited": "Unbegrenzt",
|
||||||
|
"CONFIGURABLE_REACTIONS.Builder.DropSpellOnTrigger": "Zauber auf diesen Auslöser ziehen, um nach Zaubername zu filtern.",
|
||||||
|
"CONFIGURABLE_REACTIONS.Builder.DropSpellOnAction": "Zauber hier ablegen, um ihn für diese Aktion zu speichern.",
|
||||||
|
"CONFIGURABLE_REACTIONS.Builder.DropSpellCreatesAction": "Ein Zauber kann direkt in den Effekt-Bereich gezogen werden und erzeugt eine passende Zauber-Aktion.",
|
||||||
|
"CONFIGURABLE_REACTIONS.Triggers.SpellFilter": "Zauberfilter: {name}"
|
||||||
}
|
}
|
||||||
|
|||||||
6
lang/en.json
Executable file → Normal file
6
lang/en.json
Executable file → Normal file
@ -79,5 +79,9 @@
|
|||||||
"CONFIGURABLE_REACTIONS.Actions.UseInventoryItem.Label": "Trigger item effect",
|
"CONFIGURABLE_REACTIONS.Actions.UseInventoryItem.Label": "Trigger item effect",
|
||||||
"CONFIGURABLE_REACTIONS.Actions.UseInventoryItem.Hint": "Opens or uses an item from a token inventory.",
|
"CONFIGURABLE_REACTIONS.Actions.UseInventoryItem.Hint": "Opens or uses an item from a token inventory.",
|
||||||
"CONFIGURABLE_REACTIONS.Actions.UseInventoryItem.Unconfigured": "No item configured",
|
"CONFIGURABLE_REACTIONS.Actions.UseInventoryItem.Unconfigured": "No item configured",
|
||||||
"CONFIGURABLE_REACTIONS.Duration.Unlimited": "Unlimited"
|
"CONFIGURABLE_REACTIONS.Duration.Unlimited": "Unlimited",
|
||||||
|
"CONFIGURABLE_REACTIONS.Builder.DropSpellOnTrigger": "Drop a spell on this trigger to filter by spell name.",
|
||||||
|
"CONFIGURABLE_REACTIONS.Builder.DropSpellOnAction": "Drop a spell here to store it for this action.",
|
||||||
|
"CONFIGURABLE_REACTIONS.Builder.DropSpellCreatesAction": "A spell can be dropped directly into the effect area to create a configured spell action.",
|
||||||
|
"CONFIGURABLE_REACTIONS.Triggers.SpellFilter": "Spell filter: {name}"
|
||||||
}
|
}
|
||||||
|
|||||||
0
module.json
Executable file → Normal file
0
module.json
Executable file → Normal file
0
scripts/actions/apply-status-action.js
Executable file → Normal file
0
scripts/actions/apply-status-action.js
Executable file → Normal file
19
scripts/actions/cast-spell-action.js
Executable file → Normal file
19
scripts/actions/cast-spell-action.js
Executable file → Normal file
@ -6,8 +6,13 @@ export async function executeCastSpellAction(action, context) {
|
|||||||
const item = await resolveItem(actor, action.spellSelection);
|
const item = await resolveItem(actor, action.spellSelection);
|
||||||
if (!item) return { success: false, consumed: false, reason: "NO_SPELL_ITEM" };
|
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);
|
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 };
|
return { success: true, consumed: true };
|
||||||
}
|
}
|
||||||
@ -22,7 +27,15 @@ function resolveCasterToken(action, context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function resolveItem(actor, selection = {}) {
|
async function resolveItem(actor, selection = {}) {
|
||||||
if (selection.mode === "itemUuid" && selection.itemUuid) return fromUuid(selection.itemUuid);
|
if (selection.mode === "itemUuid" && selection.itemUuid) {
|
||||||
if (selection.mode === "itemName" && selection.itemName) return actor.items.find(item => item.name === selection.itemName);
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
0
scripts/actions/teleport-action.js
Executable file → Normal file
0
scripts/actions/teleport-action.js
Executable file → Normal file
0
scripts/actions/use-inventory-item-action.js
Executable file → Normal file
0
scripts/actions/use-inventory-item-action.js
Executable file → Normal file
0
scripts/apps/assignment-manager.js
Executable file → Normal file
0
scripts/apps/assignment-manager.js
Executable file → Normal file
102
scripts/apps/reaction-config-app.js
Executable file → Normal file
102
scripts/apps/reaction-config-app.js
Executable file → Normal file
@ -31,7 +31,9 @@ export class ConfigurableReactionsConfigApp extends HandlebarsApplicationMixin(A
|
|||||||
assignSelectedTokens: ConfigurableReactionsConfigApp.#onAssignSelectedTokens,
|
assignSelectedTokens: ConfigurableReactionsConfigApp.#onAssignSelectedTokens,
|
||||||
removeAssignment: ConfigurableReactionsConfigApp.#onRemoveAssignment,
|
removeAssignment: ConfigurableReactionsConfigApp.#onRemoveAssignment,
|
||||||
removeAction: ConfigurableReactionsConfigApp.#onRemoveAction,
|
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 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 visualActions = (selectedReaction?.actions ?? []).map((action, index) => {
|
||||||
const paletteEntry = actionPalette.find(p => p.type === action.type);
|
const paletteEntry = actionPalette.find(p => p.type === action.type);
|
||||||
return {
|
return {
|
||||||
@ -69,7 +73,9 @@ export class ConfigurableReactionsConfigApp extends HandlebarsApplicationMixin(A
|
|||||||
index,
|
index,
|
||||||
label: paletteEntry?.label ?? action.type,
|
label: paletteEntry?.label ?? action.type,
|
||||||
icon: paletteEntry?.icon ?? "fa-solid fa-puzzle-piece",
|
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,
|
triggerPalette,
|
||||||
actionPalette,
|
actionPalette,
|
||||||
activeTrigger,
|
activeTrigger,
|
||||||
|
activeTriggerAcceptsSpell,
|
||||||
|
triggerSpellName,
|
||||||
visualActions,
|
visualActions,
|
||||||
statusEffects: CONFIG.statusEffects.map(s => ({ id: s.id, name: game.i18n.localize(s.name ?? s.id) }))
|
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();
|
event.preventDefault();
|
||||||
dropZone.classList.remove("cr-drop-active");
|
dropZone.classList.remove("cr-drop-active");
|
||||||
|
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
const payload = readDragPayload(event);
|
const payload = readDragPayload(event);
|
||||||
if (!payload) return;
|
if (!payload) return;
|
||||||
|
|
||||||
|
const droppedItem = await resolveDroppedItem(payload);
|
||||||
|
|
||||||
if (dropZone.dataset.crDrop === "trigger" && payload.kind === "trigger") {
|
if (dropZone.dataset.crDrop === "trigger" && payload.kind === "trigger") {
|
||||||
await this.#setTrigger(payload.type);
|
await this.#setTrigger(payload.type);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dropZone.dataset.crDrop === "trigger" && isSpellItem(droppedItem)) {
|
||||||
|
await this.#setTriggerSpell(droppedItem);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (dropZone.dataset.crDrop === "actions" && payload.kind === "action") {
|
if (dropZone.dataset.crDrop === "actions" && payload.kind === "action") {
|
||||||
await this.#addAction(createDefaultAction(payload.type));
|
await this.#addAction(createDefaultAction(payload.type));
|
||||||
return;
|
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) {
|
static async #onSaveReactionBasics(event, target) {
|
||||||
const name = this.element.querySelector("[name='reactionName']")?.value?.trim();
|
const name = this.element.querySelector("[name='reactionName']")?.value?.trim();
|
||||||
const enabled = this.element.querySelector("[name='reactionEnabled']")?.checked === true;
|
const enabled = this.element.querySelector("[name='reactionEnabled']")?.checked === true;
|
||||||
@ -244,6 +289,25 @@ export class ConfigurableReactionsConfigApp extends HandlebarsApplicationMixin(A
|
|||||||
await this.#mutateSelectedReaction(reaction => {
|
await this.#mutateSelectedReaction(reaction => {
|
||||||
reaction.trigger ??= {};
|
reaction.trigger ??= {};
|
||||||
reaction.trigger.type = triggerType;
|
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) {
|
switch (type) {
|
||||||
case ACTION_TYPES.TELEPORT:
|
case ACTION_TYPES.TELEPORT:
|
||||||
return {
|
return {
|
||||||
@ -309,7 +403,7 @@ function createDefaultAction(type) {
|
|||||||
type: ACTION_TYPES.CAST_SPELL_FROM_TOKEN,
|
type: ACTION_TYPES.CAST_SPELL_FROM_TOKEN,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
casterMode: "affectedToken",
|
casterMode: "affectedToken",
|
||||||
spellSelection: {
|
spellSelection: item ? buildSpellSelection(item) : {
|
||||||
mode: "itemName",
|
mode: "itemName",
|
||||||
itemName: ""
|
itemName: ""
|
||||||
},
|
},
|
||||||
|
|||||||
0
scripts/conditions/damage-condition.js
Executable file → Normal file
0
scripts/conditions/damage-condition.js
Executable file → Normal file
0
scripts/conditions/hp-threshold-condition.js
Executable file → Normal file
0
scripts/conditions/hp-threshold-condition.js
Executable file → Normal file
0
scripts/constants.js
Executable file → Normal file
0
scripts/constants.js
Executable file → Normal file
0
scripts/engine/action-runner.js
Executable file → Normal file
0
scripts/engine/action-runner.js
Executable file → Normal file
0
scripts/engine/assignment-resolver.js
Executable file → Normal file
0
scripts/engine/assignment-resolver.js
Executable file → Normal file
0
scripts/engine/condition-runner.js
Executable file → Normal file
0
scripts/engine/condition-runner.js
Executable file → Normal file
0
scripts/engine/consumption-manager.js
Executable file → Normal file
0
scripts/engine/consumption-manager.js
Executable file → Normal file
11
scripts/engine/reaction-engine.js
Executable file → Normal file
11
scripts/engine/reaction-engine.js
Executable file → Normal file
@ -13,6 +13,7 @@ export async function handleTrigger(triggerType, context) {
|
|||||||
try {
|
try {
|
||||||
if (!reaction?.enabled) continue;
|
if (!reaction?.enabled) continue;
|
||||||
if (reaction.trigger?.type !== triggerType) continue;
|
if (reaction.trigger?.type !== triggerType) continue;
|
||||||
|
if (!matchesTriggerSpellFilter(reaction.trigger, context)) continue;
|
||||||
if (!await checkConsumptionAvailable(reaction, context)) continue;
|
if (!await checkConsumptionAvailable(reaction, context)) continue;
|
||||||
if (!await checkConditions(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();
|
||||||
|
}
|
||||||
|
|||||||
0
scripts/main.js
Executable file → Normal file
0
scripts/main.js
Executable file → Normal file
0
scripts/settings.js
Executable file → Normal file
0
scripts/settings.js
Executable file → Normal file
0
scripts/sockets.js
Executable file → Normal file
0
scripts/sockets.js
Executable file → Normal file
0
scripts/triggers/damage-received.js
Executable file → Normal file
0
scripts/triggers/damage-received.js
Executable file → Normal file
0
scripts/triggers/target-selected.js
Executable file → Normal file
0
scripts/triggers/target-selected.js
Executable file → Normal file
0
scripts/utils/distance-utils.js
Executable file → Normal file
0
scripts/utils/distance-utils.js
Executable file → Normal file
0
scripts/utils/token-utils.js
Executable file → Normal file
0
scripts/utils/token-utils.js
Executable file → Normal file
24
styles/configurable-reactions.css
Executable file → Normal file
24
styles/configurable-reactions.css
Executable file → Normal file
@ -173,3 +173,27 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.configurable-reactions .cr-spell-chip {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.35rem;
|
||||||
|
width: fit-content;
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
padding: 0.15rem 0.4rem;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: rgb(0 0 0 / 0.12);
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.configurable-reactions .cr-inline-drop {
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
opacity: 0.8;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.configurable-reactions .cr-action-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.25rem;
|
||||||
|
justify-content: end;
|
||||||
|
}
|
||||||
|
|||||||
35
templates/reaction-config.hbs
Executable file → Normal file
35
templates/reaction-config.hbs
Executable file → Normal file
@ -92,7 +92,19 @@
|
|||||||
<div>
|
<div>
|
||||||
<strong>{{activeTrigger.label}}</strong>
|
<strong>{{activeTrigger.label}}</strong>
|
||||||
<code>{{selectedReaction.trigger.type}}</code>
|
<code>{{selectedReaction.trigger.type}}</code>
|
||||||
|
{{#if triggerSpellName}}
|
||||||
|
<span class="cr-spell-chip"><i class="fa-solid fa-book-sparkles"></i> {{triggerSpellName}}</span>
|
||||||
|
{{else}}
|
||||||
|
{{#if activeTriggerAcceptsSpell}}
|
||||||
|
<small class="cr-inline-drop">{{localize "CONFIGURABLE_REACTIONS.Builder.DropSpellOnTrigger"}}</small>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
{{#if triggerSpellName}}
|
||||||
|
<button type="button" data-action="clearTriggerSpell" title="{{localize 'CONFIGURABLE_REACTIONS.Common.Remove'}}">
|
||||||
|
<i class="fa-solid fa-xmark"></i>
|
||||||
|
</button>
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
<p class="cr-empty-drop">{{localize "CONFIGURABLE_REACTIONS.Builder.DropTriggerHere"}}</p>
|
<p class="cr-empty-drop">{{localize "CONFIGURABLE_REACTIONS.Builder.DropTriggerHere"}}</p>
|
||||||
@ -101,19 +113,34 @@
|
|||||||
|
|
||||||
<div class="cr-drop-card" data-cr-drop="actions">
|
<div class="cr-drop-card" data-cr-drop="actions">
|
||||||
<h3><i class="fa-solid fa-list-check"></i> {{localize "CONFIGURABLE_REACTIONS.Builder.ActionArea"}}</h3>
|
<h3><i class="fa-solid fa-list-check"></i> {{localize "CONFIGURABLE_REACTIONS.Builder.ActionArea"}}</h3>
|
||||||
|
<p class="notes">{{localize "CONFIGURABLE_REACTIONS.Builder.DropSpellCreatesAction"}}</p>
|
||||||
{{#if visualActions.length}}
|
{{#if visualActions.length}}
|
||||||
<ol class="cr-action-flow">
|
<ol class="cr-action-flow">
|
||||||
{{#each visualActions}}
|
{{#each visualActions}}
|
||||||
<li class="cr-flow-node cr-action-node" data-cr-drag="existingAction" data-action-index="{{this.index}}">
|
<li class="cr-flow-node cr-action-node" data-cr-drag="existingAction" data-action-index="{{this.index}}" {{#if this.isSpellAction}}data-cr-drop="action-spell"{{/if}}>
|
||||||
<i class="{{this.icon}}"></i>
|
<i class="{{this.icon}}"></i>
|
||||||
<div>
|
<div>
|
||||||
<strong>{{this.label}}</strong>
|
<strong>{{this.label}}</strong>
|
||||||
<small>{{this.summary}}</small>
|
<small>{{this.summary}}</small>
|
||||||
<code>{{this.type}}</code>
|
<code>{{this.type}}</code>
|
||||||
|
{{#if this.isSpellAction}}
|
||||||
|
{{#if this.spellName}}
|
||||||
|
<span class="cr-spell-chip"><i class="fa-solid fa-book-sparkles"></i> {{this.spellName}}</span>
|
||||||
|
{{else}}
|
||||||
|
<small class="cr-inline-drop">{{localize "CONFIGURABLE_REACTIONS.Builder.DropSpellOnAction"}}</small>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
<div class="cr-action-buttons">
|
||||||
|
{{#if this.spellName}}
|
||||||
|
<button type="button" data-action="clearActionSpell" data-action-index="{{this.index}}" title="{{localize 'CONFIGURABLE_REACTIONS.Common.Remove'}}">
|
||||||
|
<i class="fa-solid fa-book-xmark"></i>
|
||||||
|
</button>
|
||||||
|
{{/if}}
|
||||||
|
<button type="button" data-action="removeAction" data-action-index="{{this.index}}" title="{{localize 'CONFIGURABLE_REACTIONS.Common.Remove'}}">
|
||||||
|
<i class="fa-solid fa-xmark"></i>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" data-action="removeAction" data-action-index="{{this.index}}" title="{{localize 'CONFIGURABLE_REACTIONS.Common.Remove'}}">
|
|
||||||
<i class="fa-solid fa-xmark"></i>
|
|
||||||
</button>
|
|
||||||
</li>
|
</li>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</ol>
|
</ol>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user