Merge branch 'item-effects' into 'main'
Add effects for owned items See merge request dungeonslayers/ds4!203
This commit is contained in:
commit
4ac29f8f95
14 changed files with 433 additions and 59 deletions
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
@ -12,5 +12,6 @@
|
|||
"importSorter.importStringConfiguration.maximumNumberOfImportExpressionsPerLine.count": 120,
|
||||
"importSorter.importStringConfiguration.tabSize": 4,
|
||||
"importSorter.importStringConfiguration.quoteMark": "double",
|
||||
"importSorter.importStringConfiguration.trailingComma": "multiLine"
|
||||
"importSorter.importStringConfiguration.trailingComma": "multiLine",
|
||||
"vitest.commandLine": "yarn run vitest"
|
||||
}
|
||||
|
|
|
@ -366,5 +366,10 @@
|
|||
"DS4.NewLanguageName": "Neue Sprache",
|
||||
"DS4.NewAlphabetName": "Neue Schriftzeichen",
|
||||
"DS4.NewSpecialCreatureAbilityName": "Neue Besondere Kreaturenfähigkeit",
|
||||
"DS4.NewEffectLabel": "Neuer Effekt"
|
||||
"DS4.NewEffectLabel": "Neuer Effekt",
|
||||
|
||||
"DS4.ActiveEffectApplyToItems": "Auf Items Andwenden",
|
||||
"DS4.ActiveEffectItemName": "Itemname",
|
||||
"DS4.ActiveEffectItemCondition": "Bedingung",
|
||||
"DS4.TooltipDisabledDueToEffects": "inaktiv, weil von Aktiven Effekten beeinflusst"
|
||||
}
|
||||
|
|
|
@ -366,5 +366,10 @@
|
|||
"DS4.NewLanguageName": "New Language",
|
||||
"DS4.NewAlphabetName": "New Alphabet",
|
||||
"DS4.NewSpecialCreatureAbilityName": "New Special Creature Ability",
|
||||
"DS4.NewEffectLabel": "New Effect"
|
||||
"DS4.NewEffectLabel": "New Effect",
|
||||
|
||||
"DS4.ActiveEffectApplyToItems": "Apply to Items",
|
||||
"DS4.ActiveEffectItemName": "Item Name",
|
||||
"DS4.ActiveEffectItemCondition": "Condition",
|
||||
"DS4.TooltipDisabledDueToEffects": "disabled, because affected by Active Effects"
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
// global
|
||||
@use "global/accessibility";
|
||||
@use "global/fonts";
|
||||
@use "global/utils";
|
||||
|
||||
// shared
|
||||
@use "components/shared/add_button";
|
||||
|
|
14
scss/global/_utils.scss
Normal file
14
scss/global/_utils.scss
Normal file
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Johannes Loher
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
.ds4-code-input {
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
|
||||
// This is needed for higher specifity
|
||||
form .ds4-code-input {
|
||||
font-family: var(--font-mono);
|
||||
}
|
31
src/active-effect/active-effect-config.ts
Normal file
31
src/active-effect/active-effect-config.ts
Normal file
|
@ -0,0 +1,31 @@
|
|||
// SPDX-FileCopyrightText: 2022 Johannes Loher
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
export class DS4ActiveEffectConfig extends ActiveEffectConfig {
|
||||
static override get defaultOptions(): DocumentSheetOptions {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
template: "systems/ds4/templates/sheets/active-effect/active-effect-config.hbs",
|
||||
});
|
||||
}
|
||||
|
||||
override activateListeners(html: JQuery<HTMLElement>): void {
|
||||
super.activateListeners(html);
|
||||
const checkbox = html[0]?.querySelector<HTMLInputElement>(
|
||||
'input[name="flags.ds4.itemEffectConfig.applyToItems"]',
|
||||
);
|
||||
checkbox?.addEventListener("change", () => this.toggleItemEffectConfig(checkbox.checked));
|
||||
}
|
||||
|
||||
private toggleItemEffectConfig(active: boolean) {
|
||||
const elements = this.element[0]?.querySelectorAll(".ds4-item-effect-config");
|
||||
elements?.forEach((element) => {
|
||||
if (active) {
|
||||
element.classList.remove("ds4-hidden");
|
||||
} else {
|
||||
element.classList.add("ds4-hidden");
|
||||
}
|
||||
});
|
||||
this.setPosition({ height: "auto" });
|
||||
}
|
||||
}
|
|
@ -2,16 +2,27 @@
|
|||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
import { DS4Actor } from "./actor/actor";
|
||||
import { mathEvaluator } from "./expression-evaluation/evaluator";
|
||||
import { getGame } from "./helpers";
|
||||
import { mathEvaluator } from "../expression-evaluation/evaluator";
|
||||
import { getGame } from "../helpers";
|
||||
|
||||
import type { DS4Item } from "./item/item";
|
||||
import type { DS4Actor } from "../actor/actor";
|
||||
import type { DS4Item } from "../item/item";
|
||||
|
||||
declare global {
|
||||
interface DocumentClassConfig {
|
||||
ActiveEffect: typeof DS4ActiveEffect;
|
||||
}
|
||||
interface FlagConfig {
|
||||
ActiveEffect: {
|
||||
ds4?: {
|
||||
itemEffectConfig?: {
|
||||
applyToItems?: boolean;
|
||||
itemName?: string;
|
||||
condition?: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
type PromisedType<T> = T extends Promise<infer U> ? U : T;
|
||||
|
@ -42,7 +53,7 @@ export class DS4ActiveEffect extends ActiveEffect {
|
|||
* The item which this effect originates from if it has been transferred from an item to an actor.
|
||||
*/
|
||||
get originatingItem(): DS4Item | undefined {
|
||||
if (!(this.parent instanceof DS4Actor)) {
|
||||
if (!(this.parent instanceof Actor)) {
|
||||
return;
|
||||
}
|
||||
const itemIdRegex = /Item\.([a-zA-Z0-9]+)/;
|
||||
|
@ -60,15 +71,17 @@ export class DS4ActiveEffect extends ActiveEffect {
|
|||
return this.originatingItem?.activeEffectFactor ?? 1;
|
||||
}
|
||||
|
||||
override apply(actor: DS4Actor, change: foundry.data.ActiveEffectData["changes"][number]): unknown {
|
||||
change.value = Roll.replaceFormulaData(change.value, actor.data);
|
||||
override apply(document: DS4Actor | DS4Item, change: EffectChangeData): unknown {
|
||||
change.value = Roll.replaceFormulaData(change.value, document.data);
|
||||
try {
|
||||
change.value = DS4ActiveEffect.safeEval(change.value).toString();
|
||||
} catch (e) {
|
||||
logger.warn(e);
|
||||
// this is a valid case, e.g., if the effect change simply is a string
|
||||
}
|
||||
return super.apply(actor, change);
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error In the types and foundry's documentation, only actors are allowed, but the implementation actually works for all kinds of documents
|
||||
return super.apply(document, change);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -114,4 +127,55 @@ export class DS4ActiveEffect extends ActiveEffect {
|
|||
}
|
||||
return result as number | `${number | boolean}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the given effects to the gicen Actor or item.
|
||||
* @param document The Actor or Item to which to apply the effects
|
||||
* @param effetcs The effects to apply
|
||||
* @param predicate Apply only changes that fullfill this predicate
|
||||
*/
|
||||
static applyEffetcs(
|
||||
document: DS4Actor | DS4Item,
|
||||
effetcs: DS4ActiveEffect[],
|
||||
predicate: (change: EffectChangeData) => boolean = () => true,
|
||||
): void {
|
||||
const overrides: Record<string, unknown> = {};
|
||||
|
||||
// Organize non-disabled and -surpressed effects by their application priority
|
||||
const changesWithEffect = effetcs.flatMap((e) => e.getFactoredChangesWithEffect(predicate));
|
||||
changesWithEffect.sort((a, b) => (a.change.priority ?? 0) - (b.change.priority ?? 0));
|
||||
|
||||
// Apply all changes
|
||||
for (const changeWithEffect of changesWithEffect) {
|
||||
const result = changeWithEffect.effect.apply(document, changeWithEffect.change);
|
||||
if (result !== null) overrides[changeWithEffect.change.key] = result;
|
||||
}
|
||||
|
||||
// Expand the set of final overrides
|
||||
document.overrides = foundry.utils.expandObject({
|
||||
...foundry.utils.flattenObject(document.overrides),
|
||||
...overrides,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array of changes for this effect, considering the {@link DS4ActiveEffect#factor}.
|
||||
* @param predicate An optional predicate to filter which changes should be considered
|
||||
* @returns The array of changes from this effect, considering the factor.
|
||||
*/
|
||||
protected getFactoredChangesWithEffect(
|
||||
predicate: (change: EffectChangeData) => boolean = () => true,
|
||||
): EffectChangeDataWithEffect[] {
|
||||
if (this.data.disabled || this.isSurpressed) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return this.data.changes.filter(predicate).flatMap((change) => {
|
||||
change.priority = change.priority ?? change.mode * 10;
|
||||
return Array<EffectChangeDataWithEffect>(this.factor).fill({ effect: this, change });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
type EffectChangeData = foundry.data.ActiveEffectData["changes"][number];
|
||||
type EffectChangeDataWithEffect = { effect: DS4ActiveEffect; change: EffectChangeData };
|
|
@ -5,7 +5,8 @@
|
|||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
import { DS4ActiveEffect } from "../active-effect";
|
||||
import { DS4ActiveEffect } from "../active-effect/active-effect";
|
||||
import { disableOverriddenFields } from "../apps/sheet-helpers";
|
||||
import { DS4 } from "../config";
|
||||
import { getCanvas, getGame } from "../helpers";
|
||||
import { getDS4Settings } from "../settings";
|
||||
|
@ -108,6 +109,8 @@ export class DS4ActorSheet extends ActorSheet<ActorSheet.Options, DS4ActorSheetD
|
|||
html.find(".rollable-check").on("click", this.onRollCheck.bind(this));
|
||||
|
||||
html.find(".sort-items").on("click", this.onSortItems.bind(this));
|
||||
|
||||
disableOverriddenFields.call(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -3,8 +3,11 @@
|
|||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
import { DS4ActiveEffect } from "../active-effect/active-effect";
|
||||
import { DS4 } from "../config";
|
||||
import { mathEvaluator } from "../expression-evaluation/evaluator";
|
||||
import { getGame } from "../helpers";
|
||||
import logger from "../logger";
|
||||
import { createCheckRoll } from "../rolls/check-factory";
|
||||
import { isAttribute, isTrait } from "./actor-data-source-base";
|
||||
|
||||
|
@ -25,7 +28,10 @@ declare global {
|
|||
* The Actor class for DS4
|
||||
*/
|
||||
export class DS4Actor extends Actor {
|
||||
initialized: boolean | undefined;
|
||||
|
||||
override prepareData(): void {
|
||||
this.initialized = true;
|
||||
this.data.reset();
|
||||
this.prepareBaseData();
|
||||
this.prepareEmbeddedDocuments();
|
||||
|
@ -54,6 +60,41 @@ export class DS4Actor extends Actor {
|
|||
);
|
||||
}
|
||||
|
||||
private get actorEffects() {
|
||||
return this.effects.filter((effect) => !effect.data.flags.ds4?.itemEffectConfig?.applyToItems);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the effects of this actor that should be applied to the given item.
|
||||
* @param item The item for which to get effects
|
||||
* @returns The array of effects that are candidates to be applied to the item
|
||||
*/
|
||||
itemEffects(item: DS4Item) {
|
||||
return this.effects.filter((effect) => {
|
||||
const { applyToItems, itemName, condition } = effect.data.flags.ds4?.itemEffectConfig ?? {};
|
||||
|
||||
if (!applyToItems || (itemName !== undefined && itemName !== "" && itemName !== item.name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (condition !== undefined && condition !== "") {
|
||||
try {
|
||||
const replacedCondition = Roll.replaceFormulaData(condition, {
|
||||
item: item.data,
|
||||
actor: this.data,
|
||||
effect: effect.data,
|
||||
});
|
||||
return Boolean(mathEvaluator.evaluate(replacedCondition));
|
||||
} catch (error) {
|
||||
logger.warn(error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* We override this with an empty implementation because we have our own custom way of applying
|
||||
* {@link ActiveEffect}s and {@link Actor#prepareEmbeddedDocuments} calls this.
|
||||
|
@ -65,7 +106,9 @@ export class DS4Actor extends Actor {
|
|||
applyActiveEffectsToBaseData(): void {
|
||||
// reset overrides because our variant of applying active effects does not set them, it only adds overrides
|
||||
this.overrides = {};
|
||||
this.applyActiveEffectsFiltered(
|
||||
DS4ActiveEffect.applyEffetcs(
|
||||
this,
|
||||
this.actorEffects,
|
||||
(change) =>
|
||||
!this.derivedDataProperties.includes(change.key) &&
|
||||
!this.finalDerivedDataProperties.includes(change.key),
|
||||
|
@ -73,43 +116,9 @@ export class DS4Actor extends Actor {
|
|||
}
|
||||
|
||||
applyActiveEffectsToDerivedData(): void {
|
||||
this.applyActiveEffectsFiltered((change) => this.derivedDataProperties.includes(change.key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply ActiveEffectChanges to the Actor data which are caused by ActiveEffects and satisfy the given predicate.
|
||||
*
|
||||
* @param predicate - The predicate that ActiveEffectChanges need to satisfy in order to be applied
|
||||
*/
|
||||
applyActiveEffectsFiltered(predicate: (change: foundry.data.ActiveEffectData["changes"][number]) => boolean): void {
|
||||
const overrides: Record<string, unknown> = {};
|
||||
|
||||
// Organize non-disabled and -surpressed effects by their application priority
|
||||
const changes: (foundry.data.ActiveEffectData["changes"][number] & { effect: ActiveEffect })[] =
|
||||
this.effects.reduce(
|
||||
(changes: (foundry.data.ActiveEffectData["changes"][number] & { effect: ActiveEffect })[], e) => {
|
||||
if (e.data.disabled || e.isSurpressed) return changes;
|
||||
|
||||
const newChanges = e.data.changes.filter(predicate).flatMap((c) => {
|
||||
const changeSource = c.toObject();
|
||||
changeSource.priority = changeSource.priority ?? changeSource.mode * 10;
|
||||
return Array(e.factor).fill({ ...changeSource, effect: e });
|
||||
});
|
||||
|
||||
return changes.concat(newChanges);
|
||||
},
|
||||
[],
|
||||
DS4ActiveEffect.applyEffetcs(this, this.actorEffects, (change) =>
|
||||
this.derivedDataProperties.includes(change.key),
|
||||
);
|
||||
changes.sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0));
|
||||
|
||||
// Apply all changes
|
||||
for (const change of changes) {
|
||||
const result = change.effect.apply(this, change);
|
||||
if (result !== null) overrides[change.key] = result;
|
||||
}
|
||||
|
||||
// Expand the set of final overrides
|
||||
this.overrides = foundry.utils.expandObject({ ...foundry.utils.flattenObject(this.overrides), ...overrides });
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
25
src/apps/sheet-helpers.ts
Normal file
25
src/apps/sheet-helpers.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
// SPDX-FileCopyrightText: 2022 Johannes Loher
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
import { getGame } from "../helpers";
|
||||
|
||||
export function disableOverriddenFields<
|
||||
Options extends FormApplicationOptions,
|
||||
Data extends object,
|
||||
ConcreteObject extends { overrides: Record<string, unknown> },
|
||||
>(this: FormApplication<Options, Data, ConcreteObject>): void {
|
||||
const inputs = ["INPUT", "SELECT", "TEXTAREA", "BUTTON"];
|
||||
const titleAddition = `(${getGame().i18n.localize("DS4.TooltipDisabledDueToEffects")})`;
|
||||
for (const key of Object.keys(foundry.utils.flattenObject(this.object.overrides))) {
|
||||
const elements = this.form?.querySelectorAll(`[name="${key}"]`);
|
||||
elements?.forEach((element) => {
|
||||
if (inputs.includes(element.tagName)) {
|
||||
element.setAttribute("disabled", "");
|
||||
const title = element.getAttribute("title");
|
||||
const newTitle = title === null ? titleAddition : `${title} ${titleAddition}`;
|
||||
element.setAttribute("title", newTitle);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -4,7 +4,8 @@
|
|||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
import { DS4ActiveEffect } from "../active-effect";
|
||||
import { DS4ActiveEffect } from "../active-effect/active-effect";
|
||||
import { DS4ActiveEffectConfig } from "../active-effect/active-effect-config";
|
||||
import { DS4CharacterActorSheet } from "../actor/character/character-sheet";
|
||||
import { DS4CreatureActorSheet } from "../actor/creature/creature-sheet";
|
||||
import { DS4ActorProxy } from "../actor/proxy";
|
||||
|
@ -65,11 +66,16 @@ async function init() {
|
|||
|
||||
registerSystemSettings();
|
||||
|
||||
Actors.unregisterSheet("core", ActorSheet);
|
||||
Actors.registerSheet("ds4", DS4CharacterActorSheet, { types: ["character"], makeDefault: true });
|
||||
Actors.registerSheet("ds4", DS4CreatureActorSheet, { types: ["creature"], makeDefault: true });
|
||||
Items.unregisterSheet("core", ItemSheet);
|
||||
Items.registerSheet("ds4", DS4ItemSheet, { makeDefault: true });
|
||||
DocumentSheetConfig.unregisterSheet(Actor, "core", ActorSheet);
|
||||
DocumentSheetConfig.registerSheet(Actor, "ds4", DS4CharacterActorSheet, {
|
||||
types: ["character"],
|
||||
makeDefault: true,
|
||||
});
|
||||
DocumentSheetConfig.registerSheet(Actor, "ds4", DS4CreatureActorSheet, { types: ["creature"], makeDefault: true });
|
||||
DocumentSheetConfig.unregisterSheet(Item, "core", ItemSheet);
|
||||
DocumentSheetConfig.registerSheet(Item, "ds4", DS4ItemSheet, { makeDefault: true });
|
||||
DocumentSheetConfig.unregisterSheet(ActiveEffect, "core", ActiveEffectConfig);
|
||||
DocumentSheetConfig.registerSheet(ActiveEffect, "ds4", DS4ActiveEffectConfig, { makeDefault: true });
|
||||
|
||||
preloadFonts();
|
||||
await registerHandlebarsPartials();
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
import { DS4ActiveEffect } from "../active-effect";
|
||||
import { DS4ActiveEffect } from "../active-effect/active-effect";
|
||||
import { disableOverriddenFields } from "../apps/sheet-helpers";
|
||||
import { DS4 } from "../config";
|
||||
import { getGame } from "../helpers";
|
||||
import notifications from "../ui/notifications";
|
||||
|
@ -41,6 +42,16 @@ export class DS4ItemSheet extends ItemSheet<ItemSheet.Options, DS4ItemSheetData>
|
|||
return data;
|
||||
}
|
||||
|
||||
override _getSubmitData(updateData = {}) {
|
||||
const data = super._getSubmitData(updateData);
|
||||
// Prevent submitting overridden values
|
||||
const overrides = foundry.utils.flattenObject(this.item.overrides);
|
||||
for (const k of Object.keys(overrides)) {
|
||||
delete data[k];
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
override setPosition(
|
||||
options: Partial<Application.Position> = {},
|
||||
): (Application.Position & { height: number }) | void {
|
||||
|
@ -60,6 +71,8 @@ export class DS4ItemSheet extends ItemSheet<ItemSheet.Options, DS4ItemSheetData>
|
|||
if (!this.options.editable) return;
|
||||
|
||||
html.find(".control-effect").on("click", this.onControlEffect.bind(this));
|
||||
|
||||
disableOverriddenFields.call(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -86,8 +99,6 @@ export class DS4ItemSheet extends ItemSheet<ItemSheet.Options, DS4ItemSheetData>
|
|||
|
||||
/**
|
||||
* Creates a new embedded effect.
|
||||
*
|
||||
* @param event - The originating click event
|
||||
*/
|
||||
protected onCreateEffect(): void {
|
||||
DS4ActiveEffect.createDefault(this.item);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
import { DS4ActiveEffect } from "../active-effect/active-effect";
|
||||
import { getGame } from "../helpers";
|
||||
|
||||
import type { ItemType } from "./item-data-source";
|
||||
|
@ -24,6 +25,26 @@ declare global {
|
|||
* The Item class for DS4
|
||||
*/
|
||||
export class DS4Item extends Item {
|
||||
/** An object that tracks the changes to the data model which were applied by active effects */
|
||||
overrides: Record<string, unknown> = {};
|
||||
|
||||
override prepareData() {
|
||||
this.data.reset();
|
||||
this.prepareBaseData();
|
||||
this.prepareEmbeddedDocuments();
|
||||
this.prepareDerivedData();
|
||||
this.applyActiveEffects();
|
||||
}
|
||||
|
||||
applyActiveEffects(): void {
|
||||
if (!this.actor?.initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.overrides = {};
|
||||
DS4ActiveEffect.applyEffetcs(this, this.actor.itemEffects(this));
|
||||
}
|
||||
|
||||
override prepareDerivedData(): void {
|
||||
this.data.data.rollable = false;
|
||||
}
|
||||
|
|
178
templates/sheets/active-effect/active-effect-config.hbs
Normal file
178
templates/sheets/active-effect/active-effect-config.hbs
Normal file
|
@ -0,0 +1,178 @@
|
|||
{{!--
|
||||
SPDX-FileCopyrightText: 2022 Johannes Loher
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
--}}
|
||||
|
||||
<form autocomplete="off">
|
||||
|
||||
<!-- Effect Header -->
|
||||
<header class="sheet-header">
|
||||
<img class="effect-icon" src="{{ data.icon }}" data-edit="icon">
|
||||
<h1 class="effect-title">{{ data.label }}</h1>
|
||||
</header>
|
||||
|
||||
<!-- Effect Configuration Tabs -->
|
||||
<nav class="sheet-tabs tabs">
|
||||
<a class="item" data-tab="details"><i class="fas fa-book"></i> {{localize "EFFECT.TabDetails"}}</a>
|
||||
<a class="item" data-tab="duration"><i class="fas fa-clock"></i> {{localize "EFFECT.TabDuration"}}</a>
|
||||
<a class="item" data-tab="effects"><i class="fas fa-cogs"></i> {{localize "EFFECT.TabEffects"}}</a>
|
||||
</nav>
|
||||
|
||||
<!-- Details Tab -->
|
||||
<section class="tab" data-tab="details">
|
||||
|
||||
<div class="form-group">
|
||||
<label>{{ localize "EFFECT.Label" }}</label>
|
||||
<div class="form-fields">
|
||||
<input type="text" name="label" value="{{ data.label }}" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>{{ localize "EFFECT.Icon" }}</label>
|
||||
<div class="form-fields">
|
||||
{{filePicker target="icon" type="image"}}
|
||||
<input class="image" type="text" name="icon" placeholder="path/image.png" value="{{data.icon}}" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>{{ localize "EFFECT.IconTint" }}</label>
|
||||
<div class="form-fields">
|
||||
<input class="color" type="text" name="tint" value="{{data.tint}}" />
|
||||
<input type="color" value="{{data.tint}}" data-edit="tint" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>{{ localize "EFFECT.Disabled" }}</label>
|
||||
<div class="form-fields">
|
||||
<input type="checkbox" name="disabled" {{ checked data.disabled }} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if isActorEffect}}
|
||||
<div class="form-group">
|
||||
<label>{{ localize "EFFECT.Origin" }}</label>
|
||||
<div class="form-fields">
|
||||
<input type="text" name="origin" value="{{ data.origin }}" disabled />
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if isItemEffect}}
|
||||
<div class="form-group">
|
||||
<label>{{ localize "EFFECT.Transfer" }}</label>
|
||||
<div class="form-fields">
|
||||
<input type="checkbox" name="transfer" {{checked data.transfer}} />
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="form-group">
|
||||
<label>{{ localize "DS4.ActiveEffectApplyToItems" }}</label>
|
||||
<div class="form-fields">
|
||||
<input type="checkbox" name="flags.ds4.itemEffectConfig.applyToItems" {{checked
|
||||
data.flags.ds4.itemEffectConfig.applyToItems}} />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="form-group ds4-item-effect-config{{#unless data.flags.ds4.itemEffectConfig.applyToItems}} ds4-hidden{{/unless}}">
|
||||
<label>{{ localize "DS4.ActiveEffectItemName" }}</label>
|
||||
<div class="form-fields">
|
||||
<input type="text" name="flags.ds4.itemEffectConfig.itemName"
|
||||
value="{{ data.flags.ds4.itemEffectConfig.itemName }}" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="form-group ds4-item-effect-config{{#unless data.flags.ds4.itemEffectConfig.applyToItems}} ds4-hidden{{/unless}}">
|
||||
<label>{{ localize "DS4.ActiveEffectItemCondition" }}</label>
|
||||
<div class="form-fields">
|
||||
<input class="ds4-code-input" type="text" name="flags.ds4.itemEffectConfig.condition"
|
||||
value="{{ data.flags.ds4.itemEffectConfig.condition }}" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Duration Tab -->
|
||||
<section class="tab" data-tab="duration">
|
||||
<div class="form-group">
|
||||
<label>{{ localize "EFFECT.DurationSecs" }}</label>
|
||||
<div class="form-fields">
|
||||
<input type="number" name="duration.seconds" value="{{ data.duration.seconds }}" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{ localize "EFFECT.StartTime" }}</label>
|
||||
<div class="form-fields">
|
||||
<input type="number" name="duration.startTime" value="{{ data.duration.startTime }}" />
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<div class="form-group">
|
||||
<label>{{ localize "EFFECT.DurationTurns" }}</label>
|
||||
<div class="form-fields">
|
||||
<label>{{ localize "COMBAT.Rounds" }}</label>
|
||||
<input type="number" name="duration.rounds" value="{{ data.duration.rounds }}" />
|
||||
<label>{{ localize "COMBAT.Turns" }}</label>
|
||||
<input type="number" name="duration.turns" value="{{ data.duration.turns }}" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{ localize "EFFECT.Combat" }}</label>
|
||||
<div class="form-fields">
|
||||
<input type="text" name="duration.combat" value="{{ data.duration.combat }}" disabled />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>{{ localize "EFFECT.StartTurns" }}</label>
|
||||
<div class="form-fields">
|
||||
<label>{{ localize "COMBAT.Round" }}</label>
|
||||
<input type="number" name="duration.startRound" value="{{ data.duration.startRound }}" />
|
||||
<label>{{ localize "COMBAT.Turn" }}</label>
|
||||
<input type="number" name="duration.startTurn" value="{{ data.duration.startTurn }}" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Effects Tab -->
|
||||
<section class="tab" data-tab="effects">
|
||||
<header class="effect-change effects-header flexrow">
|
||||
<div class="key">{{ localize "EFFECT.ChangeKey" }}</div>
|
||||
<div class="mode">{{ localize "EFFECT.ChangeMode" }}</div>
|
||||
<div class="value">{{ localize "EFFECT.ChangeValue" }}</div>
|
||||
<div class="effect-controls">
|
||||
<a class="effect-control" data-action="add"><i class="far fa-plus-square"></i></a>
|
||||
</div>
|
||||
</header>
|
||||
<ol class="changes-list">
|
||||
{{#each data.changes as |change i|}}
|
||||
<li class="effect-change flexrow" data-index="{{i}}">
|
||||
<div class="key">
|
||||
<input type="text" name="changes.{{i}}.key" value="{{change.key}}" />
|
||||
</div>
|
||||
<div class="mode">
|
||||
<select name="changes.{{i}}.mode" data-dtype="Number">
|
||||
{{selectOptions ../modes selected=change.mode}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="value">
|
||||
<input type="text" name="changes.{{i}}.value" value="{{change.value}}" />
|
||||
</div>
|
||||
<div class="effect-controls">
|
||||
<a class="effect-control" data-action="delete"><i class="fas fa-trash"></i></a>
|
||||
</div>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
<footer class="sheet-footer">
|
||||
<button type="submit"><i class="fas fa-save"></i> {{localize submitText}}</button>
|
||||
</footer>
|
||||
</form>
|
Loading…
Reference in a new issue