Merge branch 'spell-groups' into 'main'
feat: replace spell category by spell groups See merge request dungeonslayers/ds4!214
This commit is contained in:
commit
b74919b75b
25 changed files with 3675 additions and 487 deletions
37
lang/de.json
37
lang/de.json
|
@ -118,20 +118,31 @@
|
||||||
"DS4.SortBySpellType": "Nach Zauberspruchtyp sortieren",
|
"DS4.SortBySpellType": "Nach Zauberspruchtyp sortieren",
|
||||||
"DS4.SpellTypeSpellcasting": "Zaubern",
|
"DS4.SpellTypeSpellcasting": "Zaubern",
|
||||||
"DS4.SpellTypeTargetedSpellcasting": "Zielzaubern",
|
"DS4.SpellTypeTargetedSpellcasting": "Zielzaubern",
|
||||||
"DS4.SpellCategory": "Kategorie",
|
"DS4.SpellGroups": "Zaubergruppen",
|
||||||
"DS4.SpellCategoryDescription": "Eine Kategorie, der der Zauberspruch zugehörig ist.",
|
"DS4.SpellGroupsDescription": "Zaubergruppen, denen der Zauberspruch zugehörig ist.",
|
||||||
"DS4.SpellCategoryHealing": "Heilung",
|
"DS4.SpellGroupLightning": "Blitz",
|
||||||
"DS4.SpellCategoryFire": "Feuer",
|
"DS4.SpellGroupEarth": "Erde, Fels, Stein",
|
||||||
"DS4.SpellCategoryIce": "Eis",
|
"DS4.SpellGroupWater": "Wasser",
|
||||||
"DS4.SpellCategoryLight": "Licht",
|
"DS4.SpellGroupIce": "Eis, Frost",
|
||||||
"DS4.SpellCategoryDarkness": "Schatten",
|
"DS4.SpellGroupFire": "Feuer",
|
||||||
"DS4.SpellCategoryMindAffecting": "Geistesbeeinflussend",
|
"DS4.SpellGroupHealing": "Heilung",
|
||||||
"DS4.SpellCategoryElectricity": "Elektrizität",
|
"DS4.SpellGroupLight": "Licht",
|
||||||
"DS4.SpellCategoryNone": "Keine",
|
"DS4.SpellGroupAir": "Luft",
|
||||||
"DS4.SpellCategoryUnset": "Nicht gesetzt",
|
"DS4.SpellGroupTransport": "Transport",
|
||||||
|
"DS4.SpellGroupDamage": "Schaden",
|
||||||
|
"DS4.SpellGroupShadow": "Schatten",
|
||||||
|
"DS4.SpellGroupProtection": "Schutz",
|
||||||
|
"DS4.SpellGroupMindAffecting": "Geistesbeeinflussend",
|
||||||
|
"DS4.SpellGroupDemonology": "Dämonologie",
|
||||||
|
"DS4.SpellGroupNecromancy": "Nekromantie",
|
||||||
|
"DS4.SpellGroupTransmutation": "Verwandlung",
|
||||||
|
"DS4.SpellGroupArea": "Fläche",
|
||||||
"DS4.SpellModifier": "Zauberbonus",
|
"DS4.SpellModifier": "Zauberbonus",
|
||||||
|
"DS4.SpellModifierNumerical": "Zauberbonus (numerisch)",
|
||||||
|
"DS4.SpellModifierComplex": "Zauberbonus (komplex)",
|
||||||
"DS4.SpellModifierAbbr": "ZB",
|
"DS4.SpellModifierAbbr": "ZB",
|
||||||
"DS4.SpellModifierDescription": "Der Zauberbonus auf die Probe.",
|
"DS4.SpellModifierNumericalDescription": "Der numerische Zauberbonus auf die Probe.",
|
||||||
|
"DS4.SpellModifierComplexDescription": "Ein komplexer Zauberbonus auf die Probe (zum Beispiel abhängig von Werten des Ziels). Wenn diese Art von Zauberbonus angegeben ist, wird der numerische ignoriert.",
|
||||||
"DS4.SortBySpellModifier": "Nach Zauberbonus sortieren",
|
"DS4.SortBySpellModifier": "Nach Zauberbonus sortieren",
|
||||||
"DS4.SpellDistance": "Distanz",
|
"DS4.SpellDistance": "Distanz",
|
||||||
"DS4.SpellDistanceDescription": "Die maximale Entfernung zum Ziel. „Selbst“ bedeutet, dass nur der Zauberwirker selbst das Ziel des Zaubers sein kann.",
|
"DS4.SpellDistanceDescription": "Die maximale Entfernung zum Ziel. „Selbst“ bedeutet, dass nur der Zauberwirker selbst das Ziel des Zaubers sein kann.",
|
||||||
|
@ -371,5 +382,5 @@
|
||||||
"DS4.ActiveEffectApplyToItems": "Auf Items Andwenden",
|
"DS4.ActiveEffectApplyToItems": "Auf Items Andwenden",
|
||||||
"DS4.ActiveEffectItemName": "Itemname",
|
"DS4.ActiveEffectItemName": "Itemname",
|
||||||
"DS4.ActiveEffectItemCondition": "Bedingung",
|
"DS4.ActiveEffectItemCondition": "Bedingung",
|
||||||
"DS4.TooltipDisabledDueToEffects": "inaktiv, weil von Aktiven Effekten beeinflusst"
|
"DS4.TooltipNotEditableDueToEffects": "Feld nicht bearbeitbar, weil von Aktiven Effekten beeinflusst"
|
||||||
}
|
}
|
||||||
|
|
37
lang/en.json
37
lang/en.json
|
@ -118,20 +118,31 @@
|
||||||
"DS4.SortBySpellType": "Sort by Spell Type",
|
"DS4.SortBySpellType": "Sort by Spell Type",
|
||||||
"DS4.SpellTypeSpellcasting": "Spellcasting",
|
"DS4.SpellTypeSpellcasting": "Spellcasting",
|
||||||
"DS4.SpellTypeTargetedSpellcasting": "Targeted Spellcasting",
|
"DS4.SpellTypeTargetedSpellcasting": "Targeted Spellcasting",
|
||||||
"DS4.SpellCategory": "Category",
|
"DS4.SpellGroups": "Spell Groups",
|
||||||
"DS4.SpellCategoryDescription": "A category which the spell belongs to.",
|
"DS4.SpellGroupsDescription": "Spell groups which the spell belongs to.",
|
||||||
"DS4.SpellCategoryHealing": "Healing",
|
"DS4.SpellGroupLightning": "Lightning",
|
||||||
"DS4.SpellCategoryFire": "Fire",
|
"DS4.SpellGroupEarth": "Earth, Rock, Stone",
|
||||||
"DS4.SpellCategoryIce": "Ice",
|
"DS4.SpellGroupWater": "Water",
|
||||||
"DS4.SpellCategoryLight": "Light",
|
"DS4.SpellGroupIce": "Ice, Frost",
|
||||||
"DS4.SpellCategoryDarkness": "Darkness",
|
"DS4.SpellGroupFire": "Fire",
|
||||||
"DS4.SpellCategoryMindAffecting": "Mind Affecting",
|
"DS4.SpellGroupHealing": "Healing",
|
||||||
"DS4.SpellCategoryElectricity": "Electricity",
|
"DS4.SpellGroupLight": "Light",
|
||||||
"DS4.SpellCategoryNone": "None",
|
"DS4.SpellGroupAir": "Air",
|
||||||
"DS4.SpellCategoryUnset": "Unset",
|
"DS4.SpellGroupTransport": "Transport",
|
||||||
|
"DS4.SpellGroupDamage": "Damage",
|
||||||
|
"DS4.SpellGroupShadow": "Shadow",
|
||||||
|
"DS4.SpellGroupProtection": "Protection",
|
||||||
|
"DS4.SpellGroupMindAffecting": "Mind Affecting",
|
||||||
|
"DS4.SpellGroupDemonology": "Demonologie",
|
||||||
|
"DS4.SpellGroupNecromancy": "Necromancy",
|
||||||
|
"DS4.SpellGroupTransmutation": "Transmutation",
|
||||||
|
"DS4.SpellGroupArea": "Area",
|
||||||
"DS4.SpellModifier": "Spell Modifier",
|
"DS4.SpellModifier": "Spell Modifier",
|
||||||
|
"DS4.SpellModifierNumerical": "Spell Modifier (numerical)",
|
||||||
|
"DS4.SpellModifierComplex": "Spell Modifier (complex)",
|
||||||
"DS4.SpellModifierAbbr": "SM",
|
"DS4.SpellModifierAbbr": "SM",
|
||||||
"DS4.SpellModifierDescription": "The spell modifier for the corresponding check.",
|
"DS4.SpellModifierNumericalDescription": "The numerical spell modifier for the corresponding check.",
|
||||||
|
"DS4.SpellModifierComplexDescription": "A complex spell modifier for the corresponding check (for example, dependent on the target’s values). If given, the numerical spell bonus is ignored.",
|
||||||
"DS4.SortBySpellModifier": "Sort by Spell Modifier",
|
"DS4.SortBySpellModifier": "Sort by Spell Modifier",
|
||||||
"DS4.SpellDistance": "Distance",
|
"DS4.SpellDistance": "Distance",
|
||||||
"DS4.SpellDistanceDescription": "The maximum distance to the target, “Self” meaning that only the caster can be the target of this spell.",
|
"DS4.SpellDistanceDescription": "The maximum distance to the target, “Self” meaning that only the caster can be the target of this spell.",
|
||||||
|
@ -371,5 +382,5 @@
|
||||||
"DS4.ActiveEffectApplyToItems": "Apply to Items",
|
"DS4.ActiveEffectApplyToItems": "Apply to Items",
|
||||||
"DS4.ActiveEffectItemName": "Item Name",
|
"DS4.ActiveEffectItemName": "Item Name",
|
||||||
"DS4.ActiveEffectItemCondition": "Condition",
|
"DS4.ActiveEffectItemCondition": "Condition",
|
||||||
"DS4.TooltipDisabledDueToEffects": "disabled, because affected by Active Effects"
|
"DS4.TooltipNotEditableDueToEffects": "field not editable, because affected by Active Effects"
|
||||||
}
|
}
|
||||||
|
|
|
@ -6098,7 +6098,7 @@
|
||||||
"type": "loot",
|
"type": "loot",
|
||||||
"img": "icons/consumables/potions/bottle-conical-corked-labeled-shell-cyan.webp",
|
"img": "icons/consumables/potions/bottle-conical-corked-labeled-shell-cyan.webp",
|
||||||
"data": {
|
"data": {
|
||||||
"description": null,
|
"description": "<p>Weihwasser verursacht gegen Dämonen und Untote nicht abwehrbaren Schaden. Jede Einheit Weihwasser hat einen anderen Angriffswert, der mit W20 ermittelt wird. Dieser Wert wird erst ausgewürfelt, wenn das Weihwasser den Dämonen bzw. Untoten trifft, es sei denn, es wird vorher in Bezug auf seinen Schadenswert von einem Zauberwirker mit GEI+AU, gefolgt von GEI+VE, erfolgreich analysiert.</p>\n<p>Eine Weihwassereinheit kann man auf eine Waffe/ein Geschoss auftragen (benötigt 1 Aktion) und dann einen normalen Angriff mit Schlagen bzw. Schießen würfeln. Ist dieser erfolgreich, wird bei Dämonen und Untoten neben dem normalen Schaden auch noch ein Angriff für das Weihwasser gewürfelt, der nicht abwehrbaren Schaden verursacht. Nach dem ersten Treffer ist die Einheit Weihwasser aufgebraucht.</p>\n<p>Alternativ kann man Weihwassereinheiten in zerbrechliche Phiolen (WB +0; 2 GM) füllen und diese im Nah- oder Fernkampf gegen Dämonen und Untote einsetzen, wobei die zerbrechlichen Gefäße zerspringen. In solchen Fällen verursacht nur das Weihwasser Schaden, nicht die Schießen-Probe.</p>\n<p>Weihwasser kann außerdem dazu benutzt werden, in schützenden Linien oder Kreisen (1 m pro Einheit) auf den Boden geschüttet zu werden, um für eine gewisse Zeit Dämonen bzw. Untote aufzuhalten, die das Weihwasser nicht passieren können.</p>",
|
||||||
"quantity": 1,
|
"quantity": 1,
|
||||||
"price": 0.1,
|
"price": 0.1,
|
||||||
"availability": "hamlet",
|
"availability": "hamlet",
|
||||||
|
|
3660
packs/spells.json
3660
packs/spells.json
File diff suppressed because one or more lines are too long
|
@ -5,6 +5,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.ds4-actor-sheet {
|
.ds4-actor-sheet {
|
||||||
min-height: 625px;
|
min-height: 635px;
|
||||||
min-width: 650px;
|
min-width: 650px;
|
||||||
}
|
}
|
||||||
|
|
27
scss/components/shared/_checkbox_grid.scss
Normal file
27
scss/components/shared/_checkbox_grid.scss
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 Johannes Loher
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
.ds4-checkbox-grid {
|
||||||
|
$gap: 3px;
|
||||||
|
gap: $gap;
|
||||||
|
display: grid;
|
||||||
|
font-size: var(--font-size-12);
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
|
||||||
|
&__item {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
gap: $gap;
|
||||||
|
justify-content: flex-start;
|
||||||
|
> * {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__checkbox[type="checkbox"] {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
27
scss/components/shared/_form_group.scss
Normal file
27
scss/components/shared/_form_group.scss
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 Johannes Loher
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
.ds4-form-group {
|
||||||
|
clear: both;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin: 3px 0;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&--start {
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > * {
|
||||||
|
flex: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
flex: 2;
|
||||||
|
line-height: var(--form-field-height);
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,8 +14,10 @@
|
||||||
// shared
|
// shared
|
||||||
@use "components/shared/add_button";
|
@use "components/shared/add_button";
|
||||||
@use "components/shared/control_button_group";
|
@use "components/shared/control_button_group";
|
||||||
|
@use "components/shared/checkbox_grid";
|
||||||
@use "components/shared/editor";
|
@use "components/shared/editor";
|
||||||
@use "components/shared/embedded_document_list";
|
@use "components/shared/embedded_document_list";
|
||||||
|
@use "components/shared/form_group";
|
||||||
@use "components/shared/rollable_image";
|
@use "components/shared/rollable_image";
|
||||||
@use "components/shared/sheet_body";
|
@use "components/shared/sheet_body";
|
||||||
@use "components/shared/sheet_form";
|
@use "components/shared/sheet_form";
|
||||||
|
|
|
@ -12,8 +12,29 @@ const defaultData: DS4SpellDataSourceData = {
|
||||||
description: "",
|
description: "",
|
||||||
equipped: false,
|
equipped: false,
|
||||||
spellType: "spellcasting",
|
spellType: "spellcasting",
|
||||||
bonus: "",
|
spellModifier: {
|
||||||
spellCategory: "unset",
|
numerical: 0,
|
||||||
|
complex: "",
|
||||||
|
},
|
||||||
|
spellGroups: {
|
||||||
|
lightning: false,
|
||||||
|
earth: false,
|
||||||
|
water: false,
|
||||||
|
ice: false,
|
||||||
|
fire: false,
|
||||||
|
healing: false,
|
||||||
|
light: false,
|
||||||
|
air: false,
|
||||||
|
transport: false,
|
||||||
|
damage: false,
|
||||||
|
shadow: false,
|
||||||
|
protection: false,
|
||||||
|
mindAffecting: false,
|
||||||
|
demonology: false,
|
||||||
|
necromancy: false,
|
||||||
|
transmutation: false,
|
||||||
|
area: false,
|
||||||
|
},
|
||||||
maxDistance: {
|
maxDistance: {
|
||||||
value: "",
|
value: "",
|
||||||
unit: "meter",
|
unit: "meter",
|
||||||
|
|
|
@ -76,7 +76,6 @@ export class DS4ActiveEffect extends ActiveEffect {
|
||||||
try {
|
try {
|
||||||
change.value = DS4ActiveEffect.safeEval(change.value).toString();
|
change.value = DS4ActiveEffect.safeEval(change.value).toString();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.warn(e);
|
|
||||||
// this is a valid case, e.g., if the effect change simply is a string
|
// this is a valid case, e.g., if the effect change simply is a string
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
|
|
@ -25,7 +25,7 @@ export class DS4ActorSheet extends ActorSheet<ActorSheet.Options, DS4ActorSheetD
|
||||||
static override get defaultOptions(): ActorSheet.Options {
|
static override get defaultOptions(): ActorSheet.Options {
|
||||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||||
classes: ["sheet", "ds4-actor-sheet"],
|
classes: ["sheet", "ds4-actor-sheet"],
|
||||||
height: 625,
|
height: 635,
|
||||||
scrollY: [".ds4-sheet-body"],
|
scrollY: [".ds4-sheet-body"],
|
||||||
tabs: [{ navSelector: ".ds4-sheet-tab-nav", contentSelector: ".ds4-sheet-body", initial: "values" }],
|
tabs: [{ navSelector: ".ds4-sheet-tab-nav", contentSelector: ".ds4-sheet-body", initial: "values" }],
|
||||||
dragDrop: [
|
dragDrop: [
|
||||||
|
@ -111,7 +111,14 @@ export class DS4ActorSheet extends ActorSheet<ActorSheet.Options, DS4ActorSheetD
|
||||||
|
|
||||||
html.find(".sort-items").on("click", this.onSortItems.bind(this));
|
html.find(".sort-items").on("click", this.onSortItems.bind(this));
|
||||||
|
|
||||||
disableOverriddenFields.call(this);
|
disableOverriddenFields(this.form, this.actor.overrides, (key) => `[name="${key}"]`);
|
||||||
|
for (const item of this.actor.items) {
|
||||||
|
disableOverriddenFields(
|
||||||
|
this.form,
|
||||||
|
item.overrides,
|
||||||
|
(key) => `[data-item-id="${item.id}"] .change-item[data-property="${key}"]`,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -359,6 +366,7 @@ export class DS4ActorSheet extends ActorSheet<ActorSheet.Options, DS4ActorSheetD
|
||||||
enforce(type !== undefined, `Could not find property 'type' in the dataset of the parent of ${target}`);
|
enforce(type !== undefined, `Could not find property 'type' in the dataset of the parent of ${target}`);
|
||||||
const dataPath = target.dataset["dataPath"];
|
const dataPath = target.dataset["dataPath"];
|
||||||
enforce(dataPath !== undefined, `Could not find property 'dataPath' in the dataset of ${target}`);
|
enforce(dataPath !== undefined, `Could not find property 'dataPath' in the dataset of ${target}`);
|
||||||
|
const dataPath2 = target.dataset["dataPath2"];
|
||||||
const items = this.actor.items.filter((item) => item.type === type);
|
const items = this.actor.items.filter((item) => item.type === type);
|
||||||
items.sort((a, b) => a.data.sort - b.data.sort);
|
items.sort((a, b) => a.data.sort - b.data.sort);
|
||||||
|
|
||||||
|
@ -367,13 +375,20 @@ export class DS4ActorSheet extends ActorSheet<ActorSheet.Options, DS4ActorSheetD
|
||||||
(a: DS4Item, b: DS4Item): number => {
|
(a: DS4Item, b: DS4Item): number => {
|
||||||
const propertyA = getProperty(a.data, dataPath);
|
const propertyA = getProperty(a.data, dataPath);
|
||||||
const propertyB = getProperty(b.data, dataPath);
|
const propertyB = getProperty(b.data, dataPath);
|
||||||
if (typeof propertyA === "string" || typeof propertyB === "string") {
|
const comparison =
|
||||||
return invert
|
typeof propertyA === "string" || typeof propertyB === "string"
|
||||||
? (propertyB ?? "").localeCompare(propertyA ?? "")
|
? compareAsStrings(propertyA, propertyB, invert)
|
||||||
: (propertyA ?? "").localeCompare(propertyB ?? "");
|
: compareAsNumbers(propertyA, propertyB, invert);
|
||||||
} else {
|
|
||||||
return invert ? propertyB - propertyA : propertyA - propertyB;
|
if (comparison === 0 && dataPath2 !== undefined) {
|
||||||
|
const propertyA = getProperty(a.data, dataPath);
|
||||||
|
const propertyB = getProperty(b.data, dataPath);
|
||||||
|
return typeof propertyA === "string" || typeof propertyB === "string"
|
||||||
|
? compareAsStrings(propertyA, propertyB, invert)
|
||||||
|
: compareAsNumbers(propertyA, propertyB, invert);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return comparison;
|
||||||
};
|
};
|
||||||
|
|
||||||
const sortedItems = [...items].sort(sortFunction(false));
|
const sortedItems = [...items].sort(sortFunction(false));
|
||||||
|
@ -434,3 +449,11 @@ const embeddedDocumentListEntryProperties = Object.freeze({
|
||||||
idDataAttribute: "itemId",
|
idDataAttribute: "itemId",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const compareAsStrings = (a: { toString(): string }, b: { toString(): string }, invert: boolean): number => {
|
||||||
|
return invert ? b.toString().localeCompare(a.toString()) : a.toString().localeCompare(b.toString());
|
||||||
|
};
|
||||||
|
|
||||||
|
const compareAsNumbers = (a: number, b: number, invert: boolean): number => {
|
||||||
|
return invert ? b - a : a - b;
|
||||||
|
};
|
||||||
|
|
|
@ -79,12 +79,12 @@ export class DS4Actor extends Actor {
|
||||||
|
|
||||||
if (condition !== undefined && condition !== "") {
|
if (condition !== undefined && condition !== "") {
|
||||||
try {
|
try {
|
||||||
const replacedCondition = Roll.replaceFormulaData(condition, {
|
const replacedCondition = DS4Actor.replaceFormulaData(condition, {
|
||||||
item: item.data,
|
item: item.data,
|
||||||
actor: this.data,
|
actor: this.data,
|
||||||
effect: effect.data,
|
effect: effect.data,
|
||||||
});
|
});
|
||||||
return Boolean(mathEvaluator.evaluate(replacedCondition));
|
return replacedCondition !== undefined ? Boolean(mathEvaluator.evaluate(replacedCondition)) : false;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.warn(error);
|
logger.warn(error);
|
||||||
return false;
|
return false;
|
||||||
|
@ -95,6 +95,21 @@ export class DS4Actor extends Actor {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static replaceFormulaData(formula: string, data: object): string | undefined {
|
||||||
|
const dataRgx = new RegExp(/@([a-z.0-9_\-]+)/gi);
|
||||||
|
try {
|
||||||
|
return formula.replace(dataRgx, (_, term) => {
|
||||||
|
const value = foundry.utils.getProperty(data, term);
|
||||||
|
if (value == null) {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
return String(value).trim();
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We override this with an empty implementation because we have our own custom way of applying
|
* 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.
|
* {@link ActiveEffect}s and {@link Actor#prepareEmbeddedDocuments} calls this.
|
||||||
|
|
|
@ -4,15 +4,16 @@
|
||||||
|
|
||||||
import { getGame } from "../helpers";
|
import { getGame } from "../helpers";
|
||||||
|
|
||||||
export function disableOverriddenFields<
|
export function disableOverriddenFields(
|
||||||
Options extends FormApplicationOptions,
|
form: HTMLElement | null,
|
||||||
Data extends object,
|
overrides: Record<string, unknown>,
|
||||||
ConcreteObject extends { overrides: Record<string, unknown> },
|
selector: (key: string) => string,
|
||||||
>(this: FormApplication<Options, Data, ConcreteObject>): void {
|
): void {
|
||||||
const inputs = ["INPUT", "SELECT", "TEXTAREA", "BUTTON"];
|
const inputs = ["INPUT", "SELECT", "TEXTAREA", "BUTTON"];
|
||||||
const titleAddition = `(${getGame().i18n.localize("DS4.TooltipDisabledDueToEffects")})`;
|
const titleAddition = `(${getGame().i18n.localize("DS4.TooltipNotEditableDueToEffects")})`;
|
||||||
for (const key of Object.keys(foundry.utils.flattenObject(this.object.overrides))) {
|
|
||||||
const elements = this.form?.querySelectorAll(`[name="${key}"]`);
|
for (const key of Object.keys(foundry.utils.flattenObject(overrides))) {
|
||||||
|
const elements = form?.querySelectorAll(selector(key));
|
||||||
elements?.forEach((element) => {
|
elements?.forEach((element) => {
|
||||||
if (inputs.includes(element.tagName)) {
|
if (inputs.includes(element.tagName)) {
|
||||||
element.setAttribute("disabled", "");
|
element.setAttribute("disabled", "");
|
||||||
|
|
|
@ -94,16 +94,24 @@ const i18nKeys = {
|
||||||
targetedSpellcasting: "DS4.SpellTypeTargetedSpellcasting",
|
targetedSpellcasting: "DS4.SpellTypeTargetedSpellcasting",
|
||||||
},
|
},
|
||||||
|
|
||||||
spellCategories: {
|
spellGroups: {
|
||||||
healing: "DS4.SpellCategoryHealing",
|
lightning: "DS4.SpellGroupLightning",
|
||||||
fire: "DS4.SpellCategoryFire",
|
earth: "DS4.SpellGroupEarth",
|
||||||
ice: "DS4.SpellCategoryIce",
|
water: "DS4.SpellGroupWater",
|
||||||
light: "DS4.SpellCategoryLight",
|
ice: "DS4.SpellGroupIce",
|
||||||
darkness: "DS4.SpellCategoryDarkness",
|
fire: "DS4.SpellGroupFire",
|
||||||
mindAffecting: "DS4.SpellCategoryMindAffecting",
|
healing: "DS4.SpellGroupHealing",
|
||||||
electricity: "DS4.SpellCategoryElectricity",
|
light: "DS4.SpellGroupLight",
|
||||||
none: "DS4.SpellCategoryNone",
|
air: "DS4.SpellGroupAir",
|
||||||
unset: "DS4.SpellCategoryUnset",
|
transport: "DS4.SpellGroupTransport",
|
||||||
|
damage: "DS4.SpellGroupDamage",
|
||||||
|
shadow: "DS4.SpellGroupShadow",
|
||||||
|
protection: "DS4.SpellGroupProtection",
|
||||||
|
mindAffecting: "DS4.SpellGroupMindAffecting",
|
||||||
|
demonology: "DS4.SpellGroupDemonology",
|
||||||
|
necromancy: "DS4.SpellGroupNecromancy",
|
||||||
|
transmutation: "DS4.SpellGroupTransmutation",
|
||||||
|
area: "DS4.SpellGroupArea",
|
||||||
},
|
},
|
||||||
|
|
||||||
cooldownDurations: {
|
cooldownDurations: {
|
||||||
|
|
|
@ -23,7 +23,7 @@ function localizeAndSortConfigObjects() {
|
||||||
"combatValues",
|
"combatValues",
|
||||||
"cooldownDurations",
|
"cooldownDurations",
|
||||||
"creatureSizeCategories",
|
"creatureSizeCategories",
|
||||||
"spellCategories",
|
"spellGroups",
|
||||||
"traits",
|
"traits",
|
||||||
"checkModifiers",
|
"checkModifiers",
|
||||||
];
|
];
|
||||||
|
|
|
@ -72,7 +72,7 @@ export class DS4ItemSheet extends ItemSheet<ItemSheet.Options, DS4ItemSheetData>
|
||||||
|
|
||||||
html.find(".control-effect").on("click", this.onControlEffect.bind(this));
|
html.find(".control-effect").on("click", this.onControlEffect.bind(this));
|
||||||
|
|
||||||
disableOverriddenFields.call(this);
|
disableOverriddenFields(this.form, this.item.overrides, (key) => `[name="${key}"]`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -12,8 +12,11 @@ export interface DS4SpellDataSource {
|
||||||
|
|
||||||
export interface DS4SpellDataSourceData extends DS4ItemDataSourceDataBase, DS4ItemDataSourceDataEquipable {
|
export interface DS4SpellDataSourceData extends DS4ItemDataSourceDataBase, DS4ItemDataSourceDataEquipable {
|
||||||
spellType: keyof typeof DS4.i18n.spellTypes;
|
spellType: keyof typeof DS4.i18n.spellTypes;
|
||||||
bonus: string;
|
spellModifier: {
|
||||||
spellCategory: keyof typeof DS4.i18n.spellCategories;
|
numerical: number;
|
||||||
|
complex: string;
|
||||||
|
};
|
||||||
|
spellGroups: Record<keyof typeof DS4.i18n.spellGroups, boolean>;
|
||||||
maxDistance: UnitData<DistanceUnit>;
|
maxDistance: UnitData<DistanceUnit>;
|
||||||
effectRadius: UnitData<DistanceUnit>;
|
effectRadius: UnitData<DistanceUnit>;
|
||||||
duration: UnitData<TemporalUnit>;
|
duration: UnitData<TemporalUnit>;
|
||||||
|
|
|
@ -32,17 +32,19 @@ export class DS4Spell extends DS4Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
const ownerDataData = this.actor.data.data;
|
const ownerDataData = this.actor.data.data;
|
||||||
const spellModifier = Number.isNumeric(this.data.data.bonus) ? parseInt(this.data.data.bonus) : undefined;
|
const hasComplexModifier = this.data.data.spellModifier.complex !== "";
|
||||||
if (spellModifier === undefined) {
|
if (hasComplexModifier === undefined) {
|
||||||
notifications.info(
|
notifications.info(
|
||||||
game.i18n.format("DS4.InfoManuallyEnterSpellModifier", {
|
game.i18n.format("DS4.InfoManuallyEnterSpellModifier", {
|
||||||
name: this.name,
|
name: this.name,
|
||||||
spellModifier: this.data.data.bonus,
|
spellModifier: this.data.data.spellModifier.complex,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const spellType = this.data.data.spellType;
|
const spellType = this.data.data.spellType;
|
||||||
const checkTargetNumber = ownerDataData.combatValues[spellType].total + (spellModifier ?? 0);
|
const checkTargetNumber =
|
||||||
|
ownerDataData.combatValues[spellType].total +
|
||||||
|
(hasComplexModifier ? 0 : this.data.data.spellModifier.numerical);
|
||||||
|
|
||||||
const speaker = ChatMessage.getSpeaker({ actor: this.actor, ...options.speaker });
|
const speaker = ChatMessage.getSpeaker({ actor: this.actor, ...options.speaker });
|
||||||
await createCheckRoll(checkTargetNumber, {
|
await createCheckRoll(checkTargetNumber, {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { migration as migration002 } from "./migrations/002";
|
||||||
import { migration as migration003 } from "./migrations/003";
|
import { migration as migration003 } from "./migrations/003";
|
||||||
import { migration as migration004 } from "./migrations/004";
|
import { migration as migration004 } from "./migrations/004";
|
||||||
import { migration as migration005 } from "./migrations/005";
|
import { migration as migration005 } from "./migrations/005";
|
||||||
|
import { migration as migration006 } from "./migrations/006";
|
||||||
import notifications from "./ui/notifications";
|
import notifications from "./ui/notifications";
|
||||||
|
|
||||||
async function migrate(): Promise<void> {
|
async function migrate(): Promise<void> {
|
||||||
|
@ -135,7 +136,7 @@ interface Migration {
|
||||||
migrateCompendium: (pack: CompendiumCollection<CompendiumCollection.Metadata>) => Promise<void>;
|
migrateCompendium: (pack: CompendiumCollection<CompendiumCollection.Metadata>) => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const migrations: Migration[] = [migration001, migration002, migration003, migration004, migration005];
|
const migrations: Migration[] = [migration001, migration002, migration003, migration004, migration005, migration006];
|
||||||
|
|
||||||
function isFirstWorldStart(migrationVersion: number): boolean {
|
function isFirstWorldStart(migrationVersion: number): boolean {
|
||||||
return migrationVersion < 0;
|
return migrationVersion < 0;
|
||||||
|
|
117
src/migrations/006.ts
Normal file
117
src/migrations/006.ts
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Johannes Loher
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import {
|
||||||
|
getActorUpdateDataGetter,
|
||||||
|
getCompendiumMigrator,
|
||||||
|
getSceneUpdateDataGetter,
|
||||||
|
migrateActors,
|
||||||
|
migrateCompendiums,
|
||||||
|
migrateItems,
|
||||||
|
migrateScenes,
|
||||||
|
} from "./migrationHelpers";
|
||||||
|
|
||||||
|
import type { DS4SpellDataSourceData } from "../item/spell/spell-data-source";
|
||||||
|
|
||||||
|
async function migrate(): Promise<void> {
|
||||||
|
await migrateItems(getItemUpdateData);
|
||||||
|
await migrateActors(getActorUpdateData);
|
||||||
|
await migrateScenes(getSceneUpdateData);
|
||||||
|
await migrateCompendiums(migrateCompendium);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getItemUpdateData(itemData: Partial<foundry.data.ItemData["_source"]>) {
|
||||||
|
if (itemData.type !== "spell") return;
|
||||||
|
// @ts-expect-error spellCategory is removed with this migration
|
||||||
|
const spellCategory: string | undefined = itemData.data?.spellCategory;
|
||||||
|
const spellGroups = migrateSpellCategory(spellCategory);
|
||||||
|
|
||||||
|
// @ts-expect-error bonus is removed with this migration
|
||||||
|
const bonus: string | undefined = itemData.data?.bonus;
|
||||||
|
const spellModifier = migrateBonus(bonus);
|
||||||
|
|
||||||
|
const updateData: Record<string, unknown> = {
|
||||||
|
data: {
|
||||||
|
spellGroups,
|
||||||
|
"-=spellCategory": null,
|
||||||
|
spellModifier,
|
||||||
|
"-=bonus": null,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return updateData;
|
||||||
|
}
|
||||||
|
|
||||||
|
function migrateSpellCategory(spellCategory: string | undefined): DS4SpellDataSourceData["spellGroups"] {
|
||||||
|
const spellGroups = {
|
||||||
|
lightning: false,
|
||||||
|
earth: false,
|
||||||
|
water: false,
|
||||||
|
ice: false,
|
||||||
|
fire: false,
|
||||||
|
healing: false,
|
||||||
|
light: false,
|
||||||
|
air: false,
|
||||||
|
transport: false,
|
||||||
|
damage: false,
|
||||||
|
shadow: false,
|
||||||
|
protection: false,
|
||||||
|
mindAffecting: false,
|
||||||
|
demonology: false,
|
||||||
|
necromancy: false,
|
||||||
|
transmutation: false,
|
||||||
|
area: false,
|
||||||
|
};
|
||||||
|
switch (spellCategory) {
|
||||||
|
case "healing": {
|
||||||
|
spellGroups.healing = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "fire": {
|
||||||
|
spellGroups.fire = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "ice": {
|
||||||
|
spellGroups.ice = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "light": {
|
||||||
|
spellGroups.light = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "darkness": {
|
||||||
|
spellGroups.shadow = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "mindAffecting": {
|
||||||
|
spellGroups.mindAffecting = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "electricity": {
|
||||||
|
spellGroups.lightning = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return spellGroups;
|
||||||
|
}
|
||||||
|
|
||||||
|
function migrateBonus(bonus: string | undefined): DS4SpellDataSourceData["spellModifier"] {
|
||||||
|
const spellModifier = { numerical: 0, complex: "" };
|
||||||
|
if (bonus) {
|
||||||
|
if (Number.isNumeric(bonus)) {
|
||||||
|
spellModifier.numerical = +bonus;
|
||||||
|
} else {
|
||||||
|
spellModifier.complex = bonus;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return spellModifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
const getActorUpdateData = getActorUpdateDataGetter(getItemUpdateData);
|
||||||
|
const getSceneUpdateData = getSceneUpdateDataGetter(getActorUpdateData);
|
||||||
|
const migrateCompendium = getCompendiumMigrator({ getItemUpdateData, getActorUpdateData, getSceneUpdateData });
|
||||||
|
|
||||||
|
export const migration = {
|
||||||
|
migrate,
|
||||||
|
migrateCompendium,
|
||||||
|
};
|
|
@ -72,7 +72,7 @@ type CompendiumMigrator = (compendium: CompendiumCollection<CompendiumCollection
|
||||||
export async function migrateCompendiums(migrateCompendium: CompendiumMigrator): Promise<void> {
|
export async function migrateCompendiums(migrateCompendium: CompendiumMigrator): Promise<void> {
|
||||||
for (const compendium of getGame().packs ?? []) {
|
for (const compendium of getGame().packs ?? []) {
|
||||||
if (compendium.metadata.package !== "world") continue;
|
if (compendium.metadata.package !== "world") continue;
|
||||||
if (!["Actor", "Item", "Scene"].includes(compendium.metadata.entity)) continue;
|
if (!["Actor", "Item", "Scene"].includes(compendium.metadata.type)) continue;
|
||||||
await migrateCompendium(compendium);
|
await migrateCompendium(compendium);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -144,8 +144,8 @@ export function getCompendiumMigrator(
|
||||||
{ migrateToTemplateEarly = true } = {},
|
{ migrateToTemplateEarly = true } = {},
|
||||||
) {
|
) {
|
||||||
return async (compendium: CompendiumCollection<CompendiumCollection.Metadata>): Promise<void> => {
|
return async (compendium: CompendiumCollection<CompendiumCollection.Metadata>): Promise<void> => {
|
||||||
const entityName = compendium.metadata.entity;
|
const type = compendium.metadata.type;
|
||||||
if (!["Actor", "Item", "Scene"].includes(entityName)) return;
|
if (!["Actor", "Item", "Scene"].includes(type)) return;
|
||||||
const wasLocked = compendium.locked;
|
const wasLocked = compendium.locked;
|
||||||
await compendium.configure({ locked: false });
|
await compendium.configure({ locked: false });
|
||||||
if (migrateToTemplateEarly) {
|
if (migrateToTemplateEarly) {
|
||||||
|
|
|
@ -173,8 +173,29 @@
|
||||||
"spell": {
|
"spell": {
|
||||||
"templates": ["base", "equipable"],
|
"templates": ["base", "equipable"],
|
||||||
"spellType": "spellcasting",
|
"spellType": "spellcasting",
|
||||||
"bonus": "",
|
"spellModifier": {
|
||||||
"spellCategory": "unset",
|
"numerical": 0,
|
||||||
|
"complex": ""
|
||||||
|
},
|
||||||
|
"spellGroups": {
|
||||||
|
"lightning": false,
|
||||||
|
"earth": false,
|
||||||
|
"water": false,
|
||||||
|
"ice": false,
|
||||||
|
"fire": false,
|
||||||
|
"healing": false,
|
||||||
|
"light": false,
|
||||||
|
"air": false,
|
||||||
|
"transport": false,
|
||||||
|
"damage": false,
|
||||||
|
"shadow": false,
|
||||||
|
"protection": false,
|
||||||
|
"mindAffecting": false,
|
||||||
|
"demonology": false,
|
||||||
|
"necromancy": false,
|
||||||
|
"transmutation": false,
|
||||||
|
"area": false
|
||||||
|
},
|
||||||
"maxDistance": {
|
"maxDistance": {
|
||||||
"value": "",
|
"value": "",
|
||||||
"unit": "meter"
|
"unit": "meter"
|
||||||
|
|
|
@ -53,9 +53,10 @@ titleKey=titleKey}}
|
||||||
<div class="ds4-embedded-document-list__clickable sort-items" data-data-path="data.spellType"
|
<div class="ds4-embedded-document-list__clickable sort-items" data-data-path="data.spellType"
|
||||||
title="{{localize 'DS4.SortBySpellType'}}">{{localize 'DS4.SpellTypeAbbr'}}</div>
|
title="{{localize 'DS4.SortBySpellType'}}">{{localize 'DS4.SpellTypeAbbr'}}</div>
|
||||||
|
|
||||||
{{!-- spell bonus --}}
|
{{!-- spell modifier --}}
|
||||||
<div class="ds4-embedded-document-list__clickable sort-items" data-data-path="data.bonus"
|
<div class="ds4-embedded-document-list__clickable sort-items" data-data-path="data.spellModifier.complex"
|
||||||
title="{{localize 'DS4.SortBySpellModifier'}}">{{localize 'DS4.SpellModifierAbbr'}}</div>
|
data-data-path2="data.spellModifier.numerical" title="{{localize 'DS4.SortBySpellModifier'}}">{{localize
|
||||||
|
'DS4.SpellModifierAbbr'}}</div>
|
||||||
|
|
||||||
{{!-- max. distance --}}
|
{{!-- max. distance --}}
|
||||||
<div title="{{localize 'DS4.SpellDistance'}}"><i class="fas fa-ruler"></i></div>
|
<div title="{{localize 'DS4.SpellDistance'}}"><i class="fas fa-ruler"></i></div>
|
||||||
|
@ -75,9 +76,9 @@ titleKey=titleKey}}
|
||||||
src="{{lookup @root/config.icons.spellTypes itemData.data.spellType}}"
|
src="{{lookup @root/config.icons.spellTypes itemData.data.spellType}}"
|
||||||
title="{{lookup @root/config.i18n.spellTypes itemData.data.spellType}}" />
|
title="{{lookup @root/config.i18n.spellTypes itemData.data.spellType}}" />
|
||||||
|
|
||||||
{{!-- spell bonus --}}
|
{{!-- spell modifier --}}
|
||||||
<input class="ds4-embedded-document-list__editable change-item" type="text" data-dtype="String"
|
<div title="{{localize 'DS4.SpellModifier'}}">{{#if (eq itemData.data.spellModifier.complex
|
||||||
data-property="data.bonus" value="{{itemData.data.bonus}}" title="{{localize 'DS4.SpellModifier'}}" />
|
'')}}{{itemData.data.spellModifier.numerical}}{{else}}{{itemData.data.spellModifier.complex}}{{/if}}</div>
|
||||||
|
|
||||||
{{!-- max. distance --}}
|
{{!-- max. distance --}}
|
||||||
{{> distanceUnit titleKey='DS4.SpellDistance' unitDatum=itemData.data.maxDistance
|
{{> distanceUnit titleKey='DS4.SpellDistance' unitDatum=itemData.data.maxDistance
|
||||||
|
|
|
@ -7,11 +7,21 @@ SPDX-License-Identifier: MIT
|
||||||
<div class="ds4-item-properties ds4-item-properties--spell">
|
<div class="ds4-item-properties ds4-item-properties--spell">
|
||||||
<h4 class="ds4-item-properties__title">{{localize 'DS4.ItemPropertiesSpell'}}</h4>
|
<h4 class="ds4-item-properties__title">{{localize 'DS4.ItemPropertiesSpell'}}</h4>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="data.bonus-{{data._id}}" title="{{localize 'DS4.SpellModifierDescription'}}">{{localize
|
<label for="data.spellModifier.numerical-{{data._id}}"
|
||||||
"DS4.SpellModifier"}}</label>
|
title="{{localize 'DS4.SpellModifierNumericalDescription'}}">{{localize
|
||||||
|
"DS4.SpellModifierNumerical"}}</label>
|
||||||
<div class="form-fields">
|
<div class="form-fields">
|
||||||
<input id="data.bonus-{{data._id}}" data-dtype="String" type="text" name="data.bonus" placeholder="0"
|
<input id="data.spellModifier.numerical-{{data._id}}" data-dtype="Number" type="number"
|
||||||
value="{{data.data.bonus}}" />
|
name="data.spellModifier.numerical" placeholder="0" value="{{data.data.spellModifier.numerical}}" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="data.spellModifier.complex-{{data._id}}"
|
||||||
|
title="{{localize 'DS4.SpellModifierComplexDescription'}}">{{localize
|
||||||
|
"DS4.SpellModifierComplex"}}</label>
|
||||||
|
<div class="form-fields">
|
||||||
|
<input id="data.spellModifier.complex-{{data._id}}" data-dtype="String" type="text"
|
||||||
|
name="data.spellModifier.complex" value="{{data.data.spellModifier.complex}}" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
@ -27,19 +37,6 @@ SPDX-License-Identifier: MIT
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
|
||||||
<label for="data.spellCategory-{{data._id}}" title="{{localize 'DS4.SpellCategoryDescription'}}">{{localize
|
|
||||||
"DS4.SpellCategory"}}</label>
|
|
||||||
<div class="form-fields">
|
|
||||||
<select id="data.spellCategory-{{data._id}}" name="data.spellCategory" data-dtype="String">
|
|
||||||
{{#select data.data.spellCategory}}
|
|
||||||
{{#each config.i18n.spellCategories as |value key|}}
|
|
||||||
<option value="{{key}}">{{value}}</option>
|
|
||||||
{{/each}}
|
|
||||||
{{/select}}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group slim">
|
<div class="form-group slim">
|
||||||
<label title="{{localize 'DS4.SpellDistanceDescription'}}">{{localize "DS4.SpellDistance"}}</label>
|
<label title="{{localize 'DS4.SpellDistanceDescription'}}">{{localize "DS4.SpellDistance"}}</label>
|
||||||
<div class="form-fields">
|
<div class="form-fields">
|
||||||
|
@ -94,18 +91,33 @@ SPDX-License-Identifier: MIT
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="ds4-form-group ds4-form-group--start">
|
||||||
|
<label class="ds4-form-group__label" title="{{localize 'DS4.SpellGroupsDescription'}}">{{localize
|
||||||
|
"DS4.SpellGroups"}}</label>
|
||||||
|
<div class="ds4-checkbox-grid">
|
||||||
|
{{#each config.i18n.spellGroups as |value key|}}
|
||||||
|
<div class="ds4-checkbox-grid__item">
|
||||||
|
<input class="ds4-checkbox-grid__checkbox" id="data.spellGroups.{{key}}-{{../data._id}}"
|
||||||
|
name="data.spellGroups.{{key}}" data-dtype="Boolean" type="checkbox" {{checked (lookup
|
||||||
|
../data.data.spellGroups key)}} />
|
||||||
|
<label for="data.spellGroups.{{key}}-{{../data._id}}">{{value}}</label>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="form-group slim">
|
<div class="form-group slim">
|
||||||
<label title="{{localize 'DS4.SpellMinimumLevelDescription'}}">{{localize "DS4.SpellMinimumLevel"}}</label>
|
<label title="{{localize 'DS4.SpellMinimumLevelDescription'}}">{{localize "DS4.SpellMinimumLevel"}}</label>
|
||||||
<div class="form-fields">
|
<div class="form-fields">
|
||||||
<label for="data.minimumLevels.healer-{{data._id}}">{{localize "DS4.SpellCasterClassHealer"}}</label>
|
<label for="data.minimumLevels.healer-{{data._id}}">{{localize "DS4.SpellCasterClassHealer"}}</label>
|
||||||
<input id="data.minimumLevels.healer-{{data._id}}" data-dtype="Number" type="number" min="0" step="1"
|
<input id="data.minimumLevels.healer-{{data._id}}" data-dtype="Number" type="number" min="0" step="1"
|
||||||
name="data.minimumLevels.healer" placeholder="–" value="{{data.data.minimumLevels.healer}}" />
|
name="data.minimumLevels.healer" placeholder="–" value="{{data.data.minimumLevels.healer}}" />
|
||||||
<label for="data.minimumLevels.sorcerer-{{data._id}}">{{localize "DS4.SpellCasterClassSorcerer"}}</label>
|
|
||||||
<input id="data.minimumLevels.sorcerer-{{data._id}}" data-dtype="Number" type="number" min="0" step="1"
|
|
||||||
name="data.minimumLevels.sorcerer" placeholder="–" value="{{data.data.minimumLevels.sorcerer}}" />
|
|
||||||
<label for="data.minimumLevels.wizard-{{data._id}}">{{localize "DS4.SpellCasterClassWizard"}}</label>
|
<label for="data.minimumLevels.wizard-{{data._id}}">{{localize "DS4.SpellCasterClassWizard"}}</label>
|
||||||
<input id="data.minimumLevels.wizard-{{data._id}}" data-dtype="Number" type="number" min="0" step="1"
|
<input id="data.minimumLevels.wizard-{{data._id}}" data-dtype="Number" type="number" min="0" step="1"
|
||||||
name="data.minimumLevels.wizard" placeholder="–" value="{{data.data.minimumLevels.wizard}}" />
|
name="data.minimumLevels.wizard" placeholder="–" value="{{data.data.minimumLevels.wizard}}" />
|
||||||
|
<label for="data.minimumLevels.sorcerer-{{data._id}}">{{localize
|
||||||
|
"DS4.SpellCasterClassSorcerer"}}</label>
|
||||||
|
<input id="data.minimumLevels.sorcerer-{{data._id}}" data-dtype="Number" type="number" min="0" step="1"
|
||||||
|
name="data.minimumLevels.sorcerer" placeholder="–" value="{{data.data.minimumLevels.sorcerer}}" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|
|
@ -57,7 +57,7 @@ export async function convertJSONToPack(contents) {
|
||||||
/**
|
/**
|
||||||
* Converts a pack file (NeDB) to a JSON string.
|
* Converts a pack file (NeDB) to a JSON string.
|
||||||
* @param {string} filename The name of the pack file
|
* @param {string} filename The name of the pack file
|
||||||
* @returns {Promise<Array<unknown>>} A promise that resolves to an array of the documents in the pack file
|
* @returns {Promise<string>} A promise that resolves to an array of the documents in the pack file
|
||||||
*/
|
*/
|
||||||
function convertPackFileToJSON(filename) {
|
function convertPackFileToJSON(filename) {
|
||||||
const db = new Datastore({ filename, autoload: true });
|
const db = new Datastore({ filename, autoload: true });
|
||||||
|
|
Loading…
Reference in a new issue