feat: replace spell category by spell groups
This also allows to assign a spell to multiple spell groups, which is the case for many spells in the SRD. Additionally, this makes many small improvements and fixes to the provided spell compendium.
This commit is contained in:
parent
ab31450dd8
commit
9d7c570553
20 changed files with 3615 additions and 466 deletions
|
@ -13,8 +13,7 @@ export function disableOverriddenFields(
|
|||
const titleAddition = `(${getGame().i18n.localize("DS4.TooltipNotEditableDueToEffects")})`;
|
||||
|
||||
for (const key of Object.keys(foundry.utils.flattenObject(overrides))) {
|
||||
const sel = selector(key);
|
||||
const elements = form?.querySelectorAll(sel);
|
||||
const elements = form?.querySelectorAll(selector(key));
|
||||
elements?.forEach((element) => {
|
||||
if (inputs.includes(element.tagName)) {
|
||||
element.setAttribute("disabled", "");
|
||||
|
|
|
@ -94,16 +94,24 @@ const i18nKeys = {
|
|||
targetedSpellcasting: "DS4.SpellTypeTargetedSpellcasting",
|
||||
},
|
||||
|
||||
spellCategories: {
|
||||
healing: "DS4.SpellCategoryHealing",
|
||||
fire: "DS4.SpellCategoryFire",
|
||||
ice: "DS4.SpellCategoryIce",
|
||||
light: "DS4.SpellCategoryLight",
|
||||
darkness: "DS4.SpellCategoryDarkness",
|
||||
mindAffecting: "DS4.SpellCategoryMindAffecting",
|
||||
electricity: "DS4.SpellCategoryElectricity",
|
||||
none: "DS4.SpellCategoryNone",
|
||||
unset: "DS4.SpellCategoryUnset",
|
||||
spellGroups: {
|
||||
lightning: "DS4.SpellGroupLightning",
|
||||
earth: "DS4.SpellGroupEarth",
|
||||
water: "DS4.SpellGroupWater",
|
||||
ice: "DS4.SpellGroupIce",
|
||||
fire: "DS4.SpellGroupFire",
|
||||
healing: "DS4.SpellGroupHealing",
|
||||
light: "DS4.SpellGroupLight",
|
||||
air: "DS4.SpellGroupAir",
|
||||
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: {
|
||||
|
|
|
@ -23,7 +23,7 @@ function localizeAndSortConfigObjects() {
|
|||
"combatValues",
|
||||
"cooldownDurations",
|
||||
"creatureSizeCategories",
|
||||
"spellCategories",
|
||||
"spellGroups",
|
||||
"traits",
|
||||
"checkModifiers",
|
||||
];
|
||||
|
|
|
@ -12,8 +12,11 @@ export interface DS4SpellDataSource {
|
|||
|
||||
export interface DS4SpellDataSourceData extends DS4ItemDataSourceDataBase, DS4ItemDataSourceDataEquipable {
|
||||
spellType: keyof typeof DS4.i18n.spellTypes;
|
||||
bonus: string;
|
||||
spellCategory: keyof typeof DS4.i18n.spellCategories;
|
||||
spellModifier: {
|
||||
numerical: number;
|
||||
complex: string;
|
||||
};
|
||||
spellGroups: Record<keyof typeof DS4.i18n.spellGroups, boolean>;
|
||||
maxDistance: UnitData<DistanceUnit>;
|
||||
effectRadius: UnitData<DistanceUnit>;
|
||||
duration: UnitData<TemporalUnit>;
|
||||
|
|
|
@ -32,17 +32,19 @@ export class DS4Spell extends DS4Item {
|
|||
}
|
||||
|
||||
const ownerDataData = this.actor.data.data;
|
||||
const spellModifier = Number.isNumeric(this.data.data.bonus) ? parseInt(this.data.data.bonus) : undefined;
|
||||
if (spellModifier === undefined) {
|
||||
const hasComplexModifier = this.data.data.spellModifier.complex !== "";
|
||||
if (hasComplexModifier === undefined) {
|
||||
notifications.info(
|
||||
game.i18n.format("DS4.InfoManuallyEnterSpellModifier", {
|
||||
name: this.name,
|
||||
spellModifier: this.data.data.bonus,
|
||||
spellModifier: this.data.data.spellModifier.complex,
|
||||
}),
|
||||
);
|
||||
}
|
||||
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 });
|
||||
await createCheckRoll(checkTargetNumber, {
|
||||
|
|
|
@ -9,6 +9,7 @@ import { migration as migration002 } from "./migrations/002";
|
|||
import { migration as migration003 } from "./migrations/003";
|
||||
import { migration as migration004 } from "./migrations/004";
|
||||
import { migration as migration005 } from "./migrations/005";
|
||||
import { migration as migration006 } from "./migrations/006";
|
||||
import notifications from "./ui/notifications";
|
||||
|
||||
async function migrate(): Promise<void> {
|
||||
|
@ -135,7 +136,7 @@ interface Migration {
|
|||
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 {
|
||||
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> {
|
||||
for (const compendium of getGame().packs ?? []) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -144,8 +144,8 @@ export function getCompendiumMigrator(
|
|||
{ migrateToTemplateEarly = true } = {},
|
||||
) {
|
||||
return async (compendium: CompendiumCollection<CompendiumCollection.Metadata>): Promise<void> => {
|
||||
const entityName = compendium.metadata.entity;
|
||||
if (!["Actor", "Item", "Scene"].includes(entityName)) return;
|
||||
const type = compendium.metadata.type;
|
||||
if (!["Actor", "Item", "Scene"].includes(type)) return;
|
||||
const wasLocked = compendium.locked;
|
||||
await compendium.configure({ locked: false });
|
||||
if (migrateToTemplateEarly) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue