From 3a8d6dc0798dcbda381c6a8e188e41f4dde637bd Mon Sep 17 00:00:00 2001 From: Shark that walks like a man <106829671+stwlam@users.noreply.github.com> Date: Fri, 31 May 2024 03:21:53 -0500 Subject: [PATCH] Remove migrations from system version 2 (#14908) --- .../migrations/634-purge-martial-items.ts | 32 ---- .../migrations/635-numify-ac-and-quantity.ts | 24 --- .../migrations/636-numify-armor-data.ts | 40 ---- .../migrations/637-clean-melee-items.ts | 29 --- .../migrations/638-spell-components.ts | 33 ---- .../639-normalize-level-and-price.ts | 53 ------ .../640-cantrips-are-not-zero-level.ts | 21 --- .../migrations/641-sovereign-steel-value.ts | 22 --- .../migrations/642-track-schema-version.ts | 22 --- .../migration/migrations/643-hazard-level.ts | 21 --- .../migrations/644-spellcasting-category.ts | 62 ------ .../migrations/645-token-image-size.ts | 64 ------- .../migrations/646-update-inline-links.ts | 25 --- .../migration/migrations/647-fix-pc-senses.ts | 20 -- .../648-remove-invested-property.ts | 22 --- .../migrations/649-focus-to-actor.ts | 56 ------ .../650-stringify-weapon-properties.ts | 27 --- .../migrations/651-ephemeral-focus-pool.ts | 178 ------------------ .../migrations/652-kill-halcyon-tradition.ts | 34 ---- .../migration/migrations/653-aes-to-res.ts | 88 --------- .../migrations/654-action-type-count.ts | 14 -- .../migrations/655-creature-token-sizes.ts | 63 ------- .../656-other-focus-pool-sources.ts | 62 ------ .../migrations/657-remove-set-property.ts | 61 ------ .../658-monk-unarmored-proficiency.ts | 14 -- .../migrations/659-multiple-damage-rows.ts | 96 ---------- .../migrations/660-derived-spell-traits.ts | 28 --- .../661-numify-vehicle-dimensions.ts | 16 -- .../662-link-to-actor-size-defaults.ts | 21 --- .../migrations/663-fix-spell-damage.ts | 72 ------- .../migrations/664-delete-cub-conditions.ts | 16 -- .../migrations/665-handwraps-corrections.ts | 21 --- .../666-usage-and-stowing-containers.ts | 86 --------- .../migrations/667-hp-subproperties.ts | 61 ------ .../migrations/668-armor-speed-penalty.ts | 41 ---- .../migrations/669-npc-attack-effects.ts | 18 -- .../migrations/670-ancestry-vision.ts | 56 ------ .../migrations/670-no-custom-trait.ts | 17 -- .../migrations/671-no-pc-items-on-non-pcs.ts | 18 -- .../672-remove-npc-base-properties.ts | 51 ----- .../migrations/673-remove-bulwark-res.ts | 65 ------- .../migrations/674-stable-homebrew-tag-ids.ts | 66 ------- .../675-flat-modifier-aes-to-res.ts | 66 ------- .../676-replace-items-with-re-like-aes.ts | 61 ------ .../migrations/677-rule-value-data-refs.ts | 15 -- .../678-separate-npc-attack-traits.ts | 49 ----- .../679-tower-shield-speed-penalty.ts | 45 ----- .../migrations/680-set-weapon-hands.ts | 132 ------------- .../migrations/681-giant-language-to-jotun.ts | 28 --- .../migrations/682-biography-fields.ts | 56 ------ .../683-flavortext-to-public-notes.ts | 37 ---- .../migrations/684-rations-to-consumable.ts | 78 -------- .../migrations/685-fix-melee-usage-traits.ts | 22 --- .../686-hero-points-to-resources.ts | 27 --- .../migrations/687-familiarity-aes-to-res.ts | 72 ------- .../migrations/688-clamp-spell-level.ts | 14 -- .../migrations/689-encumberance-aes.ts | 50 ----- .../migrations/690-tiebreak-items.ts | 30 --- ...691-weapon-range-ability-category-group.ts | 94 --------- .../692-crafting-entry-feat-replacement.ts | 71 ------- .../migrations/693-armor-category-group.ts | 40 ---- .../694-retire-system-token-settings.ts | 18 -- .../migrations/695-summon-to-summoned.ts | 23 --- .../migrations/696-flat-ability-modifiers.ts | 49 ----- .../migrations/697-weapon-reach-trait.ts | 17 -- .../698-remove-derived-actor-traits.ts | 38 ---- .../699-item-description-empty-string.ts | 11 -- src/module/migration/migrations/index.ts | 67 ------- src/module/migration/runner/base.ts | 4 +- 69 files changed, 2 insertions(+), 3048 deletions(-) delete mode 100644 src/module/migration/migrations/634-purge-martial-items.ts delete mode 100644 src/module/migration/migrations/635-numify-ac-and-quantity.ts delete mode 100644 src/module/migration/migrations/636-numify-armor-data.ts delete mode 100644 src/module/migration/migrations/637-clean-melee-items.ts delete mode 100644 src/module/migration/migrations/638-spell-components.ts delete mode 100644 src/module/migration/migrations/639-normalize-level-and-price.ts delete mode 100644 src/module/migration/migrations/640-cantrips-are-not-zero-level.ts delete mode 100644 src/module/migration/migrations/641-sovereign-steel-value.ts delete mode 100644 src/module/migration/migrations/642-track-schema-version.ts delete mode 100644 src/module/migration/migrations/643-hazard-level.ts delete mode 100644 src/module/migration/migrations/644-spellcasting-category.ts delete mode 100644 src/module/migration/migrations/645-token-image-size.ts delete mode 100644 src/module/migration/migrations/646-update-inline-links.ts delete mode 100644 src/module/migration/migrations/647-fix-pc-senses.ts delete mode 100644 src/module/migration/migrations/648-remove-invested-property.ts delete mode 100644 src/module/migration/migrations/649-focus-to-actor.ts delete mode 100644 src/module/migration/migrations/650-stringify-weapon-properties.ts delete mode 100644 src/module/migration/migrations/651-ephemeral-focus-pool.ts delete mode 100644 src/module/migration/migrations/652-kill-halcyon-tradition.ts delete mode 100644 src/module/migration/migrations/653-aes-to-res.ts delete mode 100644 src/module/migration/migrations/654-action-type-count.ts delete mode 100644 src/module/migration/migrations/655-creature-token-sizes.ts delete mode 100644 src/module/migration/migrations/656-other-focus-pool-sources.ts delete mode 100644 src/module/migration/migrations/657-remove-set-property.ts delete mode 100644 src/module/migration/migrations/658-monk-unarmored-proficiency.ts delete mode 100644 src/module/migration/migrations/659-multiple-damage-rows.ts delete mode 100644 src/module/migration/migrations/660-derived-spell-traits.ts delete mode 100644 src/module/migration/migrations/661-numify-vehicle-dimensions.ts delete mode 100644 src/module/migration/migrations/662-link-to-actor-size-defaults.ts delete mode 100644 src/module/migration/migrations/663-fix-spell-damage.ts delete mode 100644 src/module/migration/migrations/664-delete-cub-conditions.ts delete mode 100644 src/module/migration/migrations/665-handwraps-corrections.ts delete mode 100644 src/module/migration/migrations/666-usage-and-stowing-containers.ts delete mode 100644 src/module/migration/migrations/667-hp-subproperties.ts delete mode 100644 src/module/migration/migrations/668-armor-speed-penalty.ts delete mode 100644 src/module/migration/migrations/669-npc-attack-effects.ts delete mode 100644 src/module/migration/migrations/670-ancestry-vision.ts delete mode 100644 src/module/migration/migrations/670-no-custom-trait.ts delete mode 100644 src/module/migration/migrations/671-no-pc-items-on-non-pcs.ts delete mode 100644 src/module/migration/migrations/672-remove-npc-base-properties.ts delete mode 100644 src/module/migration/migrations/673-remove-bulwark-res.ts delete mode 100644 src/module/migration/migrations/674-stable-homebrew-tag-ids.ts delete mode 100644 src/module/migration/migrations/675-flat-modifier-aes-to-res.ts delete mode 100644 src/module/migration/migrations/676-replace-items-with-re-like-aes.ts delete mode 100644 src/module/migration/migrations/677-rule-value-data-refs.ts delete mode 100644 src/module/migration/migrations/678-separate-npc-attack-traits.ts delete mode 100644 src/module/migration/migrations/679-tower-shield-speed-penalty.ts delete mode 100644 src/module/migration/migrations/680-set-weapon-hands.ts delete mode 100644 src/module/migration/migrations/681-giant-language-to-jotun.ts delete mode 100644 src/module/migration/migrations/682-biography-fields.ts delete mode 100644 src/module/migration/migrations/683-flavortext-to-public-notes.ts delete mode 100644 src/module/migration/migrations/684-rations-to-consumable.ts delete mode 100644 src/module/migration/migrations/685-fix-melee-usage-traits.ts delete mode 100644 src/module/migration/migrations/686-hero-points-to-resources.ts delete mode 100644 src/module/migration/migrations/687-familiarity-aes-to-res.ts delete mode 100644 src/module/migration/migrations/688-clamp-spell-level.ts delete mode 100644 src/module/migration/migrations/689-encumberance-aes.ts delete mode 100644 src/module/migration/migrations/690-tiebreak-items.ts delete mode 100644 src/module/migration/migrations/691-weapon-range-ability-category-group.ts delete mode 100644 src/module/migration/migrations/692-crafting-entry-feat-replacement.ts delete mode 100644 src/module/migration/migrations/693-armor-category-group.ts delete mode 100644 src/module/migration/migrations/694-retire-system-token-settings.ts delete mode 100644 src/module/migration/migrations/695-summon-to-summoned.ts delete mode 100644 src/module/migration/migrations/696-flat-ability-modifiers.ts delete mode 100644 src/module/migration/migrations/697-weapon-reach-trait.ts delete mode 100644 src/module/migration/migrations/698-remove-derived-actor-traits.ts delete mode 100644 src/module/migration/migrations/699-item-description-empty-string.ts diff --git a/src/module/migration/migrations/634-purge-martial-items.ts b/src/module/migration/migrations/634-purge-martial-items.ts deleted file mode 100644 index f4c69d00c5e..00000000000 --- a/src/module/migration/migrations/634-purge-martial-items.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { ActorSourcePF2e } from "@actor/data/index.ts"; -import { WeaponSource } from "@item/base/data/index.ts"; -import { WeaponSystemSource } from "@item/weapon/data.ts"; -import { WeaponCategory } from "@item/weapon/types.ts"; -import { MigrationBase } from "../base.ts"; - -export class Migration634PurgeMartialItems extends MigrationBase { - static override version = 0.634; - - override async updateActor(actorData: ActorSourcePF2e): Promise { - const martialItems = actorData.items.filter((itemData: { type: string }) => itemData.type === "martial"); - const martialIds = martialItems.map((itemData) => itemData._id); - const martialItemWeapons = actorData.items.filter( - (itemData): itemData is WeaponSource & { data: MaybeOldWeaponSource } => { - if (itemData.type !== "weapon") return false; - const systemData: MaybeOldWeaponSource = itemData.system; - return martialIds.includes(systemData.weaponType?.value ?? ""); - }, - ); - - for (const weaponData of martialItemWeapons) { - weaponData.data.category = "simple"; - } - - actorData.items = actorData.items.filter((itemData: { type: string }) => itemData.type !== "martial"); - } -} - -type MaybeOldWeaponSource = WeaponSystemSource & { - weaponType?: { value: WeaponCategory }; - category: WeaponCategory; -}; diff --git a/src/module/migration/migrations/635-numify-ac-and-quantity.ts b/src/module/migration/migrations/635-numify-ac-and-quantity.ts deleted file mode 100644 index 67ceeeb38b0..00000000000 --- a/src/module/migration/migrations/635-numify-ac-and-quantity.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { ActorSourcePF2e } from "@actor/data/index.ts"; -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { itemIsOfType } from "@item/helpers.ts"; -import { MigrationBase } from "../base.ts"; - -/** Ensure AC and quantity values are numeric */ -export class Migration635NumifyACAndQuantity extends MigrationBase { - static override version = 0.635; - - override async updateActor(source: ActorSourcePF2e): Promise { - if (source.type === "hazard" || source.type === "npc" || source.type === "vehicle") { - source.system.attributes.ac.value = Number(source.system.attributes.ac.value); - } - } - - override async updateItem(source: ItemSourcePF2e): Promise { - if (itemIsOfType(source, "physical")) { - const quantity = source.system.quantity || { value: 0 }; - if (quantity instanceof Object) { - quantity.value = Number(quantity.value); - } - } - } -} diff --git a/src/module/migration/migrations/636-numify-armor-data.ts b/src/module/migration/migrations/636-numify-armor-data.ts deleted file mode 100644 index 95a7fea8971..00000000000 --- a/src/module/migration/migrations/636-numify-armor-data.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { ArmorSystemSource } from "@item/armor/index.ts"; -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import * as R from "remeda"; -import { MigrationBase } from "../base.ts"; - -export class Migration636NumifyArmorData extends MigrationBase { - static override version = 0.636; - - override async updateItem(source: ItemSourcePF2e): Promise { - if (source.type !== "armor") return; - - const system: ArmorSystemSourceWithObjectProperties = source.system; - - if (R.isObject(system.armor)) { - system.armor.value = Number(system.armor.value) || 0; - } - if (R.isObject(system.check)) { - system.check.value = Number(system.check.value) || 0; - } - if (R.isObject(system.dex)) { - system.dex.value = Number(system.dex.value) || 0; - } - if (R.isObject(system.strength)) { - system.strength.value = Number(system.strength.value) || 0; - } - - // This might have "ft" in the string - if (R.isObject(system.speed) && typeof system.speed.value === "string") { - system.speed.value = parseInt(system.speed.value, 10) || 0; - } - } -} - -type ArmorSystemSourceWithObjectProperties = ArmorSystemSource & { - armor?: { value: number }; - check?: { value: number }; - dex?: { value: number }; - strength: number | null | { value: number }; - speed?: { value: number }; -}; diff --git a/src/module/migration/migrations/637-clean-melee-items.ts b/src/module/migration/migrations/637-clean-melee-items.ts deleted file mode 100644 index 8dfb9e5ca0b..00000000000 --- a/src/module/migration/migrations/637-clean-melee-items.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { MigrationBase } from "../base.ts"; - -const meleeKeys = new Set([ - "description", - "source", - "traits", - "rules", - "slug", - "weaponType", - "attack", - "damageRolls", - "bonus", - "attackEffects", -]); - -/** Remove physical item data from melee items */ -export class Migration637CleanMeleeItems extends MigrationBase { - override async updateItem(itemData: ItemSourcePF2e): Promise { - if (itemData.type !== "melee") return; - - const systemData = itemData.system; - for (const key of Object.keys(systemData)) { - if (!meleeKeys.has(key)) { - delete systemData[key as keyof typeof systemData]; - } - } - } -} diff --git a/src/module/migration/migrations/638-spell-components.ts b/src/module/migration/migrations/638-spell-components.ts deleted file mode 100644 index 19a2c269f38..00000000000 --- a/src/module/migration/migrations/638-spell-components.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import * as R from "remeda"; -import { MigrationBase } from "../base.ts"; - -const validComponents = ["material", "somatic", "verbal"] as const; - -type ComponentsOld = { [K in (typeof validComponents)[number] | "value" | "-=value"]?: string | boolean | null }; - -/** Convert spell components from a string value to VSM */ -export class Migration638SpellComponents extends MigrationBase { - static override version = 0.638; - override async updateItem(source: ItemSourcePF2e): Promise { - if (source.type !== "spell") return; - - const components: ComponentsOld = - "components" in source.system && R.isObject(source.system.components) - ? source.system.components - : { value: "" }; - const oldComponents = new Set( - String(components.value) - .split(",") - .map((v) => v.trim().toLowerCase()), - ); - for (const component of validComponents) { - components[component] = components[component] || oldComponents.has(component); - } - - delete components["value"]; - if ("game" in globalThis) { - components["-=value"] = null; - } - } -} diff --git a/src/module/migration/migrations/639-normalize-level-and-price.ts b/src/module/migration/migrations/639-normalize-level-and-price.ts deleted file mode 100644 index a95657b59ef..00000000000 --- a/src/module/migration/migrations/639-normalize-level-and-price.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { itemIsOfType } from "@item/helpers.ts"; -import { Coins, PhysicalSystemSource } from "@item/physical/data.ts"; -import { CoinsPF2e } from "@item/physical/helpers.ts"; -import * as R from "remeda"; -import { MigrationBase } from "../base.ts"; - -/** Normalize stringy level and price values */ -export class Migration639NormalizeLevelAndPrice extends MigrationBase { - static override version = 0.639; - - private coinSlugs = new Set(["platinum-pieces", "gold-pieces", "silver-pieces", "copper-pieces"]); - - override async updateItem(source: ItemSourcePF2e): Promise { - if (source.system.level) { - source.system.level.value = Number(source.system.level.value) || 0; - } - - if (!itemIsOfType(source, "physical") || this.coinSlugs.has(source.system.slug ?? "")) { - return; - } - - const system: PhysicalDataOld = source.system; - const price = system.price; - - // This is new data being run through an old migration, shouldn't happen but we should appease typescript - if (typeof price.value !== "string" && R.isObject(price.value)) { - return; - } - - if (price.value) { - price.value = price.value.trim(); - } else { - price.value = "0 gp"; - } - - if (/^[0-9]+$/.test(price.value)) { - price.value = `${price.value} gp`; - } else { - const quantity = system.quantity; - const priceValue = price.value; - if (CoinsPF2e.fromString(priceValue, quantity).copperValue) { - price.value = "0 gp"; - } - } - } -} - -interface PhysicalDataOld extends Omit { - price: { - value?: string | Coins; - }; -} diff --git a/src/module/migration/migrations/640-cantrips-are-not-zero-level.ts b/src/module/migration/migrations/640-cantrips-are-not-zero-level.ts deleted file mode 100644 index 4a1a0fe001f..00000000000 --- a/src/module/migration/migrations/640-cantrips-are-not-zero-level.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { ZeroToTen } from "@module/data.ts"; -import { MigrationBase } from "../base.ts"; - -type LevelOld = { value?: ZeroToTen }; - -/** Increase level of 0th-level Cantrips to 1 */ -export class Migration640CantripsAreNotZeroLevel extends MigrationBase { - static override version = 0.64; - override async updateItem(itemData: ItemSourcePF2e): Promise { - if (itemData.type !== "spell") return; - - const level: LevelOld = itemData.system.level; - if (level.value === 0) { - level.value = 1; - if (!itemData.system.traits.value.includes("cantrip")) { - itemData.system.traits.value.push("cantrip"); - } - } - } -} diff --git a/src/module/migration/migrations/641-sovereign-steel-value.ts b/src/module/migration/migrations/641-sovereign-steel-value.ts deleted file mode 100644 index 5880dc47237..00000000000 --- a/src/module/migration/migrations/641-sovereign-steel-value.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { MigrationBase } from "../base.ts"; - -/** Fix precious material value of "sovereign steel" */ -export class Migration641SovereignSteelValue extends MigrationBase { - static override version = 0.641; - - override async updateItem(source: MaybeWithOldMaterialData): Promise { - if (source.type !== "weapon") return; - const material = source.system.preciousMaterial ?? {}; - if (material.value?.toLowerCase() === "sovereign steel") { - material.value = "sovereignSteel"; - } - } -} - -type MaybeWithOldMaterialData = ItemSourcePF2e & { - system: { - preciousMaterial?: { value?: string | null }; - preciousMaterialGrade?: { value?: unknown }; - }; -}; diff --git a/src/module/migration/migrations/642-track-schema-version.ts b/src/module/migration/migrations/642-track-schema-version.ts deleted file mode 100644 index 99b8db7a48a..00000000000 --- a/src/module/migration/migrations/642-track-schema-version.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { ActorSourcePF2e } from "@actor/data/index.ts"; -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { MigrationBase } from "../base.ts"; - -/** Start recording the schema version and other details of a migration */ -export class Migration642TrackSchemaVersion extends MigrationBase { - static override version = 0.642; - - override async updateActor(actorSource: ActorSourcePF2e): Promise { - actorSource.system.schema ??= { - version: null, - lastMigration: null, - }; - } - - override async updateItem(itemSource: ItemSourcePF2e): Promise { - itemSource.system.schema ??= { - version: null, - lastMigration: null, - }; - } -} diff --git a/src/module/migration/migrations/643-hazard-level.ts b/src/module/migration/migrations/643-hazard-level.ts deleted file mode 100644 index 60a97b1e2b2..00000000000 --- a/src/module/migration/migrations/643-hazard-level.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { ActorSourcePF2e } from "@actor/data/index.ts"; -import { MigrationBase } from "../base.ts"; - -/** Move hazard level property to the same position as other actor data */ -export class Migration643HazardLevel extends MigrationBase { - static override version = 0.643; - - override async updateActor(actorSource: ActorSourcePF2e): Promise { - if (actorSource.type !== "hazard") return; - - const hazardDetails: ObjectOrNumber = actorSource.system.details; - if (typeof hazardDetails.level === "number") { - const level = hazardDetails.level; - hazardDetails.level = { value: level }; - } - } -} - -interface ObjectOrNumber { - level: { value: number } | number; -} diff --git a/src/module/migration/migrations/644-spellcasting-category.ts b/src/module/migration/migrations/644-spellcasting-category.ts deleted file mode 100644 index e37b4b0edfb..00000000000 --- a/src/module/migration/migrations/644-spellcasting-category.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { ActorSourcePF2e } from "@actor/data/index.ts"; -import { ClassSource, ItemSourcePF2e } from "@item/base/data/index.ts"; -import { sluggify, tupleHasValue } from "@util"; -import { SpellcastingEntrySource, SpellcastingEntrySystemSource } from "@item/spellcasting-entry/data.ts"; -import { MigrationBase } from "../base.ts"; - -const oldTraditions = ["arcane", "occult", "primal", "divine", "focus", "ritual", "halcyon", ""] as const; - -const defaultTraditionByClass: Record = { - wizard: "arcane", - cleric: "divine", - druid: "primal", - bard: "occult", - ranger: "primal", - champion: "divine", - monk: "divine", -}; - -export class Migration644SpellcastingCategory extends MigrationBase { - static override version = 0.644; - - override async updateItem(item: ItemSourcePF2e, actor?: ActorSourcePF2e): Promise { - if (!actor || item.type !== "spellcastingEntry") return; - interface SpellcastingOld extends Omit { - tradition: { - value: (typeof oldTraditions)[number]; - }; - } - - const spellcasting: SpellcastingOld = item.system; - if (spellcasting.tradition.value === "ritual") { - spellcasting.prepared.value = "ritual"; - spellcasting.tradition.value = ""; - } else if (spellcasting.tradition.value === "focus") { - spellcasting.prepared.value = "focus"; - - // Try to see if there's a matching spellcasting entry that is similar to this one - const possibleMatch = actor.items.find((testItem): testItem is SpellcastingEntrySource => { - if (testItem.type !== "spellcastingEntry") return false; - const testSpellcasting: SpellcastingOld = testItem.system; - return ( - tupleHasValue(["prepared", "spontaneous"] as const, testSpellcasting.prepared.value) && - testSpellcasting.tradition.value !== "focus" && - (actor.type === "character" - ? testSpellcasting.proficiency.value === spellcasting.proficiency.value - : testSpellcasting.spelldc.value === spellcasting.spelldc.value) && - (testSpellcasting.ability.value || "int") === (spellcasting.ability.value || "int") - ); - }); - - if (possibleMatch) { - spellcasting.tradition.value = possibleMatch.system.tradition.value; - } else { - // Try to derive it from the class name or slug. No other way to do it. - // Users can always edit their tradition in the actual spellcasting entry. - const classItem = actor.items.find((testItem): testItem is ClassSource => testItem.type === "class"); - const className = classItem?.system.slug || sluggify(classItem?.name ?? ""); - spellcasting.tradition.value = defaultTraditionByClass[className] ?? "arcane"; - } - } - } -} diff --git a/src/module/migration/migrations/645-token-image-size.ts b/src/module/migration/migrations/645-token-image-size.ts deleted file mode 100644 index 7531c4d4498..00000000000 --- a/src/module/migration/migrations/645-token-image-size.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { ActorPF2e } from "@actor"; -import { ActorSourcePF2e } from "@actor/data/index.ts"; -import { MigrationBase } from "../base.ts"; - -/** Restore saved token images and sizes from old versions of the respective rule elements */ -export class Migration645TokenImageSize extends MigrationBase { - static override version = 0.645; - - #imageOverrides: Map = new Map(); - - #sizeOverrides: Map = new Map(); - - #isTokenImageFlag(flag: unknown): flag is VideoFilePath { - return typeof flag === "string"; - } - - #isTokenSizeFlag(flag: unknown): flag is { height: number; width: number } { - return ( - flag instanceof Object && - "height" in flag && - typeof flag["height"] === "number" && - "width" in flag && - typeof flag["width"] === "number" - ); - } - - override async updateActor(actorSource: ActorSourcePF2e): Promise { - const flags = actorSource.flags as OldTokenFlags; - const originalImg = flags.pf2e?.token?.img; - if (this.#isTokenImageFlag(originalImg)) { - this.#imageOverrides.set(actorSource._id!, originalImg); - } - - const originalSize = flags.pf2e?.token?.size; - if (this.#isTokenSizeFlag(originalSize)) { - this.#sizeOverrides.set(actorSource._id!, originalSize); - } - - if (typeof flags.pf2e?.token === "object") { - if ("game" in globalThis) flags.pf2e["-=token"] = null; - delete flags.pf2e.token; - } - } - - override async updateToken( - tokenSource: foundry.documents.TokenSource, - actor: Readonly, - ): Promise { - tokenSource.texture.src = this.#imageOverrides.get(actor?.id ?? "") ?? tokenSource.texture.src; - const sizeOverride = this.#sizeOverrides.get(actor?.id ?? ""); - tokenSource.height = sizeOverride?.height ?? tokenSource.height; - tokenSource.width = sizeOverride?.width ?? tokenSource.width; - } -} - -type OldTokenFlags = { - pf2e?: { - token?: { - img?: string; - size?: string; - }; - "-=token"?: null; - }; -}; diff --git a/src/module/migration/migrations/646-update-inline-links.ts b/src/module/migration/migrations/646-update-inline-links.ts deleted file mode 100644 index b5617323278..00000000000 --- a/src/module/migration/migrations/646-update-inline-links.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { ActorSourcePF2e } from "@actor/data/index.ts"; -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { MigrationBase } from "../base.ts"; - -export class Migration646UpdateInlineLinks extends MigrationBase { - static override version = 0.646; - - private updateCheckAttributes(markup = ""): string { - return markup - .replace(/\bdata-pf2-([a-z]+)-check="\w*"/g, 'data-pf2-check="$1"') - .replace(/\bdata-pf2-(?:saving-throw|skill-check)\b/g, "data-pf2-check"); - } - - override async updateActor(actorData: ActorSourcePF2e): Promise { - if (actorData.type === "hazard") { - const hazardDetails = actorData.system.details; - hazardDetails.disable = this.updateCheckAttributes(hazardDetails.disable ?? ""); - } - } - - override async updateItem(itemData: ItemSourcePF2e): Promise { - const description = itemData.system.description; - description.value = this.updateCheckAttributes(description.value ?? ""); - } -} diff --git a/src/module/migration/migrations/647-fix-pc-senses.ts b/src/module/migration/migrations/647-fix-pc-senses.ts deleted file mode 100644 index 094477965c6..00000000000 --- a/src/module/migration/migrations/647-fix-pc-senses.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { ActorSourcePF2e } from "@actor/data/index.ts"; -import * as R from "remeda"; -import { MigrationBase } from "../base.ts"; - -/** Fix PC `senses` properties, misshapen by some mysterious source */ -export class Migration647FixPCSenses extends MigrationBase { - static override version = 0.647; - - override async updateActor(source: ActorSourcePF2e): Promise { - if (source.type !== "character") return; - - const notTraits: unknown = source.system.traits; - if (!R.isObject(notTraits)) return; - if (Array.isArray(notTraits.senses)) { - notTraits.senses = R.compact(notTraits.senses); - } else { - notTraits.senses = []; - } - } -} diff --git a/src/module/migration/migrations/648-remove-invested-property.ts b/src/module/migration/migrations/648-remove-invested-property.ts deleted file mode 100644 index a37be0b9ab7..00000000000 --- a/src/module/migration/migrations/648-remove-invested-property.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { PhysicalSystemSource } from "@item/physical/data.ts"; -import { MigrationBase } from "../base.ts"; - -/** Remove the `invested` property from uninvestable item types */ -export class Migration648RemoveInvestedProperty extends MigrationBase { - static override version = 0.648; - - override async updateItem(itemSource: ItemSourcePF2e): Promise { - if (!(itemSource.type === "treasure" || itemSource.type === "consumable")) return; - const systemData: NotInvestableSource = itemSource.system; - delete systemData.invested; - if ("game" in globalThis) { - systemData["-=invested"] = null; - } - } -} - -interface NotInvestableSource extends PhysicalSystemSource { - invested?: unknown; - "-=invested"?: null; -} diff --git a/src/module/migration/migrations/649-focus-to-actor.ts b/src/module/migration/migrations/649-focus-to-actor.ts deleted file mode 100644 index 824cc8054e8..00000000000 --- a/src/module/migration/migrations/649-focus-to-actor.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { CharacterSource } from "@actor/character/data.ts"; -import { CreatureResourcesSource } from "@actor/creature/index.ts"; -import { ActorSourcePF2e } from "@actor/data/index.ts"; -import { NPCSource } from "@actor/npc/data.ts"; -import { ItemSourcePF2e, SpellcastingEntrySource } from "@item/base/data/index.ts"; -import { SpellcastingEntrySystemSource } from "@item/spellcasting-entry/data.ts"; -import { MigrationBase } from "../base.ts"; - -interface SpellcastingEntrySystemDataOld extends Omit { - focus?: { - points: number; - pool: number; - }; - "-=focus"?: null; -} - -function isCreatureSource(source: ActorSourcePF2e): source is CharacterSource | NPCSource { - return ["character", "npc"].includes(source.type); -} - -/** Focus Points became an actor resource. Relies on items running after actor */ -export class Migration649FocusToActor extends MigrationBase { - static override version = 0.649; - - override async updateActor(actorData: ActorSourcePF2e): Promise { - if (!isCreatureSource(actorData)) return; - const systemData: { resources: object } = actorData.system; - if (!systemData.resources) systemData.resources = {}; - - // Focus points in descending order by max pool, and then "most recent". - // Javascript sort is stable, so we first sort by order, filter to focus, and then sort by max. - const spellLists = actorData.items - .filter((i): i is SpellcastingEntrySource => i.type === "spellcastingEntry") - .sort((a, b) => (a.sort || 0) - (b.sort || 0)) - .map((i) => i.system as SpellcastingEntrySystemDataOld) - .filter((i) => i.prepared.value === "focus" && i.focus) - .sort((a, b) => (b.focus?.pool || 0) - (a.focus?.pool || 0)); - - if (spellLists.length === 0) return; - const focusOld = spellLists[0].focus; - const resources: CreatureResourcesSource = actorData.system.resources; - resources.focus = { - value: focusOld?.points ?? 0, - max: focusOld?.pool ?? 1, - }; - } - - override async updateItem(itemData: ItemSourcePF2e): Promise { - if (itemData.type !== "spellcastingEntry") return; - const data: SpellcastingEntrySystemDataOld = itemData.system; - delete data.focus; - if ("game" in globalThis) { - data["-=focus"] = null; - } - } -} diff --git a/src/module/migration/migrations/650-stringify-weapon-properties.ts b/src/module/migration/migrations/650-stringify-weapon-properties.ts deleted file mode 100644 index 8fcfef566da..00000000000 --- a/src/module/migration/migrations/650-stringify-weapon-properties.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { WeaponSystemSource } from "@item/weapon/data.ts"; -import { WeaponCategory, WeaponRangeIncrement } from "@item/weapon/types.ts"; -import { MigrationBase } from "../base.ts"; - -/** Ensure weapon categories and ranges have valid properties */ -export class Migration650StringifyWeaponProperties extends MigrationBase { - static override version = 0.65; - override async updateItem(itemData: ItemSourcePF2e): Promise { - if (itemData.type !== "weapon") return; - - const systemData: MaybeOldData = itemData.system; - if (systemData.weaponType) { - systemData.weaponType.value ||= "simple"; - } - const range = (systemData.range ??= { value: "melee" }); - if (range instanceof Object && typeof range.value === "string") { - range.value = range.value.trim() || "melee"; - if (range.value === "reach") range.value = "melee"; - } - } -} - -type MaybeOldData = Omit & { - weaponType?: { value: WeaponCategory }; - range: WeaponRangeIncrement | null | { value: unknown }; -}; diff --git a/src/module/migration/migrations/651-ephemeral-focus-pool.ts b/src/module/migration/migrations/651-ephemeral-focus-pool.ts deleted file mode 100644 index e3d5b357f81..00000000000 --- a/src/module/migration/migrations/651-ephemeral-focus-pool.ts +++ /dev/null @@ -1,178 +0,0 @@ -import { ActorSourcePF2e } from "@actor/data/index.ts"; -import { FeatSource, ItemSourcePF2e } from "@item/base/data/index.ts"; -import { RuleElementSource } from "@module/rules/index.ts"; -import { isObject, sluggify } from "@util"; -import { MigrationBase } from "../base.ts"; - -/** Don't store the max value of the focus pool */ -export class Migration651EphemeralFocusPool extends MigrationBase { - static override version = 0.651; - - #needsRuleElement(rules: (RuleElementSource & { path?: string })[]): boolean { - return !rules.some((rule) => rule.key === "ActiveEffectLike" && rule.path === "system.resources.focus.max"); - } - - #increasesByOne = new Set([ - "abundant-step", - "achaekeks-grip", - "advanced-bloodline", - "advanced-deitys-domain", - "advanced-domain", - "advanced-revelation", - "advanced-school-spell", - "advanced-seeker-of-truths", - "animal-feature", - "arcane-school-spell", - "apex-companion", - "basic-lesson", - "beastmasters-trance", - "blessed-sacrifice", - "cackle", - "champions-sacrifice", - "clinging-shadows-initiate", - "counter-perform", - "debilitating-dichotomy", - "domain-acumen", - "domain-fluency", - "empty-body", - "enlarge-companion", - "ephemeral-tracking", - "familiar-form", - "fatal-aria", - "gaze-of-veracity", - "greater-bloodline", - "greater-lesson", - "greater-revelation", - "heal-animal", - "healing-touch", - "hunters-luck", - "hunters-vision", - "impaling-briars", - "inspire-heroics", - "invoke-disaster", - "invoke-the-crimson-oath", - "ki-blast", - "ki-form", - "ki-rush", - "ki-strike", - "leaf-order", - "light-of-revelation", - "lingering-composition", - "litany-against-sloth", - "litany-against-wrath", - "litany-of-depravity", - "litany-of-righteousness", - "litany-of-self-interest", - "loremasters-etude", - "magic-hide", - "major-lesson", - "mantis-form", - "medusas-wrath", - "order-spell", - "perfect-ki-adept", - "perfect-strike", - "quivering-palm", - "rangers-bramble", - "shadow-magic", - "shadows-web", - "shall-not-falter-shall-not-rout", - "snare-hopping", - "song-of-the-fallen", - "soothing-ballad", - "soothing-mist", - "speaking-sky", - "spellmasters-ward", - "steal-the-sky", - "storm-order", - "sun-blade", - "suns-fury", - "terrain-transposition", - "transcribe-moment", - "universal-versatility", - "vision-of-weakness", - "wholeness-of-body", - "wild-winds-initiate", - "wind-caller", - "wind-jump", - "wronged-monks-wrath", - ]); - - #isClassFeature(source: ItemSourcePF2e): source is FeatSource & { system: { featType: "classfeature" } } { - return ( - source.type === "feat" && - "featType" in source.system && - isObject<{ value: string }>(source.system.featType) && - source.system.featType.value === "classfeature" - ); - } - - override async updateActor(source: ActorSourcePF2e): Promise { - if (source.type !== "character") return; - const systemData: { resources: { focus?: { value?: unknown; max?: never; "-=max"?: null } } } = source.system; - systemData.resources ??= {}; - - const resources = systemData.resources; - if (typeof resources.focus?.max === "number" && "game" in globalThis) { - resources.focus["-=max"] = null; - } else { - delete resources.focus?.max; - } - } - - override async updateItem(source: ItemSourcePF2e): Promise { - if (source.type !== "feat") return; - - const systemData = source.system; - - const rule = ((): (RuleElementSource & { [key: string]: unknown }) | null => { - const slug = systemData.slug ?? sluggify(source.name); - - if (slug === "revelation-spells") { - return { - key: "ActiveEffectLike", - path: "system.resources.focus.max", - mode: "upgrade", - value: 2, - priority: 10, // Before any adds - }; - } - - if (slug === "major-curse") { - return { - key: "ActiveEffectLike", - path: "system.resources.focus.max", - mode: "upgrade", - value: 3, - }; - } - - if ( - ["composition-spells", "devotion-spells", "druidic-order", "hexes"].includes(slug) || - (/^(?:arcane-school|bloodline)-/.test(slug) && this.#isClassFeature(source)) - ) { - return { - key: "ActiveEffectLike", - path: "system.resources.focus.max", - mode: "upgrade", - value: 1, - priority: 10, // Before any adds - }; - } - - if (this.#increasesByOne.has(slug) || slug.startsWith("first-revelation-")) { - return { - key: "ActiveEffectLike", - path: "system.resources.focus.max", - mode: "add", - value: 1, - }; - } - - return null; - })(); - - if (rule && this.#needsRuleElement(source.system.rules)) { - systemData.rules.push(rule); - } - } -} diff --git a/src/module/migration/migrations/652-kill-halcyon-tradition.ts b/src/module/migration/migrations/652-kill-halcyon-tradition.ts deleted file mode 100644 index 011a551fe55..00000000000 --- a/src/module/migration/migrations/652-kill-halcyon-tradition.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { ActorSourcePF2e } from "@actor/data/index.ts"; -import { ClassSource, ItemSourcePF2e } from "@item/base/data/index.ts"; -import { SpellcastingEntrySystemData } from "@item/spellcasting-entry/data.ts"; -import { sluggify } from "@util"; -import { MigrationBase } from "../base.ts"; - -interface TraditionDataOld { - value: SpellcastingEntrySystemData["tradition"]["value"] | "halcyon"; - halcyon?: boolean; -} - -const defaultTraditionByClass: Record = { - wizard: "arcane", - druid: "primal", - ranger: "primal", -}; - -/** Halcyon is not a tradition, as it did nothing it was removed without replacement. */ -export class Migration652KillHalcyonTradition extends MigrationBase { - static override version = 0.652; - - override async updateItem(itemData: ItemSourcePF2e, actor?: ActorSourcePF2e): Promise { - if (itemData.type !== "spellcastingEntry") return; - - const tradition: TraditionDataOld = itemData.system.tradition; - if (tradition.value === "halcyon") { - // Try to derive it from the class name. No other way to do it. - // Users can always edit their tradition in the actual spellcasting entry. - const classItem = actor?.items.find((testItem): testItem is ClassSource => testItem.type === "class"); - const className = classItem?.system.slug || sluggify(classItem?.name ?? ""); - tradition.value = defaultTraditionByClass[className] ?? "arcane"; - } - } -} diff --git a/src/module/migration/migrations/653-aes-to-res.ts b/src/module/migration/migrations/653-aes-to-res.ts deleted file mode 100644 index be3da94f5fe..00000000000 --- a/src/module/migration/migrations/653-aes-to-res.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { ActorSourcePF2e } from "@actor/data/index.ts"; -import { AttributeString } from "@actor/types.ts"; -import { ClassSource, ItemSourcePF2e } from "@item/base/data/index.ts"; -import type { EffectChangeData } from "types/foundry/common/documents/active-effect.d.ts"; -import { MigrationBase } from "../base.ts"; - -/** Remove `ActiveEffect`s from classes, convert AE changes on several item types to AE-likes */ -export class Migration653AEstoREs extends MigrationBase { - static override version = 0.653; - - /** Remove the AE if the originating item is a class and is modifying any of the below property paths */ - #pathsToRemove = new Set([ - ...["unarmored", "light", "medium", "heavy"].map((category) => `system.martial.${category}.rank`), - ...["unarmed", "simple", "martial", "advanced"].map((category) => `system.martial.${category}.rank`), - ...["fortitude", "reflex", "will"].map((save) => `system.saves.${save}.rank`), - "system.details.keyability.value", - "system.attributes.perception.rank", - "system.attributes.classDC.rank", - ]); - - #isRemovableAE(effect: foundry.documents.ActiveEffectSource): boolean { - return effect.changes.every(this.#isRemoveableChange); - } - - #isRemoveableChange(change: EffectChangeData): boolean { - return ( - (change.mode !== 0 && Number.isInteger(Number(change.value))) || - (change.mode === 5 && !change.value.startsWith("{")) - ); - } - - #fixClassKeyAbilities(classSource: ClassSource): void { - type MaybeOldKeyAbility = { value: AttributeString[] | { value: AttributeString }[] }; - const keyAbility: MaybeOldKeyAbility = classSource.system.keyAbility; - keyAbility.value = keyAbility.value.map((value) => (typeof value === "string" ? value : value.value)); - } - - override async updateActor(actorSource: ActorSourcePF2e): Promise { - if (actorSource.type !== "character") return; - const systemData: { saves?: object; martial?: object } = actorSource.system; - systemData.martial = {}; // Only remove on compendium JSON - - // Remove transferred ActiveEffects, some of which will be converted to RuleElements - actorSource.effects = actorSource.effects.filter((effect) => { - const origin = effect.origin ?? ""; - const itemId = /\bItem\.([A-Za-z0-9]{16})$/.exec(origin)?.[1]; - const itemSource = actorSource.items.find((maybeSource) => maybeSource._id === itemId); - return ( - itemSource && !(["class", "effect", "feat"].includes(itemSource.type) && this.#isRemovableAE(effect)) - ); - }); - } - - override async updateItem(itemSource: ItemSourcePF2e): Promise { - if (!(itemSource.type === "class" || itemSource.type === "effect" || itemSource.type === "feat")) return; - - if (itemSource.type === "class") this.#fixClassKeyAbilities(itemSource); - - // Collect changes from item and recreate some as rule elements - const modes = { 1: "multiply", 2: "add", 3: "downgrade", 4: "upgrade", 5: "override" }; - - for (const effect of [...itemSource.effects]) { - // Remove any handled by class data - if (itemSource.type === "class") { - effect.changes = effect.changes.filter((change) => !this.#pathsToRemove.has(change.key)); - } - - // Turn what remains into AE-Like rule elements - const toAELikes = effect.changes.filter(this.#isRemoveableChange); - const rules = itemSource.system.rules; - for (const change of toAELikes) { - if (change.mode === 0) continue; - const newRule = { - key: "ActiveEffectLike", - path: change.key, - mode: modes[change.mode], - value: Number.isNaN(Number(change.value)) ? change.value : Number(change.value), - priority: change.priority ?? 50, - }; - rules.push(newRule); - } - - // Remove the ActiveEffect unless complex changes are present - effect.changes = effect.changes.filter((change) => !this.#isRemoveableChange(change)); - } - itemSource.effects = itemSource.effects.filter((effect) => !this.#isRemovableAE(effect)); - } -} diff --git a/src/module/migration/migrations/654-action-type-count.ts b/src/module/migration/migrations/654-action-type-count.ts deleted file mode 100644 index 00bdc1985db..00000000000 --- a/src/module/migration/migrations/654-action-type-count.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { MigrationBase } from "../base.ts"; -import { OneToThree } from "@module/data.ts"; - -export class Migration654ActionTypeAndCount extends MigrationBase { - static override version = 0.654; - - override async updateItem(item: ItemSourcePF2e): Promise { - if (item.type !== "feat" && item.type !== "action") return; - const systemData = item.system; - systemData.actions.value = (Math.min(3, Math.max(Number(systemData.actions.value), 0)) || - null) as OneToThree | null; - } -} diff --git a/src/module/migration/migrations/655-creature-token-sizes.ts b/src/module/migration/migrations/655-creature-token-sizes.ts deleted file mode 100644 index 67674a500b9..00000000000 --- a/src/module/migration/migrations/655-creature-token-sizes.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { Size } from "@module/data.ts"; -import { BracketedValue, RuleElementSource } from "@module/rules/rule-element/index.ts"; -import { isObject } from "@util"; -import { MigrationBase } from "../base.ts"; - -/** Combine AE-likes altering creature size and TokenSize RuleElements into CreatureSize RuleElements */ -export class Migration655CreatureTokenSizes extends MigrationBase { - static override version = 0.655; - - #isTokenSizeRE(rule: MaybeAELike): boolean { - return typeof rule.key === "string" && rule.key.endsWith("TokenSize"); - } - - #isActorSizeAELike(rule: MaybeAELike): boolean { - return ( - typeof rule.key === "string" && - rule.key.endsWith("ActiveEffectLike") && - rule.path === "system.traits.size.value" - ); - } - - #isBracketedValue(value: unknown): value is BracketedValue { - return isObject<{ brackets?: unknown }>(value) && Array.isArray(value.brackets); - } - - private dimensionToSize: Record = { - 0.5: "tiny", - 2: "lg", - 3: "huge", - 4: "grg", - }; - - override async updateItem(itemSource: ItemSourcePF2e): Promise { - itemSource.system.rules ??= []; - const rules: MaybeAELike[] = itemSource.system.rules; - const actorSizeAELike = rules.find(this.#isActorSizeAELike); - const tokenSizeRE: (RuleElementSource & { value?: JSONValue }) | undefined = rules.find(this.#isTokenSizeRE); - - if (actorSizeAELike) { - actorSizeAELike.key = "CreatureSize"; - delete actorSizeAELike.path; - delete actorSizeAELike.mode; - } else if (tokenSizeRE && ["number", "string", "object"].includes(typeof tokenSizeRE.value)) { - tokenSizeRE.key = "CreatureSize"; - if (this.#isBracketedValue(tokenSizeRE.value)) { - for (const bracket of tokenSizeRE.value.brackets) { - if (typeof bracket.value === "number") { - bracket.value = this.dimensionToSize[bracket.value] ?? "med"; - } - } - } else if (typeof tokenSizeRE.value === "number") { - tokenSizeRE.value = this.dimensionToSize[tokenSizeRE.value] ?? "med"; - } - } - - if (tokenSizeRE && tokenSizeRE.key !== "CreatureSize") { - itemSource.system.rules.splice(itemSource.system.rules.indexOf(tokenSizeRE), 1); - } - } -} - -type MaybeAELike = RuleElementSource & { path?: string; mode?: string }; diff --git a/src/module/migration/migrations/656-other-focus-pool-sources.ts b/src/module/migration/migrations/656-other-focus-pool-sources.ts deleted file mode 100644 index 639cceea551..00000000000 --- a/src/module/migration/migrations/656-other-focus-pool-sources.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { RuleElementSource } from "@module/rules/index.ts"; -import { sluggify } from "@util"; -import { MigrationBase } from "../base.ts"; - -/** Set focus pool for druids */ -export class Migration656OtherFocusPoolSources extends MigrationBase { - static override version = 0.656; - - private needsRuleElement(rules: (RuleElementSource & { path?: string })[]): boolean { - return !rules.some((rule) => rule.key === "ActiveEffectLike" && rule.path === "system.resources.focus.max"); - } - - private increasesByOne = new Set([ - "additional-shadow-magic", - "basic-bloodline-spell", - "blessed-one-dedication", - "breath-of-the-dragon", - "crystal-ward-spells", - "domain-initiate", - "expanded-domain-initiate", - "gravity-weapon", - "heal-companion", - "leaf-order", - "shadow-illusion", - "storm-order", - "wings-of-the-dragon", - ]); - - override async updateItem(itemSource: ItemSourcePF2e): Promise { - if (itemSource.type !== "feat") return; - - const systemData = itemSource.system; - - const rule = ((): (RuleElementSource & { [key: string]: unknown }) | null => { - const slug = systemData.slug ?? sluggify(itemSource.name); - - if (slug === "druidic-order") { - return { - key: "ActiveEffectLike", - path: "system.resources.focus.max", - mode: "upgrade", - value: 1, - priority: 10, // Before any adds - }; - } - - if (this.increasesByOne.has(slug)) { - return { - key: "ActiveEffectLike", - path: "system.resources.focus.max", - mode: "add", - value: 1, - }; - } - - return null; - })(); - - if (rule && this.needsRuleElement(itemSource.system.rules)) systemData.rules.push(rule); - } -} diff --git a/src/module/migration/migrations/657-remove-set-property.ts b/src/module/migration/migrations/657-remove-set-property.ts deleted file mode 100644 index f99a49bde76..00000000000 --- a/src/module/migration/migrations/657-remove-set-property.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { MigrationBase } from "../base.ts"; -import { RuleElementSource, RuleValue } from "@module/rules/rule-element/index.ts"; -import { ActorSourcePF2e } from "@actor/data/index.ts"; - -export class Migration657RemoveSetProperty extends MigrationBase { - static override version = 0.657; - - override async updateActor(actorSource: ActorSourcePF2e): Promise { - const systemFlags = actorSource.flags.pf2e ?? {}; - delete systemFlags["set-property"]; - if ("game" in globalThis && "set-property" in systemFlags) { - systemFlags["-=set-property"] = null; - } - } - - override async updateItem(itemSource: ItemSourcePF2e): Promise { - itemSource.system.rules ??= []; - const rules = itemSource.system.rules; - const setPropertyRules = itemSource.system.rules.filter( - (rule: Record): rule is SetPropertySource => - typeof rule.key === "string" && - ["SetProperty", "PF2E.RuleElement.SetProperty"].includes(rule.key) && - typeof rule["property"] === "string" && - typeof rule["on"] === "object" && - rule["on"] !== null && - "added" in rule["on"], - ); - const aeLikes = setPropertyRules.map( - (setProperty): AELikeSource => ({ - key: "ActiveEffectLike", - mode: "override", - path: setProperty.property.replace(/^flags\.2e/, "flags.pf2e"), - value: setProperty.on.added, - priority: 10, - }), - ); - for (const setPropertyRule of setPropertyRules) { - const index = rules.indexOf(setPropertyRule); - rules.splice(index, 1, aeLikes.shift()!); - } - - // Remove any surviving (likely malformed) SetProperty rule elements - itemSource.system.rules = itemSource.system.rules.filter( - (rule) => rule && typeof rule.key === "string" && !rule.key.trim().endsWith("SetProperty"), - ); - } -} - -type SetPropertySource = RuleElementSource & { - property: string; - on: { - added: RuleValue; - }; -}; - -interface AELikeSource extends RuleElementSource { - mode: "override"; - path: string; - value: RuleValue; -} diff --git a/src/module/migration/migrations/658-monk-unarmored-proficiency.ts b/src/module/migration/migrations/658-monk-unarmored-proficiency.ts deleted file mode 100644 index 4b78967b837..00000000000 --- a/src/module/migration/migrations/658-monk-unarmored-proficiency.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { sluggify } from "@util"; -import { MigrationBase } from "../base.ts"; - -export class Migration658MonkUnarmoredProficiency extends MigrationBase { - static override version = 0.658; - - override async updateItem(itemSource: ItemSourcePF2e): Promise { - const slug = itemSource.system.slug ?? sluggify(itemSource.name); - if (itemSource.type === "class" && slug === "monk" && itemSource.system.defenses.unarmored !== 2) { - itemSource.system.defenses.unarmored = 2; - } - } -} diff --git a/src/module/migration/migrations/659-multiple-damage-rows.ts b/src/module/migration/migrations/659-multiple-damage-rows.ts deleted file mode 100644 index e9ff2275e09..00000000000 --- a/src/module/migration/migrations/659-multiple-damage-rows.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { SpellSystemSource } from "@item/spell/data.ts"; -import { DamageType } from "@system/damage/index.ts"; -import { tupleHasValue } from "@util"; -import { MigrationBase } from "../base.ts"; - -const formulaHasValue = (value?: string | null): value is string => { - return !!value && value !== "0"; -}; - -const modes = ["level1", "level2", "level3", "level4"] as const; - -/** Damage can now be split into multiple rows for spells */ -export class Migration659MultipleDamageRows extends MigrationBase { - static override version = 0.659; - - override async updateItem(source: ItemSourcePF2e): Promise { - if (source.type !== "spell") return; - - const data: SpellSystemDataOld = source.system; - - // Migrate scaling (standalone) - if (data.scaling instanceof Object) { - if (typeof data.scaling.mode === "string" && tupleHasValue(modes, data.scaling.mode)) { - data.scaling.interval = modes.indexOf(data.scaling.mode) + 1; - } - - if ( - typeof data.scaling.formula === "string" && - formulaHasValue(data.scaling.formula) && - !data.scaling.damage - ) { - data.scaling.damage = { 0: data.scaling.formula }; - } - - if (!data.scaling.interval || !data.scaling.damage) { - if ("game" in globalThis) { - data["-=scaling"] = null; - } else { - delete data.scaling; - } - } else { - if ("game" in globalThis) { - data.scaling["-=mode"] = null; - data.scaling["-=formula"] = null; - } else { - delete data.scaling.mode; - delete data.scaling.formula; - } - } - } - - // Migrate damage and damage type - if (typeof data.damage.value === "string") { - if (formulaHasValue(data.damage.value) || data.damage.applyMod) { - const value = data.damage.value; - data.damage.value = { - 0: { - value, - applyMod: data.damage.applyMod || undefined, - type: { value: data.damageType?.value || "untyped", categories: [] }, - }, - }; - } else { - data.damage.value = {}; - } - - if ("game" in globalThis) { - data.damage["-=applyMod"] = null; - } else { - delete data.damage.applyMod; - } - } - - if ("damageType" in data) { - data["-=damageType"] = null; - } - } -} - -interface SpellSystemDataOld extends Omit { - damage: Record; - damageType?: { - value?: DamageType; - }; - "-=damageType"?: null; - scaling?: { - mode?: string; - formula?: string; - interval?: number; - damage?: Record; - "-=mode"?: null; - "-=formula"?: null; - }; - "-=scaling"?: null; -} diff --git a/src/module/migration/migrations/660-derived-spell-traits.ts b/src/module/migration/migrations/660-derived-spell-traits.ts deleted file mode 100644 index b75f9ae2f4b..00000000000 --- a/src/module/migration/migrations/660-derived-spell-traits.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { MAGIC_TRADITIONS } from "@item/spell/values.ts"; -import { MigrationBase } from "../base.ts"; - -/** Remove manually set magic school and tradition traits from spells */ -export class Migration660DerivedSpellTraits extends MigrationBase { - static override version = 0.66; - - #MAGIC_SCHOOLS = new Set([ - "abjuration", - "conjuration", - "divination", - "enchantment", - "evocation", - "illusion", - "necromancy", - "transmutation", - ]); - - #derivedTraits: string[] = [...this.#MAGIC_SCHOOLS, ...MAGIC_TRADITIONS]; - - override async updateItem(source: ItemSourcePF2e): Promise { - if (source.type === "spell") { - const traits: { value: string[] } = source.system.traits; - traits.value = traits.value.filter((trait) => !this.#derivedTraits.includes(trait)); - } - } -} diff --git a/src/module/migration/migrations/661-numify-vehicle-dimensions.ts b/src/module/migration/migrations/661-numify-vehicle-dimensions.ts deleted file mode 100644 index c489b17e5f8..00000000000 --- a/src/module/migration/migrations/661-numify-vehicle-dimensions.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { ActorSourcePF2e } from "@actor/data/index.ts"; -import { MigrationBase } from "../base.ts"; - -/** Ensure that a vehicle's dimensions are `number`s */ -export class Migration661NumifyVehicleDimensions extends MigrationBase { - static override version = 0.661; - - override async updateActor(actorSource: ActorSourcePF2e): Promise { - if (actorSource.type === "vehicle") { - const { space } = actorSource.system.details; - space.long = Number(space.long) || 2; - space.wide = Number(space.wide) || 2; - space.high = Number(space.high) || 1; - } - } -} diff --git a/src/module/migration/migrations/662-link-to-actor-size-defaults.ts b/src/module/migration/migrations/662-link-to-actor-size-defaults.ts deleted file mode 100644 index c469b3b07cf..00000000000 --- a/src/module/migration/migrations/662-link-to-actor-size-defaults.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { ActorPF2e } from "@actor"; -import { ActorSourcePF2e } from "@actor/data/index.ts"; -import { MigrationBase } from "../base.ts"; - -/** Set default linkToActorSize flag */ -export class Migration662LinkToActorSizeDefaults extends MigrationBase { - static override version = 0.662; - - override async updateActor(actorSource: ActorSourcePF2e): Promise { - const linkToActorSize = !["hazard", "loot"].includes(actorSource.type); - actorSource.prototypeToken.flags ??= { pf2e: { linkToActorSize } }; - actorSource.prototypeToken.flags.pf2e ??= { linkToActorSize }; - actorSource.prototypeToken.flags.pf2e.linkToActorSize ??= linkToActorSize; - } - - override async updateToken(tokenSource: foundry.documents.TokenSource, actor: ActorPF2e): Promise { - const linkToActorSize = !["hazard", "loot"].includes(actor.type); - tokenSource.flags.pf2e ??= { linkToActorSize }; - tokenSource.flags.pf2e.linkToActorSize ??= linkToActorSize; - } -} diff --git a/src/module/migration/migrations/663-fix-spell-damage.ts b/src/module/migration/migrations/663-fix-spell-damage.ts deleted file mode 100644 index 427f53a338f..00000000000 --- a/src/module/migration/migrations/663-fix-spell-damage.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { SpellSystemSource } from "@item/spell/data.ts"; -import { DamageType } from "@system/damage/index.ts"; -import { sluggify } from "@util"; -import { MigrationBase } from "../base.ts"; - -function createBasicDamage(value: string, applyMod: boolean, damageType: DamageType) { - return { - 0: { - applyMod, - type: { categories: [], value: damageType }, - value, - }, - }; -} - -function createBasicScaling(interval: number, scaling: string) { - return { interval, damage: { 0: scaling } }; -} - -/** Damage can now be split into multiple rows for spells */ -export class Migration663FixSpellDamage extends MigrationBase { - static override version = 0.663; - - override async updateItem(source: ItemSourcePF2e): Promise { - if (source.type !== "spell") return; - if (Object.keys(source.system.damage?.value ?? {}).length > 0) return; - - const itemName = source.system.slug ?? sluggify(source.name); - const systemData: SpellScalingOld = source.system; - - switch (itemName) { - case "animated-assault": - systemData.damage.value = createBasicDamage("2d10", false, "bludgeoning"); - systemData.scaling = createBasicScaling(2, "2d10"); - break; - case "daze": - systemData.damage.value = createBasicDamage("0", true, "mental"); - systemData.scaling = createBasicScaling(2, "1d6"); - break; - case "personal-blizzard": - systemData.damage.value = { - 0: { - applyMod: false, - type: { value: "cold", categories: [] }, - value: "1d6", - }, - 1: { - applyMod: false, - type: { value: "cold", subtype: "persistent", categories: [] }, - value: "1d6", - }, - }; - systemData.scaling = { - interval: 1, - damage: { 0: "1", 1: "1" }, - }; - break; - case "power-word-kill": - systemData.damage.value = createBasicDamage("50", false, "untyped"); - break; - } - } -} - -interface SpellScalingOld extends Omit { - damage: Record; - scaling?: { - interval: number; - damage: Record; - }; -} diff --git a/src/module/migration/migrations/664-delete-cub-conditions.ts b/src/module/migration/migrations/664-delete-cub-conditions.ts deleted file mode 100644 index a512bbb818b..00000000000 --- a/src/module/migration/migrations/664-delete-cub-conditions.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { ActorSourcePF2e } from "@actor/data/index.ts"; -import { MigrationBase } from "../base.ts"; - -/** Delete conditions originating from the Combat Utility Belt module */ -export class Migration664DeleteCUBConditions extends MigrationBase { - static override version = 0.664; - - override async updateActor(actorSource: ActorSourcePF2e): Promise { - const cubConditions = actorSource.items.filter( - (item) => item.type === "condition" && !item.system.references?.overriddenBy, - ); - for (const condition of cubConditions) { - actorSource.items.findSplice((item) => item === condition); - } - } -} diff --git a/src/module/migration/migrations/665-handwraps-corrections.ts b/src/module/migration/migrations/665-handwraps-corrections.ts deleted file mode 100644 index ee5d62747aa..00000000000 --- a/src/module/migration/migrations/665-handwraps-corrections.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { sluggify } from "@util"; -import { MigrationBase } from "../base.ts"; - -/** Correct the usage and misspelled icon filename for handwraps of mighty blows */ -export class Migration665HandwrapsCorrections extends MigrationBase { - static override version = 0.665; - - override async updateItem(itemSource: ItemSourcePF2e): Promise { - const slug = itemSource.system.slug ?? sluggify(itemSource.name); - if (itemSource.type === "weapon" && slug === "handwraps-of-mighty-blows") { - const usage: { value: string } = itemSource.system.usage; - usage.value = "worn-gloves"; - } - - const dirPath = "systems/pf2e/icons/equipment/worn-items/other-worn-items"; - if (itemSource.img === `${dirPath}/handwraps-of-nighty-blows.webp`) { - itemSource.img = `${dirPath}/handwraps-of-mighty-blows.webp`; - } - } -} diff --git a/src/module/migration/migrations/666-usage-and-stowing-containers.ts b/src/module/migration/migrations/666-usage-and-stowing-containers.ts deleted file mode 100644 index 7ebb1ae4fc7..00000000000 --- a/src/module/migration/migrations/666-usage-and-stowing-containers.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { itemIsOfType } from "@item/helpers.ts"; -import { sluggify } from "@util"; -import { MigrationBase } from "../base.ts"; - -/** Set appropriate usage and mark certain containers as being for stowing */ -export class Migration666UsageAndStowingContainers extends MigrationBase { - static override version = 0.666; - - #backpacks = new Set([ - "backpack", - "knapsack-of-halflingkind", - "knapsack-of-halflingkind-greater", - "sturdy-satchel", - "vaultbreaker-harness", - "voyagers-pack", - ]); - - #wornGarment = new Set(["sleeves-of-storage", "sleeves-of-storage-greater"]); - - #wornGloves = new Set(["gloves-of-storing"]); - - #heldInTwoHands = new Set([ - "bag-of-devouring-type-i", - "bag-of-devouring-type-ii", - "bag-of-devouring-type-iii", - "bag-of-holding-type-i", - "bag-of-holding-type-ii", - "bag-of-holding-type-iii", - "bag-of-holding-type-iv", - "bag-of-weasels", - "chest", - "sealing-chest-greater", - "sealing-chest-lesser", - "sealing-chest-moderate", - ]); - - #stowingContainers = new Set([ - ...this.#backpacks, - ...this.#heldInTwoHands, - ...this.#wornGarment, - ...this.#wornGloves, - "extradimensional-stash", - ]); - - override async updateItem(source: ItemSourcePF2e): Promise { - if (!source.system.traits) return; - - const traits: TraitsWithUsage = source.system.traits; - // Delete old usage "traits": - if (typeof traits.usage?.value === "string") { - const traitUsage = traits.usage.value; - const isPhysical = itemIsOfType(source, "physical"); - const keepUsage = isPhysical && (traitUsage !== "held-in-one-hand" || source.system.usage?.value === ""); - if (isPhysical && keepUsage && source.system.usage) { - source.system.usage.value = traits.usage.value; - } - traits["-=usage"] = null; - } - - // Set usage on containers and whether they're for stowing - if (source.type !== "backpack") return; - - const slug = source.system.slug ?? sluggify(source.name); - - if (this.#backpacks.has(slug)) { - source.system.usage.value = "wornbackpack"; - } else if (this.#heldInTwoHands.has(slug)) { - source.system.usage.value = "held-in-two-hands"; - } else if (this.#wornGarment.has(slug)) { - source.system.usage.value = "worngarment"; - } else if (this.#wornGloves.has(slug)) { - source.system.usage.value = "worngloves"; - } else { - source.system.usage.value = "worn"; - } - - source.system.stowing = this.#stowingContainers.has(slug); - } -} - -interface TraitsWithUsage { - value?: string[]; - usage?: { value?: unknown }; - "-=usage"?: null; -} diff --git a/src/module/migration/migrations/667-hp-subproperties.ts b/src/module/migration/migrations/667-hp-subproperties.ts deleted file mode 100644 index 2c639e8baf6..00000000000 --- a/src/module/migration/migrations/667-hp-subproperties.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { RuleElementSource } from "@module/rules/index.ts"; -import { sluggify } from "@util"; -import { MigrationBase } from "../base.ts"; - -/** Add negativeHealing and recoveryMultiplier AE-like rules elements to certain feats */ -export class Migration667HPSubProperties extends MigrationBase { - static override version = 0.667; - - addRecoveryMultiplier(itemSource: ItemSourcePF2e, slug: string): void { - if (!["dream-may", "fast-recovery"].includes(slug)) return; - - const rules = itemSource.system.rules; - const needsRuleElement = !rules.some( - (rule: Record) => - "path" in rule && rule["path"] === "system.attributes.hp.recoveryMultiplier", - ); - if (needsRuleElement) { - const element: AELikeSource = { - key: "ActiveEffectLike", - mode: "add", - path: "system.attributes.hp.recoveryMultiplier", - value: 1, - }; - rules.push(element); - } - } - - addNegativeHealing(itemSource: ItemSourcePF2e, slug: string): void { - if (!["dhampir", "negative-healing"].includes(slug)) return; - const rules = itemSource.system.rules; - const needsRuleElement = !rules.some( - (rule: Record) => - "path" in rule && rule["path"] === "system.attributes.hp.negativeHealing", - ); - - if (needsRuleElement) { - const element: AELikeSource = { - key: "ActiveEffectLike", - mode: "override", - path: "system.attributes.hp.negativeHealing", - value: true, - }; - rules.push(element); - } - } - - override async updateItem(itemSource: ItemSourcePF2e): Promise { - if (itemSource.type !== "feat" && itemSource.type !== "action") return; - - const slug = itemSource.system.slug ?? sluggify(itemSource.name); - this.addRecoveryMultiplier(itemSource, slug); - this.addNegativeHealing(itemSource, slug); - } -} - -interface AELikeSource extends RuleElementSource { - mode?: JSONValue; - path?: JSONValue; - value?: JSONValue; -} diff --git a/src/module/migration/migrations/668-armor-speed-penalty.ts b/src/module/migration/migrations/668-armor-speed-penalty.ts deleted file mode 100644 index d75bdb451fc..00000000000 --- a/src/module/migration/migrations/668-armor-speed-penalty.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { RuleElementSource } from "@module/rules/index.ts"; -import { sluggify } from "@util"; -import * as R from "remeda"; -import { MigrationBase } from "../base.ts"; - -/** Remove RuleElement implementation of armor speed penalties */ -export class Migration668ArmorSpeedPenalty extends MigrationBase { - static override version = 0.668; - - override async updateItem(itemSource: ItemSourcePF2e): Promise { - const slug = itemSource.system.slug ?? sluggify(itemSource.name); - if (itemSource.type === "armor") { - const rules = (itemSource.system.rules ??= []); - const rule = rules.find( - (r: RuleElementSource & { selector?: unknown }) => - typeof r.key === "string" && - r.key.endsWith("FlatModifier") && - r.selector === "speed" && - "value" in r && - R.isPlainObject(typeof r.value) && - JSON.stringify(r.predicate ?? null) === JSON.stringify({ not: ["unburdened-iron"] }), - ); - if (rule) rules.splice(rules.indexOf(rule), 1); - } else if (itemSource.type === "feat") { - // Use rollOptions flags for ignoring the armor speed and stealth penalties - if (slug === "unburdened-iron") { - const rule: RollOption = { key: "RollOption", domain: "speed", option: "armor:ignore-speed-penalty" }; - itemSource.system.rules = [rule]; - } else if (slug === "armored-stealth") { - const rule: RollOption = { key: "RollOption", domain: "stealth", option: "armor:ignore-noisy-penalty" }; - itemSource.system.rules = [rule]; - } - } - } -} - -interface RollOption extends RuleElementSource { - domain?: string; - option: string; -} diff --git a/src/module/migration/migrations/669-npc-attack-effects.ts b/src/module/migration/migrations/669-npc-attack-effects.ts deleted file mode 100644 index 512c048dddc..00000000000 --- a/src/module/migration/migrations/669-npc-attack-effects.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { ActorSourcePF2e } from "@actor/data/index.ts"; -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { sluggify } from "@util"; -import { MigrationBase } from "../base.ts"; - -export class Migration669NPCAttackEffects extends MigrationBase { - static override version = 0.669; - - override async updateItem(item: ItemSourcePF2e, actor?: ActorSourcePF2e): Promise { - if (!actor || item.type !== "melee") return; - item.system.attackEffects ??= { value: [] }; - if (Array.isArray(item.system.attackEffects.value)) { - item.system.attackEffects.value.forEach((entry, index, arr) => { - arr[index] = sluggify(entry); - }); - } - } -} diff --git a/src/module/migration/migrations/670-ancestry-vision.ts b/src/module/migration/migrations/670-ancestry-vision.ts deleted file mode 100644 index b2b736f2fa9..00000000000 --- a/src/module/migration/migrations/670-ancestry-vision.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { ActorSourcePF2e } from "@actor/data/index.ts"; -import { ABCFeatureEntryData } from "@item/abc/data.ts"; -import { AncestrySource, ItemSourcePF2e } from "@item/base/data/index.ts"; -import { sluggify } from "@util"; -import { MigrationBase } from "../base.ts"; - -/** Move ancestry vision features from independent items to system data on ancestry items */ -export class Migration670AncestryVision extends MigrationBase { - static override version = 0.67; - - private DARKVISION_ID = "HHVQDp61ehcpdiU8"; - private LOWLIGHTVISION_ID = "DRtaqOHXTRtGRIUT"; - - override async updateActor(source: ActorSourcePF2e): Promise { - if (source.type !== "character") return; - - const ancestry = source.items.find((item): item is AncestrySource => item.type === "ancestry"); - if (ancestry) { - this.#setAncestryVision(ancestry); - for (const vision of ["darkvision", "low-light-vision"]) { - const index = source.items.findIndex( - (item) => item.type === "feat" && (item.system.slug ?? sluggify(item.name)) === vision, - ); - if (index !== -1) source.items.splice(index, 1); - } - } - } - - /** Only update independent world items */ - override async updateItem(source: ItemSourcePF2e, actor?: ActorSourcePF2e): Promise { - if (source.type === "ancestry" && !actor) { - this.#setAncestryVision(source); - } - } - - #setAncestryVision(ancestry: AncestrySource): void { - const features: Record = ancestry.system.items; - for (const [key, value] of Object.entries(features)) { - if (value?.id === this.LOWLIGHTVISION_ID) { - features[`-=${key}`] = null; - // Prefer darkvision if the ancestry item somehow has both features - const system: { vision: string } = ancestry.system; - system.vision = system.vision === "darkvision" ? "darkvision" : "lowLightVision"; - } else if (value?.id === this.DARKVISION_ID) { - features[`-=${key}`] = null; - ancestry.system.vision = "darkvision"; - } - } - ancestry.system.vision ??= "normal"; - } -} - -interface MaybeOldABCFeatureEntryData extends ABCFeatureEntryData { - pack?: string; - id?: string; -} diff --git a/src/module/migration/migrations/670-no-custom-trait.ts b/src/module/migration/migrations/670-no-custom-trait.ts deleted file mode 100644 index 12b4d247506..00000000000 --- a/src/module/migration/migrations/670-no-custom-trait.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { MigrationBase } from "../base.ts"; - -/** Remove the "custom" trait that snuck into item traits */ -export class Migration670NoCustomTrait extends MigrationBase { - static override version = 0.67; - - override async updateItem(itemSource: ItemSourcePF2e): Promise { - const traits: { value?: string[] } | undefined = itemSource.system.traits; - if (!traits) return; - if (Array.isArray(traits.value)) { - traits.value = traits.value.filter((trait) => trait && trait !== "custom"); - } else { - traits.value = []; - } - } -} diff --git a/src/module/migration/migrations/671-no-pc-items-on-non-pcs.ts b/src/module/migration/migrations/671-no-pc-items-on-non-pcs.ts deleted file mode 100644 index 520944c63c2..00000000000 --- a/src/module/migration/migrations/671-no-pc-items-on-non-pcs.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { ActorSourcePF2e } from "@actor/data/index.ts"; -import { MigrationBase } from "../base.ts"; - -/** Remove PC-only items from non-PCs */ -export class Migration671NoPCItemsOnNonPCs extends MigrationBase { - static override version = 0.671; - - override async updateActor(actorSource: ActorSourcePF2e): Promise { - if (actorSource.type === "character") return; - - const pcOnlyTypes = ["ancestry", "background", "class", "feat"]; - const forbiddenItems = actorSource.items.filter((item) => pcOnlyTypes.includes(item.type)); - for (const forbiddenItem of forbiddenItems) { - const index = actorSource.items.findIndex((item) => item === forbiddenItem); - if (index !== -1) actorSource.items.splice(index, 1); - } - } -} diff --git a/src/module/migration/migrations/672-remove-npc-base-properties.ts b/src/module/migration/migrations/672-remove-npc-base-properties.ts deleted file mode 100644 index 0d581d0b076..00000000000 --- a/src/module/migration/migrations/672-remove-npc-base-properties.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { ActorSourcePF2e } from "@actor/data/index.ts"; -import { SAVE_TYPES } from "@actor/values.ts"; -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { MigrationBase } from "../base.ts"; - -/** Remove the extra `base` subproperty of labeled values on NPCs */ -export class Migration672RemoveNPCBaseProperties extends MigrationBase { - static override version = 0.672; - - #removeBase(property: PropertyWithBase, replace: "value" | "max" = "value"): void { - property[replace] = Number(property[replace]) || 0; - if (typeof property.base === "number") { - property[replace] = property.base; - property["-=base"] = null; - } - } - - override async updateActor(source: ActorSourcePF2e): Promise { - if (source.type !== "npc") return; - - const attributes: OldNPCAttributes = source.system.attributes; - const { ac, hp, perception } = attributes; - if (ac) this.#removeBase(ac); - if (hp) this.#removeBase(hp, "max"); - if (perception) this.#removeBase(perception); - - const { saves } = source.system; - for (const saveType of SAVE_TYPES) { - this.#removeBase(saves[saveType]); - } - } - - override async updateItem(source: ItemSourcePF2e, actorSource?: ActorSourcePF2e): Promise { - if (actorSource?.type === "npc" && source.type === "lore") { - this.#removeBase(source.system.mod); - } - } -} - -interface PropertyWithBase { - value: number; - max?: number; - base?: number; - "-=base"?: null; -} - -interface OldNPCAttributes { - ac?: PropertyWithBase; - hp?: PropertyWithBase; - perception?: PropertyWithBase; -} diff --git a/src/module/migration/migrations/673-remove-bulwark-res.ts b/src/module/migration/migrations/673-remove-bulwark-res.ts deleted file mode 100644 index 02cdb004045..00000000000 --- a/src/module/migration/migrations/673-remove-bulwark-res.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { MigrationBase } from "../base.ts"; -import { isObject, sluggify } from "@util"; -import { RuleElementSource } from "@module/rules/index.ts"; -import { PredicateStatement } from "@system/predication.ts"; - -/** Remove bulwark armor rule elements */ -export class Migration673RemoveBulwarkREs extends MigrationBase { - static override version = 0.673; - - private hasRuleElement(rules: RuleElementSource[]): boolean { - return rules.some( - (r) => - typeof r.key === "string" && - r.key.endsWith("FlatModifier") && - isObject(r.predicate) && - !!r.predicate.all?.includes("self:armor:trait:bulwark"), - ); - } - - override async updateItem(item: ItemSourcePF2e): Promise { - const { rules } = item.system; - if (item.type === "armor") { - const index = rules.findIndex( - (rule: RESourceWithAbility) => - typeof rule.key === "string" && - rule.key.endsWith("FlatModifier") && - rule.selector === "reflex" && - rule.type === "ability" && - /bulwark/i.test(String(rule.label ?? "")), - ); - if (index !== -1) rules.splice(index); - } - - const slug = item.system.slug ?? sluggify(item.name); - if (item.type === "feat" && slug === "mighty-bulwark" && !this.hasRuleElement(rules)) { - const newRules = [ - { - key: "FlatModifier", - predicate: { all: ["self:armor:trait:bulwark"] }, - selector: "reflex", - type: "untyped", - value: 4, - }, - { - key: "RollOption", - domain: "reflex", - option: "self:armor:bulwark-all", - }, - ]; - rules.push(...newRules); - } - } -} - -interface RESourceWithAbility extends RuleElementSource { - selector?: string; - type?: string; -} - -interface OldRawPredicate { - all?: PredicateStatement[]; - any?: PredicateStatement[]; - not?: PredicateStatement[]; -} diff --git a/src/module/migration/migrations/674-stable-homebrew-tag-ids.ts b/src/module/migration/migrations/674-stable-homebrew-tag-ids.ts deleted file mode 100644 index 56eaddb90aa..00000000000 --- a/src/module/migration/migrations/674-stable-homebrew-tag-ids.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { ActorSourcePF2e } from "@actor/data/index.ts"; -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { HOMEBREW_TRAIT_KEYS, HomebrewTag } from "@system/settings/homebrew/index.ts"; -import { sluggify } from "@util"; -import * as R from "remeda"; -import { MigrationBase } from "../base.ts"; - -export class Migration674StableHomebrewTagIDs extends MigrationBase { - static override version = 0.674; - - #homebrewKeys = fu.deepClone(HOMEBREW_TRAIT_KEYS); - - #homebrewTags = this.#homebrewKeys.reduce( - (settings, key) => fu.mergeObject(settings, { [key]: game.settings.get("pf2e", `homebrew.${key}`) }), - {} as Record<(typeof HOMEBREW_TRAIT_KEYS)[number], HomebrewTag[]>, - ); - - #updateDocumentTags(documentTags: string[] = []): void { - for (const key of this.#homebrewKeys) { - const homebrewTags = this.#homebrewTags[key]; - for (const tag of homebrewTags) { - const index = documentTags.indexOf(tag.id); - if (index !== -1) documentTags.splice(index, 1, `hb_${sluggify(tag.value)}`); - } - } - } - - override async updateActor(source: MaybeWithExtraNestedTraits): Promise { - if (source.type === "familiar" || !source.system.traits?.traits) return; - - this.#updateDocumentTags(source.system.traits.traits.value); - if (source.type === "character" || source.type === "npc") { - const traits: unknown = source.system.traits; - if (R.isObject(traits) && R.isObject(traits.languages) && Array.isArray(traits.languages.value)) { - this.#updateDocumentTags(traits.languages.value); - } - } - } - - override async updateItem(source: ItemSourcePF2e): Promise { - this.#updateDocumentTags(source.system.traits?.value); - } - - override async migrate(): Promise { - for (const key of this.#homebrewKeys) { - const tags: { id: string; value: string }[] = this.#homebrewTags[key]; - - for (const tag of tags) { - tag.id = `hb_${sluggify(tag.value)}`; - const tagMap: Record = - key === "baseWeapons" ? CONFIG.PF2E.baseWeaponTypes : CONFIG.PF2E[key]; - tagMap[tag.id] = tag.value; - delete tagMap[key]; - } - if (tags.length > 0) await game.settings.set("pf2e", `homebrew.${key}`, tags); - } - } -} - -type MaybeWithExtraNestedTraits = ActorSourcePF2e & { - system: { - traits: { - traits?: { value: string[] }; - }; - }; -}; diff --git a/src/module/migration/migrations/675-flat-modifier-aes-to-res.ts b/src/module/migration/migrations/675-flat-modifier-aes-to-res.ts deleted file mode 100644 index 7503c0cb440..00000000000 --- a/src/module/migration/migrations/675-flat-modifier-aes-to-res.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { ActorSourcePF2e } from "@actor/data/index.ts"; -import type { ModifierType } from "@actor/modifiers.ts"; -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import type { FlatModifierSource } from "@module/rules/rule-element/flat-modifier.ts"; -import { MigrationBase } from "../base.ts"; - -/** Convert experimental FlatModifier `ActiveEffect`s to Rule Elements */ -export class Migration675FlatModifierAEsToREs extends MigrationBase { - static override version = 0.675; - - #isFlatModifier(data: unknown): data is ActiveEffectModifier { - const dataIsModifier = (obj: { - modifier?: unknown; - type?: unknown; - }): obj is { modifier: string | number; type: string } => - (typeof obj.modifier === "number" || typeof obj.modifier === "string") && typeof obj.type === "string"; - return typeof data === "object" && data !== null && dataIsModifier(data); - } - - #toRuleElement(aeValue: string): FlatModifierSource | null { - const aeModifier = ((): ActiveEffectModifier | null => { - try { - const parsed = JSON.parse(aeValue); - return this.#isFlatModifier(parsed) ? parsed : null; - } catch (error) { - console.warn(error); - return null; - } - })(); - if (typeof aeModifier?.modifier === "string") { - aeModifier.modifier.replace("@data.", "@"); - } - - return aeModifier && { key: "FlatModifier", type: aeModifier.type, value: aeModifier.modifier, selector: "hp" }; - } - - override async updateActor(actorSource: ActorSourcePF2e): Promise { - for (const effect of [...actorSource.effects]) { - if (effect.changes.some((change) => change.key.endsWith(".modifiers"))) { - actorSource.effects.splice(actorSource.effects.indexOf(effect), 1); - } - } - } - - override async updateItem(itemSource: ItemSourcePF2e): Promise { - for (const effect of [...itemSource.effects]) { - for (const change of effect.changes.filter((change) => change.key.endsWith(".modifiers"))) { - const reData = this.#toRuleElement(change.value); - if (reData) { - effect.changes.splice(effect.changes.indexOf(change), 1); - itemSource.system.rules.push(reData); - } - } - if (effect.changes.length === 0) { - itemSource.effects.splice(itemSource.effects.indexOf(effect), 1); - } - } - } -} - -interface ActiveEffectModifier { - name: string; - key: string; - type: ModifierType; - modifier: string | number; -} diff --git a/src/module/migration/migrations/676-replace-items-with-re-like-aes.ts b/src/module/migration/migrations/676-replace-items-with-re-like-aes.ts deleted file mode 100644 index 9fe96fa70d6..00000000000 --- a/src/module/migration/migrations/676-replace-items-with-re-like-aes.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { ActorSourcePF2e } from "@actor/data/index.ts"; -import type { ItemPF2e } from "@item"; -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { MigrationBase } from "../base.ts"; - -/** Replace items containing FlatModifier `ActiveEffect`s with latest ones without */ -export class Migration676ReplaceItemsWithRELikeAEs extends MigrationBase { - static override version = 0.676; - - /** The feats Toughness and Mountain's Stoutness */ - private toughnessPromise = fromUuid("Compendium.pf2e.feats-srd.AmP0qu7c5dlBSath"); - private stoutnessPromise = fromUuid("Compendium.pf2e.feats-srd.COP89tjrNhEucuRW"); - - /** The familiar ability Tough */ - private toughPromise = fromUuid("Compendium.pf2e.familiar-abilities.Le8UWr5BU8rV3iBf"); - - private replaceItem({ items, type, slug, replacement }: ReplaceItemArgs): void { - if (!replacement) throw new Error("Unexpected error retrieving compendium item"); - const current = items.find( - (itemSource) => itemSource.type === type && itemSource.system.slug?.replace(/'/g, "") === slug, - ); - if (current) { - const newSource = replacement.toObject(); - if (current.type === "feat" && newSource.type === "feat") { - newSource.system.location = current.system.location; - } - items.splice(items.indexOf(current), 1, newSource); - } - } - - override async updateActor(actorSource: ActorSourcePF2e): Promise { - if (actorSource.type === "familiar") { - this.replaceItem({ - items: actorSource.items, - type: "effect", - slug: "tough", - replacement: await this.toughPromise, - }); - } else if (actorSource.type === "character") { - this.replaceItem({ - items: actorSource.items, - type: "feat", - slug: "toughness", - replacement: await this.toughnessPromise, - }); - this.replaceItem({ - items: actorSource.items, - type: "feat", - slug: "mountains-stoutness", - replacement: await this.stoutnessPromise, - }); - } - } -} - -interface ReplaceItemArgs { - items: ItemSourcePF2e[]; - type: string; - slug: string; - replacement: ItemPF2e | null; -} diff --git a/src/module/migration/migrations/677-rule-value-data-refs.ts b/src/module/migration/migrations/677-rule-value-data-refs.ts deleted file mode 100644 index ddb8fb5f4cd..00000000000 --- a/src/module/migration/migrations/677-rule-value-data-refs.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { MigrationBase } from "../base.ts"; - -/** Fix Rule Element values reaching for too many datas */ -export class Migration677RuleValueDataRefs extends MigrationBase { - static override version = 0.677; - - override async updateItem(source: ItemSourcePF2e): Promise { - for (const rule of source.system.rules) { - if ("value" in rule && typeof rule.value === "string") { - rule.value = rule.value.replace("@data.", "@"); - } - } - } -} diff --git a/src/module/migration/migrations/678-separate-npc-attack-traits.ts b/src/module/migration/migrations/678-separate-npc-attack-traits.ts deleted file mode 100644 index 049e8237b3d..00000000000 --- a/src/module/migration/migrations/678-separate-npc-attack-traits.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { RARITIES } from "@module/data.ts"; -import { tupleHasValue } from "@util"; -import * as R from "remeda"; -import { MigrationBase } from "../base.ts"; - -/** Remove exclusive NPC attack traits from weapons */ -export class Migration678SeparateNPCAttackTraits extends MigrationBase { - static override version = 0.678; - - override async updateItem(itemSource: ItemWithRarityObject): Promise { - if (itemSource.type === "weapon") { - const weaponTraits = itemSource.system.traits.value; - const rangeTraits = weaponTraits.filter((trait) => /^range(?!d)/.test(trait)); - for (const trait of rangeTraits) { - weaponTraits.splice(weaponTraits.indexOf(trait), 1); - } - const reloadTraits = weaponTraits.filter((trait) => trait.startsWith("reload")); - for (const trait of reloadTraits) { - weaponTraits.splice(weaponTraits.indexOf(trait), 1); - } - itemSource.system.traits.value = [...new Set(weaponTraits)].sort(); - } - - // While we're at it ... - if (!itemSource.system.traits) return; - const itemTraits: string[] = itemSource.system.traits.value ?? []; - for (const trait of itemTraits) { - if (tupleHasValue(RARITIES, trait)) { - itemTraits.splice(itemTraits.indexOf(trait), 1); - if ( - trait !== "common" && - R.isObject(itemSource.system.traits.rarity) && - itemSource.system.traits.rarity.value === "common" - ) { - itemSource.system.traits.rarity.value = trait; - } - } - } - } -} - -type ItemWithRarityObject = ItemSourcePF2e & { - system: { - traits?: { - rarity: string | { value: string }; - }; - }; -}; diff --git a/src/module/migration/migrations/679-tower-shield-speed-penalty.ts b/src/module/migration/migrations/679-tower-shield-speed-penalty.ts deleted file mode 100644 index 43b36332cb6..00000000000 --- a/src/module/migration/migrations/679-tower-shield-speed-penalty.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { ArmorSource, ItemSourcePF2e } from "@item/base/data/index.ts"; -import { ZeroToFour } from "@module/data.ts"; -import { MigrationBase } from "../base.ts"; - -/** Set a speed penalty of -5 on all tower shields, plus some basic tidying */ -export class Migration679TowerShieldSpeedPenalty extends MigrationBase { - static override version = 0.679; - - private towerShieldSlugs = [ - "darkwood-tower-shield-high-grade", - "darkwood-tower-shield-standard-grade", - "tower-shield", - ]; - - override async updateItem(source: ItemSourcePF2e): Promise { - if (source.type === "armor") { - const systemData: ArmorSystemSourceWithResilient = source.system; - - if (systemData.speed && this.towerShieldSlugs.includes(systemData.slug ?? "")) { - systemData.speed.value = -5; - } - - if (systemData.armor) { - systemData.armor.value = Number(systemData.armor.value) || 0; - } - if (systemData.speed) { - systemData.speed.value = Number(systemData.speed.value) || 0; - } - const potencyRune = systemData.potencyRune ?? {}; - potencyRune.value = (Number(systemData.potencyRune?.value) || 0) as ZeroToFour; - if ("resilient" in systemData) { - // Aborted attempt to store rune data? - systemData["-=resilient"] = null; - } - } - } -} - -type ArmorSystemSourceWithResilient = ArmorSource["system"] & { - potencyRune?: { value?: ZeroToFour | null } | undefined; - resilient?: unknown; - speed?: { value: number }; - armor?: { value: number }; - "-=resilient"?: null; -}; diff --git a/src/module/migration/migrations/680-set-weapon-hands.ts b/src/module/migration/migrations/680-set-weapon-hands.ts deleted file mode 100644 index d908204dc4b..00000000000 --- a/src/module/migration/migrations/680-set-weapon-hands.ts +++ /dev/null @@ -1,132 +0,0 @@ -import type { ItemSourcePF2e } from "@item/base/data/index.ts"; -import * as R from "remeda"; -import type { ItemSource } from "types/foundry/common/documents/item.d.ts"; -import { MigrationBase } from "../base.ts"; - -/** Set the "hands" (usage) property of weapons */ -export class Migration680SetWeaponHands extends MigrationBase { - static override version = 0.68; - - #oneHandedWeapons = new Set(["repeating-hand-crossbow"]); - - #onePlusHandedWeapons = new Set([ - "backpack-ballista", - "backpack-catapult", - "composite-longbow", - "composite-shortbow", - "hongali-hornbow", - "longbow", - "shortbow", - "sun-sling", - ]); - - #shieldAttachments = new Set(["shield-boss", "shield-spikes"]); - - #firearmAttachments = new Set(["bayonette", "reinforced-stock"]); - - #wornGloves = new Set(["handwraps-of-mighty-blows"]); - - #twoHandedWeapons = new Set([ - "adze", - "alchemical-crossbow", - "arquebus", - "axe-musket", - "bladed-diabolo", - "bladed-scarf", - "blunderbuss", - "bo-staff", - "boarding-pike", - "butchering-axe", - "combat-grapnel", - "crossbow", - "double-barreled-musket", - "dueling-spear", - "dwarven-scattergun", - "elven-branched-spear", - "elven-curve-blade", - "explosive-dogslicer", - "falchion", - "fauchard", - "fire-lance", - "flingflenser", - "flintlock-musket", - "gill-hook", - "glaive", - "gnome-amalgam-musket", - "greataxe", - "greatclub", - "greatpick", - "greatsword", - "guisarme", - "gun-sword", - "halberd", - "halfling-sling-staff", - "hammer-gun", - "harmona-gun", - "heavy-crossbow", - "horsechopper", - "kusarigama", - "lance", - "longspear", - "maul", - "meteor-hammer", - "mithral-tree", - "naginata", - "ogre-hook", - "ranseur", - "repeating-crossbow", - "repeating-heavy-crossbow", - "scythe", - "shauth-lash", - "spiked-chain", - "taw-launcher", - "three-peaked-tree", - "thundermace", - "war-flail", - "whip-claw", - ]); - - #isShield( - source: ItemSource & { system: { category?: unknown; armorType?: { value?: unknown } } }, - ): source is MaybeOldShieldData { - const category: unknown = - source.type === "armor" ? source.system.armorType?.value || source.system.category : null; - return category === "shield"; - } - - override async updateItem(source: ItemSourcePF2e): Promise { - if (this.#isShield(source) && R.isObject(source.system.usage)) { - source.system.usage.value = "held-in-one-hand"; - } else if (source.type === "weapon") { - source.system.usage ??= { value: "held-in-one-hand" }; - - const { baseItem, slug, traits } = source.system; - const usage: { value: string } = source.system.usage; - - if (this.#twoHandedWeapons.has(baseItem || slug || "")) { - usage.value = "held-in-two-hands"; - } else if (this.#onePlusHandedWeapons.has(baseItem || slug || "")) { - usage.value = "held-in-one-plus-hands"; - } else if (this.#oneHandedWeapons.has(baseItem || slug || "")) { - usage.value = "held-in-one-hand"; - } else if (this.#shieldAttachments.has(baseItem || slug || "")) { - usage.value = "held-in-one-hand"; - const attachedIndex = traits.value.findIndex((trait: string) => trait === "attached"); - if (attachedIndex !== -1) traits.value.splice(attachedIndex, 1, "attached-to-shield"); - } else if (this.#firearmAttachments.has(baseItem || slug || "")) { - usage.value = "held-in-one-hand"; - const attachedIndex = traits.value.findIndex((trait: string) => trait === "attached"); - if (attachedIndex !== -1) traits.value.splice(attachedIndex, 1, "attached-to-crossbow-or-firearm"); - } else if (this.#wornGloves.has(baseItem || slug || "")) { - usage.value = "worn-gloves"; - } - } - } -} - -interface MaybeOldShieldData extends ItemSource { - system: { - usage?: { value?: string }; - armorType?: { value?: unknown }; - }; -} diff --git a/src/module/migration/migrations/681-giant-language-to-jotun.ts b/src/module/migration/migrations/681-giant-language-to-jotun.ts deleted file mode 100644 index e5287ef67ee..00000000000 --- a/src/module/migration/migrations/681-giant-language-to-jotun.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { ActorSourcePF2e } from "@actor/data/index.ts"; -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import * as R from "remeda"; -import { MigrationBase } from "../base.ts"; - -/** Replace the "Giant" language with "Jotun" */ -export class Migration681GiantLanguageToJotun extends MigrationBase { - static override version = 0.681; - - #replaceGiant(languages: string[]): void { - const giantIndex = languages.indexOf("giant"); - if (giantIndex !== -1) languages.splice(giantIndex, 1, "jotun"); - } - - override async updateActor(source: ActorSourcePF2e): Promise { - if (!(source.type === "character" || source.type === "npc")) return; - const traits: unknown = source.system.traits; - if (R.isObject(traits) && R.isObject(traits.languages) && Array.isArray(traits.languages.value)) { - this.#replaceGiant(traits.languages.value); - } - } - - override async updateItem(source: ItemSourcePF2e): Promise { - if (source.type === "ancestry") { - this.#replaceGiant(source.system.additionalLanguages.value); - } - } -} diff --git a/src/module/migration/migrations/682-biography-fields.ts b/src/module/migration/migrations/682-biography-fields.ts deleted file mode 100644 index 69929334499..00000000000 --- a/src/module/migration/migrations/682-biography-fields.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { CharacterSystemData, CharacterSystemSource } from "@actor/character/data.ts"; -import { ActorSourcePF2e } from "@actor/data/index.ts"; -import { MigrationBase } from "../base.ts"; - -interface CharacterSystemDataOld extends CharacterSystemSource { - details: CharacterSystemData["details"] & { - biography: CharacterSystemData["details"]["biography"] & { - organaizations?: string; - public?: string | null; - "-=public"?: string | null; - value?: string | null; - "-=value"?: string | null; - }; - }; -} - -/** change Biography fields on characters. Public to appearanc, Private to campaignNotes */ -export class Migration682BiographyFields extends MigrationBase { - static override version = 0.682; - - /** Fix Biography migration. Correctly migrate fields and then remove them*/ - replaceBiographyData(old: CharacterSystemDataOld): void { - if (old.details.biography.public) { - old.details.biography.appearance = old.details.biography.public; - old.details.biography["-=public"] = null; - } else { - old.details.biography.appearance ??= ""; - } - if (old.details.biography.value) { - old.details.biography.campaignNotes = old.details.biography.value; - old.details.biography["-=value"] = null; - } else { - old.details.biography.campaignNotes ??= ""; - } - if (!("game" in globalThis)) { - // migration runner - delete old.details.biography.public; - delete old.details.biography.value; - } - old.details.biography.backstory ??= ""; - old.details.biography.birthPlace ??= ""; - old.details.biography.attitude ??= ""; - old.details.biography.beliefs ??= ""; - old.details.biography.likes ??= ""; - old.details.biography.dislikes ??= ""; - old.details.biography.catchphrases ??= ""; - old.details.biography.allies ??= ""; - old.details.biography.enemies ??= ""; - old.details.biography.organaizations ??= ""; - } - - override async updateActor(source: ActorSourcePF2e): Promise { - if (source.type !== "character") return; - this.replaceBiographyData(source.system as CharacterSystemDataOld); - } -} diff --git a/src/module/migration/migrations/683-flavortext-to-public-notes.ts b/src/module/migration/migrations/683-flavortext-to-public-notes.ts deleted file mode 100644 index 5b718f86666..00000000000 --- a/src/module/migration/migrations/683-flavortext-to-public-notes.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { ActorSourcePF2e } from "@actor/data/index.ts"; -import { NPCSystemSource } from "@actor/npc/data.ts"; -import { MigrationBase } from "../base.ts"; - -interface NPCSystemDataOld extends NPCSystemSource { - details: NPCSystemSource["details"] & { - flavorText?: string; - "-=flavorText"?: string | null; - }; -} - -/** Change Flavortext field on NPCs to PublicNotes and add new fields to NPCs */ -export class Migration683FlavorTextToPublicNotes extends MigrationBase { - static override version = 0.683; - - /** Migrate flavorText to public Notes and remove flavorText */ - replaceFlavorTextData(old: NPCSystemDataOld): void { - if (old.details.flavorText) { - old.details.publicNotes = old.details.flavorText; - old.details["-=flavorText"] = null; - } else { - old.details.publicNotes ??= ""; - } - if (!("game" in globalThis)) { - // migration runner - delete old.details.flavorText; - } - old.details.blurb ??= ""; - old.details.privateNotes ??= ""; - } - - override async updateActor(source: ActorSourcePF2e): Promise { - if (source.type === "npc") { - this.replaceFlavorTextData(source.system); - } - } -} diff --git a/src/module/migration/migrations/684-rations-to-consumable.ts b/src/module/migration/migrations/684-rations-to-consumable.ts deleted file mode 100644 index 58a90f0124b..00000000000 --- a/src/module/migration/migrations/684-rations-to-consumable.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { ActorSourcePF2e } from "@actor/data/index.ts"; -import type { ConsumablePF2e } from "@item"; -import { EquipmentSource, ItemSourcePF2e } from "@item/base/data/index.ts"; -import { KitEntryData } from "@item/kit/data.ts"; -import { isObject } from "@util"; -import { MigrationBase } from "../base.ts"; - -/** Convert rations to a consumable with seven uses */ -export class Migration684RationsToConsumable extends MigrationBase { - static override version = 0.684; - - #rationsSourceId = "Compendium.pf2e.equipment-srd.L9ZV076913otGtiB"; - - #rationsPromise = fromUuid(this.#rationsSourceId); - - #isOldRations(itemSource: ItemSourcePF2e | null): itemSource is EquipmentSource { - return itemSource?.type === "equipment" && itemSource.flags.core?.sourceId === this.#rationsSourceId; - } - - /** Get all references to the Rations item in a kit */ - #getRationRefs(itemRefs: KitEntryWithPackAndID[]): RationEntryData[] { - return itemRefs.reduce((rationRefs: RationEntryData[], itemRef) => { - if (itemRef.isContainer && itemRef.items) { - rationRefs.push(...this.#getRationRefs(Object.values(itemRef.items))); - } else if (itemRef.pack === "pf2e.equipment-srd" && itemRef.id === "L9ZV076913otGtiB") { - rationRefs.push(itemRef as RationEntryData); - } - return rationRefs; - }, []); - } - - /** Swap "equipment" rations for new consumable */ - override async updateActor(source: ActorSourcePF2e): Promise { - const oldRations = source.items.filter((i): i is EquipmentSource => this.#isOldRations(i)); - const rations = await this.#rationsPromise; - if (!rations) { - throw new Error("Unexpected error acquiring compendium item"); - } - - for (const oldRation of oldRations) { - const newRation = rations.toObject(); - newRation.folder = oldRation.folder; - newRation.sort = oldRation.sort; - - const oldContainerId = oldRation.system.containerId ?? { value: null }; - if (oldContainerId instanceof Object) { - newRation.system.containerId = oldContainerId.value; - } - - const oldQuantity: number | { value: number } = oldRation.system.quantity; - if (isObject<{ value: number }>(oldQuantity)) { - newRation.system.quantity = Math.ceil((oldQuantity.value ?? 1) / 7); - } - source.items.findSplice((item) => item === oldRation, newRation); - } - } - - /** Lower the quantity of rations contained in kits */ - override async updateItem(source: ItemSourcePF2e): Promise { - if (source.type !== "kit") return; - - const rationRefs = this.#getRationRefs(Object.values(source.system.items)); - for (const rationRef of rationRefs) { - rationRef.quantity = Math.ceil(rationRef.quantity / 7); - } - } -} - -interface KitEntryWithPackAndID extends KitEntryData { - pack?: string; - id?: string; - items?: Record; -} - -interface RationEntryData extends KitEntryWithPackAndID { - id: "L9ZV076913otGtiB"; - pack: "pf2e.equipment-srd"; -} diff --git a/src/module/migration/migrations/685-fix-melee-usage-traits.ts b/src/module/migration/migrations/685-fix-melee-usage-traits.ts deleted file mode 100644 index 472844b5fee..00000000000 --- a/src/module/migration/migrations/685-fix-melee-usage-traits.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { ComboWeaponMeleeUsage } from "@item/weapon/data.ts"; -import { WeaponTrait } from "@item/weapon/types.ts"; -import { MigrationBase } from "../base.ts"; - -/** Fix melee-usage traits on combination weapons */ -export class Migration685FixMeleeUsageTraits extends MigrationBase { - static override version = 0.685; - - override async updateItem(itemSource: ItemSourcePF2e): Promise { - if (itemSource.type === "weapon") { - const systemData: { meleeUsage?: MaybeBadMeleeUsage } = itemSource.system; - if (systemData.meleeUsage && !Array.isArray(systemData.meleeUsage.traits)) { - systemData.meleeUsage.traits = systemData.meleeUsage.traits?.value ?? []; - } - } - } -} - -interface MaybeBadMeleeUsage extends Omit { - traits?: WeaponTrait[] | { value: WeaponTrait[] }; -} diff --git a/src/module/migration/migrations/686-hero-points-to-resources.ts b/src/module/migration/migrations/686-hero-points-to-resources.ts deleted file mode 100644 index ecca16e53e7..00000000000 --- a/src/module/migration/migrations/686-hero-points-to-resources.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { CharacterSystemSource } from "@actor/character/data.ts"; -import { ActorSourcePF2e } from "@actor/data/index.ts"; -import { MigrationBase } from "../base.ts"; - -/** Move hero points from attributes to resources */ -export class Migration686HeroPointsToResources extends MigrationBase { - static override version = 0.686; - - override async updateActor(source: ActorSourcePF2e): Promise { - if (source.type !== "character") return; - - const systemSource: MaybeWithOldHeroPoints = source.system; - if (systemSource.attributes.heroPoints) { - const resources: { heroPoints: { value: number } } = systemSource.resources; - resources.heroPoints = { value: systemSource.attributes.heroPoints.rank }; - systemSource.attributes["-=heroPoints"] = null; - if (!("game" in globalThis)) delete systemSource.attributes.heroPoints; - } - } -} - -type MaybeWithOldHeroPoints = CharacterSystemSource & { - attributes: { - heroPoints?: { rank: number; max: number }; - "-=heroPoints"?: null; - }; -}; diff --git a/src/module/migration/migrations/687-familiarity-aes-to-res.ts b/src/module/migration/migrations/687-familiarity-aes-to-res.ts deleted file mode 100644 index ffaf914d18d..00000000000 --- a/src/module/migration/migrations/687-familiarity-aes-to-res.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { ActorSourcePF2e } from "@actor/data/index.ts"; -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { RuleElementSource } from "@module/rules/index.ts"; -import { PredicateStatement } from "@system/predication.ts"; -import { MigrationBase } from "../base.ts"; - -/** Convert weapon familiarity `ActiveEffect`s to Rule Elements */ -export class Migration687FamiliarityAEsToREs extends MigrationBase { - static override version = 0.687; - - private isFamiliarityAE(data: unknown): data is AEFamiliarityValue { - const dataIsFamiliarity = (obj: { - trait?: unknown; - category?: unknown; - }): obj is { trait: string; category: string } => - typeof obj.trait === "string" && typeof obj.category === "string"; - return typeof data === "object" && data !== null && dataIsFamiliarity(data); - } - - private toRuleElement(sameAs: string, aeValue: string): LinkedProficiencySource | null { - const aeData = ((): AEFamiliarityValue | null => { - try { - const parsed = JSON.parse(aeValue); - return this.isFamiliarityAE(parsed) ? parsed : null; - } catch (error) { - console.warn(error); - return null; - } - })(); - return ( - aeData && { - key: "LinkedProficiency", - slug: `${aeData.category}-${aeData.trait}-weapons`, - predicate: { all: [`weapon:trait:${aeData.trait}`, `weapon:category:${aeData.category}`] }, - sameAs, - } - ); - } - - override async updateActor(actorSource: ActorSourcePF2e): Promise { - actorSource.effects = []; - } - - override async updateItem(itemSource: ItemSourcePF2e): Promise { - for (const effect of [...itemSource.effects]) { - for (const change of effect.changes.filter((change) => change.key.startsWith("system.martial."))) { - const linkTo = change.key.replace(/^data\.martial\.|\.familiarity$/g, ""); - const reData = this.toRuleElement(linkTo, change.value); - if (reData) itemSource.system.rules.push(reData); - } - } - itemSource.effects = []; - } -} - -interface AEFamiliarityValue { - trait: string; - category: string; -} - -interface LinkedProficiencySource extends Omit { - key: "LinkedProficiency"; - slug: string; - predicate: OldRawPredicate; - sameAs: string; -} - -interface OldRawPredicate { - all?: PredicateStatement[]; - any?: PredicateStatement[]; - not?: PredicateStatement[]; -} diff --git a/src/module/migration/migrations/688-clamp-spell-level.ts b/src/module/migration/migrations/688-clamp-spell-level.ts deleted file mode 100644 index 6cdb81f26a3..00000000000 --- a/src/module/migration/migrations/688-clamp-spell-level.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { OneToTen } from "@module/data.ts"; -import { MigrationBase } from "../base.ts"; - -/** Ensure spells have a minimum level of one */ -export class Migration688ClampSpellLevel extends MigrationBase { - static override version = 0.688; - - override async updateItem(itemSource: ItemSourcePF2e): Promise { - if (itemSource.type === "spell") { - itemSource.system.level.value = Math.min(Math.max(itemSource.system.level.value, 1), 10) as OneToTen; - } - } -} diff --git a/src/module/migration/migrations/689-encumberance-aes.ts b/src/module/migration/migrations/689-encumberance-aes.ts deleted file mode 100644 index 7f7ce7ec2b8..00000000000 --- a/src/module/migration/migrations/689-encumberance-aes.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { RuleElementSource } from "@module/rules/index.ts"; -import { sluggify } from "@util"; -import { MigrationBase } from "../base.ts"; - -/** Add AE-likes to set encumbrance "bonuses" */ -export class Migration689EncumberanceActiveEffects extends MigrationBase { - static override version = 0.689; - - override async updateItem(itemSource: ItemSourcePF2e): Promise { - const systemData = itemSource.system; - const slug = systemData.slug ?? sluggify(itemSource.name); - - const amountToIncreaseBy = (() => { - if (itemSource.type === "feat" && slug === "hefty-hauler") { - return 2; - } else if (itemSource.type === "feat" && slug === "hardy-traveler") { - return 1; - } else if (itemSource.type === "equipment" && slug === "lifting-belt") { - return 1; - } - - return 0; - })(); - - if (amountToIncreaseBy === 0) return; - - const alreadyMigrated = systemData.rules.some((r) => r.key === "ActiveEffectLike"); - if (alreadyMigrated) return; - - const rules: (RuleElementSource & { [key: string]: unknown })[] = [ - { - key: "ActiveEffectLike", - path: "system.attributes.bonusEncumbranceBulk", - mode: "add", - value: amountToIncreaseBy, - }, - { - key: "ActiveEffectLike", - path: "system.attributes.bonusLimitBulk", - mode: "add", - value: amountToIncreaseBy, - }, - ]; - - for (const rule of rules) { - systemData.rules.push(rule); - } - } -} diff --git a/src/module/migration/migrations/690-tiebreak-items.ts b/src/module/migration/migrations/690-tiebreak-items.ts deleted file mode 100644 index d16845e5cfa..00000000000 --- a/src/module/migration/migrations/690-tiebreak-items.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { MigrationBase } from "../base.ts"; - -/** Add AE-likes to certain items that give the owner a higher tiebreak priority */ -export class Migration690InitiativeTiebreakItems extends MigrationBase { - /** Nice */ - static override version = 0.69; - - /** Feats and equipment with a tie-breaking feature */ - private itemSlugs = ["ambush-awareness", "elven-instincts", "pilgrims-token"]; - - /** Sets the tiebreak priority for the item owner from 2 (PCs) to 0 */ - private rule = { - key: "ActiveEffectLike", - path: "system.attributes.initiative.tiebreakPriority", - mode: "override", - value: 0, - }; - - override async updateItem(itemSource: ItemSourcePF2e): Promise { - if ( - this.itemSlugs.includes(itemSource.system.slug ?? "") && - !itemSource.system.rules.some((rule) => rule.key === "ActiveEffectLike") && - // Add the RE to the Pilgrim's Token physical item rather than the feat - !(itemSource.system.slug === "pilgrims-token" && itemSource.type !== "equipment") - ) { - itemSource.system.rules.push(this.rule); - } - } -} diff --git a/src/module/migration/migrations/691-weapon-range-ability-category-group.ts b/src/module/migration/migrations/691-weapon-range-ability-category-group.ts deleted file mode 100644 index c950032cc2b..00000000000 --- a/src/module/migration/migrations/691-weapon-range-ability-category-group.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { WeaponSystemSource } from "@item/weapon/data.ts"; -import { WeaponCategory, WeaponGroup, WeaponRangeIncrement } from "@item/weapon/types.ts"; -import { MANDATORY_RANGED_GROUPS } from "@item/weapon/values.ts"; -import { RuleElementSource } from "@module/rules/index.ts"; -import { setHasElement } from "@util"; -import * as R from "remeda"; -import { MigrationBase } from "../base.ts"; - -/** Normalize weapon range to numeric or null, remove ability property, and let's do category and group too! */ -export class Migration691WeaponRangeAbilityCategoryGroup extends MigrationBase { - static override version = 0.691; - - private isOldGroupData(group: OldOrNewGroup): group is { value: WeaponGroup | null } { - return R.isObject(group) && (typeof group.value === "string" || group.value === null); - } - - private isOldRangeData(range: WeaponRangeIncrement | null | { value: string }): range is { value: string } { - return range instanceof Object && "value" in range && typeof range["value"] === "string"; - } - - override async updateItem(itemSource: ItemSourcePF2e): Promise { - if (itemSource.type === "weapon") { - const systemData: MaybeOldData = itemSource.system; - - // Category - systemData.category = - (systemData.weaponType ? systemData.weaponType.value : systemData.category) || "simple"; - if (systemData.weaponType) { - systemData["-=weaponType"] = null; - if (!("game" in globalThis)) delete systemData.weaponType; - } - - // Group - systemData.group = - (this.isOldGroupData(systemData.group) ? systemData.group.value : systemData.group) || null; - - // Range - const hasOldRangeData = this.isOldRangeData(systemData.range); - systemData.range = hasOldRangeData - ? ((Number((systemData.range as { value: string }).value) || null) as WeaponRangeIncrement | null) - : systemData.range; - - if (hasOldRangeData && R.isObject(systemData.ability)) { - if (systemData.ability.value === "str" && !setHasElement(MANDATORY_RANGED_GROUPS, systemData.group)) { - // The range thrown melee weapons are set by a thrown trait - systemData.range = null; - } - delete systemData.ability; - systemData["-=ability"] = null; - } - - // Correct thrown trait on ranged (rather than melee) thrown weapons - if (setHasElement(MANDATORY_RANGED_GROUPS, systemData.group)) { - const thrownIndex = systemData.traits.value.findIndex((trait) => /^thrown-\d+/.test(trait)); - if (thrownIndex !== -1) { - systemData.traits.value[thrownIndex] = "thrown"; - systemData.reload.value = "-"; - } - } - - // Falchions and knuckle daggers have been languishing with no group for a very long time - if (systemData.baseItem === "falchion") { - systemData.group = "sword"; - } else if (systemData.baseItem === "orc-knuckle-dagger") { - systemData.group = "knife"; - } - } - - // Remove setting of ability on Strike rule elements - const { rules } = itemSource.system; - const strikeRules = rules.filter((rule): rule is StrikeRuleSource => /\bStrike$/.test(String(rule.key))); - for (const rule of strikeRules) { - rule.key = "Strike"; - rule.range = Number(rule.range) || null; - delete rule.ability; - } - } -} - -interface StrikeRuleSource extends RuleElementSource { - ability?: unknown; - range?: unknown; -} - -type OldOrNewGroup = WeaponGroup | null | { value: WeaponGroup | null }; -type MaybeOldData = WeaponSystemSource & { - weaponType?: { value: WeaponCategory }; - "-=weaponType"?: null; - group: OldOrNewGroup; - range: WeaponRangeIncrement | null | { value: string }; - ability?: string | { value: string } | null; - "-=ability"?: null; -}; diff --git a/src/module/migration/migrations/692-crafting-entry-feat-replacement.ts b/src/module/migration/migrations/692-crafting-entry-feat-replacement.ts deleted file mode 100644 index 0390df8311a..00000000000 --- a/src/module/migration/migrations/692-crafting-entry-feat-replacement.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { ActorSourcePF2e } from "@actor/data/index.ts"; -import type { ItemPF2e } from "@item"; -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { MigrationBase } from "../base.ts"; - -/** Normalize weapon range to numeric or null, remove ability property, and let's do category and group too! */ -export class Migration692CraftingEntryFeatReplacement extends MigrationBase { - static override version = 0.692; - override requiresFlush = true; - - private slugToPromise = new Map>([ - ["advanced-alchemy", fromUuid("Compendium.pf2e.classfeatures.Pe0zmIqyTBc2Td0I")], - ["field-discovery-bomber", fromUuid("Compendium.pf2e.classfeatures.8QAFgy9U8PxEa7Dw")], - ["field-discovery-chirurgeon", fromUuid("Compendium.pf2e.classfeatures.qC0Iz6SlG2i9gv6g")], - ["field-discovery-mutagenist", fromUuid("Compendium.pf2e.classfeatures.V4Jt7eDnJBLv5bDj")], - ["field-discovery-toxicologist", fromUuid("Compendium.pf2e.classfeatures.6zo2PJGYoig7nFpR")], - ["infused-reagents", fromUuid("Compendium.pf2e.classfeatures.wySB9VHOW1v3TX1L")], - ["alchemist-dedication", fromUuid("Compendium.pf2e.feats-srd.CJMkxlxHiHZQYDCz")], - ["deeper-dabbler", fromUuid("Compendium.pf2e.feats-srd.PTXZ2C3AV8tZf0iX")], - ["efficient-alchemy-paragon", fromUuid("Compendium.pf2e.feats-srd.2FBZ0apnmZ7b61ct")], - ["expert-alchemy", fromUuid("Compendium.pf2e.feats-srd.soHLtpMM9h3AE7PD")], - ["expert-fireworks-crafter", fromUuid("Compendium.pf2e.feats-srd.dDFQJem5K9Jzxgda")], - ["expert-herbalism", fromUuid("Compendium.pf2e.feats-srd.owJorCBZmUi5lIV0")], - ["expert-poisoner", fromUuid("Compendium.pf2e.feats-srd.VruIzuysxw4tY6rk")], - ["firework-technician-dedication", fromUuid("Compendium.pf2e.feats-srd.MVbNnjqQOK9d8Ki3")], - ["gadget-specialist", fromUuid("Compendium.pf2e.feats-srd.DQN7YC7s7T0pL6Aa")], - ["herbalist-dedication", fromUuid("Compendium.pf2e.feats-srd.5CRt5Dy9eLv5LpRF")], - ["master-alchemy", fromUuid("Compendium.pf2e.feats-srd.f6k9lIrIS4SfnCnG")], - ["munitions-crafter", fromUuid("Compendium.pf2e.feats-srd.lFVqejlf52cdYrZy")], - ["munitions-machinist", fromUuid("Compendium.pf2e.feats-srd.lh3STEvbGnP7jVMr")], - ["plentiful-snares", fromUuid("Compendium.pf2e.feats-srd.wGaxWwJhIXbMJft1")], - ["poisoner-dedication", fromUuid("Compendium.pf2e.feats-srd.y7DDs03GtDnmhxFp")], - ["snare-genius", fromUuid("Compendium.pf2e.feats-srd.8DIzXO1YpsU3DpJw")], - ["snare-specialist", fromUuid("Compendium.pf2e.feats-srd.0haS0qXR9xTYKoTG")], - ["snarecrafter-dedication", fromUuid("Compendium.pf2e.feats-srd.4MUbwilvb9dI0X59")], - ["talisman-dabbler-dedication", fromUuid("Compendium.pf2e.feats-srd.1t5479E6bdvFs4E7")], - ["ubiquitous-gadgets", fromUuid("Compendium.pf2e.feats-srd.ny0nfGTDUE4p8TtO")], - ["ubiquitous-snares", fromUuid("Compendium.pf2e.feats-srd.bX2WI5k0afqPpCfm")], - ]); - - private replaceItem({ items, current, replacement }: ReplaceItemArgs): void { - if (!replacement) throw new Error("Unexpected error retrieving compendium item"); - const newSource = replacement.toObject(); - if (current.type === "feat" && newSource.type === "feat") { - newSource.system.location = current.system.location; - } - items.splice(items.indexOf(current), 1, newSource); - } - - override async updateActor(actorSource: ActorSourcePF2e): Promise { - if (actorSource.type === "character") { - this.slugToPromise.forEach(async (promise, slug) => { - const current = actorSource.items.find( - (itemSource) => itemSource.type === "feat" && itemSource.system.slug === slug, - ); - if (current) - this.replaceItem({ - items: actorSource.items, - current: current, - replacement: await promise, - }); - }); - } - } -} - -interface ReplaceItemArgs { - items: ItemSourcePF2e[]; - current: ItemSourcePF2e; - replacement: ItemPF2e | null; -} diff --git a/src/module/migration/migrations/693-armor-category-group.ts b/src/module/migration/migrations/693-armor-category-group.ts deleted file mode 100644 index d9d01d77c05..00000000000 --- a/src/module/migration/migrations/693-armor-category-group.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { ArmorSystemSource } from "@item/armor/data.ts"; -import { ArmorCategory, ArmorGroup } from "@item/armor/types.ts"; -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { MigrationBase } from "../base.ts"; - -/** Normalize armor range to numeric or null, remove ability property, and let's do category and group too! */ -export class Migration693ArmorCategoryGroup extends MigrationBase { - static override version = 0.693; - - private isOldGroupData(group: OldOrNewGroup): group is { value: ArmorGroup | "" | null } { - return ( - group instanceof Object && - "value" in group && - (typeof group["value"] === "string" || group["value"] === null) - ); - } - - override async updateItem(itemSource: ItemSourcePF2e): Promise { - if (itemSource.type !== "armor") return; - - const systemData: MaybeOldData = itemSource.system; - - // Category - systemData.category = (systemData.armorType ? systemData.armorType.value : systemData.category) || "simple"; - if (systemData.armorType) { - systemData["-=armorType"] = null; - if (!("game" in globalThis)) delete systemData.armorType; - } - - // Group - systemData.group = (this.isOldGroupData(systemData.group) ? systemData.group.value : systemData.group) || null; - } -} - -type OldOrNewGroup = ArmorGroup | null | { value: ArmorGroup | "" | null }; -type MaybeOldData = ArmorSystemSource & { - armorType?: { value: ArmorCategory }; - "-=armorType"?: null; - group: OldOrNewGroup; -}; diff --git a/src/module/migration/migrations/694-retire-system-token-settings.ts b/src/module/migration/migrations/694-retire-system-token-settings.ts deleted file mode 100644 index 2d8f14443b7..00000000000 --- a/src/module/migration/migrations/694-retire-system-token-settings.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { MigrationBase } from "../base.ts"; - -/** Retire system token-hover settings in favor of Foundry's "Default Token Configuration" */ -export class Migration694RetireSystemTokenSettings extends MigrationBase { - static override version = 0.694; - - override async migrate(): Promise { - const systemNameHover = - (Number(game.settings.storage.get("world").getItem("pf2e.defaultTokenSettingsName")) as TokenDisplayMode) || - CONST.TOKEN_DISPLAY_MODES.OWNER_HOVER; - const systemBarHover = - (Number(game.settings.storage.get("world").getItem("pf2e.defaultTokenSettingsBar")) as TokenDisplayMode) || - CONST.TOKEN_DISPLAY_MODES.OWNER_HOVER; - const coreTokenDefaults = game.settings.get("core", "defaultToken"); - coreTokenDefaults.displayName = systemNameHover; - coreTokenDefaults.displayBars = systemBarHover; - } -} diff --git a/src/module/migration/migrations/695-summon-to-summoned.ts b/src/module/migration/migrations/695-summon-to-summoned.ts deleted file mode 100644 index df960f48178..00000000000 --- a/src/module/migration/migrations/695-summon-to-summoned.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { MigrationBase } from "../base.ts"; - -/** Change the "summon" creature trait to "summoned", correctly set "summon" trait on npc/hazard actions */ -export class Migration695SummonToSummoned extends MigrationBase { - static override version = 0.695; - - override async updateItem(itemSource: ItemSourcePF2e): Promise { - const traits: { value?: string[]; custom?: string } | undefined = itemSource.system.traits; - if (!traits?.value) return; - - if (itemSource.type === "action") { - traits.custom ??= ""; - if (traits.custom.toLowerCase() === "summon") { - traits.custom = ""; - traits.value = Array.from(new Set([...traits.value, "summon"])).sort(); - } - } else { - const index = traits.value.indexOf("summon"); - if (index !== -1) traits.value[index] = "summoned"; - } - } -} diff --git a/src/module/migration/migrations/696-flat-ability-modifiers.ts b/src/module/migration/migrations/696-flat-ability-modifiers.ts deleted file mode 100644 index 0804d0e4d76..00000000000 --- a/src/module/migration/migrations/696-flat-ability-modifiers.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { AttributeString } from "@actor/types.ts"; -import { ATTRIBUTE_ABBREVIATIONS } from "@actor/values.ts"; -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import type { FlatModifierSource } from "@module/rules/rule-element/flat-modifier.ts"; -import { setHasElement, sluggify } from "@util"; -import { MigrationBase } from "../base.ts"; - -/** Set the "ability" property on ability FlatModifier REs */ -export class Migration696FlatAbilityModifiers extends MigrationBase { - static override version = 0.696; - - private abilityModPattern = /@abilities\.([a-z]{3})\.mod\b/; - - private abbreviationMap = new Map( - Array.from(ATTRIBUTE_ABBREVIATIONS).map((a) => [`PF2E.Ability${sluggify(a, { camel: "bactrian" })}`, a]), - ); - - override async updateItem(itemSource: ItemSourcePF2e): Promise { - const rules: FlatModifierSource[] = itemSource.system.rules; - for (const rule of rules) { - if ( - typeof rule.key === "string" && - rule.key.endsWith("FlatModifier") && - rule.type === "ability" && - !setHasElement(ATTRIBUTE_ABBREVIATIONS, rule.ability) - ) { - const abilityFromValue = (this.abilityModPattern.exec(String(rule.value))?.[1] ?? - null) as AttributeString | null; - rule.ability = abilityFromValue ?? this.abbreviationMap.get(String(rule.label ?? "")) ?? "str"; - if (typeof rule.value === "string" && rule.value.startsWith("@") && rule.value.endsWith(".mod")) { - delete rule.value; - } - } - } - - if (itemSource.system.slug === "thief-racket" && !rules.some((rule) => rule.ability === "dex")) { - rules.unshift({ - ability: "dex", - key: "FlatModifier", - predicate: { - all: ["weapon:melee", "weapon:trait:finesse"], - not: ["weapon:category:unarmed"], - }, - selector: "damage", - type: "ability", - }); - } - } -} diff --git a/src/module/migration/migrations/697-weapon-reach-trait.ts b/src/module/migration/migrations/697-weapon-reach-trait.ts deleted file mode 100644 index a3f7debdb4e..00000000000 --- a/src/module/migration/migrations/697-weapon-reach-trait.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { MigrationBase } from "../base.ts"; - -/** Correct the reach trait on weapons */ -export class Migration697WeaponReachTrait extends MigrationBase { - static override version = 0.697; - - override async updateItem(itemSource: ItemSourcePF2e): Promise { - if (itemSource.type === "weapon") { - const traits = itemSource.system.traits.value; - const numericReach = traits.find((t) => /^reach-\d+$/.test(t)); - if (numericReach) { - traits.splice(traits.indexOf(numericReach), 1, "reach"); - } - } - } -} diff --git a/src/module/migration/migrations/698-remove-derived-actor-traits.ts b/src/module/migration/migrations/698-remove-derived-actor-traits.ts deleted file mode 100644 index 1d516dc2f42..00000000000 --- a/src/module/migration/migrations/698-remove-derived-actor-traits.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { ActorSourcePF2e } from "@actor/data/index.ts"; -import { AncestrySource } from "@item/base/data/index.ts"; -import * as R from "remeda"; -import { MigrationBase } from "../base.ts"; - -/** Remove alignment traits from PCs and NPCs, ancestry traits from PCs */ -export class Migration698RemoveDerivedActorTraits extends MigrationBase { - static override version = 0.698; - - override async updateActor(source: ActorSourcePF2e): Promise { - const maybeTraits: unknown = source.system.traits; - if ( - !( - (source.type === "character" || source.type === "npc") && - R.isObject(maybeTraits) && - R.isObject(maybeTraits.traits) && - Array.isArray(maybeTraits.traits.value) - ) - ) { - return; - } - - const traits = maybeTraits.traits.value; - for (const trait of ["good", "evil", "lawful", "chaotic"]) { - const index = traits.indexOf(trait); - if (index >= 0) traits.splice(index, 1); - } - - if (source.type === "character") { - const ancestry = source.items.find((i): i is AncestrySource => i.type === "ancestry"); - if (!ancestry) return; - for (const trait of ancestry.system.traits.value) { - const index = traits.indexOf(trait); - if (index >= 0) traits.splice(index, 1); - } - } - } -} diff --git a/src/module/migration/migrations/699-item-description-empty-string.ts b/src/module/migration/migrations/699-item-description-empty-string.ts deleted file mode 100644 index 26e01a3609d..00000000000 --- a/src/module/migration/migrations/699-item-description-empty-string.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { MigrationBase } from "../base.ts"; - -/** Fix item descriptions set to `null` by `TextEditor` */ -export class Migration699ItemDescriptionEmptyString extends MigrationBase { - static override version = 0.699; - - override async updateItem(itemSource: ItemSourcePF2e): Promise { - itemSource.system.description.value ??= ""; - } -} diff --git a/src/module/migration/migrations/index.ts b/src/module/migration/migrations/index.ts index 010280f0746..20101a3a695 100644 --- a/src/module/migration/migrations/index.ts +++ b/src/module/migration/migrations/index.ts @@ -1,70 +1,3 @@ -export { Migration634PurgeMartialItems } from "./634-purge-martial-items.ts"; -export { Migration635NumifyACAndQuantity } from "./635-numify-ac-and-quantity.ts"; -export { Migration636NumifyArmorData } from "./636-numify-armor-data.ts"; -export { Migration637CleanMeleeItems } from "./637-clean-melee-items.ts"; -export { Migration638SpellComponents } from "./638-spell-components.ts"; -export { Migration639NormalizeLevelAndPrice } from "./639-normalize-level-and-price.ts"; -export { Migration640CantripsAreNotZeroLevel } from "./640-cantrips-are-not-zero-level.ts"; -export { Migration641SovereignSteelValue } from "./641-sovereign-steel-value.ts"; -export { Migration642TrackSchemaVersion } from "./642-track-schema-version.ts"; -export { Migration643HazardLevel } from "./643-hazard-level.ts"; -export { Migration644SpellcastingCategory } from "./644-spellcasting-category.ts"; -export { Migration645TokenImageSize } from "./645-token-image-size.ts"; -export { Migration646UpdateInlineLinks } from "./646-update-inline-links.ts"; -export { Migration647FixPCSenses } from "./647-fix-pc-senses.ts"; -export { Migration648RemoveInvestedProperty } from "./648-remove-invested-property.ts"; -export { Migration649FocusToActor } from "./649-focus-to-actor.ts"; -export { Migration650StringifyWeaponProperties } from "./650-stringify-weapon-properties.ts"; -export { Migration651EphemeralFocusPool } from "./651-ephemeral-focus-pool.ts"; -export { Migration652KillHalcyonTradition } from "./652-kill-halcyon-tradition.ts"; -export { Migration653AEstoREs } from "./653-aes-to-res.ts"; -export { Migration654ActionTypeAndCount } from "./654-action-type-count.ts"; -export { Migration655CreatureTokenSizes } from "./655-creature-token-sizes.ts"; -export { Migration656OtherFocusPoolSources } from "./656-other-focus-pool-sources.ts"; -export { Migration657RemoveSetProperty } from "./657-remove-set-property.ts"; -export { Migration658MonkUnarmoredProficiency } from "./658-monk-unarmored-proficiency.ts"; -export { Migration659MultipleDamageRows } from "./659-multiple-damage-rows.ts"; -export { Migration660DerivedSpellTraits } from "./660-derived-spell-traits.ts"; -export { Migration661NumifyVehicleDimensions } from "./661-numify-vehicle-dimensions.ts"; -export { Migration662LinkToActorSizeDefaults } from "./662-link-to-actor-size-defaults.ts"; -export { Migration663FixSpellDamage } from "./663-fix-spell-damage.ts"; -export { Migration664DeleteCUBConditions } from "./664-delete-cub-conditions.ts"; -export { Migration665HandwrapsCorrections } from "./665-handwraps-corrections.ts"; -export { Migration666UsageAndStowingContainers } from "./666-usage-and-stowing-containers.ts"; -export { Migration667HPSubProperties } from "./667-hp-subproperties.ts"; -export { Migration668ArmorSpeedPenalty } from "./668-armor-speed-penalty.ts"; -export { Migration669NPCAttackEffects } from "./669-npc-attack-effects.ts"; -export { Migration670AncestryVision } from "./670-ancestry-vision.ts"; -export { Migration670NoCustomTrait } from "./670-no-custom-trait.ts"; -export { Migration671NoPCItemsOnNonPCs } from "./671-no-pc-items-on-non-pcs.ts"; -export { Migration672RemoveNPCBaseProperties } from "./672-remove-npc-base-properties.ts"; -export { Migration673RemoveBulwarkREs } from "./673-remove-bulwark-res.ts"; -export { Migration674StableHomebrewTagIDs } from "./674-stable-homebrew-tag-ids.ts"; -export { Migration675FlatModifierAEsToREs } from "./675-flat-modifier-aes-to-res.ts"; -export { Migration676ReplaceItemsWithRELikeAEs } from "./676-replace-items-with-re-like-aes.ts"; -export { Migration677RuleValueDataRefs } from "./677-rule-value-data-refs.ts"; -export { Migration678SeparateNPCAttackTraits } from "./678-separate-npc-attack-traits.ts"; -export { Migration679TowerShieldSpeedPenalty } from "./679-tower-shield-speed-penalty.ts"; -export { Migration680SetWeaponHands } from "./680-set-weapon-hands.ts"; -export { Migration681GiantLanguageToJotun } from "./681-giant-language-to-jotun.ts"; -export { Migration682BiographyFields } from "./682-biography-fields.ts"; -export { Migration683FlavorTextToPublicNotes } from "./683-flavortext-to-public-notes.ts"; -export { Migration684RationsToConsumable } from "./684-rations-to-consumable.ts"; -export { Migration685FixMeleeUsageTraits } from "./685-fix-melee-usage-traits.ts"; -export { Migration686HeroPointsToResources } from "./686-hero-points-to-resources.ts"; -export { Migration687FamiliarityAEsToREs } from "./687-familiarity-aes-to-res.ts"; -export { Migration688ClampSpellLevel } from "./688-clamp-spell-level.ts"; -export { Migration689EncumberanceActiveEffects } from "./689-encumberance-aes.ts"; -export { Migration690InitiativeTiebreakItems } from "./690-tiebreak-items.ts"; -export { Migration691WeaponRangeAbilityCategoryGroup } from "./691-weapon-range-ability-category-group.ts"; -export { Migration692CraftingEntryFeatReplacement } from "./692-crafting-entry-feat-replacement.ts"; -export { Migration693ArmorCategoryGroup } from "./693-armor-category-group.ts"; -export { Migration694RetireSystemTokenSettings } from "./694-retire-system-token-settings.ts"; -export { Migration695SummonToSummoned } from "./695-summon-to-summoned.ts"; -export { Migration696FlatAbilityModifiers } from "./696-flat-ability-modifiers.ts"; -export { Migration697WeaponReachTrait } from "./697-weapon-reach-trait.ts"; -export { Migration698RemoveDerivedActorTraits } from "./698-remove-derived-actor-traits.ts"; -export { Migration699ItemDescriptionEmptyString } from "./699-item-description-empty-string.ts"; export { Migration700SingleClassFeatures } from "./700-single-class-features.ts"; export { Migration701ModifierNameToSlug } from "./701-modifier-name-to-slug.ts"; export { Migration702REFormulasAtInstanceLevel } from "./702-re-formulas-at-instance-level.ts"; diff --git a/src/module/migration/runner/base.ts b/src/module/migration/runner/base.ts index 6508caadd6b..c19fa620455 100644 --- a/src/module/migration/runner/base.ts +++ b/src/module/migration/runner/base.ts @@ -16,9 +16,9 @@ export class MigrationRunnerBase { static LATEST_SCHEMA_VERSION = 0.927; - static MINIMUM_SAFE_VERSION = 0.634; + static MINIMUM_SAFE_VERSION = 0.7; - static RECOMMENDED_SAFE_VERSION = 0.781; + static RECOMMENDED_SAFE_VERSION = 0.841; /** The minimum schema version for the foundry version number */ static FOUNDRY_SCHEMA_VERSIONS = {