From c33538c45dc83f9a0a4332e6733a2abf69ac11e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20Sim=C3=B5es?= Date: Thu, 14 Dec 2023 00:14:22 -0600 Subject: [PATCH] Remove type and usage of global foundry helpers Plus remove some needless system helpers --- build/lib/compendium-pack.ts | 2 +- build/lib/core-helpers.ts | 27 --- .../lib/foundry-utils.ts | 59 +++++- build/run-migration.ts | 17 +- src/global.ts | 2 + src/module/actor/base.ts | 26 +-- .../actor/character/attribute-builder.ts | 8 +- src/module/actor/character/document.ts | 34 +-- src/module/actor/character/feats.ts | 2 +- src/module/actor/character/helpers.ts | 2 +- src/module/actor/character/sheet.ts | 8 +- src/module/actor/creature/document.ts | 8 +- src/module/actor/creature/helpers.ts | 2 +- src/module/actor/data/iwr.ts | 8 +- src/module/actor/familiar/document.ts | 8 +- src/module/actor/familiar/sheet.ts | 8 +- src/module/actor/hazard/document.ts | 2 +- src/module/actor/hazard/sheet.ts | 8 +- src/module/actor/helpers.ts | 6 +- src/module/actor/initiative.ts | 2 +- src/module/actor/inventory/index.ts | 2 +- src/module/actor/modifiers.ts | 8 +- src/module/actor/npc/document.ts | 20 +- src/module/actor/party/document.ts | 14 +- src/module/actor/party/kingdom/chat.ts | 4 +- src/module/actor/party/kingdom/helpers.ts | 2 +- src/module/actor/party/kingdom/model.ts | 4 +- src/module/actor/party/kingdom/schema.ts | 4 +- src/module/actor/party/kingdom/sheet.ts | 4 +- src/module/actor/party/kingdom/types.ts | 4 +- src/module/actor/party/kingdom/values.ts | 12 +- src/module/actor/party/sheet.ts | 13 +- src/module/actor/sheet/base.ts | 14 +- src/module/actor/sheet/helpers.ts | 2 +- .../sheet/popups/distribute-coins-popup.ts | 2 +- src/module/actor/sheet/spellcasting-dialog.ts | 7 +- src/module/actor/vehicle/document.ts | 6 +- src/module/apps/compendium-browser/index.ts | 5 +- src/module/apps/compendium-browser/loader.ts | 2 +- .../apps/compendium-browser/tabs/base.ts | 8 +- .../apps/compendium-browser/tabs/equipment.ts | 4 +- .../apps/compendium-browser/tabs/feat.ts | 4 +- .../apps/compendium-migration-status.ts | 2 +- src/module/apps/effects-panel.ts | 2 +- src/module/apps/license-viewer/app.ts | 2 +- src/module/apps/migration-summary.ts | 2 +- src/module/apps/sidebar/actor-directory.ts | 4 +- src/module/apps/sidebar/encounter-tracker.ts | 2 +- src/module/apps/sidebar/item-directory.ts | 4 +- src/module/apps/world-clock/app.ts | 2 +- src/module/canvas/status-effects.ts | 2 +- src/module/canvas/token/aura/renderer.ts | 3 +- src/module/canvas/token/object.ts | 11 +- src/module/chat-message/document.ts | 4 +- src/module/doc-helpers.ts | 2 +- src/module/encounter/combatant.ts | 2 +- src/module/item/abc/sheet.ts | 2 +- src/module/item/ability/helpers.ts | 4 +- src/module/item/affliction/sheet.ts | 12 +- src/module/item/armor/document.ts | 4 +- src/module/item/base/document.ts | 34 +-- .../item/base/sheet/rule-element-form/aura.ts | 8 +- .../item/base/sheet/rule-element-form/base.ts | 18 +- src/module/item/base/sheet/sheet.ts | 16 +- src/module/item/class/document.ts | 2 +- src/module/item/condition/document.ts | 2 +- .../condition/persistent-damage-dialog.ts | 13 +- src/module/item/consumable/document.ts | 2 +- src/module/item/deity/document.ts | 4 +- src/module/item/effect/sheet.ts | 4 +- src/module/item/equipment/document.ts | 2 +- src/module/item/feat/document.ts | 4 +- src/module/item/kit/document.ts | 6 +- src/module/item/kit/sheet.ts | 2 +- src/module/item/melee/sheet.ts | 2 +- src/module/item/physical/document.ts | 10 +- src/module/item/physical/helpers.ts | 6 +- src/module/item/physical/runes.ts | 2 +- src/module/item/physical/sheet.ts | 6 +- src/module/item/shield/document.ts | 10 +- src/module/item/shield/sheet.ts | 2 +- src/module/item/shield/types.ts | 1 - src/module/item/spell/document.ts | 18 +- src/module/item/spell/overlay.ts | 7 +- src/module/item/spell/sheet.ts | 2 +- .../item/spellcasting-entry/document.ts | 2 +- src/module/item/spellcasting-entry/trick.ts | 2 +- src/module/item/weapon/document.ts | 11 +- src/module/item/weapon/sheet.ts | 2 +- .../migrations/674-stable-homebrew-tag-ids.ts | 4 +- .../migrations/711-heritage-items.ts | 2 +- .../migrations/724-crafting-max-item-level.ts | 2 +- .../725-quick-climb-rule-elements.ts | 2 +- .../migrations/738-update-laughing-shadow.ts | 4 +- .../745-effect-target-to-choice-set.ts | 7 +- .../migration/migrations/768-add-new-auras.ts | 8 +- .../migrations/793-make-predicates-arrays.ts | 6 +- .../migrations/812-restructure-iwr.ts | 15 +- .../migrations/841-v11-uuid-format.ts | 4 +- .../866-link-to-actor-size-again.ts | 2 +- .../882-spell-data-reorganization.ts | 4 +- .../885-convert-alignment-damage.ts | 2 +- .../899-armor-shields-to-shield-shields.ts | 4 +- .../907-restructure-armor-weapon-runes.ts | 6 +- src/module/migration/runner/base.ts | 12 +- src/module/migration/runner/index.ts | 10 +- src/module/rules/helpers.ts | 7 +- .../rules/rule-element/adjust-modifier.ts | 2 +- src/module/rules/rule-element/ae-like.ts | 10 +- src/module/rules/rule-element/aura.ts | 2 +- src/module/rules/rule-element/base.ts | 12 +- .../rule-element/battle-form/rule-element.ts | 6 +- .../rule-element/choice-set/rule-element.ts | 6 +- src/module/rules/rule-element/damage-dice.ts | 2 +- .../rule-element/grant-item/rule-element.ts | 30 +-- src/module/rules/rule-element/roll-option.ts | 2 +- src/module/rules/rule-element/strike.ts | 6 +- src/module/rules/rule-element/temp-hp.ts | 20 +- src/module/scene/document.ts | 2 +- src/module/scene/helpers.ts | 2 +- .../scene/measured-template-document.ts | 2 +- .../scene/token-document/actor-delta.ts | 4 +- src/module/scene/token-document/document.ts | 16 +- .../system/action-macros/basic/escape.ts | 12 +- src/module/system/action-macros/helpers.ts | 2 +- src/module/system/check/check.ts | 6 +- src/module/system/damage/damage.ts | 4 +- src/module/system/damage/dialog.ts | 9 +- src/module/system/damage/formula.ts | 15 +- src/module/system/damage/roll.ts | 6 +- src/module/system/predication.ts | 2 +- src/module/system/settings/homebrew/menu.ts | 9 +- src/module/system/settings/menu.ts | 4 +- src/module/system/settings/world-clock.ts | 4 +- src/module/system/statistic/statistic.ts | 12 +- src/module/system/tag-selector/base.ts | 6 +- src/module/system/tag-selector/basic.ts | 8 +- src/module/system/tag-selector/senses.ts | 2 +- src/module/system/tag-selector/speeds.ts | 2 +- src/module/user/document.ts | 4 +- src/scripts/config/index.ts | 4 +- src/scripts/hooks/drop-canvas-data.ts | 2 +- src/scripts/hooks/load.ts | 2 +- src/scripts/hooks/ready.ts | 5 +- .../macros/travel/travel-speed-sheet.ts | 21 +- src/scripts/macros/travel/travel-speed.ts | 22 +- src/scripts/macros/treat-wounds.ts | 4 +- src/scripts/set-game-pf2e.ts | 2 +- src/scripts/ui/inline-roll-links.ts | 2 +- src/util/index.ts | 2 +- src/util/misc.ts | 75 +------ src/util/uuid.ts | 10 +- tests/mocks/actor.ts | 14 +- tests/mocks/chat-message.ts | 2 +- tests/mocks/item.ts | 10 +- tests/mocks/journal-entry.ts | 2 +- tests/mocks/macro.ts | 2 +- tests/mocks/roll-table.ts | 2 +- tests/mocks/scene.ts | 8 +- tests/mocks/token.ts | 2 +- tests/mocks/user.ts | 4 +- tests/module/migration.test.ts | 8 +- tests/module/utils.test.ts | 37 +--- tests/setup.ts | 195 +----------------- tests/utils.ts | 15 -- types/foundry/client/pixi/core/index.d.ts | 3 +- types/foundry/client/pixi/core/loader.d.ts | 9 + types/foundry/common/module.d.ts | 8 +- types/foundry/common/utils/helpers.d.ts | 28 +-- vite.config.ts | 3 +- 170 files changed, 585 insertions(+), 900 deletions(-) delete mode 100644 build/lib/core-helpers.ts rename tests/fixtures/foundryshim.ts => build/lib/foundry-utils.ts (79%) delete mode 100644 tests/utils.ts create mode 100644 types/foundry/client/pixi/core/loader.d.ts diff --git a/build/lib/compendium-pack.ts b/build/lib/compendium-pack.ts index 790e351ab68..f973bcfc491 100644 --- a/build/lib/compendium-pack.ts +++ b/build/lib/compendium-pack.ts @@ -9,7 +9,7 @@ import { isObject, recursiveReplaceString, setHasElement, sluggify, tupleHasValu import fs from "fs"; import path from "path"; import coreIconsJSON from "../core-icons.json" assert { type: "json" }; -import "./core-helpers.ts"; +import "./foundry-utils.ts"; import { getFilesRecursively, PackError } from "./helpers.ts"; import { DBFolder, LevelDatabase } from "./level-database.ts"; import { PackEntry } from "./types.ts"; diff --git a/build/lib/core-helpers.ts b/build/lib/core-helpers.ts deleted file mode 100644 index 2190ab2ad19..00000000000 --- a/build/lib/core-helpers.ts +++ /dev/null @@ -1,27 +0,0 @@ -global.deepClone = (original: T): T => { - // Simple types - if (typeof original !== "object" || original === null) return original; - - // Arrays - if (Array.isArray(original)) return original.map(deepClone) as unknown as T; - - // Dates - if (original instanceof Date) return new Date(original) as T & Date; - - // Unsupported advanced objects - if ("constructor" in original && (original as { constructor?: unknown }).constructor !== Object) return original; - - // Other objects - const clone: Record = {}; - for (const k of Object.keys(original)) { - clone[k] = deepClone((original as Record)[k]); - } - return clone as T; -}; - -global.randomID = function randomID(length = 16): string { - const rnd = () => Math.random().toString(36).substring(2); - let id = ""; - while (id.length < length) id += rnd(); - return id.substring(0, length); -}; diff --git a/tests/fixtures/foundryshim.ts b/build/lib/foundry-utils.ts similarity index 79% rename from tests/fixtures/foundryshim.ts rename to build/lib/foundry-utils.ts index 83bebe0ad6f..10592918403 100644 --- a/tests/fixtures/foundryshim.ts +++ b/build/lib/foundry-utils.ts @@ -1,3 +1,41 @@ +/** + * Quickly clone a simple piece of data, returning a copy which can be mutated safely. + * This method DOES support recursive data structures containing inner objects or arrays. + * This method DOES NOT support advanced object types like Set, Map, or other specialized classes. + * @param original Some sort of data + * @return The clone of that data + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function deepClone(original: T): T extends Set | Map | Collection ? never : T { + // Simple types + if (typeof original !== "object" || original === null) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return original as T extends Set | Map | Collection ? never : T; + + // Arrays + if (original instanceof Array) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return original.map(deepClone) as unknown as T extends Set | Map | Collection ? never : T; + + // Dates + if (original instanceof Date) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return new Date(original) as unknown as T extends Set | Map | Collection ? never : T; + + // Unsupported advanced objects + if ((original as { constructor: unknown }).constructor !== Object) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return original as T extends Set | Map | Collection ? never : T; + + // Other objects + const clone: Record = {}; + for (const k of Object.keys(original)) { + clone[k] = deepClone(original[k as keyof typeof original]); + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return clone as unknown as T extends Set | Map | Collection ? never : T; +} + /** * A helper function which searches through an object to assign a value using a string key * This string key supports the notation a.b.c which would target object[a][b][c] @@ -112,7 +150,7 @@ export function mergeObject( Object.keys(original).forEach((k) => delete (original as Record)[k]); Object.assign(original, expanded); } else original = expanded; - } else if (!inplace) original = deepClone(original); + } else if (!inplace) original = foundry.utils.deepClone(original); } // Iterate over the other object @@ -246,10 +284,15 @@ function diffObject(original: any, other: any, { inner = false } = {}): any { ); } -export function populateFoundryUtilFunctions(): void { - global.setProperty = setProperty; - global.getType = getType; - global.mergeObject = mergeObject; - global.diffObject = diffObject; - global.duplicate = duplicate; -} +const f = (global.foundry = { + utils: { + deepClone, + diffObject, + duplicate, + expandObject, + mergeObject, + setProperty, + }, +} as typeof foundry); + +global.fu = f.utils; diff --git a/build/run-migration.ts b/build/run-migration.ts index 2f463e9c1c5..82a6b90e8c0 100644 --- a/build/run-migration.ts +++ b/build/run-migration.ts @@ -8,8 +8,7 @@ import { sluggify } from "@util"; import fs from "fs-extra"; import { JSDOM } from "jsdom"; import path from "path"; -import { populateFoundryUtilFunctions } from "../tests/fixtures/foundryshim.ts"; -import "./lib/core-helpers.ts"; +import "./lib/foundry-utils.ts"; import { getFilesRecursively } from "./lib/helpers.ts"; import { Migration890RMClassItemClassDC } from "@module/migration/migrations/890-rm-class-item-class-dc.ts"; @@ -175,7 +174,7 @@ async function migrate() { pruneFlags(item); } - update.items = update.items.map((i) => mergeObject({}, i, { performDeletions: true })); + update.items = update.items.map((i) => fu.mergeObject({}, i, { performDeletions: true })); for (const updatedItem of update.items) { delete (updatedItem.system as { _migrations?: object })._migrations; if (updatedItem.type === "consumable" && !updatedItem.system.spell) { @@ -184,7 +183,7 @@ async function migrate() { pruneFlags(updatedItem); } - return mergeObject(source, update, { inplace: false, performDeletions: true }); + return fu.mergeObject(source, update, { inplace: false, performDeletions: true }); } else if (isItemData(source)) { source.system.slug = sluggify(source.name); const update = await migrationRunner.getUpdatedItem(source, migrationRunner.migrations); @@ -198,22 +197,22 @@ async function migrate() { pruneFlags(source); pruneFlags(update); - return mergeObject(source, update, { inplace: false, performDeletions: true }); + return fu.mergeObject(source, update, { inplace: false, performDeletions: true }); } else if (isJournalEntryData(source)) { const update = await migrationRunner.getUpdatedJournalEntry(source, migrationRunner.migrations); pruneFlags(source); pruneFlags(update); - return mergeObject(source, update, { inplace: false, performDeletions: true }); + return fu.mergeObject(source, update, { inplace: false, performDeletions: true }); } else if (isMacroData(source)) { const update = await migrationRunner.getUpdatedMacro(source, migrationRunner.migrations); pruneFlags(source); pruneFlags(update); - return mergeObject(source, update, { inplace: false, performDeletions: true }); + return fu.mergeObject(source, update, { inplace: false, performDeletions: true }); } else if (isTableData(source)) { const update = await migrationRunner.getUpdatedTable(source, migrationRunner.migrations); pruneFlags(source); pruneFlags(update); - return mergeObject(source, update, { inplace: false, performDeletions: true }); + return fu.mergeObject(source, update, { inplace: false, performDeletions: true }); } else { pruneFlags(source); return source; @@ -249,6 +248,4 @@ function pruneFlags(source: { flags?: Record | u } } -populateFoundryUtilFunctions(); - migrate().catch((err) => console.error(err)); diff --git a/src/global.ts b/src/global.ts index 7f946dc6050..4ebf829a4e7 100644 --- a/src/global.ts +++ b/src/global.ts @@ -198,6 +198,8 @@ declare global { namespace globalThis { // eslint-disable-next-line no-var var game: GamePF2e; + // eslint-disable-next-line no-var + var fu: typeof foundry.utils; // eslint-disable-next-line no-var var ui: FoundryUI< diff --git a/src/module/actor/base.ts b/src/module/actor/base.ts index fa0f027281d..4b38d1f4ab3 100644 --- a/src/module/actor/base.ts +++ b/src/module/actor/base.ts @@ -140,7 +140,7 @@ class ActorPF2e preparationWarnings.add(warning), - flush: foundry.utils.debounce(() => { + flush: fu.debounce(() => { for (const warning of preparationWarnings) { console.warn(warning); } @@ -691,7 +691,7 @@ class ActorPF2e new Immunity(i)) ?? []; attributes.weaknesses = attributes.weaknesses?.map((w) => new Weakness(w)) ?? []; attributes.resistances = attributes.resistances?.map((r) => new Resistance(r)) ?? []; @@ -756,7 +756,7 @@ class ActorPF2e c.id), updates: Object.entries(damageResult.updates) .map(([path, newValue]) => { - const preUpdateValue = getProperty(preUpdateSource, path); + const preUpdateValue = fu.getProperty(preUpdateSource, path); if (typeof preUpdateValue === "number") { const difference = preUpdateValue - newValue; if (difference === 0) { @@ -1494,7 +1494,7 @@ class ActorPF2e[]> = {}; for (const update of updates) { - const currentValue = getProperty(this, update.path); + const currentValue = fu.getProperty(this, update.path); if (typeof currentValue === "number") { actorUpdates[update.path] = currentValue + update.value; } @@ -1706,11 +1706,11 @@ class ActorPF2e { - foundry.utils.logCompatibilityWarning(`@${prop} is deprecated`, { + fu.logCompatibilityWarning(`@${prop} is deprecated`, { since: "5.0.1", until: "6", }); - return objectHasKey(this.system, prop) ? deepClone(this.system[prop]) : null; + return objectHasKey(this.system, prop) ? fu.deepClone(this.system[prop]) : null; }, }); } diff --git a/src/module/actor/character/attribute-builder.ts b/src/module/actor/character/attribute-builder.ts index e06e2f32fba..8f2fc0e2fbb 100644 --- a/src/module/actor/character/attribute-builder.ts +++ b/src/module/actor/character/attribute-builder.ts @@ -3,7 +3,7 @@ import { AttributeString } from "@actor/types.ts"; import { ATTRIBUTE_ABBREVIATIONS } from "@actor/values.ts"; import { AncestryPF2e, BackgroundPF2e, ClassPF2e } from "@item"; import { maintainFocusInRender } from "@module/sheet/helpers.ts"; -import { ErrorPF2e, addSign, htmlClosest, htmlQuery, htmlQueryAll, setHasElement, tupleHasValue } from "@util"; +import { ErrorPF2e, htmlClosest, htmlQuery, htmlQueryAll, setHasElement, signedInteger, tupleHasValue } from "@util"; import * as R from "remeda"; class AttributeBuilder extends Application { @@ -49,7 +49,7 @@ class AttributeBuilder extends Application { // Allow decimal values in manual mode (to track partial boosts) const mod = build.manual ? actor._source.system.abilities?.[attribute].mod ?? 0 : value.base; return { - mod: addSign(Number(mod.toFixed(1))), + mod: Number(mod.toFixed(1)).signedString(), label: CONFIG.PF2E.abilities[attribute], }; }), @@ -316,7 +316,7 @@ class AttributeBuilder extends Application { if (input.type === "number" && input.dataset.dtype === "Number") { input.type = "text"; const newValue = Math.clamped(Number(input.value) || 0, -5, 10); - input.value = addSign(newValue); + input.value = signedInteger(newValue); const propertyPath = input.dataset.property; if (!propertyPath) throw ErrorPF2e("Empty property path"); @@ -473,7 +473,7 @@ class AttributeBuilder extends Application { return; } - const buildSource = mergeObject(actor.toObject().system.build ?? {}, { attributes: { boosts: {} } }); + const buildSource = fu.mergeObject(actor.toObject().system.build ?? {}, { attributes: { boosts: {} } }); const boosts = (buildSource.attributes.boosts[level] ??= []); if (boosts.includes(attribute)) { boosts.splice(boosts.indexOf(attribute), 1); diff --git a/src/module/actor/character/document.ts b/src/module/actor/character/document.ts index 194323a6e4f..fa5761403e0 100644 --- a/src/module/actor/character/document.ts +++ b/src/module/actor/character/document.ts @@ -145,7 +145,7 @@ class CharacterPF2e ({ ...tabs, @@ -343,7 +343,7 @@ class CharacterPF2e 0; this.system.abilities = R.mapToObj(Array.from(ATTRIBUTE_ABBREVIATIONS), (a) => [ a, - mergeObject({ mod: 0 }, this.system.abilities?.[a] ?? {}), + fu.mergeObject({ mod: 0 }, this.system.abilities?.[a] ?? {}), ]); type SystemDataPartial = DeepPartial< @@ -376,7 +376,7 @@ class CharacterPF2e [t, { rank: 0, ability: SAVING_THROW_DEFAULT_ATTRIBUTES[t] }]), systemData.saves ?? {}, ); @@ -453,7 +453,7 @@ class CharacterPF2e; @@ -951,7 +951,7 @@ class CharacterPF2e w.slug === handwrapsSlug && w.category === "unarmed" && w.isEquipped && w.isInvested, ); - const unarmedRunes = deepClone(handwraps?._source.system.runes) ?? { potency: 0, striking: 0, property: [] }; + const unarmedRunes = fu.deepClone(handwraps?._source.system.runes) ?? { potency: 0, striking: 0, property: [] }; // Add a basic unarmed strike const basicUnarmed = includeBasicUnarmed @@ -1252,7 +1252,7 @@ class CharacterPF2e { const potency = attackDomains - .flatMap((key) => deepClone(synthetics.weaponPotency[key] ?? [])) + .flatMap((key) => fu.deepClone(synthetics.weaponPotency[key] ?? [])) .filter((wp) => wp.predicate.test(initialRollOptions)); if (weapon.system.runes.potency > 0) { @@ -1409,7 +1409,7 @@ class CharacterPF2e 0); - const action: CharacterStrike = mergeObject(strikeStat, { + const action: CharacterStrike = fu.mergeObject(strikeStat, { label: weapon.name, quantity: weapon.quantity, ready, @@ -1838,7 +1838,7 @@ class CharacterPF2e extends CreatureSheetPF2e ), ) as Record; - sheetData.tabVisibility = deepClone(actor.flags.pf2e.sheetTabs); + sheetData.tabVisibility = fu.deepClone(actor.flags.pf2e.sheetTabs); // Enrich content const rollData = actor.getRollData(); @@ -1144,7 +1144,7 @@ class CharacterSheetPF2e extends CreatureSheetPF2e } else if (filterType === "traits") { const trait = traits.options.find((t) => t.value === value); if (trait) { - traits.selected.push(deepClone(trait)); + traits.selected.push(fu.deepClone(trait)); } } else if (filterType === "conjunction" && (value === "and" || value === "or")) { filter.multiselects.traits.conjunction = value; @@ -1181,7 +1181,7 @@ class CharacterSheetPF2e extends CreatureSheetPF2e if (typeof newValue === "number") { await item.update({ [propertyKey]: newValue }); } - if (newValue !== getProperty(item, propertyKey)) { + if (newValue !== fu.getProperty(item, propertyKey)) { ui.notifications.warn(game.i18n.localize("PF2E.ErrorMessage.MinimumProfLevelSetByFeatures")); } } @@ -1358,7 +1358,7 @@ class CharacterSheetPF2e extends CreatureSheetPF2e if (!entry) { throw ErrorPF2e(`Crafting entry "${entrySelector}" doesn't exist!`); } - const formulas = deepClone(entry.preparedFormulaData); + const formulas = fu.deepClone(entry.preparedFormulaData); const source = formulas.find((f) => f.itemUUID === sourceFormula.uuid); const target = formulas.find((f) => f.itemUUID === targetUuid); if (source && target) { diff --git a/src/module/actor/creature/document.ts b/src/module/actor/creature/document.ts index b8c98b19d6a..acf272bee00 100644 --- a/src/module/actor/creature/document.ts +++ b/src/module/actor/creature/document.ts @@ -257,7 +257,7 @@ abstract class CreaturePF2e< attributes.flanking.flankable = true; attributes.flanking.offGuardable = true; attributes.reach = { base: 0, manipulate: 0 }; - attributes.speed = mergeObject({ total: 0, value: 0 }, attributes.speed ?? {}); + attributes.speed = fu.mergeObject({ total: 0, value: 0 }, attributes.speed ?? {}); if (attributes.initiative) { attributes.initiative.tiebreakPriority = this.hasPlayerOwner ? 2 : 1; @@ -573,7 +573,7 @@ abstract class CreaturePF2e< landSpeed.value = Math.max(landSpeed.value, ...fromSynthetics.map((s) => s.value)); const modifiers = extractModifiers(this.synthetics, domains); - const stat: CreatureSpeeds = mergeObject( + const stat: CreatureSpeeds = fu.mergeObject( new StatisticModifier(`${movementType}-speed`, modifiers, rollOptions), landSpeed, { overwrite: false }, @@ -586,7 +586,7 @@ abstract class CreaturePF2e< }; this.rollOptions.all["speed:land"] = true; - const merged = mergeObject(stat, otherData); + const merged = fu.mergeObject(stat, otherData); Object.defineProperties(merged, { total: { get(): number { @@ -632,7 +632,7 @@ abstract class CreaturePF2e< const modifiers = extractModifiers(this.synthetics, domains); const stat = new StatisticModifier(`${movementType}-speed`, modifiers, rollOptions); - const merged = mergeObject(stat, speed, { overwrite: false }); + const merged = fu.mergeObject(stat, speed, { overwrite: false }); Object.defineProperties(merged, { total: { get(): number { diff --git a/src/module/actor/creature/helpers.ts b/src/module/actor/creature/helpers.ts index e233d559fac..61fc5194edb 100644 --- a/src/module/actor/creature/helpers.ts +++ b/src/module/actor/creature/helpers.ts @@ -119,7 +119,7 @@ function imposeEncumberedCondition(actor: CreaturePF2e): void { if (!game.pf2e.settings.encumbrance) return; if (actor.inventory.bulk.isEncumbered && actor.conditions.bySlug("encumbered").length === 0) { const source = game.pf2e.ConditionManager.getCondition("encumbered").toObject(); - const encumbered = new ConditionPF2e(mergeObject(source, { _id: "xxxENCUMBEREDxxx" }), { parent: actor }); + const encumbered = new ConditionPF2e(fu.mergeObject(source, { _id: "xxxENCUMBEREDxxx" }), { parent: actor }); actor.conditions.set(encumbered.id, encumbered); encumbered.prepareSiblingData(); encumbered.prepareActorData(); diff --git a/src/module/actor/data/iwr.ts b/src/module/actor/data/iwr.ts index 471e79625ce..fa98274da6c 100644 --- a/src/module/actor/data/iwr.ts +++ b/src/module/actor/data/iwr.ts @@ -22,7 +22,7 @@ abstract class IWR { constructor(data: IWRConstructorData) { this.type = data.type; - this.exceptions = deepClone(data.exceptions ?? []); + this.exceptions = fu.deepClone(data.exceptions ?? []); this.definition = data.definition ?? null; this.source = data.source ?? null; this.#customLabel = this.type === "custom" ? data.customLabel ?? null : null; @@ -210,7 +210,7 @@ abstract class IWR { toObject(): Readonly> { return { type: this.type, - exceptions: deepClone(this.exceptions), + exceptions: fu.deepClone(this.exceptions), source: this.source, label: this.label, }; @@ -308,7 +308,7 @@ class Resistance extends IWR implements ResistanceSource { ) { super(data); this.value = data.value; - this.doubleVs = deepClone(data.doubleVs ?? []); + this.doubleVs = fu.deepClone(data.doubleVs ?? []); } get label(): string { @@ -346,7 +346,7 @@ class Resistance extends IWR implements ResistanceSource { return { ...super.toObject(), value: this.value, - doubleVs: deepClone(this.doubleVs), + doubleVs: fu.deepClone(this.doubleVs), }; } diff --git a/src/module/actor/familiar/document.ts b/src/module/actor/familiar/document.ts index 2952982455f..6863959c067 100644 --- a/src/module/actor/familiar/document.ts +++ b/src/module/actor/familiar/document.ts @@ -8,7 +8,7 @@ import { SAVE_TYPES, SKILL_ABBREVIATIONS, SKILL_DICTIONARY, SKILL_EXPANDED } fro import { ItemType } from "@item/base/data/index.ts"; import type { CombatantPF2e, EncounterPF2e } from "@module/encounter/index.ts"; import type { RuleElementPF2e } from "@module/rules/index.ts"; -import type { TokenDocumentPF2e } from "@scene/index.ts"; +import type { TokenDocumentPF2e } from "@scene"; import { PredicatePF2e } from "@system/predication.ts"; import { ArmorStatistic, HitPointsStatistic, Statistic } from "@system/statistic/index.ts"; import * as R from "remeda"; @@ -101,7 +101,7 @@ class FamiliarPF2e extends CreatureShee static override get defaultOptions(): ActorSheetOptions { const options = super.defaultOptions; - mergeObject(options, { + return { + ...options, classes: [...options.classes, "familiar"], width: 650, height: 680, tabs: [{ navSelector: ".sheet-navigation", contentSelector: ".sheet-content", initial: "attributes" }], - }); - return options; + }; } override get template(): string { diff --git a/src/module/actor/hazard/document.ts b/src/module/actor/hazard/document.ts index a559d3aafca..e44815f4e3b 100644 --- a/src/module/actor/hazard/document.ts +++ b/src/module/actor/hazard/document.ts @@ -196,7 +196,7 @@ class HazardPF2e { static override get defaultOptions(): ActorSheetOptions { const options = super.defaultOptions; - mergeObject(options, { + fu.mergeObject(options, { classes: [...options.classes, "hazard"], scrollY: [".container > section"], width: 700, @@ -56,7 +56,7 @@ export class HazardSheetPF2e extends ActorSheetPF2e { return TextEditor.enrichHTML(content ?? "", { rollData, async: true }); }; - sheetData.enrichedContent = mergeObject(sheetData.enrichedContent, { + sheetData.enrichedContent = fu.mergeObject(sheetData.enrichedContent, { stealthDetails: await enrich(systemData.attributes.stealth.details), description: await enrich(systemData.details.description), disable: await enrich(systemData.details.disable), @@ -155,7 +155,7 @@ export class HazardSheetPF2e extends ActorSheetPF2e { $html.find("input[data-property]").on("focus", (event) => { const $input = $(event.target); const propertyPath = $input.attr("data-property") ?? ""; - const baseValue = Number(getProperty(this.actor._source, propertyPath)); + const baseValue = Number(fu.getProperty(this.actor._source, propertyPath)); $input.val(baseValue).attr({ name: propertyPath }); }); @@ -167,7 +167,7 @@ export class HazardSheetPF2e extends ActorSheetPF2e { if (valueAttr) { $input.val(valueAttr); } else { - const preparedValue = Number(getProperty(this.actor, propertyPath)); + const preparedValue = Number(fu.getProperty(this.actor, propertyPath)); $input.val(preparedValue !== null && preparedValue >= 0 ? `+${preparedValue}` : preparedValue); } }); diff --git a/src/module/actor/helpers.ts b/src/module/actor/helpers.ts index 5d52b1f912e..530999fbe77 100644 --- a/src/module/actor/helpers.ts +++ b/src/module/actor/helpers.ts @@ -105,8 +105,8 @@ async function migrateActorSource(source: PreCreate): Promise i?.system?._migration?.version ?? MigrationRunnerBase.LATEST_SCHEMA_VERSION), ); - const tokenDefaults = deepClone(game.settings.get("core", "defaultToken")); - const actor = new ActorProxyPF2e(mergeObject({ prototypeToken: tokenDefaults }, source)); + const tokenDefaults = fu.deepClone(game.settings.get("core", "defaultToken")); + const actor = new ActorProxyPF2e(fu.mergeObject({ prototypeToken: tokenDefaults }, source)); await MigrationRunner.ensureSchemaVersion(actor, MigrationList.constructFromVersion(lowestSchemaVersion)); return actor.toObject(); @@ -435,7 +435,7 @@ function strikeFromMeleeItem(item: MeleePF2e): NPCStrike { adjustment.adjustTraits?.(item, actionTraits); } - const strike: NPCStrike = mergeObject(statistic, { + const strike: NPCStrike = fu.mergeObject(statistic, { label: item.name, type: "strike" as const, glyph: getActionGlyph({ type: "action", value: 1 }), diff --git a/src/module/actor/initiative.ts b/src/module/actor/initiative.ts index 13ada175efa..9e913a38d2b 100644 --- a/src/module/actor/initiative.ts +++ b/src/module/actor/initiative.ts @@ -28,7 +28,7 @@ class ActorInitiative { /** @deprecated */ get ability(): AttributeString | null { - foundry.utils.logCompatibilityWarning( + fu.logCompatibilityWarning( "`ActorInitiative#ability` is deprecated. Use `ActorInitiative#attribute` instead.", { since: "5.3.0", until: "6.0.0" }, ); diff --git a/src/module/actor/inventory/index.ts b/src/module/actor/inventory/index.ts index 4b53e249ccf..bff67141e67 100644 --- a/src/module/actor/inventory/index.ts +++ b/src/module/actor/inventory/index.ts @@ -45,7 +45,7 @@ class ActorInventory extends DelegatedCollection | null { // Prevent upstream from mutating property descriptors - const testItem = item instanceof PhysicalItemPF2e ? item.clone() : new ItemProxyPF2e(deepClone(item)); + const testItem = item instanceof PhysicalItemPF2e ? item.clone() : new ItemProxyPF2e(fu.deepClone(item)); if (!testItem.isOfType("physical")) return null; const stackCandidates = this.filter((i) => !i.isInContainer && i.isStackableWith(testItem)); diff --git a/src/module/actor/modifiers.ts b/src/module/actor/modifiers.ts index 412f8e1b39e..48781ddb4ed 100644 --- a/src/module/actor/modifiers.ts +++ b/src/module/actor/modifiers.ts @@ -165,13 +165,13 @@ class ModifierPF2e implements RawModifier { this.type = setHasElement(MODIFIER_TYPES, params.type) ? params.type : "untyped"; this.ability = params.ability ?? null; this.force = params.force ?? false; - this.adjustments = deepClone(params.adjustments ?? []); + this.adjustments = fu.deepClone(params.adjustments ?? []); this.enabled = params.enabled ?? true; this.ignored = params.ignored ?? false; this.custom = params.custom ?? false; this.source = params.source ?? null; this.predicate = new PredicatePF2e(params.predicate ?? []); - this.traits = deepClone(params.traits ?? []); + this.traits = fu.deepClone(params.traits ?? []); this.hideIfDisabled = params.hideIfDisabled ?? false; this.modifier = params.modifier; @@ -245,7 +245,7 @@ class ModifierPF2e implements RawModifier { } toObject(): Required { - return deepClone({ + return fu.deepClone({ ...this, predicate: [...this.predicate], rule: this.rule?.toObject(), @@ -676,7 +676,7 @@ class DamageDicePF2e { } toObject(): RawDamageDice { - return deepClone({ ...this, predicate: [...this.predicate] }); + return fu.deepClone({ ...this, predicate: [...this.predicate] }); } } diff --git a/src/module/actor/npc/document.ts b/src/module/actor/npc/document.ts index 0940f9282c2..dab4e20d48a 100644 --- a/src/module/actor/npc/document.ts +++ b/src/module/actor/npc/document.ts @@ -14,7 +14,7 @@ import { CreatureIdentificationData, creatureIdentificationDCs } from "@module/r import { extractModifierAdjustments, extractModifiers } from "@module/rules/helpers.ts"; import { TokenDocumentPF2e } from "@scene/index.ts"; import { ArmorStatistic, Statistic } from "@system/statistic/index.ts"; -import { createHTMLElement, objectHasKey, sluggify } from "@util"; +import { createHTMLElement, objectHasKey, signedInteger, sluggify } from "@util"; import { NPCFlags, NPCSource, NPCSystemData } from "./data.ts"; import { VariantCloneParams } from "./types.ts"; @@ -32,7 +32,7 @@ class NPCPF2e m.enabled) - .map((m) => `${m.label} ${m.modifier < 0 ? "" : "+"}${m.modifier}`), + ...stat.modifiers.filter((m) => m.enabled).map((m) => `${m.label} ${signedInteger(m.modifier)}`), ].join(", "); system.attributes.hp = stat; setHitPointsRollOptions(this); @@ -233,7 +231,7 @@ class NPCPF2e = expandObject(data ?? {}); + const expanded: DeepPartial = fu.expandObject(data ?? {}); const campaignDiff = expanded?.system?.campaign ? this.campaign.updateSource(expanded.system.campaign, options) : {}; const diff = super.updateSource(data, options); - return R.isEmpty(campaignDiff) ? diff : mergeObject(diff, campaignDiff); + return R.isEmpty(campaignDiff) ? diff : fu.mergeObject(diff, campaignDiff); } /** Only prepare rule elements for non-physical items (in case campaigin items exist) */ @@ -104,7 +104,7 @@ class PartyPF2e fromUuidSync(m.uuid)) .filter((a): a is CreaturePF2e => a instanceof ActorPF2e && a.isOfType("creature")) - .sort(sortBy((a) => a.name)); + .sort((a, b) => a.name.localeCompare(b.name, game.i18n.lang)); for (const member of this.members) { member?.parties.add(this); @@ -128,7 +128,7 @@ class PartyPF2e { - return mergeObject(super.getRollData(), this.campaign?.getRollData?.() ?? {}); + return fu.mergeObject(super.getRollData(), this.campaign?.getRollData?.() ?? {}); } /** Re-render the sheet if data preparation is called from the familiar's master */ @@ -236,7 +236,7 @@ class PartyPF2e { + private _resetAndRerenderDebounced = fu.debounce(() => { super.reset(); this.sheet.render(false, { actor: true } as PartySheetRenderOptions); }, 50); diff --git a/src/module/actor/party/kingdom/chat.ts b/src/module/actor/party/kingdom/chat.ts index b868ae08219..5b786d2b047 100644 --- a/src/module/actor/party/kingdom/chat.ts +++ b/src/module/actor/party/kingdom/chat.ts @@ -1,6 +1,6 @@ -import { ChatMessagePF2e } from "@module/chat-message/document.ts"; -import { Kingdom } from "./model.ts"; +import type { ChatMessagePF2e } from "@module/chat-message/document.ts"; import { createHTMLElement, fontAwesomeIcon, htmlClosest, htmlQuery } from "@util"; +import { Kingdom } from "./model.ts"; /** Handler for kingdom chat messages. Does nothing if there is no kingdom */ export async function handleKingdomChatMessageEvents(options: KingdomChatMessageParams): Promise { diff --git a/src/module/actor/party/kingdom/helpers.ts b/src/module/actor/party/kingdom/helpers.ts index 6a31c6a166d..562525791c7 100644 --- a/src/module/actor/party/kingdom/helpers.ts +++ b/src/module/actor/party/kingdom/helpers.ts @@ -38,7 +38,7 @@ async function importDocuments(actor: ActorPF2e, items: ItemPF2e[], skipDialog: if (!incoming) return null; const data = R.pick(incoming, ["name", "img", "system"]); - const diff = diffObject(d.toObject(true), data); + const diff = fu.diffObject(d.toObject(true), data); return R.isEmpty(diff) ? null : { _id: d.id, ...diff }; }), ); diff --git a/src/module/actor/party/kingdom/model.ts b/src/module/actor/party/kingdom/model.ts index c38365e5dfd..2b5eccb3426 100644 --- a/src/module/actor/party/kingdom/model.ts +++ b/src/module/actor/party/kingdom/model.ts @@ -181,9 +181,9 @@ class Kingdom extends DataModel implements PartyCampai */ async update(data: DeepPartial & Record): Promise { const expanded: DeepPartial & { system?: { campaign?: DeepPartial } } = - expandObject(data); + fu.expandObject(data); - const updateData = mergeObject(expanded, expanded.system?.campaign ?? {}); + const updateData = fu.mergeObject(expanded, expanded.system?.campaign ?? {}); delete updateData.system; await this.actor.update({ "system.campaign": updateData }); diff --git a/src/module/actor/party/kingdom/schema.ts b/src/module/actor/party/kingdom/schema.ts index 3b3ee29e0da..e5c0936c58f 100644 --- a/src/module/actor/party/kingdom/schema.ts +++ b/src/module/actor/party/kingdom/schema.ts @@ -1,4 +1,6 @@ +import { RawModifier } from "@actor/modifiers.ts"; import { ZeroToFour } from "@module/data.ts"; +import { DataUnionField, RecordField, StrictBooleanField, StrictStringField } from "@system/schema-data-fields.ts"; import * as R from "remeda"; import type { ArrayField, SchemaField, StringField } from "types/foundry/common/data/fields.d.ts"; import { KingdomAbility, KingdomSettlementData, KingdomSettlementType, KingdomSkill } from "./types.ts"; @@ -9,8 +11,6 @@ import { KINGDOM_SETTLEMENT_TYPES, KINGDOM_SKILLS, } from "./values.ts"; -import { RawModifier } from "@actor/modifiers.ts"; -import { DataUnionField, RecordField, StrictBooleanField, StrictStringField } from "@system/schema-data-fields.ts"; const { fields } = foundry.data; diff --git a/src/module/actor/party/kingdom/sheet.ts b/src/module/actor/party/kingdom/sheet.ts index b00243faecc..a591fb327b6 100644 --- a/src/module/actor/party/kingdom/sheet.ts +++ b/src/module/actor/party/kingdom/sheet.ts @@ -384,7 +384,7 @@ class KingdomSheetPF2e extends ActorSheetPF2e { // Add settlement and individual settlement actions htmlQuery(html, "[data-action=add-settlement]")?.addEventListener("click", () => { - const id = randomID(16); + const id = fu.randomID(); this.#editingSettlements[id] = true; this.focusElement = `[name="settlements.${id}.name"]`; this.kingdom.update({ [`settlements.${id}`]: {} }); @@ -649,7 +649,7 @@ class KingdomSheetPF2e extends ActorSheetPF2e { protected override async _updateObject(_event: Event, formData: Record): Promise { if (!this.actor.id) return; - const data: DeepPartial = expandObject(formData); + const data: DeepPartial = fu.expandObject(formData); // Ensure penalties are all negative numbers for (const abilitySlug of KINGDOM_ABILITIES) { diff --git a/src/module/actor/party/kingdom/types.ts b/src/module/actor/party/kingdom/types.ts index 7640d1dce37..17409a71d98 100644 --- a/src/module/actor/party/kingdom/types.ts +++ b/src/module/actor/party/kingdom/types.ts @@ -42,11 +42,12 @@ type KingdomSettlementData = ModelPropsFromSchema { overviewSummary: this.#prepareOverviewSummary(), inventorySummary: { totalCoins: - sum(members.map((actor) => actor.inventory.coins.goldValue ?? 0)) + + R.sumBy(members, (actor) => actor.inventory.coins.goldValue ?? 0) + this.actor.inventory.coins.goldValue, totalWealth: - sum(members.map((actor) => actor.inventory.totalWealth.goldValue ?? 0)) + + R.sumBy(members, (actor) => actor.inventory.totalWealth.goldValue ?? 0) + this.actor.inventory.totalWealth.goldValue, totalBulk: members .map((actor) => actor.inventory.bulk.value) @@ -160,8 +160,7 @@ class PartySheetPF2e extends ActorSheetPF2e { hasBulk: actor.inventory.bulk.encumberedAfter !== Infinity, bestSkills: Object.values(actor.skills ?? {}) .filter((s): s is Statistic => !!s?.proficient && !s.lore) - .sort(sortBy((s) => s.mod ?? 0)) - .reverse() + .sort((a, b) => (b.mod ?? 0) - (a.mod ?? 0)) .slice(0, 4) .map((s) => ({ slug: s.slug, mod: s.mod, label: s.label, rank: s.rank })), genderPronouns, @@ -394,7 +393,7 @@ class PartySheetPF2e extends ActorSheetPF2e { const labels = R.sortBy(statistics, (s) => s.mod).map((statistic) => { const rank = statistic.rank ?? (statistic.proficient ? 1 : 0); const prof = game.i18n.localize(CONFIG.PF2E.proficiencyLevels[rank]); - const label = `${statistic.actor.name} (${prof}) ${addSign(statistic.mod)}`; + const label = `${statistic.actor.name} (${prof}) ${signedInteger(statistic.mod)}`; const row = createHTMLElement("div", { children: [label] }); row.style.textAlign = "right"; return row; @@ -473,7 +472,7 @@ class PartySheetPF2e extends ActorSheetPF2e { if (droppedRegion === "inventoryMembers" && targetActor) { const item = await ItemPF2e.fromDropData(data); if (!item) return []; - const actorUuid = foundry.utils.parseUuid(targetActor).documentId; + const actorUuid = fu.parseUuid(targetActor).documentId; if (actorUuid && item.actor && item.isOfType("physical")) { await this.moveItemBetweenActors( event, diff --git a/src/module/actor/sheet/base.ts b/src/module/actor/sheet/base.ts index e19876410c4..295f76559c4 100644 --- a/src/module/actor/sheet/base.ts +++ b/src/module/actor/sheet/base.ts @@ -70,7 +70,7 @@ abstract class ActorSheetPF2e extends ActorSheet extends ActorSheet extends ActorSheet { input.removeAttribute("name"); const propertyPath = input.dataset.property ?? ""; - const preparedValue = Number(getProperty(this.actor, propertyPath)) || 0; - const baseValue = Math.trunc(Number(getProperty(this.actor._source, propertyPath)) || 0); + const preparedValue = Number(fu.getProperty(this.actor, propertyPath)) || 0; + const baseValue = Math.trunc(Number(fu.getProperty(this.actor._source, propertyPath)) || 0); const newValue = Math.trunc(Number(input.value)); if (input instanceof HTMLInputElement) { if (input.type === "number" && input.dataset.dtype === "Number") { @@ -1016,7 +1016,7 @@ abstract class ActorSheetPF2e extends ActorSheet f.uuid === item.uuid)) { actorFormulas.push({ uuid: item.uuid }); await actor.update({ "system.crafting.formulas": actorFormulas }); @@ -1161,7 +1161,7 @@ abstract class ActorSheetPF2e extends ActorSheet extends ActorSheet; if (data.location === "rituals") { - source.system = mergeObject(source.system, { category: { value: "ritual" } }); + source.system = fu.mergeObject(source.system, { category: { value: "ritual" } }); } actor.createEmbeddedDocuments("Item", [source]); diff --git a/src/module/actor/sheet/popups/distribute-coins-popup.ts b/src/module/actor/sheet/popups/distribute-coins-popup.ts index f2611aec509..6c07def7b69 100644 --- a/src/module/actor/sheet/popups/distribute-coins-popup.ts +++ b/src/module/actor/sheet/popups/distribute-coins-popup.ts @@ -122,7 +122,7 @@ export class DistributeCoinsPopup extends FormApplication element instanceof HTMLInputElement && element.name === "actorIds" && element.checked ? element.value : [], ); - options.updateData = mergeObject(options.updateData ?? {}, { actorIds: actorIds }); + options.updateData = fu.mergeObject(options.updateData ?? {}, { actorIds: actorIds }); return super._onSubmit(event, options); } } diff --git a/src/module/actor/sheet/spellcasting-dialog.ts b/src/module/actor/sheet/spellcasting-dialog.ts index 371a474869a..e765a203f1a 100644 --- a/src/module/actor/sheet/spellcasting-dialog.ts +++ b/src/module/actor/sheet/spellcasting-dialog.ts @@ -2,7 +2,6 @@ import { ActorPF2e } from "@actor"; import { AttributeString } from "@actor/types.ts"; import { SpellcastingEntryPF2e } from "@item"; import { SpellcastingEntrySource, SpellcastingEntrySystemSource } from "@item/spellcasting-entry/data.ts"; -import { pick } from "@util/misc.ts"; import * as R from "remeda"; function createEmptySpellcastingEntry(actor: ActorPF2e): SpellcastingEntryPF2e { @@ -85,10 +84,10 @@ class SpellcastingCreateAndEditDialog extends FormApplication = expandObject(formData); + const inputData: DeepPartial = fu.expandObject(formData); // We may disable certain form data, so reinject it - const system = mergeObject( + const system = fu.mergeObject( inputData.system ?? {}, { prepared: { @@ -157,7 +156,7 @@ class SpellcastingCreateAndEditDialog extends FormApplication !categoryPaths.includes(f)); - const categoryPathFound = categoryPaths.some((p) => foundry.utils.hasProperty(featData, p)); + const categoryPathFound = categoryPaths.some((p) => fu.hasProperty(featData, p)); if (!this.hasAllIndexFields(featData, nonCategoryPaths) || !categoryPathFound) { console.warn( diff --git a/src/module/apps/compendium-migration-status.ts b/src/module/apps/compendium-migration-status.ts index 1774ae3dab9..f5336e0cb2c 100644 --- a/src/module/apps/compendium-migration-status.ts +++ b/src/module/apps/compendium-migration-status.ts @@ -31,7 +31,7 @@ class CompendiumMigrationStatus extends Application { // We pass a random string as a field to work around a stale index bug (as of 11.309) // https://github.com/foundryvtt/foundryvtt/issues/9984 const index = await this.compendium.getIndex({ - fields: [randomID(), "system._migration", "system.schema", "data.schema"], + fields: [fu.randomID(), "system._migration", "system.schema", "data.schema"], }); const schemaVersion = Math.min( ...index.map((d) => { diff --git a/src/module/apps/effects-panel.ts b/src/module/apps/effects-panel.ts index a7a9405d753..ff1ee24a3e0 100644 --- a/src/module/apps/effects-panel.ts +++ b/src/module/apps/effects-panel.ts @@ -20,7 +20,7 @@ export class EffectsPanel extends Application { * Debounce and slightly delayed request to re-render this panel. Necessary for situations where it is not possible * to properly wait for promises to resolve before refreshing the UI. */ - refresh = foundry.utils.debounce(this.render, 100); + refresh = fu.debounce(this.render, 100); static override get defaultOptions(): ApplicationOptions { return { diff --git a/src/module/apps/license-viewer/app.ts b/src/module/apps/license-viewer/app.ts index c1e75eee004..a101e1724d7 100644 --- a/src/module/apps/license-viewer/app.ts +++ b/src/module/apps/license-viewer/app.ts @@ -1,6 +1,6 @@ export class LicenseViewer extends Application { static override get defaultOptions(): ApplicationOptions { - return mergeObject(super.defaultOptions, { + return fu.mergeObject(super.defaultOptions, { id: "license-viewer", title: game.i18n.localize("PF2E.LicenseViewer.Label"), template: "systems/pf2e/templates/packs/license-viewer.hbs", diff --git a/src/module/apps/migration-summary.ts b/src/module/apps/migration-summary.ts index d0ae7a53971..aa05dbdc840 100644 --- a/src/module/apps/migration-summary.ts +++ b/src/module/apps/migration-summary.ts @@ -16,7 +16,7 @@ export class MigrationSummary extends Application { (app): app is MigrationSummary => app instanceof MigrationSummary, ); if (existing) { - existing.options = mergeObject(existing.options, options); + existing.options = fu.mergeObject(existing.options, options); return existing; } } diff --git a/src/module/apps/sidebar/actor-directory.ts b/src/module/apps/sidebar/actor-directory.ts index c13ed2c3310..bfcd4a37ac0 100644 --- a/src/module/apps/sidebar/actor-directory.ts +++ b/src/module/apps/sidebar/actor-directory.ts @@ -259,10 +259,10 @@ class ActorDirectoryPF2e extends ActorDirectory> { /** Include flattened update data so parent method can read nested update keys */ protected override async _render(force?: boolean, context: SidebarDirectoryRenderOptions = {}): Promise { // Create new reference in case other applications are using the same context object - context = deepClone(context); + context = fu.deepClone(context); if (context.action === "update" && context.documentType === "Actor" && context.data) { - context.data = context.data.map((d) => ({ ...d, ...flattenObject(d) })); + context.data = context.data.map((d) => ({ ...d, ...fu.flattenObject(d) })); } return super._render(force, context); diff --git a/src/module/apps/sidebar/encounter-tracker.ts b/src/module/apps/sidebar/encounter-tracker.ts index c20ddc958c0..5db8c7ec7bb 100644 --- a/src/module/apps/sidebar/encounter-tracker.ts +++ b/src/module/apps/sidebar/encounter-tracker.ts @@ -84,7 +84,7 @@ export class EncounterTrackerPF2e exten // Highlight the active-turn participant's alliance color if (combatant?.actor && this.viewed?.combatant === combatant) { const alliance = combatant.actor.alliance; - const dispositionColor = new foundry.utils.Color( + const dispositionColor = new fu.Color( alliance === "party" ? allyColor(combatant) : alliance === "opposition" diff --git a/src/module/apps/sidebar/item-directory.ts b/src/module/apps/sidebar/item-directory.ts index 57517fa8e7f..6e2a0dee6d5 100644 --- a/src/module/apps/sidebar/item-directory.ts +++ b/src/module/apps/sidebar/item-directory.ts @@ -28,10 +28,10 @@ export class ItemDirectoryPF2e> extends ItemDirecto /** Include flattened update data so parent method can read nested update keys */ protected override async _render(force?: boolean, context: SidebarDirectoryRenderOptions = {}): Promise { // Create new reference in case other applications are using the same context object - context = deepClone(context); + context = fu.deepClone(context); if (context.action === "update" && context.documentType === "Item" && context.data) { - context.data = context.data.map((d) => ({ ...d, ...flattenObject(d) })); + context.data = context.data.map((d) => ({ ...d, ...fu.flattenObject(d) })); } return super._render(force, context); diff --git a/src/module/apps/world-clock/app.ts b/src/module/apps/world-clock/app.ts index 5429d7e3f6d..b52534b63a3 100644 --- a/src/module/apps/world-clock/app.ts +++ b/src/module/apps/world-clock/app.ts @@ -66,7 +66,7 @@ export class WorldClock extends Application { } static override get defaultOptions(): ApplicationOptions { - return mergeObject(super.defaultOptions, { + return fu.mergeObject(super.defaultOptions, { id: "world-clock", width: 400, template: "systems/pf2e/templates/system/world-clock.hbs", diff --git a/src/module/canvas/status-effects.ts b/src/module/canvas/status-effects.ts index 7ade0434a17..d23ac5e7006 100644 --- a/src/module/canvas/status-effects.ts +++ b/src/module/canvas/status-effects.ts @@ -11,7 +11,7 @@ import { StatusEffectIconTheme } from "@scripts/config/index.ts"; import { ErrorPF2e, fontAwesomeIcon, htmlQueryAll, objectHasKey, setHasElement } from "@util"; import * as R from "remeda"; -const debouncedRender = foundry.utils.debounce(() => { +const debouncedRender = fu.debounce(() => { canvas.tokens.hud.render(); }, 20); diff --git a/src/module/canvas/token/aura/renderer.ts b/src/module/canvas/token/aura/renderer.ts index c7ee35f3040..2986d9678ba 100644 --- a/src/module/canvas/token/aura/renderer.ts +++ b/src/module/canvas/token/aura/renderer.ts @@ -5,7 +5,6 @@ import { isVideoFilePath } from "@util"; import type { EffectAreaSquare } from "../../effect-area-square.ts"; import type { TokenPF2e } from "../index.ts"; import { getAreaSquares } from "./util.ts"; -import { IDestroyOptions } from "pixi.js"; /** Visual rendering of auras emanated by a token's actor */ class AuraRenderer extends PIXI.Graphics implements TokenAuraData { @@ -190,7 +189,7 @@ class AuraRenderer extends PIXI.Graphics implements TokenAuraData { .addChild(text); } - override destroy(options?: boolean | IDestroyOptions): void { + override destroy(options?: boolean | PIXI.IDestroyOptions): void { super.destroy(options); if (this.textureContainer) { diff --git a/src/module/canvas/token/object.ts b/src/module/canvas/token/object.ts index 5f96c619557..26e51b2935a 100644 --- a/src/module/canvas/token/object.ts +++ b/src/module/canvas/token/object.ts @@ -135,8 +135,6 @@ class TokenPF2e extends * @param flankee Potentially flanked token */ protected onOppositeSides(flankerA: TokenPF2e, flankerB: TokenPF2e, flankee: TokenPF2e): boolean { - const { lineSegmentIntersects } = foundry.utils; - const [centerA, centerB] = [flankerA.center, flankerB.center]; const { bounds } = flankee; @@ -144,7 +142,7 @@ class TokenPF2e extends const right = new Ray({ x: bounds.right, y: bounds.top }, { x: bounds.right, y: bounds.bottom }); const top = new Ray({ x: bounds.left, y: bounds.top }, { x: bounds.right, y: bounds.top }); const bottom = new Ray({ x: bounds.left, y: bounds.bottom }, { x: bounds.right, y: bounds.bottom }); - const intersectsSide = (side: Ray): boolean => lineSegmentIntersects(centerA, centerB, side.A, side.B); + const intersectsSide = (side: Ray): boolean => fu.lineSegmentIntersects(centerA, centerB, side.A, side.B); return (intersectsSide(left) && intersectsSide(right)) || (intersectsSide(top) && intersectsSide(bottom)); } @@ -312,13 +310,10 @@ class TokenPF2e extends this.document.auras.get(slug) ?? { radius: null, appearance: null }, properties, ); - const canvasData = R.pick(aura, properties); if (sceneData.radius === null) return true; + const canvasData = R.pick(aura, properties); - const diffCount = - Object.keys(diffObject(canvasData, sceneData)).length + - Object.keys(diffObject(sceneData, canvasData)).length; - return diffCount > 0; + return !R.equals(sceneData, canvasData); }) .map(([slug]) => slug); const newAuraSlugs = Array.from(this.document.auras.keys()).filter((s) => !this.auras.has(s)); diff --git a/src/module/chat-message/document.ts b/src/module/chat-message/document.ts index 0a4feb76c33..7cd4ef5f412 100644 --- a/src/module/chat-message/document.ts +++ b/src/module/chat-message/document.ts @@ -18,8 +18,8 @@ import { RollInspector } from "./roll-inspector.ts"; class ChatMessagePF2e extends ChatMessage { /** The chat log doesn't wait for data preparation before rendering, so set some data in the constructor */ constructor(data: DeepPartial = {}, context: DocumentConstructionContext = {}) { - const expandedFlags = expandObject>(data.flags ?? {}); - data.flags = mergeObject(expandedFlags, { + const expandedFlags = fu.expandObject>(data.flags ?? {}); + data.flags = fu.mergeObject(expandedFlags, { core: { canPopout: expandedFlags.core?.canPopout ?? true }, pf2e: {}, }); diff --git a/src/module/doc-helpers.ts b/src/module/doc-helpers.ts index 6b4d976fe26..e1a1215d245 100644 --- a/src/module/doc-helpers.ts +++ b/src/module/doc-helpers.ts @@ -24,7 +24,7 @@ async function preImportJSON( const sourceSchemaVersion = Number(source.system?._migration?.version) || 0; const worldSchemaVersion = MigrationRunnerBase.LATEST_SCHEMA_VERSION; - if (foundry.utils.isNewerVersion(sourceSchemaVersion, worldSchemaVersion)) { + if (fu.isNewerVersion(sourceSchemaVersion, worldSchemaVersion)) { // Refuse to import if the schema version on the document is higher than the system schema verson; ui.notifications.error( game.i18n.format("PF2E.ErrorMessage.CantImportTooHighVersion", { diff --git a/src/module/encounter/combatant.ts b/src/module/encounter/combatant.ts index 908e4570b1b..3c9f0030f1e 100644 --- a/src/module/encounter/combatant.ts +++ b/src/module/encounter/combatant.ts @@ -158,7 +158,7 @@ class CombatantPF2e< override prepareBaseData(): void { super.prepareBaseData(); - this.flags.pf2e = mergeObject(this.flags.pf2e ?? {}, { overridePriority: {} }); + this.flags.pf2e = fu.mergeObject(this.flags.pf2e ?? {}, { overridePriority: {} }); this.flags.pf2e.roundOfLastTurn ??= null; this.flags.pf2e.initiativeStatistic ??= null; } diff --git a/src/module/item/abc/sheet.ts b/src/module/item/abc/sheet.ts index 280028698dd..9a3af974c51 100644 --- a/src/module/item/abc/sheet.ts +++ b/src/module/item/abc/sheet.ts @@ -84,7 +84,7 @@ abstract class ABCSheetPF2e extends ItemSheetPF2e let id: string; do { - id = randomID(5); + id = fu.randomID(5); } while (items[id]); await this.item.update({ diff --git a/src/module/item/ability/helpers.ts b/src/module/item/ability/helpers.ts index 0941a6b91ed..cd6a116aa1f 100644 --- a/src/module/item/ability/helpers.ts +++ b/src/module/item/ability/helpers.ts @@ -27,7 +27,7 @@ function normalizeActionChangeData(document: SourceWithActionData, changed: Deep if (changed.system && ("actionType" in changed.system || "actions" in changed.system)) { const actionType = changed.system?.actionType?.value ?? document.system.actionType.value; const actionCount = Number(changed.system?.actions?.value ?? document.system.actions.value); - changed.system = mergeObject(changed.system, { + changed.system = fu.mergeObject(changed.system, { actionType: { value: actionType }, actions: { value: actionType !== "action" ? null : Math.clamped(actionCount, 1, 3) }, }); @@ -64,7 +64,7 @@ function createSelfEffectSheetData(data: SelfEffectReference | null): SelfEffect data.name = indexEntry.name; data.img = indexEntry.img; } - const parsedUUID = foundry.utils.parseUuid(data.uuid); + const parsedUUID = fu.parseUuid(data.uuid); const linkData = { id: parsedUUID.documentId ?? null, type: parsedUUID.documentType ?? null, diff --git a/src/module/item/affliction/sheet.ts b/src/module/item/affliction/sheet.ts index f565c66d99a..1f302c7d8c7 100644 --- a/src/module/item/affliction/sheet.ts +++ b/src/module/item/affliction/sheet.ts @@ -3,8 +3,7 @@ import { ActionTrait } from "@item/ability/types.ts"; import { ItemSheetDataPF2e, ItemSheetOptions, ItemSheetPF2e } from "@item/base/sheet/sheet.ts"; import { ConditionManager } from "@system/conditions/index.ts"; import { DamageCategoryUnique } from "@system/damage/types.ts"; -import { DAMAGE_CATEGORIES_UNIQUE } from "@system/damage/values.ts"; -import { htmlClosest, htmlQuery, htmlQueryAll, pick } from "@util"; +import { htmlClosest, htmlQuery, htmlQueryAll } from "@util"; import { UUIDUtils } from "@util/uuid.ts"; import * as R from "remeda"; import { AfflictionConditionData, AfflictionDamage, AfflictionOnset, AfflictionStageData } from "./data.ts"; @@ -31,7 +30,7 @@ class AfflictionSheetPF2e extends ItemSheetPF2e { itemType: game.i18n.localize(definingTrait ? CONFIG.PF2E.actionTraits[definingTrait] : "PF2E.LevelLabel"), conditionTypes: R.omit(CONFIG.PF2E.conditionTypes, ["persistent-damage"]), damageTypes: CONFIG.PF2E.damageTypes, - damageCategories: pick(CONFIG.PF2E.damageCategories, DAMAGE_CATEGORIES_UNIQUE), + damageCategories: R.pick(CONFIG.PF2E.damageCategories, ["precision", "persistent", "splash"]), durationUnits: R.omit(CONFIG.PF2E.timeUnits, ["encounter"]), onsetUnits: R.omit(CONFIG.PF2E.timeUnits, ["encounter", "unlimited"]), saves: CONFIG.PF2E.saves, @@ -95,8 +94,7 @@ class AfflictionSheetPF2e extends ItemSheetPF2e { }, }; - const id = randomID(); - this.item.update({ system: { stages: { [id]: stage } } }); + this.item.update({ system: { stages: { [fu.randomID()]: stage } } }); }); for (const deleteIcon of htmlQueryAll(html, "[data-action=stage-delete]")) { @@ -114,7 +112,7 @@ class AfflictionSheetPF2e extends ItemSheetPF2e { if (!this.item.system.stages[stageId ?? ""]) return; const damage: AfflictionDamage = { formula: "", type: "untyped" }; - this.item.update({ [`system.stages.${stageId}.damage.${randomID()}`]: damage }); + this.item.update({ [`system.stages.${stageId}.damage.${fu.randomID()}`]: damage }); }); } @@ -138,7 +136,7 @@ class AfflictionSheetPF2e extends ItemSheetPF2e { value: 1, }; - this.item.update({ [`system.stages.${stageId}.conditions.${randomID()}`]: newCondition }); + this.item.update({ [`system.stages.${stageId}.conditions.${fu.randomID()}`]: newCondition }); }); } diff --git a/src/module/item/armor/document.ts b/src/module/item/armor/document.ts index 31f3e29d87e..dd4d477bcd6 100644 --- a/src/module/item/armor/document.ts +++ b/src/module/item/armor/document.ts @@ -4,7 +4,7 @@ import { ItemSummaryData } from "@item/base/data/index.ts"; import { PhysicalItemPF2e, getPropertyRuneSlots } from "@item/physical/index.ts"; import { MAGIC_TRADITIONS } from "@item/spell/values.ts"; import { UserPF2e } from "@module/user/index.ts"; -import { ErrorPF2e, addSign, setHasElement, sluggify } from "@util"; +import { ErrorPF2e, setHasElement, signedInteger, sluggify } from "@util"; import * as R from "remeda"; import { ArmorSource, ArmorSystemData } from "./data.ts"; import { ArmorCategory, ArmorGroup, BaseArmorType } from "./types.ts"; @@ -124,7 +124,7 @@ class ArmorPF2e extends Phy ): Promise { const properties = [ CONFIG.PF2E.armorCategories[this.category], - `${addSign(this.acBonus)} ${game.i18n.localize("PF2E.ArmorArmorLabel")}`, + `${signedInteger(this.acBonus)} ${game.i18n.localize("PF2E.ArmorArmorLabel")}`, `${this.system.dexCap || 0} ${game.i18n.localize("PF2E.ArmorDexLabel")}`, `${this.system.checkPenalty || 0} ${game.i18n.localize("PF2E.ArmorCheckLabel")}`, this.speedPenalty ? `${this.system.speedPenalty} ${game.i18n.localize("PF2E.ArmorSpeedLabel")}` : null, diff --git a/src/module/item/base/document.ts b/src/module/item/base/document.ts index 8bfcbaa2933..a6aab6933dc 100644 --- a/src/module/item/base/document.ts +++ b/src/module/item/base/document.ts @@ -88,8 +88,8 @@ class ItemPF2e extends Item const item = fromUuidSync(data.uuid); if (item instanceof ItemPF2e && item.parent && !item.sourceId) { // Upstream would do this via `item.updateSource(...)`, causing a data reset - item._source.flags = mergeObject(item._source.flags, { core: { sourceId: item.uuid } }); - item.flags = mergeObject(item.flags, { core: { sourceId: item.uuid } }); + item._source.flags = fu.mergeObject(item._source.flags, { core: { sourceId: item.uuid } }); + item.flags = fu.mergeObject(item.flags, { core: { sourceId: item.uuid } }); } } @@ -179,7 +179,7 @@ class ItemPF2e extends Item */ async toMessage( event?: MouseEvent | JQuery.TriggeredEvent, - options: { rollMode?: RollMode; create?: boolean; data?: Record } = {}, + options: { rollMode?: RollMode | "roll"; create?: boolean; data?: Record } = {}, ): Promise { if (!this.actor) throw ErrorPF2e(`Cannot create message for unowned item ${this.name}`); @@ -239,7 +239,7 @@ class ItemPF2e extends Item super.prepareBaseData(); const { flags } = this; - flags.pf2e = mergeObject(flags.pf2e ?? {}, { rulesSelections: {} }); + flags.pf2e = fu.mergeObject(flags.pf2e ?? {}, { rulesSelections: {} }); // Temporary measure until upstream issue is addressed (`null` slug is being set to empty string) this.system.slug ||= null; @@ -310,7 +310,7 @@ class ItemPF2e extends Item const updates: Partial & { system: ItemSourcePF2e["system"] } = { name: options.name ? latestSource.name : currentSource.name, img: latestSource.img, - system: deepClone(latestSource.system), + system: fu.deepClone(latestSource.system), }; if (updates.system.level && currentSource.type === "feat") { @@ -322,9 +322,9 @@ class ItemPF2e extends Item if (isPhysicalData(currentSource)) { // Preserve basic physical data - mergeObject( + fu.mergeObject( updates, - expandObject({ + fu.expandObject({ "system.containerId": currentSource.system.containerId, "system.equipped": currentSource.system.equipped, "system.material": currentSource.system.material, @@ -335,7 +335,7 @@ class ItemPF2e extends Item // Preserve runes if (itemIsOfType(currentSource, "armor", "shield", "weapon")) { - mergeObject(updates, expandObject({ "system.runes": currentSource.system.runes })); + fu.mergeObject(updates, fu.expandObject({ "system.runes": currentSource.system.runes })); } if ( @@ -353,7 +353,7 @@ class ItemPF2e extends Item type: currentSource.system.category, heightenedLevel: currentSource.system.spell.system.location.heightenedLevel, }); - mergeObject(updates, { + fu.mergeObject(updates, { name: spellConsumableData.name, system: { spell: spellConsumableData.system.spell }, }); @@ -369,11 +369,11 @@ class ItemPF2e extends Item } } else if (itemIsOfType(currentSource, "campaignFeature", "feat", "spell")) { // Preserve feat and spellcasting entry location - mergeObject(updates, expandObject({ "system.location": currentSource.system.location })); + fu.mergeObject(updates, fu.expandObject({ "system.location": currentSource.system.location })); } if (currentSource.type === "feat" && currentSource.system.level.taken) { - mergeObject(updates, expandObject({ "system.level.taken": currentSource.system.level.taken })); + fu.mergeObject(updates, fu.expandObject({ "system.level.taken": currentSource.system.level.taken })); } await this.update(updates, { diff: false, recursive: false }); @@ -398,8 +398,8 @@ class ItemPF2e extends Item ): Promise { data.properties = data.properties?.filter((property) => property !== null) ?? []; if (isItemSystemData(data)) { - const chatData = duplicate(data); - htmlOptions.rollData = mergeObject(this.getRollData(), htmlOptions.rollData ?? {}); + const chatData = fu.duplicate(data); + htmlOptions.rollData = fu.mergeObject(this.getRollData(), htmlOptions.rollData ?? {}); chatData.description.value = await TextEditor.enrichHTML(chatData.description.value, { ...htmlOptions, async: true, @@ -417,7 +417,7 @@ class ItemPF2e extends Item ): Promise { if (!this.actor) throw ErrorPF2e(`Cannot retrieve chat data for unowned item ${this.name}`); const systemData: Record = { ...this.system, traits: this.traitChatData() }; - return this.processChatData(htmlOptions, deepClone(systemData)); + return this.processChatData(htmlOptions, fu.deepClone(systemData)); } protected traitChatData( @@ -526,7 +526,7 @@ class ItemPF2e extends Item ["affliction", "condition", "effect"].includes(s.type), ); for (const source of effectSources) { - const effect = new CONFIG.PF2E.Item.documentClasses[source.type](deepClone(source), { parent: actor }); + const effect = new CONFIG.PF2E.Item.documentClasses[source.type](fu.deepClone(source), { parent: actor }); const isUnaffected = effect.isOfType("condition") && !actor.isAffectedBy(effect); const isImmune = actor.isImmuneTo(effect); if (isUnaffected || isImmune) { @@ -570,7 +570,7 @@ class ItemPF2e extends Item const items = sources.map((source): ItemPF2e => { if (!(context.keepId || context.keepEmbeddedIds)) { - source._id = randomID(); + source._id = fu.randomID(); } return new CONFIG.Item.documentClass(source, { parent: actor }) as ItemPF2e; }); @@ -809,7 +809,7 @@ class ItemPF2e extends Item } } if (itemUpdates.length > 0) { - mergeObject(actorUpdates, { items: itemUpdates }); + fu.mergeObject(actorUpdates, { items: itemUpdates }); } } else { // The above method of updating embedded items in an actor update does not work with synthetic actors diff --git a/src/module/item/base/sheet/rule-element-form/aura.ts b/src/module/item/base/sheet/rule-element-form/aura.ts index 2e18931d168..228071a46d9 100644 --- a/src/module/item/base/sheet/rule-element-form/aura.ts +++ b/src/module/item/base/sheet/rule-element-form/aura.ts @@ -21,7 +21,7 @@ class AuraForm extends RuleElementForm { protected override getInitialValue(): object { this.#effectsMap.clear(); this.#effectsMap = new Map( - this.object.effects.map((e, index): [number, AuraEffectSource] => [index, deepClone(e)]), + this.object.effects.map((e, index): [number, AuraEffectSource] => [index, fu.deepClone(e)]), ); return super.getInitialValue(); @@ -148,7 +148,7 @@ class AuraForm extends RuleElementForm { } override async updateItem(updates: Partial | Record): Promise { - const expanded = expandObject>(updates); + const expanded = fu.expandObject>(updates); if (expanded.effects) { // Restore clobbered effects array and perform updates expanded.effects = this.#updateEffectsMap(expanded); @@ -217,7 +217,7 @@ class AuraForm extends RuleElementForm { // Reconstruct the map with deleted defaults this.#effectsMap = new Map( Object.values(source.effects ?? {}).map((data, index): [number, AuraREEffectSource] => { - const updatedData = deepClone(data); + const updatedData = fu.deepClone(data); const deletions: { [K in `-=${keyof AuraREEffectSource}`]?: null | undefined } = {}; // Clean up save data @@ -247,7 +247,7 @@ class AuraForm extends RuleElementForm { deletions["-=predicate"] = null; } - return [index, mergeObject(updatedData, deletions, { performDeletions: true })]; + return [index, fu.mergeObject(updatedData, deletions, { performDeletions: true })]; }), ); diff --git a/src/module/item/base/sheet/rule-element-form/base.ts b/src/module/item/base/sheet/rule-element-form/base.ts index 363eb853a54..379301f0027 100644 --- a/src/module/item/base/sheet/rule-element-form/base.ts +++ b/src/module/item/base/sheet/rule-element-form/base.ts @@ -54,9 +54,9 @@ class RuleElementForm< (() => { const RuleElementClass = RuleElements.all[String(this.rule.key)]; if (!RuleElementClass) return null as TObject; - const actor = new ActorProxyPF2e({ _id: randomID(), name: "temp", type: "character" }); + const actor = new ActorProxyPF2e({ _id: fu.randomID(), name: "temp", type: "character" }); const item = new ItemProxyPF2e(this.item.toObject(), { parent: actor }); - return new RuleElementClass(deepClone(this.rule), { + return new RuleElementClass(fu.deepClone(this.rule), { parent: item, strict: false, suppressWarnings: true, @@ -107,7 +107,7 @@ class RuleElementForm< ? [game.i18n.localize("PF2E.RuleElement.Unrecognized"), false] : [localized, true]; })(); - const mergedRule = mergeObject(this.getInitialValue(), this.rule); + const mergedRule = fu.mergeObject(this.getInitialValue(), this.rule); const validationFailures = ((): string[] => { const fieldFailures = this.object?.validationFailures.fields?.asError().getAllFailures() ?? {}; @@ -143,7 +143,7 @@ class RuleElementForm< const dropZoneTemplate = await getTemplate("systems/pf2e/templates/items/rules/partials/drop-zone.hbs"); const getResolvableData = (property: string) => { - const value = getProperty(rule, property); + const value = fu.getProperty(rule, property); const mode = isBracketedValue(value) ? "brackets" : isObject(value) ? "object" : "primitive"; return { value, mode, property, path: `${this.basePath}.${property}` }; }; @@ -187,7 +187,7 @@ class RuleElementForm< */ async updateItem(updates: Partial | Record): Promise { const rules: Record[] = this.item.toObject().system.rules; - const result = mergeObject(this.rule, updates, { performDeletions: true }); + const result = fu.mergeObject(this.rule, updates, { performDeletions: true }); if (this.schema) { cleanDataUsingSchema(this.schema.fields, result); } @@ -217,7 +217,7 @@ class RuleElementForm< for (const button of htmlQueryAll(html, "[data-action=toggle-brackets]")) { button.addEventListener("click", () => { const property = button.dataset.property ?? "value"; - const value = getProperty(this.rule, property); + const value = fu.getProperty(this.rule, property); if (isBracketedValue(value)) { this.updateItem({ [property]: "" }); } else { @@ -229,7 +229,7 @@ class RuleElementForm< for (const button of htmlQueryAll(html, "[data-action=add-bracket]")) { const property = button.dataset.property ?? "value"; button.addEventListener("click", () => { - const value = getProperty(this.rule, property); + const value = fu.getProperty(this.rule, property); if (isBracketedValue(value)) { value.brackets.push({ value: "" }); this.updateItem({ [property]: value }); @@ -240,7 +240,7 @@ class RuleElementForm< for (const button of htmlQueryAll(html, "[data-action=delete-bracket]")) { const property = button.dataset.property ?? "value"; button.addEventListener("click", () => { - const value = getProperty(this.rule, property); + const value = fu.getProperty(this.rule, property); const idx = Number(htmlClosest(button, "[data-idx]")?.dataset.idx); if (isBracketedValue(value)) { value.brackets.splice(idx, 1); @@ -311,7 +311,7 @@ class RuleElementForm< return; } - source = mergeObject(duplicate(this.rule), source); + source = fu.mergeObject(fu.duplicate(this.rule), source); // Prevent wheel events on the sliders from spamming updates for (const slider of htmlQueryAll(this.element, "input[type=range")) { diff --git a/src/module/item/base/sheet/sheet.ts b/src/module/item/base/sheet/sheet.ts index 88f812e9f25..4490b0be247 100644 --- a/src/module/item/base/sheet/sheet.ts +++ b/src/module/item/base/sheet/sheet.ts @@ -158,7 +158,7 @@ class ItemSheetPF2e extends ItemSheet, key) => - mergeObject(result, { [key]: `PF2E.RuleElement.${key}` }), + fu.mergeObject(result, { [key]: `PF2E.RuleElement.${key}` }), {}, ), ), @@ -258,7 +258,7 @@ class ItemSheetPF2e extends ItemSheet { const key = item.slug ?? sluggify(item.name); return { ...options, [key]: item.name }; - }, deepClone(CONFIG.PF2E.attackEffects)); + }, fu.deepClone(CONFIG.PF2E.attackEffects)); } override async activateEditor( @@ -456,7 +456,7 @@ class ItemSheetPF2e extends ItemSheet { input.dataset.value = input.value; @@ -560,10 +560,10 @@ class ItemSheetPF2e extends ItemSheet & { system?: { rules?: string[] } } = updateData - ? mergeObject(fd.object, updateData) - : expandObject(fd.object); + ? fu.mergeObject(fd.object, updateData) + : fu.expandObject(fd.object); - const flattenedData = flattenObject(data); + const flattenedData = fu.flattenObject(data); processTagifyInSubmitData(this.form, flattenedData); return flattenedData; } @@ -610,7 +610,7 @@ class ItemSheetPF2e extends ItemSheet): Promise { - const expanded = expandObject(formData) as DeepPartial; + const expanded = fu.expandObject(formData) as DeepPartial; // If the submission is coming from a rule element, update that rule element // This avoids updates from forms if that form has a problematic implementation @@ -634,7 +634,7 @@ class ItemSheetPF2e extends ItemSheet extends ABC const system = this.system; return { - ancestry: deepClone(system.ancestryFeatLevels.value), + ancestry: fu.deepClone(system.ancestryFeatLevels.value), class: [...system.classFeatLevels.value], skill: [...system.skillFeatLevels.value], general: [...system.generalFeatLevels.value], diff --git a/src/module/item/condition/document.ts b/src/module/item/condition/document.ts index 269f319ebcc..7e5dcd60137 100644 --- a/src/module/item/condition/document.ts +++ b/src/module/item/condition/document.ts @@ -169,7 +169,7 @@ class ConditionPF2e extends const systemData = this.system; systemData.value.value = systemData.value.isValued ? Number(systemData.value.value) || 1 : null; - systemData.duration = mergeObject(systemData.duration, { + systemData.duration = fu.mergeObject(systemData.duration, { value: -1, unit: "unlimited", expiry: null, diff --git a/src/module/item/condition/persistent-damage-dialog.ts b/src/module/item/condition/persistent-damage-dialog.ts index 96cb4fd6172..8aeed014058 100644 --- a/src/module/item/condition/persistent-damage-dialog.ts +++ b/src/module/item/condition/persistent-damage-dialog.ts @@ -1,10 +1,11 @@ -import { ActorPF2e } from "@actor"; +import type { ActorPF2e } from "@actor"; import { damageDiceIcon } from "@system/damage/helpers.ts"; import { DamageRoll } from "@system/damage/roll.ts"; import { DamageType } from "@system/damage/types.ts"; import { DAMAGE_TYPE_ICONS } from "@system/damage/values.ts"; -import { htmlClosest, htmlQuery, htmlQueryAll, pick, sortBy } from "@util"; -import { PersistentDamagePF2e } from "./document.ts"; +import { htmlClosest, htmlQuery, htmlQueryAll } from "@util"; +import * as R from "remeda"; +import type { PersistentDamagePF2e } from "./document.ts"; class PersistentDamageDialog extends Application { constructor( @@ -41,7 +42,7 @@ class PersistentDamageDialog extends Application id: c.id, bullet: damageDiceIcon(c.system.persistent.damage).outerHTML, active: c.active, - ...pick(c.system.persistent, ["formula", "damageType", "dc"]), + ...R.pick(c.system.persistent, ["formula", "damageType", "dc"]), })); return { @@ -62,7 +63,7 @@ class PersistentDamageDialog extends Application }; }); - return types.sort(sortBy((type) => type.label)); + return types.sort((a, b) => a.label.localeCompare(b.label)); } /** Determine whether an inputted formula is valid, reporting to the user if not. */ @@ -117,7 +118,7 @@ class PersistentDamageDialog extends Application if (this.#reportFormulaValidity(`(${formula})[${damageType}]`, elements.formula)) { const baseConditionSource = game.pf2e.ConditionManager.getCondition("persistent-damage").toObject(); - const persistentSource = mergeObject(baseConditionSource, { + const persistentSource = fu.mergeObject(baseConditionSource, { system: { persistent: { formula, damageType, dc }, }, diff --git a/src/module/item/consumable/document.ts b/src/module/item/consumable/document.ts index 153a0515432..aca1fa1aa0b 100644 --- a/src/module/item/consumable/document.ts +++ b/src/module/item/consumable/document.ts @@ -35,7 +35,7 @@ class ConsumablePF2e extend if (!this.actor) throw ErrorPF2e(`No owning actor found for "${this.name}" (${this.id})`); if (!this.system.spell) return null; - return new SpellPF2e(deepClone(this.system.spell), { + return new SpellPF2e(fu.deepClone(this.system.spell), { parent: this.actor, fromConsumable: true, }) as SpellPF2e; diff --git a/src/module/item/deity/document.ts b/src/module/item/deity/document.ts index 53df31d808d..34c7fe0816a 100644 --- a/src/module/item/deity/document.ts +++ b/src/module/item/deity/document.ts @@ -45,8 +45,8 @@ class DeityPF2e extends Ite const { deities } = this.actor.system.details; const systemData = this.system; deities.primary = { - skill: deepClone(systemData.skill), - weapons: deepClone(systemData.weapons), + skill: fu.deepClone(systemData.skill), + weapons: fu.deepClone(systemData.weapons), }; // Set available domains from this deity diff --git a/src/module/item/effect/sheet.ts b/src/module/item/effect/sheet.ts index 538a3656631..70dcc37abec 100644 --- a/src/module/item/effect/sheet.ts +++ b/src/module/item/effect/sheet.ts @@ -65,7 +65,7 @@ export class EffectSheetPF2e extends ItemSheetPF2e { } protected override async _updateObject(event: Event, formData: Record): Promise { - const expanded = expandObject>(formData); + const expanded = fu.expandObject>(formData); const badge = expanded.system?.badge; if (badge) { // Ensure badge labels remain an array @@ -78,7 +78,7 @@ export class EffectSheetPF2e extends ItemSheetPF2e { } } - super._updateObject(event, flattenObject(expanded)); + super._updateObject(event, fu.flattenObject(expanded)); } } diff --git a/src/module/item/equipment/document.ts b/src/module/item/equipment/document.ts index 40a9f22d901..4fd28b79ce6 100644 --- a/src/module/item/equipment/document.ts +++ b/src/module/item/equipment/document.ts @@ -107,7 +107,7 @@ class EquipmentPF2e extends if (!hasApexTrait && this._source.system.apex) { delete changed.system?.apex; (changed.system satisfies object | undefined) ??= {}; // workaround of `DeepPartial` limitations - changed.system = mergeObject(changed.system!, { "-=apex": null }); + changed.system = fu.mergeObject(changed.system!, { "-=apex": null }); } return super._preUpdate(changed, options, user); diff --git a/src/module/item/feat/document.ts b/src/module/item/feat/document.ts index 0613acfd36c..d7d1748101f 100644 --- a/src/module/item/feat/document.ts +++ b/src/module/item/feat/document.ts @@ -114,7 +114,7 @@ class FeatPF2e extends Item this.system.frequency.value ??= this.system.frequency.max; } - this.system.subfeatures = mergeObject({ keyOptions: [] }, this.system.subfeatures ?? {}); + this.system.subfeatures = fu.mergeObject({ keyOptions: [] }, this.system.subfeatures ?? {}); this.system.selfEffect ??= null; // Self effects are only usable with actions @@ -229,7 +229,7 @@ class FeatPF2e extends Item traits.findSplice((t) => t === "lineage"); } } else if ((Array.isArray(traits) && traits.includes("lineage")) || changed.system?.onlyLevel1) { - mergeObject(changed, { system: { maxTakable: 1 } }); + fu.mergeObject(changed, { system: { maxTakable: 1 } }); } return super._preUpdate(changed, options, user); diff --git a/src/module/item/kit/document.ts b/src/module/item/kit/document.ts index d642d4d2b1e..c203b626ec0 100644 --- a/src/module/item/kit/document.ts +++ b/src/module/item/kit/document.ts @@ -1,14 +1,14 @@ import { ActorPF2e } from "@actor"; +import { ActorSizePF2e } from "@actor/data/size.ts"; import { ItemPF2e, PhysicalItemPF2e } from "@item"; import { Price } from "@item/physical/data.ts"; import { CoinsPF2e } from "@item/physical/helpers.ts"; import { DENOMINATIONS } from "@item/physical/values.ts"; +import { Size } from "@module/data.ts"; import { UserPF2e } from "@module/user/index.ts"; import { ErrorPF2e, isObject } from "@util"; import { UUIDUtils } from "@util/uuid.ts"; import { KitEntryData, KitSource, KitSystemData } from "./data.ts"; -import { Size } from "@module/data.ts"; -import { ActorSizePF2e } from "@actor/data/size.ts"; class KitPF2e extends ItemPF2e { get entries(): KitEntryData[] { @@ -36,7 +36,7 @@ class KitPF2e extends ItemP return items.reduce( async (promise: PhysicalItemPF2e[] | Promise[]>, item, index) => { const prepared = await promise; - const clone = item.clone({ _id: randomID(), system: { size } }, { keepId: true }); + const clone = item.clone({ _id: fu.randomID(), system: { size } }, { keepId: true }); const entry = entries[index]; if (clone.isOfType("physical")) { clone.updateSource({ diff --git a/src/module/item/kit/sheet.ts b/src/module/item/kit/sheet.ts index dbed96b012a..e85f52baa1c 100644 --- a/src/module/item/kit/sheet.ts +++ b/src/module/item/kit/sheet.ts @@ -60,7 +60,7 @@ class KitSheetPF2e extends ItemSheetPF2e { } let id: string; do { - id = randomID(5); + id = fu.randomID(5); } while (items[id]); await this.item.update({ [`${pathPrefix}.${id}`]: entry }); diff --git a/src/module/item/melee/sheet.ts b/src/module/item/melee/sheet.ts index 7bf72433c49..001b08f4151 100644 --- a/src/module/item/melee/sheet.ts +++ b/src/module/item/melee/sheet.ts @@ -31,7 +31,7 @@ export class MeleeSheetPF2e extends ItemSheetPF2e { // Add damage partial for (const button of htmlQueryAll(html, "a[data-action=add-partial]")) { button.addEventListener("click", () => { - const newKey = randomID(); + const newKey = fu.randomID(); this.item.update({ [`system.damageRolls.${newKey}`]: { damage: "1d4", damageType: "bludgeoning" }, }); diff --git a/src/module/item/physical/document.ts b/src/module/item/physical/document.ts index 72012cffc03..b2ee2867d5c 100644 --- a/src/module/item/physical/document.ts +++ b/src/module/item/physical/document.ts @@ -5,7 +5,7 @@ import { MystifiedTraits } from "@item/base/data/values.ts"; import { isCycle } from "@item/container/helpers.ts"; import { Rarity, Size, ZeroToTwo } from "@module/data.ts"; import type { UserPF2e } from "@module/user/document.ts"; -import { ErrorPF2e, isObject, sortBy } from "@util"; +import { ErrorPF2e, isObject } from "@util"; import * as R from "remeda"; import { getUnidentifiedPlaceholderImage } from "../identification.ts"; import { Bulk } from "./bulk.ts"; @@ -49,7 +49,7 @@ abstract class PhysicalItemPF2e i.sort)); + const siblings = (containerResolved?.contents.contents ?? inventory.contents).sort((a, b) => a.sort - b.sort); // If there is nothing to sort, perform the normal update and end here if (!sortBefore && !siblings.length && !!mainContainerUpdate) { @@ -407,7 +407,7 @@ abstract class PhysicalItemPF2e { const baseUpdate = { _id: s.target.id, ...s.update }; if (mainContainerUpdate && s.target.id === this.id) { - return mergeObject(baseUpdate, mainContainerUpdate); + return fu.mergeObject(baseUpdate, mainContainerUpdate); } return baseUpdate; }); diff --git a/src/module/item/physical/helpers.ts b/src/module/item/physical/helpers.ts index afe0d63f359..759de751b25 100644 --- a/src/module/item/physical/helpers.ts +++ b/src/module/item/physical/helpers.ts @@ -170,7 +170,7 @@ function handleHPChange(item: PhysicalItemPF2e, changed: DeepPartial i._id === item._id); if (itemIndex === -1) return; actorSource?.items.splice(itemIndex ?? 0, 1, changedSource); @@ -180,7 +180,7 @@ function handleHPChange(item: PhysicalItemPF2e, changed: DeepPartial itemClone.system.hp.max) { - changed.system = mergeObject(changed.system ?? {}, { hp: { value: itemClone.system.hp.max } }); + changed.system = fu.mergeObject(changed.system ?? {}, { hp: { value: itemClone.system.hp.max } }); } } diff --git a/src/module/item/physical/runes.ts b/src/module/item/physical/runes.ts index 8628bd9d74f..2a05077f6bb 100644 --- a/src/module/item/physical/runes.ts +++ b/src/module/item/physical/runes.ts @@ -94,7 +94,7 @@ function getPropertyRuneDegreeAdjustments(item: WeaponPF2e): DegreeOfSuccessAdju function getPropertyRuneDice(runes: WeaponPropertyRuneType[], options: Set): DamageDicePF2e[] { return runes.flatMap((rune) => { const runeData = WEAPON_PROPERTY_RUNES[rune]; - return deepClone(runeData.damage?.dice ?? []).map((data) => { + return fu.deepClone(runeData.damage?.dice ?? []).map((data) => { const dice = new DamageDicePF2e({ selector: "strike-damage", slug: rune, diff --git a/src/module/item/physical/sheet.ts b/src/module/item/physical/sheet.ts index b75f85b5db5..81193499c93 100644 --- a/src/module/item/physical/sheet.ts +++ b/src/module/item/physical/sheet.ts @@ -166,7 +166,7 @@ class PhysicalItemSheetPF2e extends ItemSheetPF2 $html.find("[data-action=activation-add]").on("click", (event) => { event.preventDefault(); - const id = randomID(16); + const id = fu.randomID(16); const action: ItemActivation = { id, actionCost: { value: 1, type: "action" }, @@ -232,7 +232,7 @@ class PhysicalItemSheetPF2e extends ItemSheetPF2 } // Normalize nullable fields for embedded actions - const expanded = expandObject(formData) as DeepPartial>; + const expanded = fu.expandObject(formData) as DeepPartial>; for (const action of Object.values(expanded.system?.activations ?? [])) { // Ensure activation time is in a proper format const actionCost = action.actionCost; @@ -244,7 +244,7 @@ class PhysicalItemSheetPF2e extends ItemSheetPF2 } } - return super._updateObject(event, flattenObject(expanded)); + return super._updateObject(event, fu.flattenObject(expanded)); } } diff --git a/src/module/item/shield/document.ts b/src/module/item/shield/document.ts index 00e5b81b7ce..b32a50a0c46 100644 --- a/src/module/item/shield/document.ts +++ b/src/module/item/shield/document.ts @@ -90,7 +90,7 @@ class ShieldPF2e extends Ph } if (this.system.traits.integrated) { - this.system.traits.integrated.runes = mergeObject( + this.system.traits.integrated.runes = fu.mergeObject( { potency: 0, striking: 0, property: [] } satisfies IntegratedWeaponData["runes"], this.system.traits.integrated.runes, ); @@ -137,7 +137,7 @@ class ShieldPF2e extends Ph const mainDamageType = damageTypeMap[traitParts.at(2) ?? ""] ?? "slashing"; const versatileDamageType = isVersatileWeapon ? damageTypeMap[traitParts.at(-1) ?? ""] : null; if (this.system.traits.integrated && versatileDamageType) { - this.system.traits.integrated.versatile = mergeObject( + this.system.traits.integrated.versatile = fu.mergeObject( { options: [mainDamageType, versatileDamageType], selection: mainDamageType }, this.system.traits.integrated.versatile ?? {}, ); @@ -146,7 +146,7 @@ class ShieldPF2e extends Ph this.system.traits.integrated.versatile = null; } - this.system.traits.integrated = mergeObject( + this.system.traits.integrated = fu.mergeObject( { damageType: mainDamageType, runes: { potency: 0, striking: 0, property: [] }, @@ -207,7 +207,7 @@ class ShieldPF2e extends Ph system: Partial & { traits: WeaponTraitsSource }; }; const shieldThrowTrait = this.system.traits.value.find((t) => t.startsWith("shield-throw-")); - const baseData: BaseWeaponData = deepClone({ + const baseData: BaseWeaponData = fu.deepClone({ ...R.pick(this, ["_id", "name", "img"]), type: "weapon", system: { @@ -248,7 +248,7 @@ class ShieldPF2e extends Ph if (integratedWeaponRunes?.potency || integratedWeaponRunes?.striking) { additionalData.name = this._source.name; } - const combinedData = mergeObject(baseData, additionalData); + const combinedData = fu.mergeObject(baseData, additionalData); return new ItemProxyPF2e(combinedData, { parent: this.parent, shield: this }) as WeaponPF2e; } diff --git a/src/module/item/shield/sheet.ts b/src/module/item/shield/sheet.ts index 1b727c60f21..6cc66098cf3 100644 --- a/src/module/item/shield/sheet.ts +++ b/src/module/item/shield/sheet.ts @@ -61,7 +61,7 @@ class ShieldSheetPF2e extends PhysicalItemSheetPF2e { htmlQuery(html, "input[data-action=toggle-specific")?.addEventListener("change", () => { const newValue: SpecificShieldData | null = this.item.system.specific ? null - : deepClone({ + : fu.deepClone({ ...R.pick(this.item._source.system, ["material", "runes"]), integrated: this.item._source.system.traits.integrated ? { runes: this.item._source.system.traits.integrated.runes } diff --git a/src/module/item/shield/types.ts b/src/module/item/shield/types.ts index aa403b371cf..0ef378f3755 100644 --- a/src/module/item/shield/types.ts +++ b/src/module/item/shield/types.ts @@ -1,5 +1,4 @@ type BaseShieldType = keyof typeof CONFIG.PF2E.baseShieldTypes; - type ShieldTrait = keyof typeof CONFIG.PF2E.shieldTraits; export type { BaseShieldType, ShieldTrait }; diff --git a/src/module/item/spell/document.ts b/src/module/item/spell/document.ts index e00137039d9..bb445e4af14 100644 --- a/src/module/item/spell/document.ts +++ b/src/module/item/spell/document.ts @@ -167,7 +167,7 @@ class SpellPF2e extends Ite /** @deprecated */ get ability(): AttributeString { - foundry.utils.logCompatibilityWarning("`SpellPF2e#ability` is deprecated. Use `SpellPF2e#attribute` instead.", { + fu.logCompatibilityWarning("`SpellPF2e#ability` is deprecated. Use `SpellPF2e#attribute` instead.", { since: "5.3.0", until: "6.0.0", }); @@ -271,7 +271,7 @@ class SpellPF2e extends Ite if (scalingFormula && partCount > 0) { const scalingTerms = parseTermsFromSimpleFormula(scalingFormula, { rollData }); for (let i = 0; i < partCount; i++) { - terms.push(...deepClone(scalingTerms)); + terms.push(...fu.deepClone(scalingTerms)); } } } @@ -415,7 +415,7 @@ class SpellPF2e extends Ite // If there are no overlays, only return an override if this is a simple heighten if (!heightenEntries.length && !overlays.length) { if (castLevel !== this.rank) { - return mergeObject(this.toObject(), { system: { location: { heightenedLevel: castLevel } } }); + return fu.mergeObject(this.toObject(), { system: { location: { heightenedLevel: castLevel } } }); } else { return null; } @@ -437,7 +437,7 @@ class SpellPF2e extends Ite delete source.system.overlays; source.system.rules = []; - source = mergeObject(source, data, { overwrite: true }); + source = fu.mergeObject(source, data, { overwrite: true }); break; } } @@ -445,7 +445,7 @@ class SpellPF2e extends Ite } for (const overlay of heightenEntries) { - mergeObject(source.system, overlay.system); + fu.mergeObject(source.system, overlay.system); } // Set the spell as heightened if necessary (either up or down) @@ -508,7 +508,7 @@ class SpellPF2e extends Ite origin: { name: this.name, slug: this.slug, - traits: deepClone(this.system.traits.value), + traits: fu.deepClone(this.system.traits.value), ...this.getOriginData(), }, areaType: this.system.area?.type ?? null, @@ -562,7 +562,7 @@ class SpellPF2e extends Ite this.system.traits.value = this.system.traits.value.filter((t) => t in CONFIG.PF2E.spellTraits); if (this.system.traits.value.includes("attack")) { - this.system.defense = mergeObject(this.system.defense ?? {}, { + this.system.defense = fu.mergeObject(this.system.defense ?? {}, { passive: { statistic: "ac" as const }, save: this.system.defense?.save ?? null, }); @@ -610,7 +610,7 @@ class SpellPF2e extends Ite override prepareSiblingData(this: SpellPF2e): void { if (this.spellcasting?.isInnate) { - mergeObject(this.system.location, { uses: { value: 1, max: 1 } }, { overwrite: false }); + fu.mergeObject(this.system.location, { uses: { value: 1, max: 1 } }, { overwrite: false }); } } @@ -685,7 +685,7 @@ class SpellPF2e extends Ite // Only spells/consumables currently use DOM data. // Eventually sheets should be handling "retrieve spell but heightened" const domData = htmlClosest(event?.currentTarget, ".item")?.dataset; - const castData = mergeObject(data ?? {}, domData ?? {}); + const castData = fu.mergeObject(data ?? {}, domData ?? {}); // If this is for a higher level spell, heighten it first const castLevel = Number(castData.castLevel ?? ""); diff --git a/src/module/item/spell/overlay.ts b/src/module/item/spell/overlay.ts index 028ae561932..f1724fccefa 100644 --- a/src/module/item/spell/overlay.ts +++ b/src/module/item/spell/overlay.ts @@ -30,7 +30,7 @@ class SpellOverlayCollection extends Collection { overlayType: SpellOverlayType, options: { renderSheet: boolean } = { renderSheet: false }, ): Promise { - const id = randomID(); + const id = fu.randomID(); switch (overlayType) { case "override": @@ -66,7 +66,10 @@ class SpellOverlayCollection extends Collection { // Diff data and only save the difference const variantSource = variantSpell.toObject(); const originSource = this.spell.toObject(); - const difference = diffObject & { overlayType: string }>(originSource, variantSource); + const difference = fu.diffObject & { overlayType: string }>( + originSource, + variantSource, + ); if (Object.keys(difference).length === 0) return variantSpell; diff --git a/src/module/item/spell/sheet.ts b/src/module/item/spell/sheet.ts index 5dc4b0fa09f..de6ea8426d0 100644 --- a/src/module/item/spell/sheet.ts +++ b/src/module/item/spell/sheet.ts @@ -172,7 +172,7 @@ export class SpellSheetPF2e extends ItemSheetPF2e { category: null, materials: [], }; - this.item.update({ [`${baseKey}.damage.${randomID()}`]: emptyDamage }); + this.item.update({ [`${baseKey}.damage.${fu.randomID()}`]: emptyDamage }); }); } diff --git a/src/module/item/spellcasting-entry/document.ts b/src/module/item/spellcasting-entry/document.ts index f549c4fc425..a94ecdc2ff3 100644 --- a/src/module/item/spellcasting-entry/document.ts +++ b/src/module/item/spellcasting-entry/document.ts @@ -32,7 +32,7 @@ class SpellcastingEntryPF2e /** @deprecated */ get ability(): AttributeString { - foundry.utils.logCompatibilityWarning( + fu.logCompatibilityWarning( "`SpellcastingEntryPF2e#ability` is deprecated. Use `SpellcastingEntryPF2e#attribute` instead.", { since: "5.3.0", until: "6.0.0" }, ); diff --git a/src/module/item/spellcasting-entry/trick.ts b/src/module/item/spellcasting-entry/trick.ts index 88e1c91701b..4ccf12a9658 100644 --- a/src/module/item/spellcasting-entry/trick.ts +++ b/src/module/item/spellcasting-entry/trick.ts @@ -38,7 +38,7 @@ class TrickMagicItemEntry implements Spell /** @deprecated */ get ability(): AttributeString { - foundry.utils.logCompatibilityWarning( + fu.logCompatibilityWarning( "`TrickMagicItemEntry#ability` is deprecated. Use `TrickMagicItemEntry#attribute` instead.", { since: "5.3.0", until: "6.0.0" }, ); diff --git a/src/module/item/weapon/document.ts b/src/module/item/weapon/document.ts index 3d2a74c4297..1d124f92131 100644 --- a/src/module/item/weapon/document.ts +++ b/src/module/item/weapon/document.ts @@ -147,7 +147,7 @@ class WeaponPF2e extends Ph /** This weapon's damage before modification by creature abilities, effects, etc. */ get baseDamage(): WeaponDamage { return { - ...deepClone(this.system.damage), + ...fu.deepClone(this.system.damage), // Damage types from trait toggles are not applied as data mutations so as to delay it for rule elements to // add options damageType: @@ -388,7 +388,7 @@ class WeaponPF2e extends Ph override prepareSiblingData(): void { super.prepareSiblingData(); // Set the default label to the ammunition item's name - const ammoRules = this.ammo?.system.rules.map((r) => ({ label: this.ammo?.name, ...deepClone(r) })) ?? []; + const ammoRules = this.ammo?.system.rules.map((r) => ({ label: this.ammo?.name, ...fu.deepClone(r) })) ?? []; this.system.rules.push(...ammoRules); } @@ -467,7 +467,7 @@ class WeaponPF2e extends Ph if (this.isRanged || !thrownTrait) return null; const range = Number(/(\d{1,3})$/.exec(thrownTrait)?.at(1)) as WeaponRangeIncrement; - const newTraits = deepClone(traits); + const newTraits = fu.deepClone(traits); newTraits.splice(newTraits.indexOf(thrownTrait), 1, "thrown"); const overlay: DeepPartial = { system: { @@ -649,13 +649,14 @@ class WeaponPF2e extends Ph damageRolls: [baseDamage, splashDamage, fromPropertyRunes, persistentDamage] .flat() .reduce( - (rolls: Record, roll) => mergeObject(rolls, { [randomID()]: roll }), + (rolls: Record, roll) => + fu.mergeObject(rolls, { [fu.randomID()]: roll }), {}, ), traits: { value: toAttackTraits(this.system.traits.value), }, - rules: deepClone(this._source.system.rules), + rules: fu.deepClone(this._source.system.rules), }, flags: { pf2e: { linkedWeapon: this.id } }, }; diff --git a/src/module/item/weapon/sheet.ts b/src/module/item/weapon/sheet.ts index bff56fec6bc..496c9776c45 100644 --- a/src/module/item/weapon/sheet.ts +++ b/src/module/item/weapon/sheet.ts @@ -72,7 +72,7 @@ export class WeaponSheetPF2e extends PhysicalItemSheetPF2e { // Restrict the Implement tag to one-handed weapons const otherTags = ((): SheetOptions => { - const otherWeaponTags: Record = deepClone(CONFIG.PF2E.otherWeaponTags); + const otherWeaponTags: Record = fu.deepClone(CONFIG.PF2E.otherWeaponTags); if (weapon.hands !== "1") delete otherWeaponTags.implement; return createSheetTags(otherWeaponTags, sheetData.data.traits.otherTags); })(); diff --git a/src/module/migration/migrations/674-stable-homebrew-tag-ids.ts b/src/module/migration/migrations/674-stable-homebrew-tag-ids.ts index a4ab802b33f..7bce9161f17 100644 --- a/src/module/migration/migrations/674-stable-homebrew-tag-ids.ts +++ b/src/module/migration/migrations/674-stable-homebrew-tag-ids.ts @@ -7,10 +7,10 @@ import { MigrationBase } from "../base.ts"; export class Migration674StableHomebrewTagIDs extends MigrationBase { static override version = 0.674; - #homebrewKeys = deepClone(HOMEBREW_TRAIT_KEYS); + #homebrewKeys = fu.deepClone(HOMEBREW_TRAIT_KEYS); #homebrewTags = this.#homebrewKeys.reduce( - (settings, key) => mergeObject(settings, { [key]: game.settings.get("pf2e", `homebrew.${key}`) }), + (settings, key) => fu.mergeObject(settings, { [key]: game.settings.get("pf2e", `homebrew.${key}`) }), {} as Record<(typeof HOMEBREW_TRAIT_KEYS)[number], HomebrewTag[]>, ); diff --git a/src/module/migration/migrations/711-heritage-items.ts b/src/module/migration/migrations/711-heritage-items.ts index 58a7e8d6481..b4a78ac6938 100644 --- a/src/module/migration/migrations/711-heritage-items.ts +++ b/src/module/migration/migrations/711-heritage-items.ts @@ -180,7 +180,7 @@ export class Migration711HeritageItems extends MigrationBase { } return { - _id: randomID(), + _id: fu.randomID(), type: "heritage", img: feature.img.endsWith("/feat.svg") ? "systems/pf2e/icons/default-icons/heritage.svg" : feature.img, name: feature.name, diff --git a/src/module/migration/migrations/724-crafting-max-item-level.ts b/src/module/migration/migrations/724-crafting-max-item-level.ts index 0d7ea067e77..912448b8a37 100644 --- a/src/module/migration/migrations/724-crafting-max-item-level.ts +++ b/src/module/migration/migrations/724-crafting-max-item-level.ts @@ -42,7 +42,7 @@ export class Migration724CraftingMaxItemLevel extends MigrationBase { const selector = this.pathPattern.exec(rule.path)?.[1] ?? null; if (selector) { type RawPredicateAll = OldRawPredicate & { all: PredicateStatement[] }; - const predicate: RawPredicateAll = (rule.predicate = mergeObject( + const predicate: RawPredicateAll = (rule.predicate = fu.mergeObject( { all: [] }, isObject(rule.predicate) ? rule.predicate : {}, )); diff --git a/src/module/migration/migrations/725-quick-climb-rule-elements.ts b/src/module/migration/migrations/725-quick-climb-rule-elements.ts index b27e49e7d09..e7a803647c9 100644 --- a/src/module/migration/migrations/725-quick-climb-rule-elements.ts +++ b/src/module/migration/migrations/725-quick-climb-rule-elements.ts @@ -27,7 +27,7 @@ export class Migration725QuickClimbREs extends MigrationBase { override async updateItem(source: ItemSourcePF2e): Promise { if (source.type === "feat" && source.system.slug === "quick-climb") { - source.system.rules = deepClone(this.quickClimb); + source.system.rules = fu.deepClone(this.quickClimb); } } } diff --git a/src/module/migration/migrations/738-update-laughing-shadow.ts b/src/module/migration/migrations/738-update-laughing-shadow.ts index 173c584ebc3..086cdc3444b 100644 --- a/src/module/migration/migrations/738-update-laughing-shadow.ts +++ b/src/module/migration/migrations/738-update-laughing-shadow.ts @@ -23,12 +23,12 @@ export class Migration738UpdateLaughingShadow extends MigrationBase { if (source.type === "feat" && source.system.slug === "laughing-shadow") { const laughingShadow = await this.#shadowPromise; if (!(laughingShadow instanceof ItemPF2e)) return; - source.system.rules = deepClone(laughingShadow._source.system.rules); + source.system.rules = fu.deepClone(laughingShadow._source.system.rules); } else if (source.type === "effect" && source.system.slug === "stance-arcane-cascade") { const arcaneCascade = await this.#cascadePromise; if (!(arcaneCascade instanceof ItemPF2e)) return; - const newRules = deepClone(arcaneCascade._source.system.rules); + const newRules = fu.deepClone(arcaneCascade._source.system.rules); // Retrieve the ChoiceSet selection if one has been made const withSelection = source.system.rules.find( diff --git a/src/module/migration/migrations/745-effect-target-to-choice-set.ts b/src/module/migration/migrations/745-effect-target-to-choice-set.ts index 70683da150b..1b4d0e449fd 100644 --- a/src/module/migration/migrations/745-effect-target-to-choice-set.ts +++ b/src/module/migration/migrations/745-effect-target-to-choice-set.ts @@ -3,7 +3,8 @@ import { ItemSourcePF2e, WeaponSource } from "@item/base/data/index.ts"; import { RuleElementSource } from "@module/rules/index.ts"; import { ChoiceSetSchema } from "@module/rules/rule-element/choice-set/data.ts"; import { PredicateStatement } from "@system/predication.ts"; -import { isObject, sluggify } from "@util"; +import { sluggify } from "@util"; +import * as R from "remeda"; import { MigrationBase } from "../base.ts"; /** Convert EffectTarget REs into ChoiceSets */ @@ -43,8 +44,8 @@ export class Migration745EffectTargetToChoiceSet extends MigrationBase { all: ["item:equipped"], any: ["item:base:club", "item:base:staff"], }; - } else if (isObject(rule.predicate)) { - newRE.choices.predicate = deepClone(rule.predicate); + } else if (R.isObject(rule.predicate)) { + newRE.choices.predicate = fu.deepClone(rule.predicate); } return newRE; diff --git a/src/module/migration/migrations/768-add-new-auras.ts b/src/module/migration/migrations/768-add-new-auras.ts index 0b2b345018c..746766c5cbe 100644 --- a/src/module/migration/migrations/768-add-new-auras.ts +++ b/src/module/migration/migrations/768-add-new-auras.ts @@ -77,16 +77,16 @@ export class Migration768AddNewAuras extends MigrationBase { switch (source.system.slug) { case "aura-of-life": - source.system.rules = [deepClone(this.#auraOfLife)]; + source.system.rules = [fu.deepClone(this.#auraOfLife)]; break; case "enlightened-presence": - source.system.rules = [deepClone(this.#enlightenedPresence)]; + source.system.rules = [fu.deepClone(this.#enlightenedPresence)]; break; case "eternal-blessing": - source.system.rules = deepClone(this.#eternalBlessing); + source.system.rules = fu.deepClone(this.#eternalBlessing); break; case "marshal-dedication": - source.system.rules = [deepClone(this.#marshalsAura)]; + source.system.rules = [fu.deepClone(this.#marshalsAura)]; break; } } diff --git a/src/module/migration/migrations/793-make-predicates-arrays.ts b/src/module/migration/migrations/793-make-predicates-arrays.ts index 600a5fb7b87..490107d17d4 100644 --- a/src/module/migration/migrations/793-make-predicates-arrays.ts +++ b/src/module/migration/migrations/793-make-predicates-arrays.ts @@ -13,13 +13,13 @@ export class Migration793MakePredicatesArrays extends MigrationBase { const keys = Object.keys(predicate); if (keys.length === 0) return []; if (keys.length === 1 && Array.isArray(predicate.all)) { - return deepClone(predicate.all); + return fu.deepClone(predicate.all); } if (keys.length === 1 && Array.isArray(predicate.any) && predicate.any.length === 1) { - return deepClone(predicate.any); + return fu.deepClone(predicate.any); } - return deepClone( + return fu.deepClone( [ predicate.all ?? [], Array.isArray(predicate.any) ? { or: predicate.any } : [], diff --git a/src/module/migration/migrations/812-restructure-iwr.ts b/src/module/migration/migrations/812-restructure-iwr.ts index 26e6b840a3d..0a2c1553886 100644 --- a/src/module/migration/migrations/812-restructure-iwr.ts +++ b/src/module/migration/migrations/812-restructure-iwr.ts @@ -1,10 +1,11 @@ -import { ActorSourcePF2e } from "@actor/data/index.ts"; import { ActorTraitsSource } from "@actor/data/base.ts"; +import { ActorSourcePF2e } from "@actor/data/index.ts"; import { ImmunitySource, ResistanceSource, WeaknessSource } from "@actor/data/iwr.ts"; import { ImmunityType, ResistanceType, WeaknessType } from "@actor/types.ts"; import { IMMUNITY_TYPES, RESISTANCE_TYPES, WEAKNESS_TYPES } from "@actor/values.ts"; import { ItemSourcePF2e } from "@item/base/data/index.ts"; -import { isObject, pick, setHasElement, sluggify } from "@util"; +import { setHasElement, sluggify } from "@util"; +import * as R from "remeda"; import { MigrationBase } from "../base.ts"; /** Move IWR data to `actor.system.attributes` */ @@ -26,7 +27,7 @@ export class Migration812RestructureIWR extends MigrationBase { if (!("game" in globalThis)) delete traits.di; traits["-=di"] = null; - if (isObject(oldData) && "value" in oldData && Array.isArray(oldData.value) && oldData.value.length > 0) { + if (R.isObject(oldData) && "value" in oldData && Array.isArray(oldData.value) && oldData.value.length > 0) { const immunities = oldData.value .map((i: unknown) => this.#normalizeType(String(i))) .filter((i): i is ImmunityType => setHasElement(IMMUNITY_TYPES, i)) @@ -43,7 +44,7 @@ export class Migration812RestructureIWR extends MigrationBase { if (Array.isArray(oldData) && oldData.length > 0) { const weaknesses = this.#getWR(oldData, WEAKNESS_TYPES).map((data): WeaknessSource => { - const weakness: WeaknessSource = pick(data, ["type", "value"]); + const weakness: WeaknessSource = R.pick(data, ["type", "value"]); // If parsable exceptions are found, add those as well const parsed = this.#parseExceptions(String(data.exceptions ?? "")); @@ -67,7 +68,7 @@ export class Migration812RestructureIWR extends MigrationBase { if (Array.isArray(oldData) && oldData.length > 0) { const resistances = this.#getWR(oldData, RESISTANCE_TYPES).map((data): ResistanceSource => { - const resistance: ResistanceSource = pick(data, ["type", "value"]); + const resistance: ResistanceSource = R.pick(data, ["type", "value"]); // If parsable exceptions and resistance-doubling are found, add those as well const parsed = this.#parseExceptions(String(data.exceptions ?? "")); @@ -143,9 +144,7 @@ export class Migration812RestructureIWR extends MigrationBase { return maybeWR .filter( (r: unknown): r is { type: string; value: number; exceptions?: string } => - isObject<{ type: unknown; value: unknown }>(r) && - typeof r.type === "string" && - typeof r.value === "number", + R.isObject(r) && typeof r.type === "string" && typeof r.value === "number", ) .map((wr) => { wr.type = this.#normalizeType(wr.type); diff --git a/src/module/migration/migrations/841-v11-uuid-format.ts b/src/module/migration/migrations/841-v11-uuid-format.ts index a35db3b3df5..56770a2dca8 100644 --- a/src/module/migration/migrations/841-v11-uuid-format.ts +++ b/src/module/migration/migrations/841-v11-uuid-format.ts @@ -1,8 +1,8 @@ import { ActorSourcePF2e } from "@actor/data/index.ts"; import { ItemSourcePF2e } from "@item/base/data/index.ts"; +import { itemIsOfType } from "@item/helpers.ts"; import { isObject, recursiveReplaceString } from "@util"; import { MigrationBase } from "../base.ts"; -import { itemIsOfType } from "@item/helpers.ts"; /** Convert UUIDs to V11 format */ export class Migration841V11UUIDFormat extends MigrationBase { @@ -18,7 +18,7 @@ export class Migration841V11UUIDFormat extends MigrationBase { const documentType = ((): CompendiumDocumentType | null => { if (explicitDocType) return explicitDocType; if ("game" in globalThis) { - const { collection } = foundry.utils.parseUuid(uuid) ?? {}; + const { collection } = fu.parseUuid(uuid) ?? {}; return collection instanceof CompendiumCollection ? collection.metadata.type ?? null : null; } return null; diff --git a/src/module/migration/migrations/866-link-to-actor-size-again.ts b/src/module/migration/migrations/866-link-to-actor-size-again.ts index ae33fb1cf6a..18f466ce670 100644 --- a/src/module/migration/migrations/866-link-to-actor-size-again.ts +++ b/src/module/migration/migrations/866-link-to-actor-size-again.ts @@ -20,7 +20,7 @@ export class Migration866LinkToActorSizeAgain extends MigrationBase { return; } - mergeObject(tokenSource.flags, { + fu.mergeObject(tokenSource.flags, { pf2e: { linkToActorSize: false, autoscale: false, diff --git a/src/module/migration/migrations/882-spell-data-reorganization.ts b/src/module/migration/migrations/882-spell-data-reorganization.ts index efdc2c290ca..04e15f4ab71 100644 --- a/src/module/migration/migrations/882-spell-data-reorganization.ts +++ b/src/module/migration/migrations/882-spell-data-reorganization.ts @@ -28,7 +28,7 @@ export class Migration882SpellDataReorganization extends MigrationBase { #DAMAGE_TYPES = new Set([...DAMAGE_TYPES, "good", "evil", "lawful", "chaotic"]); #ensureTraitsPresence(system: MaybeOldSpellSystemSource): { value: SpellTrait[] } { - return mergeObject({ value: [] }, system.traits ?? { value: [] }); + return fu.mergeObject({ value: [] }, system.traits ?? { value: [] }); } #migrateRule(rule: DeepPartial): DeepPartial | never[] { @@ -161,7 +161,7 @@ export class Migration882SpellDataReorganization extends MigrationBase { if ("save" in system) system["-=save"] = null; // Flatten `damage` object - const oldSpellDamage = deepClone(system.damage); + const oldSpellDamage = fu.deepClone(system.damage); if (isObject(oldSpellDamage) && R.isObject(oldSpellDamage?.value)) { system.damage = {}; for (const [key, partial] of Object.entries(oldSpellDamage?.value)) { diff --git a/src/module/migration/migrations/885-convert-alignment-damage.ts b/src/module/migration/migrations/885-convert-alignment-damage.ts index 87998063862..3dd80ddad2e 100644 --- a/src/module/migration/migrations/885-convert-alignment-damage.ts +++ b/src/module/migration/migrations/885-convert-alignment-damage.ts @@ -76,7 +76,7 @@ export class Migration885ConvertAlignmentDamage extends MigrationBase { } } - mergeObject(source.system.attributes, iwr); + fu.mergeObject(source.system.attributes, iwr); traits.value = R.uniq(traits.value.sort()); if (traits.value.includes("holy") && traits.value.includes("unholy")) { // Something weird about this one! diff --git a/src/module/migration/migrations/899-armor-shields-to-shield-shields.ts b/src/module/migration/migrations/899-armor-shields-to-shield-shields.ts index 2658ee15aef..9014468ae06 100644 --- a/src/module/migration/migrations/899-armor-shields-to-shield-shields.ts +++ b/src/module/migration/migrations/899-armor-shields-to-shield-shields.ts @@ -222,9 +222,9 @@ export class Migration899ArmorShieldToShieldShield extends MigrationBase { const setSpecific = (): void => { system.specific = { - material: deepClone(system.material), + material: fu.deepClone(system.material), runes: { reinforcing: 0 }, - integrated: system.traits.integrated ? deepClone({ runes: system.traits.integrated.runes }) : null, + integrated: system.traits.integrated ? fu.deepClone({ runes: system.traits.integrated.runes }) : null, } satisfies SpecificShieldData; }; diff --git a/src/module/migration/migrations/907-restructure-armor-weapon-runes.ts b/src/module/migration/migrations/907-restructure-armor-weapon-runes.ts index 172737bf630..a5533c30a21 100644 --- a/src/module/migration/migrations/907-restructure-armor-weapon-runes.ts +++ b/src/module/migration/migrations/907-restructure-armor-weapon-runes.ts @@ -43,12 +43,12 @@ export class Migration907RestructureArmorWeaponRunes extends MigrationBase { } if (specificData.value === true) { specificData["-=value"] = null; - specificData.runes = deepClone(system.runes); + specificData.runes = fu.deepClone(system.runes); if (R.isObject(specificData.material) && "precious" in specificData.material) { specificData.material["-=precious"] = null; - specificData.material = mergeObject(specificData.material, deepClone(system.material)); + specificData.material = fu.mergeObject(specificData.material, fu.deepClone(system.material)); } else { - specificData.material = deepClone(system.material); + specificData.material = fu.deepClone(system.material); } } else if ("value" in specificData) { system.specific = null; diff --git a/src/module/migration/runner/base.ts b/src/module/migration/runner/base.ts index c4ebb6ca0c2..2a783dcf87d 100644 --- a/src/module/migration/runner/base.ts +++ b/src/module/migration/runner/base.ts @@ -70,7 +70,7 @@ export class MigrationRunnerBase { } async getUpdatedActor(actor: ActorSourcePF2e, migrations: MigrationBase[]): Promise { - const currentActor = deepClone(actor); + const currentActor = fu.deepClone(actor); for (const migration of migrations) { for (const currentItem of currentActor.items) { @@ -108,7 +108,7 @@ export class MigrationRunnerBase { } async getUpdatedItem(item: ItemSourcePF2e, migrations: MigrationBase[]): Promise { - const current = deepClone(item); + const current = fu.deepClone(item); for (const migration of migrations) { await migration.preUpdateItem?.(current); @@ -134,7 +134,7 @@ export class MigrationRunnerBase { tableSource: foundry.documents.RollTableSource, migrations: MigrationBase[], ): Promise { - const current = deepClone(tableSource); + const current = fu.deepClone(tableSource); for (const migration of migrations) { try { @@ -151,7 +151,7 @@ export class MigrationRunnerBase { macroSource: foundry.documents.MacroSource, migrations: MigrationBase[], ): Promise { - const current = deepClone(macroSource); + const current = fu.deepClone(macroSource); for (const migration of migrations) { try { @@ -168,7 +168,7 @@ export class MigrationRunnerBase { source: foundry.documents.JournalEntrySource, migrations: MigrationBase[], ): Promise { - const clone = deepClone(source); + const clone = fu.deepClone(source); for (const migration of migrations) { try { @@ -197,7 +197,7 @@ export class MigrationRunnerBase { userData: foundry.documents.UserSource, migrations: MigrationBase[], ): Promise { - const current = deepClone(userData); + const current = fu.deepClone(userData); for (const migration of migrations) { try { await migration.updateUser?.(current); diff --git a/src/module/migration/runner/index.ts b/src/module/migration/runner/index.ts index 52a523a060d..6f5e2f84dc2 100644 --- a/src/module/migration/runner/index.ts +++ b/src/module/migration/runner/index.ts @@ -146,7 +146,7 @@ export class MigrationRunner extends MigrationRunnerBase { try { const updated = await this.getUpdatedJournalEntry(journalEntry.toObject(), migrations); - const changes = diffObject(journalEntry.toObject(), updated); + const changes = fu.diffObject(journalEntry.toObject(), updated); if (Object.keys(changes).length > 0) { await journalEntry.update(changes, { noHook: true }); } @@ -160,7 +160,7 @@ export class MigrationRunner extends MigrationRunnerBase { try { const updatedMacro = await this.getUpdatedMacro(macro.toObject(), migrations); - const changes = diffObject(macro.toObject(), updatedMacro); + const changes = fu.diffObject(macro.toObject(), updatedMacro); if (Object.keys(changes).length > 0) { await macro.update(changes, { noHook: true }); } @@ -174,7 +174,7 @@ export class MigrationRunner extends MigrationRunnerBase { try { const updatedMacro = await this.getUpdatedTable(table.toObject(), migrations); - const changes = diffObject(table.toObject(), updatedMacro); + const changes = fu.diffObject(table.toObject(), updatedMacro); if (Object.keys(changes).length > 0) { table.update(changes, { noHook: true }); } @@ -191,7 +191,7 @@ export class MigrationRunner extends MigrationRunnerBase { try { const updatedToken = await this.getUpdatedToken(token, migrations); - const changes = diffObject(token.toObject(), updatedToken); + const changes = fu.diffObject(token.toObject(), updatedToken); if (Object.keys(changes).length > 0) { try { @@ -213,7 +213,7 @@ export class MigrationRunner extends MigrationRunnerBase { try { const baseUser = user.toObject(); const updatedUser = await this.getUpdatedUser(baseUser, migrations); - const changes = diffObject(user.toObject(), updatedUser); + const changes = fu.diffObject(user.toObject(), updatedUser); if (Object.keys(changes).length > 0) { await user.update(changes, { noHook: true }); } diff --git a/src/module/rules/helpers.ts b/src/module/rules/helpers.ts index 70b25a128ff..493aabff5af 100644 --- a/src/module/rules/helpers.ts +++ b/src/module/rules/helpers.ts @@ -13,7 +13,6 @@ import { RollNotePF2e } from "@module/notes.ts"; import { BaseDamageData } from "@system/damage/index.ts"; import { DegreeOfSuccessAdjustment } from "@system/degree-of-success.ts"; import { RollTwiceOption } from "@system/rolls.ts"; -import { isObject, pick } from "@util"; import * as R from "remeda"; import { BracketedValue, RuleElementPF2e } from "./rule-element/index.ts"; import { DamageDiceSynthetics, RollSubstitution, RollTwiceSynthetic, RuleElementSynthetics } from "./synthetics.ts"; @@ -128,7 +127,7 @@ function extractRollSubstitutions( rollOptions: Set, ): RollSubstitution[] { return domains - .flatMap((d) => deepClone(substitutions[d] ?? [])) + .flatMap((d) => fu.deepClone(substitutions[d] ?? [])) .filter((s) => s.predicate?.test(rollOptions) ?? true); } @@ -136,11 +135,11 @@ function extractDegreeOfSuccessAdjustments( synthetics: Pick, selectors: string[], ): DegreeOfSuccessAdjustment[] { - return Object.values(pick(synthetics.degreeOfSuccessAdjustments, selectors)).flat(); + return Object.values(R.pick(synthetics.degreeOfSuccessAdjustments, selectors)).flat(); } function isBracketedValue(value: unknown): value is BracketedValue { - return isObject<{ brackets?: unknown }>(value) && Array.isArray(value.brackets); + return R.isObject(value) && Array.isArray(value.brackets); } async function processPreUpdateActorHooks( diff --git a/src/module/rules/rule-element/adjust-modifier.ts b/src/module/rules/rule-element/adjust-modifier.ts index 2e6d36bcee9..ef79e91fc14 100644 --- a/src/module/rules/rule-element/adjust-modifier.ts +++ b/src/module/rules/rule-element/adjust-modifier.ts @@ -71,7 +71,7 @@ class AdjustModifierRuleElement extends RuleElementPF2e { override beforePrepareData(): void { if (this.ignored) return; - const predicate = new PredicatePF2e(this.resolveInjectedProperties(deepClone([...this.predicate]))); + const predicate = new PredicatePF2e(this.resolveInjectedProperties(fu.deepClone([...this.predicate]))); const adjustment: ModifierAdjustment = { slug: this.slug, diff --git a/src/module/rules/rule-element/ae-like.ts b/src/module/rules/rule-element/ae-like.ts index 6d1bd901fb3..9ad69faf76f 100644 --- a/src/module/rules/rule-element/ae-like.ts +++ b/src/module/rules/rule-element/ae-like.ts @@ -29,7 +29,7 @@ class AELikeRuleElement extends RuleElementPF2e extends RuleElementPF2e getProperty(actor, path) !== undefined, + (path) => fu.getProperty(actor, path) !== undefined, )) ); } @@ -127,7 +127,7 @@ class AELikeRuleElement extends RuleElementPF2e extends RuleElementPF2e extends RuleElementPF2e { } #processAppearanceData(): AuraAppearanceData { - const appearance = deepClone(this.appearance); + const appearance = fu.deepClone(this.appearance); const { border, highlight, texture } = appearance; const textureSrc = ((): ImageFilePath | VideoFilePath | null => { if (!texture) return null; diff --git a/src/module/rules/rule-element/base.ts b/src/module/rules/rule-element/base.ts index e14f70815b9..6e619985886 100644 --- a/src/module/rules/rule-element/base.ts +++ b/src/module/rules/rule-element/base.ts @@ -239,7 +239,7 @@ abstract class RuleElementPF2e { const data = key === "rule" ? this : key === "actor" || key === "item" ? this[key] : this.item; - const value = getProperty(data, prop); + const value = fu.getProperty(data, prop); if (value === undefined) { this.ignored = true; if (warn) this.failValidation(`Failed to resolve injected property "${source}"`); @@ -284,7 +284,7 @@ abstract class RuleElementPF2e { modifiers: [baseMod], filter: this.#filterModifier, }) as CharacterSkill; - this.actor.system.skills[skillShort] = mergeObject( + this.actor.system.skills[skillShort] = fu.mergeObject( this.actor.system.skills[skillShort], this.actor.skills[key].getTraceData(), ); @@ -568,9 +568,9 @@ class BattleFormRuleElement extends RuleElementPF2e { category: weapon.category, group: weapon.group, baseType: weapon.baseType, - traits: deepClone(weapon.system.traits.value), + traits: fu.deepClone(weapon.system.traits.value), modifier: strike.modifier, - damage: deepClone(weapon.system.damage), + damage: fu.deepClone(weapon.system.damage), ownIfHigher: strike.ownIfHigher, }; diff --git a/src/module/rules/rule-element/choice-set/rule-element.ts b/src/module/rules/rule-element/choice-set/rule-element.ts index f7f9afef9a5..5c6c3b8e80b 100644 --- a/src/module/rules/rule-element/choice-set/rule-element.ts +++ b/src/module/rules/rule-element/choice-set/rule-element.ts @@ -304,7 +304,7 @@ class ChoiceSetRuleElement extends RuleElementPF2e { } #choicesFromPath(path: string): PickableThing[] { - const choiceObject: unknown = getProperty(CONFIG.PF2E, path) ?? getProperty(this.actor, path) ?? {}; + const choiceObject: unknown = fu.getProperty(CONFIG.PF2E, path) ?? fu.getProperty(this.actor, path) ?? {}; if ( Array.isArray(choiceObject) && choiceObject.every((c) => R.isObject<{ value: string }>(c) && typeof c.value === "string") @@ -439,10 +439,10 @@ class ChoiceSetRuleElement extends RuleElementPF2e { .flatMap((d): { name: string; type: string; uuid: string }[] => d.contents) .filter((s): s is PreCreate & { uuid: DocumentUUID } => s.type === itemType) .map((source) => { - const parsedUUID = foundry.utils.parseUuid(source.uuid); + const parsedUUID = fu.parseUuid(source.uuid); const pack = parsedUUID.collection instanceof CompendiumCollection ? parsedUUID.collection.metadata.id : null; - return new ItemProxyPF2e(deepClone(source), { pack }); + return new ItemProxyPF2e(fu.deepClone(source), { pack }); }) .concat(game.items.filter((i) => i.type === itemType)) .filter((i) => filter.test([...i.getRollOptions("item"), ...actorRollOptions])); diff --git a/src/module/rules/rule-element/damage-dice.ts b/src/module/rules/rule-element/damage-dice.ts index c6874ec3d58..076bdd3f6d0 100644 --- a/src/module/rules/rule-element/damage-dice.ts +++ b/src/module/rules/rule-element/damage-dice.ts @@ -130,7 +130,7 @@ class DamageDiceRuleElement extends RuleElementPF2e { category: this.category, damageType, predicate: this.predicate, - override: deepClone(this.override), + override: fu.deepClone(this.override), enabled: testPassed, ...resolvedBrackets, }); diff --git a/src/module/rules/rule-element/grant-item/rule-element.ts b/src/module/rules/rule-element/grant-item/rule-element.ts index 37a71a6c3ca..bafeb00f3bc 100644 --- a/src/module/rules/rule-element/grant-item/rule-element.ts +++ b/src/module/rules/rule-element/grant-item/rule-element.ts @@ -5,8 +5,9 @@ import { ItemSourcePF2e } from "@item/base/data/index.ts"; import { ItemGrantDeleteAction } from "@item/base/data/system.ts"; import { PHYSICAL_ITEM_TYPES } from "@item/physical/values.ts"; import { SlugField, StrictArrayField } from "@system/schema-data-fields.ts"; -import { ErrorPF2e, isObject, pick, setHasElement, sluggify, tupleHasValue } from "@util"; +import { ErrorPF2e, isObject, setHasElement, sluggify, tupleHasValue } from "@util"; import { UUIDUtils } from "@util/uuid.ts"; +import * as R from "remeda"; import { RuleElementOptions, RuleElementPF2e } from "../base.ts"; import { ChoiceSetSource } from "../choice-set/data.ts"; import { ChoiceSetRuleElement } from "../choice-set/rule-element.ts"; @@ -50,8 +51,8 @@ class GrantItemRuleElement extends RuleElementPF2e { const isValidPreselect = (p: Record): p is Record => Object.values(p).every((v) => ["string", "number"].includes(typeof v)); this.preselectChoices = - isObject(data.preselectChoices) && isValidPreselect(data.preselectChoices) - ? deepClone(data.preselectChoices) + R.isObject(data.preselectChoices) && isValidPreselect(data.preselectChoices) + ? fu.deepClone(data.preselectChoices) : {}; this.grantedId = this.item.flags.pf2e.itemGrants[this.flag ?? ""]?.id ?? null; @@ -146,17 +147,17 @@ class GrantItemRuleElement extends RuleElementPF2e { } // Set ids and flags on the granting and granted items - itemSource._id ??= randomID(); + itemSource._id ??= fu.randomID(); const grantedSource = grantedItem.toObject(); - grantedSource._id = randomID(); + grantedSource._id = fu.randomID(); // Special case until configurable item alterations are supported: if (itemSource.type === "effect" && grantedSource.type === "effect") { grantedSource.system.level.value = itemSource.system?.level?.value ?? grantedSource.system.level.value; } - // Guarantee future alreadyGranted checks pass in all cases by re-assigning sourceId - grantedSource.flags = mergeObject(grantedSource.flags, { core: { sourceId: uuid } }); + // Guarantee future already-granted checks pass in all cases by re-assigning sourceId + grantedSource.flags = fu.mergeObject(grantedSource.flags, { core: { sourceId: uuid } }); // Apply alterations try { @@ -168,7 +169,7 @@ class GrantItemRuleElement extends RuleElementPF2e { } // Create a temporary owned item and run its actor-data preparation and early-stage rule-element callbacks - const tempGranted = new ItemProxyPF2e(deepClone(grantedSource), { parent: this.actor }); + const tempGranted = new ItemProxyPF2e(fu.deepClone(grantedSource), { parent: this.actor }); // Check for immunity and bail if a match if (tempGranted.isOfType("affliction", "condition", "effect") && this.actor.isImmuneTo(tempGranted)) { @@ -252,7 +253,10 @@ class GrantItemRuleElement extends RuleElementPF2e { if (isObject(actions)) { const ACTIONS = GrantItemRuleElement.ON_DELETE_ACTIONS; return tupleHasValue(ACTIONS, actions.granter) || tupleHasValue(ACTIONS, actions.grantee) - ? pick(actions, ([actions.granter ? "granter" : [], actions.grantee ? "grantee" : []] as const).flat()) + ? R.pick( + actions, + ([actions.granter ? "granter" : [], actions.grantee ? "grantee" : []] as const).flat(), + ) : null; } @@ -276,7 +280,7 @@ class GrantItemRuleElement extends RuleElementPF2e { /** Set flags on granting and grantee items to indicate relationship between the two */ #setGrantFlags(granter: PreCreate, grantee: ItemSourcePF2e | ItemPF2e): void { - const flags = mergeObject(granter.flags ?? {}, { pf2e: { itemGrants: {} } }); + const flags = fu.mergeObject(granter.flags ?? {}, { pf2e: { itemGrants: {} } }); if (!this.flag) throw ErrorPF2e("Unexpected failure looking up RE flag key"); flags.pf2e.itemGrants[this.flag] = { // The granting item records the granted item's ID in an array at `flags.pf2e.itemGrants` @@ -301,7 +305,7 @@ class GrantItemRuleElement extends RuleElementPF2e { // Don't await since it will trigger a data reset, possibly wiping temporary roll options grantee.update({ "flags.pf2e.grantedBy": grantedBy }, { render: false }); } else { - grantee.flags = mergeObject(grantee.flags ?? {}, { pf2e: { grantedBy } }); + grantee.flags = fu.mergeObject(grantee.flags ?? {}, { pf2e: { grantedBy } }); } } @@ -345,8 +349,8 @@ class GrantItemRuleElement extends RuleElementPF2e { const flags = { pf2e: { grantedBy: { id: this.item.id, onDelete: "cascade" } } }; conditionSource.flags.pf2e?.grantedBy; const condition = new ConditionPF2e( - mergeObject(conditionSource, { - _id: randomID(), + fu.mergeObject(conditionSource, { + _id: fu.randomID(), flags, system: { references: { parent: { id: this.item.id } } }, }), diff --git a/src/module/rules/rule-element/roll-option.ts b/src/module/rules/rule-element/roll-option.ts index 0235a435d3a..73bcced92e5 100644 --- a/src/module/rules/rule-element/roll-option.ts +++ b/src/module/rules/rule-element/roll-option.ts @@ -49,7 +49,7 @@ class RollOptionRuleElement extends RuleElementPF2e { phase: new fields.StringField({ required: false, nullable: false, - choices: deepClone(AELikeRuleElement.PHASES), + choices: fu.deepClone(AELikeRuleElement.PHASES), initial: "applyAEs", }), suboptions: new fields.ArrayField( diff --git a/src/module/rules/rule-element/strike.ts b/src/module/rules/rule-element/strike.ts index 10fb9242320..ed5da631c35 100644 --- a/src/module/rules/rule-element/strike.ts +++ b/src/module/rules/rule-element/strike.ts @@ -187,7 +187,7 @@ class StrikeRuleElement extends RuleElementPF2e { this.group = "brawling"; this.baseType = "fist"; this.traits = ["agile", "finesse", "nonlethal"]; - this.traitToggles = mergeObject({ modular: null, versatile: null }, this._source.traitToggles ?? {}); + this.traitToggles = fu.mergeObject({ modular: null, versatile: null }, this._source.traitToggles ?? {}); this.otherTags = []; this.range = null; this.damage = { @@ -261,7 +261,7 @@ class StrikeRuleElement extends RuleElementPF2e { */ #constructWeapon(damageType: DamageType, dice: number): WeaponPF2e { const actorIsNPC = this.actor.isOfType("npc"); - const source: PreCreate = deepClone({ + const source: PreCreate = fu.deepClone({ _id: this.item.id, name: this.label, type: "weapon", @@ -313,7 +313,7 @@ class StrikeRuleElement extends RuleElementPF2e { /** Toggle the modular or versatile trait of this strike's weapon */ async toggleTrait({ trait, selection }: UpdateToggleParams): Promise { - const ruleSources = deepClone(this.item._source.system.rules); + const ruleSources = fu.deepClone(this.item._source.system.rules); const rule: StrikeSource | undefined = ruleSources.at(this.sourceIndex ?? NaN); if (rule?.key === "Strike") { rule.traitToggles = { ...this.traitToggles, [trait]: selection }; diff --git a/src/module/rules/rule-element/temp-hp.ts b/src/module/rules/rule-element/temp-hp.ts index fa55f56c2a1..ad740c288bd 100644 --- a/src/module/rules/rule-element/temp-hp.ts +++ b/src/module/rules/rule-element/temp-hp.ts @@ -37,7 +37,7 @@ class TempHPRuleElement extends RuleElementPF2e { override onCreate(actorUpdates: Record): void { if (this.ignored || !this.events.onCreate) return; - const updatedActorData = mergeObject(this.actor._source, actorUpdates, { inplace: false }); + const updatedActorData = fu.mergeObject(this.actor._source, actorUpdates, { inplace: false }); const value = Math.trunc(Number(this.resolveValue(this.value))); const rollOptions = Array.from( @@ -54,9 +54,9 @@ class TempHPRuleElement extends RuleElementPF2e { return this.failValidation("value: must resolve to a positive number"); } - const currentTempHP = Number(getProperty(updatedActorData, "system.attributes.hp.temp")) || 0; + const currentTempHP = Number(fu.getProperty(updatedActorData, "system.attributes.hp.temp")) || 0; if (value > currentTempHP) { - mergeObject(actorUpdates, { + fu.mergeObject(actorUpdates, { "system.attributes.hp.temp": value, "system.attributes.hp.tempsource": this.item.id, }); @@ -83,8 +83,8 @@ class TempHPRuleElement extends RuleElementPF2e { return this.failValidation("value: must resolve to a number"); } - const updatedActorData = mergeObject(this.actor._source, actorUpdates, { inplace: false }); - const currentTempHP = Number(getProperty(updatedActorData, "system.attributes.hp.temp")) || 0; + const updatedActorData = fu.mergeObject(this.actor._source, actorUpdates, { inplace: false }); + const currentTempHP = Number(fu.getProperty(updatedActorData, "system.attributes.hp.temp")) || 0; if (value > currentTempHP) { actorUpdates["system.attributes.hp.temp"] = value; this.broadcast(value, currentTempHP); @@ -92,12 +92,10 @@ class TempHPRuleElement extends RuleElementPF2e { } override onDelete(actorUpdates: Record): void { - const updatedActorData = mergeObject(this.actor._source, actorUpdates, { inplace: false }); - if (getProperty(updatedActorData, "system.attributes.hp.tempsource") === this.item.id) { - mergeObject(actorUpdates, { - "system.attributes.hp.temp": 0, - }); - const hpData = getProperty(actorUpdates, "system.attributes.hp"); + const updatedActorData = fu.mergeObject(this.actor._source, actorUpdates, { inplace: false }); + if (fu.getProperty(updatedActorData, "system.attributes.hp.tempsource") === this.item.id) { + fu.mergeObject(actorUpdates, { "system.attributes.hp.temp": 0 }); + const hpData = fu.getProperty(actorUpdates, "system.attributes.hp"); if (isObject<{ "-=tempsource": unknown }>(hpData)) { hpData["-=tempsource"] = null; } diff --git a/src/module/scene/document.ts b/src/module/scene/document.ts index eeeae33cbed..e19d71c3c65 100644 --- a/src/module/scene/document.ts +++ b/src/module/scene/document.ts @@ -62,7 +62,7 @@ class ScenePF2e extends Scene { override prepareBaseData(): void { super.prepareBaseData(); - this.flags.pf2e = mergeObject( + this.flags.pf2e = fu.mergeObject( { hearingRange: null, rulesBasedVision: null, diff --git a/src/module/scene/helpers.ts b/src/module/scene/helpers.ts index adad1716cea..2988950ccab 100644 --- a/src/module/scene/helpers.ts +++ b/src/module/scene/helpers.ts @@ -5,7 +5,7 @@ import { TokenDocumentPF2e } from "./token-document/index.ts"; let auraCheckLock = Promise.resolve(); /** Check for auras containing newly-placed or moved tokens */ -const checkAuras = foundry.utils.debounce(async function (this: ScenePF2e): Promise { +const checkAuras = fu.debounce(async function (this: ScenePF2e): Promise { if (!(canvas.ready && this.isInFocus && this.grid.type === CONST.GRID_TYPES.SQUARE)) { return; } diff --git a/src/module/scene/measured-template-document.ts b/src/module/scene/measured-template-document.ts index 88117a5e32a..b1175d74f67 100644 --- a/src/module/scene/measured-template-document.ts +++ b/src/module/scene/measured-template-document.ts @@ -50,7 +50,7 @@ class MeasuredTemplateDocumentPF2e< ): this["_source"] { const initialized = super._initializeSource(data, options); const areaType = initialized.t === "cone" ? "cone" : initialized.t === "ray" ? "line" : null; - initialized.flags.pf2e = mergeObject({ areaType }, initialized.flags.pf2e ?? {}); + initialized.flags.pf2e = fu.mergeObject({ areaType }, initialized.flags.pf2e ?? {}); return initialized; } diff --git a/src/module/scene/token-document/actor-delta.ts b/src/module/scene/token-document/actor-delta.ts index 4b393231523..48893a79489 100644 --- a/src/module/scene/token-document/actor-delta.ts +++ b/src/module/scene/token-document/actor-delta.ts @@ -57,8 +57,8 @@ class ActorDeltaPF2e extends ActorDelt const fakeUpdates: Partial = {}; if (nameChanged) fakeUpdates.name = this.parent.name; if (sizeChanged) fakeUpdates.width = this.parent.width; - if (textureChanged) fakeUpdates.texture = deepClone(this.parent._source.texture); - if (lightChanged) fakeUpdates.light = deepClone(this.parent._source.light); + if (textureChanged) fakeUpdates.texture = fu.deepClone(this.parent._source.texture); + if (lightChanged) fakeUpdates.light = fu.deepClone(this.parent._source.light); this.parent.object?._onUpdate(fakeUpdates, { parent: this.parent.scene }, game.user.id); } } diff --git a/src/module/scene/token-document/document.ts b/src/module/scene/token-document/document.ts index 46d4b7ad1d1..21e48da9e97 100644 --- a/src/module/scene/token-document/document.ts +++ b/src/module/scene/token-document/document.ts @@ -51,7 +51,7 @@ class TokenDocumentPF2e ext if (_path.length === 0 && Object.keys(data).length === 0) { for (const [type, model] of Object.entries(game.system.model.Actor)) { if (!["character", "npc"].includes(type)) continue; - foundry.utils.mergeObject(data, model); + fu.mergeObject(data, model); } } @@ -64,9 +64,9 @@ class TokenDocumentPF2e ext negative: /\b(?:rank|_?modifiers|item|classdc|dexcap|familiar|\w+hp\b)|bonus/i, }; - const prunedData = expandObject>( + const prunedData = fu.expandObject>( Object.fromEntries( - Object.entries(flattenObject(data)).filter( + Object.entries(fu.flattenObject(data)).filter( ([k, v]) => patterns.positive.test(k) && !patterns.negative.test(k) && @@ -189,7 +189,7 @@ class TokenDocumentPF2e ext override prepareBaseData(): void { super.prepareBaseData(); - this.flags = mergeObject(this.flags, { pf2e: {} }); + this.flags = fu.mergeObject(this.flags, { pf2e: {} }); this.auras.clear(); if (!this.actor || !this.isEmbedded) return; @@ -197,7 +197,7 @@ class TokenDocumentPF2e ext TokenDocumentPF2e.assignDefaultImage(this); for (const [key, data] of this.actor.auras.entries()) { - this.auras.set(key, new TokenAura({ token: this, ...deepClone(data) })); + this.auras.set(key, new TokenAura({ token: this, ...fu.deepClone(data) })); } if (!this.constructed) return; @@ -209,7 +209,7 @@ class TokenDocumentPF2e ext const autoscaleDefault = game.pf2e.settings.tokens.autoscale; // Autoscaling is a secondary feature of linking to actor size const autoscale = linkToActorSize ? this.flags.pf2e.autoscale ?? autoscaleDefault : false; - this.flags.pf2e = mergeObject(this.flags.pf2e ?? {}, { linkToActorSize, autoscale }); + this.flags.pf2e = fu.mergeObject(this.flags.pf2e ?? {}, { linkToActorSize, autoscale }); // Alliance coloration, appropriating core token dispositions const { alliance } = this.actor.system.details; @@ -426,7 +426,7 @@ class TokenDocumentPF2e ext const initializeVision = !!this.scene?.isView && this.sight.enabled && - Object.keys(flattenObject(actorUpdates)).some((k) => k.startsWith("system.traits.senses")); + Object.keys(fu.flattenObject(actorUpdates)).some((k) => k.startsWith("system.traits.senses")); if (initializeVision) canvas.perception.update({ initializeVision }, true); const preUpdate = this.toObject(false); @@ -434,7 +434,7 @@ class TokenDocumentPF2e ext this.reset(); const postUpdate = this.toObject(false); const postUpdateAuras = Array.from(this.auras.values()).map((a) => R.omit(a, ["appearance", "token"])); - const tokenChanges = diffObject>(preUpdate, postUpdate); + const tokenChanges = fu.diffObject>(preUpdate, postUpdate); if (this.scene?.isView && Object.keys(tokenChanges).length > 0) { this.object?._onUpdate(tokenChanges, {}, game.user.id); diff --git a/src/module/system/action-macros/basic/escape.ts b/src/module/system/action-macros/basic/escape.ts index ec744b32ee6..5e2780af1c7 100644 --- a/src/module/system/action-macros/basic/escape.ts +++ b/src/module/system/action-macros/basic/escape.ts @@ -1,10 +1,10 @@ -import { ActionMacroHelpers, SkillActionOptions } from "../index.ts"; -import { ActorPF2e, CharacterPF2e, NPCPF2e } from "@actor"; +import { CharacterPF2e, NPCPF2e, type ActorPF2e } from "@actor"; +import { SingleCheckAction, SingleCheckActionVariant, SingleCheckActionVariantData } from "@actor/actions/index.ts"; import { StrikeData } from "@actor/data/base.ts"; import { StatisticModifier } from "@actor/modifiers.ts"; +import type { ItemPF2e } from "@item"; import { CheckContext, CheckContextData, CheckContextError, CheckContextOptions } from "@system/action-macros/types.ts"; -import { SingleCheckAction, SingleCheckActionVariant, SingleCheckActionVariantData } from "@actor/actions/index.ts"; -import { ItemPF2e } from "@item"; +import { ActionMacroHelpers, SkillActionOptions } from "../index.ts"; const toHighestModifier = (highest: StatisticModifier | null, current: StatisticModifier): StatisticModifier | null => { return current.totalModifier > (highest?.totalModifier ?? 0) ? current : highest; @@ -59,7 +59,7 @@ function escapeCheckContext>( rollOptions: actionRollOptions, target: opts.target, }); - const statistic = getProperty(opts.actor, property) as StatisticModifier & { rank?: number }; + const statistic = fu.getProperty(opts.actor, property) as StatisticModifier & { rank?: number }; return { actor: opts.actor, rollOptions, @@ -155,4 +155,4 @@ class EscapeAction extends SingleCheckAction { const action = new EscapeAction(); -export { escape as legacy, action }; +export { action, escape as legacy }; diff --git a/src/module/system/action-macros/helpers.ts b/src/module/system/action-macros/helpers.ts index 616e145138b..44ed6fd3cfe 100644 --- a/src/module/system/action-macros/helpers.ts +++ b/src/module/system/action-macros/helpers.ts @@ -69,7 +69,7 @@ export class ActionMacroHelpers { ): CheckContext | undefined { const { checkType: type, property, stat: slug, subtitle } = this.resolveStat(data.slug); const statistic = - options.actor.getStatistic(data.slug) ?? (getProperty(options.actor, property) as StatisticModifier); + options.actor.getStatistic(data.slug) ?? (fu.getProperty(options.actor, property) as StatisticModifier); if (!statistic) { const { actor } = options; const message = `Actor ${actor.name} (${actor.id}) does not have a statistic for ${slug}.`; diff --git a/src/module/system/check/check.ts b/src/module/system/check/check.ts index e5861923f65..4460f084bc6 100644 --- a/src/module/system/check/check.ts +++ b/src/module/system/check/check.ts @@ -57,7 +57,7 @@ class CheckPF2e { ): Promise | null> { // If event is supplied, merge into context // Eventually the event parameter will go away entirely - if (event) mergeObject(context, eventToRollParams(event, { type: "check" })); + if (event) fu.mergeObject(context, eventToRollParams(event, { type: "check" })); context.skipDialog ??= !game.user.settings.showCheckDialogs; context.createMessage ??= true; @@ -179,7 +179,7 @@ class CheckPF2e { .reduce((record, data) => { for (const outcome of ["all", ...DEGREE_OF_SUCCESS_STRINGS] as const) { if (data.adjustments[outcome]) { - record[outcome] = deepClone(data.adjustments[outcome]); + record[outcome] = fu.deepClone(data.adjustments[outcome]); } } return record; @@ -414,7 +414,7 @@ class CheckPF2e { } } - const systemFlags = deepClone(message.flags.pf2e); + const systemFlags = fu.deepClone(message.flags.pf2e); const context = systemFlags.context; if (!isCheckContextFlag(context)) return; diff --git a/src/module/system/damage/damage.ts b/src/module/system/damage/damage.ts index 7229b222c87..fb5c1c9e4ce 100644 --- a/src/module/system/damage/damage.ts +++ b/src/module/system/damage/damage.ts @@ -51,7 +51,7 @@ export class DamagePF2e { ): string => slugs .map((s) => ({ value: s, label: game.i18n.localize(labels[s] ?? "") })) - .sort((a, b) => a.label.localeCompare(b.label)) + .sort((a, b) => a.label.localeCompare(b.label, game.i18n.lang)) .map((tag) => { const description = descriptions[tag.value] ?? ""; @@ -148,7 +148,7 @@ export class DamagePF2e { return damage.roll.evaluate({ async: true }); } - const formula = deepClone(damage.formula[outcome ?? "success"]); + const formula = fu.deepClone(damage.formula[outcome ?? "success"]); if (!formula) { ui.notifications.error(game.i18n.format("PF2E.UI.noDamageInfoForOutcome", { outcome })); return null; diff --git a/src/module/system/damage/dialog.ts b/src/module/system/damage/dialog.ts index 52a50e054d4..c86588dcf0e 100644 --- a/src/module/system/damage/dialog.ts +++ b/src/module/system/damage/dialog.ts @@ -2,12 +2,11 @@ import { DamageDicePF2e, MODIFIER_TYPES, ModifierPF2e, applyStackingRules } from import { DEGREE_OF_SUCCESS, DEGREE_OF_SUCCESS_STRINGS, DegreeOfSuccessIndex } from "@system/degree-of-success.ts"; import { ErrorPF2e, - addSign, fontAwesomeIcon, htmlQuery, htmlQueryAll, - pick, setHasElement, + signedInteger, sluggify, sortStringRecord, tupleHasValue, @@ -165,7 +164,7 @@ class DamageModifierDialog extends Application { d.diceNumber && d.dieSize ? `${d.diceNumber}${d.dieSize}` : d.diceNumber - ? game.i18n.format("PF2E.Roll.Dialog.Damage.Dice", { dice: addSign(d.diceNumber) }) + ? game.i18n.format("PF2E.Roll.Dialog.Damage.Dice", { dice: signedInteger(d.diceNumber) }) : "", enabled: d.enabled, ignored: d.ignored, @@ -205,7 +204,9 @@ class DamageModifierDialog extends Application { ), isCritical: this.isCritical, damageTypes: sortStringRecord(CONFIG.PF2E.damageTypes), - damageSubtypes: sortStringRecord(pick(CONFIG.PF2E.damageCategories, DAMAGE_CATEGORIES_UNIQUE)), + damageSubtypes: sortStringRecord( + R.pick(CONFIG.PF2E.damageCategories, Array.from(DAMAGE_CATEGORIES_UNIQUE)), + ), rollModes: CONFIG.Dice.rollModes, rollMode: this.context?.rollMode, showDamageDialogs: game.user.settings.showDamageDialogs, diff --git a/src/module/system/damage/formula.ts b/src/module/system/damage/formula.ts index 9de432cdb17..29973eddc46 100644 --- a/src/module/system/damage/formula.ts +++ b/src/module/system/damage/formula.ts @@ -1,6 +1,6 @@ import type { DamageDicePF2e } from "@actor/modifiers.ts"; import { DEGREE_OF_SUCCESS, DegreeOfSuccessIndex } from "@system/degree-of-success.ts"; -import { groupBy, signedInteger, sortBy, sum } from "@util"; +import { groupBy, signedInteger } from "@util"; import * as R from "remeda"; import { applyDamageDiceOverrides } from "./helpers.ts"; import { @@ -34,7 +34,7 @@ function createDamageFormula( ): AssembledFormula | null { damage = { // TODO: clone the modifiers as well, once ModifierPF2e.clone() can preserve adjustments - ...deepClone(R.omit(damage, ["dice"])), + ...fu.deepClone(R.omit(damage, ["dice"])), dice: damage.dice.map((d) => d.clone()), }; @@ -291,14 +291,17 @@ function combinePartialTerms(terms: DamagePartialTerm[]): DamagePartialTerm[] { const constantTerm: DamagePartialTerm | null = modifier ? { dice: null, modifier } : null; // Group dice by number of faces - const dice = terms - .filter((p): p is DamagePartial & { dice: NonNullable } => !!p.dice && p.dice.number > 0) - .sort(sortBy((t) => -t.dice.faces)); + const dice = R.sortBy( + terms.filter( + (p): p is DamagePartial & { dice: NonNullable } => !!p.dice && p.dice.number > 0, + ), + (t) => -t.dice.faces, + ); const byFace = [...groupBy(dice, (t) => t.dice.faces).values()]; const combinedDice = byFace.map((terms) => ({ modifier: 0, - dice: { ...terms[0].dice, number: sum(terms.map((d) => d.dice.number)) }, + dice: { ...terms[0].dice, number: R.sumBy(terms, (t) => t.dice.number) }, })); const combined = R.compact([...combinedDice, constantTerm]); diff --git a/src/module/system/damage/roll.ts b/src/module/system/damage/roll.ts index 31cd2b676b1..2249dbeaa82 100644 --- a/src/module/system/damage/roll.ts +++ b/src/module/system/damage/roll.ts @@ -281,11 +281,11 @@ class DamageRoll extends AbstractDamageRoll { const multiplierTerm: NumericTermData = { class: "NumericTerm", number: multiplier }; const expression = ArithmeticExpression.fromData({ operator: "*", - operands: [deepClone(multiplierTerm), rightOperand], + operands: [fu.deepClone(multiplierTerm), rightOperand], }); if ([2, 3].includes(multiplier)) expression.options.crit = multiplier; - return DamageInstance.fromTerms([expression], deepClone(instance.options)); + return DamageInstance.fromTerms([expression], fu.deepClone(instance.options)); }); if (addend !== 0) { @@ -303,7 +303,7 @@ class DamageRoll extends AbstractDamageRoll { operands: [termClone, addendTerm], evaluated: true, }); - instanceClones[0] = DamageInstance.fromTerms([expression], deepClone(firstInstance.options)); + instanceClones[0] = DamageInstance.fromTerms([expression], fu.deepClone(firstInstance.options)); } return DamageRoll.fromTerms([InstancePool.fromRolls(instanceClones)]) as this; diff --git a/src/module/system/predication.ts b/src/module/system/predication.ts index 5ee5800e770..c2a43e538df 100644 --- a/src/module/system/predication.ts +++ b/src/module/system/predication.ts @@ -47,7 +47,7 @@ class PredicatePF2e extends Array { } toObject(): RawPredicate { - return deepClone([...this]); + return fu.deepClone([...this]); } clone(): PredicatePF2e { diff --git a/src/module/system/settings/homebrew/menu.ts b/src/module/system/settings/homebrew/menu.ts index baba6e9858d..df3b8f63990 100644 --- a/src/module/system/settings/homebrew/menu.ts +++ b/src/module/system/settings/homebrew/menu.ts @@ -18,7 +18,6 @@ import { isObject, localizer, objectHasKey, - pick, sluggify, tupleHasValue, } from "@util"; @@ -63,7 +62,9 @@ class HomebrewElements extends SettingsMenuPF2e { } static override get defaultOptions(): FormApplicationOptions { - return mergeObject(super.defaultOptions, { template: "systems/pf2e/templates/system/settings/homebrew.hbs" }); + return fu.mergeObject(super.defaultOptions, { + template: "systems/pf2e/templates/system/settings/homebrew.hbs", + }); } protected static get traitSettings(): Record { @@ -157,7 +158,7 @@ class HomebrewElements extends SettingsMenuPF2e { override async getData(): Promise { const data = await super.getData(); const traitSettings = settingsToSheetData(this.constructor.traitSettings, this.cache, this.prefix); - const damageCategories = pick(CONFIG.PF2E.damageCategories, ["physical", "energy"]); + const damageCategories = R.pick(CONFIG.PF2E.damageCategories, ["physical", "energy"]); return { ...data, traitSettings, @@ -190,7 +191,7 @@ class HomebrewElements extends SettingsMenuPF2e { protected override _getSubmitData(updateData?: Record | undefined): Record { const original = super._getSubmitData(updateData); - const data: Partial = expandObject>(original); + const data: Partial = fu.expandObject(original); // Sanitize damage types data, including ensuring they are valid font awesome icons if ("damageTypes" in data && !!data.damageTypes && typeof data.damageTypes === "object") { diff --git a/src/module/system/settings/menu.ts b/src/module/system/settings/menu.ts index 2fb975ae9ec..bd718b1d66c 100644 --- a/src/module/system/settings/menu.ts +++ b/src/module/system/settings/menu.ts @@ -8,7 +8,7 @@ abstract class SettingsMenuPF2e extends FormApplication { const options = super.defaultOptions; options.classes.push("settings-menu", "sheet"); - return mergeObject(options, { + return fu.mergeObject(options, { title: `PF2E.SETTINGS.${this.namespace.titleCase()}.Name`, id: `${this.namespace}-settings`, template: `systems/pf2e/templates/system/settings/menu.hbs`, @@ -53,7 +53,7 @@ abstract class SettingsMenuPF2e extends FormApplication { override async getData(): Promise { const settings = (this.constructor as typeof SettingsMenuPF2e).settings; const templateData = settingsToSheetData(settings, this.cache, this.prefix); - return mergeObject(await super.getData(), { + return fu.mergeObject(await super.getData(), { settings: templateData, instructions: `PF2E.SETTINGS.${this.namespace.titleCase()}.Hint`, }); diff --git a/src/module/system/settings/world-clock.ts b/src/module/system/settings/world-clock.ts index f872bd8beae..1f01321b372 100644 --- a/src/module/system/settings/world-clock.ts +++ b/src/module/system/settings/world-clock.ts @@ -32,7 +32,7 @@ interface UpdateData { export class WorldClockSettings extends FormApplication { static override get defaultOptions(): FormApplicationOptions { - return mergeObject(super.defaultOptions, { + return fu.mergeObject(super.defaultOptions, { title: CONFIG.PF2E.SETTINGS.worldClock.name, id: "world-clock-settings", template: "systems/pf2e/templates/system/settings/world-clock/index.hbs", @@ -89,7 +89,7 @@ export class WorldClockSettings extends FormApplication { isDateTime: setting.type === String && !("choices" in setting), }; }); - return mergeObject(await super.getData(), { settings }); + return fu.mergeObject(await super.getData(), { settings }); } /** Register World Clock settings */ diff --git a/src/module/system/statistic/statistic.ts b/src/module/system/statistic/statistic.ts index 6dffe4bedda..3af4e03878c 100644 --- a/src/module/system/statistic/statistic.ts +++ b/src/module/system/statistic/statistic.ts @@ -129,7 +129,7 @@ class Statistic extends BaseStatistic { /** @deprecated */ get ability(): AttributeString | null { - foundry.utils.logCompatibilityWarning("`Statistic#ability` is deprecated. Use `Statistic#attribute` instead.", { + fu.logCompatibilityWarning("`Statistic#ability` is deprecated. Use `Statistic#attribute` instead.", { since: "5.5.0", until: "6.0.0", }); @@ -180,8 +180,8 @@ class Statistic extends BaseStatistic { } withRollOptions(options?: RollOptionConfig): Statistic { - const newOptions = mergeObject(this.config ?? {}, options ?? {}, { inplace: false }); - return new Statistic(this.actor, deepClone(this.data), newOptions); + const newOptions = fu.mergeObject(this.config ?? {}, options ?? {}, { inplace: false }); + return new Statistic(this.actor, fu.deepClone(this.data), newOptions); } /** @@ -200,7 +200,7 @@ class Statistic extends BaseStatistic { return [...new Set([arr1 ?? [], arr2 ?? []].flat())]; } - const result = mergeObject(deepClone(this.data), data); + const result = fu.mergeObject(fu.deepClone(this.data), data); result.domains = maybeMergeArrays(this.domains, data.domains); result.modifiers = maybeMergeArrays(this.data.modifiers, data.modifiers); result.rollOptions = maybeMergeArrays(this.data.rollOptions, data.rollOptions); @@ -277,7 +277,7 @@ class StatisticCheck { constructor(parent: TParent, data: StatisticData, config: RollOptionConfig = {}) { this.parent = parent; this.type = data.check?.type ?? "check"; - data.check = mergeObject(data.check ?? {}, { type: this.type }); + data.check = fu.mergeObject(data.check ?? {}, { type: this.type }); const checkDomains = new Set(R.compact(["check", data.check.domains].flat())); if (this.type === "attack-roll") { @@ -375,7 +375,7 @@ class StatisticCheck { const event = args.event?.originalEvent ?? args.event; if (event instanceof MouseEvent) { const { rollMode, skipDialog } = args; - return mergeObject({ rollMode, skipDialog }, eventToRollParams(event, { type: "check" })); + return fu.mergeObject({ rollMode, skipDialog }, eventToRollParams(event, { type: "check" })); } } diff --git a/src/module/system/tag-selector/base.ts b/src/module/system/tag-selector/base.ts index 385e4d60891..28616b57951 100644 --- a/src/module/system/tag-selector/base.ts +++ b/src/module/system/tag-selector/base.ts @@ -17,7 +17,7 @@ abstract class BaseTagSelector extends D TagSelectorOptions > { static override get defaultOptions(): TagSelectorOptions { - return mergeObject(super.defaultOptions, { + return fu.mergeObject(super.defaultOptions, { id: "tag-selector", classes: ["pf2e", "tag-selector"], sheetConfig: false, @@ -72,7 +72,7 @@ abstract class BaseTagSelector extends D */ #getChoices(): Record { const choices = this.configTypes.reduce( - (types: Record, key) => mergeObject(types, CONFIG.PF2E[key]), + (types: Record, key) => fu.mergeObject(types, CONFIG.PF2E[key]), {}, ); return this.sortChoices(choices); @@ -84,7 +84,7 @@ abstract class BaseTagSelector extends D .map(([key, value]) => [key, game.i18n.localize(value)]) .sort(([_keyA, valueA], [_keyB, valueB]) => valueA.localeCompare(valueB)) .reduce( - (accumulated: Record, [key, value]) => mergeObject(accumulated, { [key]: value }), + (accumulated: Record, [key, value]) => fu.mergeObject(accumulated, { [key]: value }), {}, ); } diff --git a/src/module/system/tag-selector/basic.ts b/src/module/system/tag-selector/basic.ts index 1a29c1550ff..be934ed58df 100644 --- a/src/module/system/tag-selector/basic.ts +++ b/src/module/system/tag-selector/basic.ts @@ -23,7 +23,7 @@ function isValuesList(value: unknown): value is ValuesList { class TagSelectorBasic extends BaseTagSelector { static override get defaultOptions(): TagSelectorOptions { - return mergeObject(super.defaultOptions, { + return fu.mergeObject(super.defaultOptions, { template: "systems/pf2e/templates/system/tag-selector/basic.hbs", }); } @@ -40,7 +40,7 @@ class TagSelectorBasic extends BaseTagSe this.objectProperty = options.objectProperty; this.allowCustom = options.allowCustom ?? true; if (options.customChoices) { - mergeObject(this.choices, options.customChoices); + fu.mergeObject(this.choices, options.customChoices); this.choices = this.sortChoices(this.choices); } } @@ -53,8 +53,8 @@ class TagSelectorBasic extends BaseTagSe const { chosen, custom, flat, disabled } = (() => { const document: { toObject(): ActorSourcePF2e | ItemSourcePF2e } = this.document; // Compare source and prepared properties to determine which tags were automatically selected - const sourceProperty: unknown = getProperty(document.toObject(), this.objectProperty); - const preparedProperty: unknown = getProperty(document, this.objectProperty); + const sourceProperty: unknown = fu.getProperty(document.toObject(), this.objectProperty); + const preparedProperty: unknown = fu.getProperty(document, this.objectProperty); if (Array.isArray(preparedProperty)) { const manuallyChosen = Array.isArray(sourceProperty) ? sourceProperty.map((prop) => String(prop)) : []; diff --git a/src/module/system/tag-selector/senses.ts b/src/module/system/tag-selector/senses.ts index d2f1994282a..0cfa6aae729 100644 --- a/src/module/system/tag-selector/senses.ts +++ b/src/module/system/tag-selector/senses.ts @@ -8,7 +8,7 @@ class SenseSelector extends BaseTagSelector { protected objectProperty = "system.traits.senses"; static override get defaultOptions(): TagSelectorOptions { - return mergeObject(super.defaultOptions, { + return fu.mergeObject(super.defaultOptions, { height: "auto", template: "systems/pf2e/templates/system/tag-selector/senses.hbs", id: "sense-selector", diff --git a/src/module/system/tag-selector/speeds.ts b/src/module/system/tag-selector/speeds.ts index f0f5b782845..34a683539e4 100644 --- a/src/module/system/tag-selector/speeds.ts +++ b/src/module/system/tag-selector/speeds.ts @@ -7,7 +7,7 @@ class SpeedSelector extends BaseTagSelector { protected objectProperty = "system.attributes.speed.otherSpeeds"; static override get defaultOptions(): TagSelectorOptions { - return mergeObject(super.defaultOptions, { + return fu.mergeObject(super.defaultOptions, { id: "speed-selector", template: "systems/pf2e/templates/system/tag-selector/speeds.hbs", title: "PF2E.SpeedTypes", diff --git a/src/module/user/document.ts b/src/module/user/document.ts index 19a1fe6a665..4d85b48bed8 100644 --- a/src/module/user/document.ts +++ b/src/module/user/document.ts @@ -15,7 +15,7 @@ class UserPF2e extends User> { /** Set user settings defaults */ override prepareBaseData(): void { super.prepareBaseData(); - this.flags = mergeObject( + this.flags = fu.mergeObject( { pf2e: { settings: { @@ -56,7 +56,7 @@ class UserPF2e extends User> { super._onUpdate(changed, options, userId); if (game.user.id !== userId) return; - const keys = Object.keys(flattenObject(changed)); + const keys = Object.keys(fu.flattenObject(changed)); if (keys.includes("flags.pf2e.settings.showEffectPanel")) { game.pf2e.effectPanel.refresh(); } diff --git a/src/scripts/config/index.ts b/src/scripts/config/index.ts index abb3372035b..9b492c1e734 100644 --- a/src/scripts/config/index.ts +++ b/src/scripts/config/index.ts @@ -286,7 +286,7 @@ const weaponReload: Record = { }; function notifyDeprecatedPath(configPath: string, locPath: string): void { - foundry.utils.logCompatibilityWarning( + fu.logCompatibilityWarning( `CONFIG.PF2E.${configPath} is deprecated. Use localization path ${locPath} directly instead.`, { since: "5.2.0", until: "6.0.0" }, ); @@ -876,7 +876,7 @@ export const PF2ECONFIG = { }, // Year offsets relative to the current actual year - worldClock: mergeObject(configFromLocalization(EN_JSON.PF2E.WorldClock, "PF2E.WorldClock"), { + worldClock: fu.mergeObject(configFromLocalization(EN_JSON.PF2E.WorldClock, "PF2E.WorldClock"), { AR: { yearOffset: 2700 }, IC: { yearOffset: 5200 }, AD: { yearOffset: -95 }, diff --git a/src/scripts/hooks/drop-canvas-data.ts b/src/scripts/hooks/drop-canvas-data.ts index 39640447e4a..5c1f61fe510 100644 --- a/src/scripts/hooks/drop-canvas-data.ts +++ b/src/scripts/hooks/drop-canvas-data.ts @@ -23,7 +23,7 @@ export const DropCanvasData = { const instances = roll.instances.filter((i) => i.persistent); const baseConditionSource = game.pf2e.ConditionManager.getCondition("persistent-damage").toObject(); const conditions = instances.map((i) => - mergeObject(baseConditionSource, { + fu.mergeObject(baseConditionSource, { system: { persistent: { formula: i.head.expression, damageType: i.type, dc: 15 }, }, diff --git a/src/scripts/hooks/load.ts b/src/scripts/hooks/load.ts index f077394a244..00be79f8bae 100644 --- a/src/scripts/hooks/load.ts +++ b/src/scripts/hooks/load.ts @@ -129,7 +129,7 @@ export const Load = { } const lang = await response.json(); const apply = (): void => { - mergeObject(game.i18n.translations, lang); + fu.mergeObject(game.i18n.translations, lang); rerenderApps(path); }; if (game.ready) { diff --git a/src/scripts/hooks/ready.ts b/src/scripts/hooks/ready.ts index fe30e88609a..8cf9da55bf6 100644 --- a/src/scripts/hooks/ready.ts +++ b/src/scripts/hooks/ready.ts @@ -53,7 +53,7 @@ export const Ready = { // Update the world system version const previous = game.settings.get("pf2e", "worldSystemVersion"); const current = game.system.version; - if (foundry.utils.isNewerVersion(current, previous)) { + if (fu.isNewerVersion(current, previous)) { await game.settings.set("pf2e", "worldSystemVersion", current); } @@ -69,8 +69,7 @@ export const Ready = { // without it will also not be listed in the package manager. Skip warning those without it in // case they were made for private use. !!m.compatibility.verified && - (abandonedModules.has(m.id) || - !foundry.utils.isNewerVersion(m.compatibility.verified, "10.312")), + (abandonedModules.has(m.id) || !fu.isNewerVersion(m.compatibility.verified, "10.312")), ); for (const badModule of subV10Modules) { diff --git a/src/scripts/macros/travel/travel-speed-sheet.ts b/src/scripts/macros/travel/travel-speed-sheet.ts index 8fdc37c5e71..8314834af51 100644 --- a/src/scripts/macros/travel/travel-speed-sheet.ts +++ b/src/scripts/macros/travel/travel-speed-sheet.ts @@ -1,17 +1,18 @@ +import type { CharacterPF2e } from "@actor/character/document.ts"; +import * as R from "remeda"; import { - calculateNormalizedCharacterSpeed, - calculateTravelDuration, DetectionMode, ExplorationActivities, ExplorationOptions, + Fraction, LengthUnit, - speedToVelocity, Terrain, TravelDuration, Trip, + calculateNormalizedCharacterSpeed, + calculateTravelDuration, + speedToVelocity, } from "./travel-speed.ts"; -import { Fraction, zip } from "@util"; -import { CharacterPF2e } from "@actor/character/document.ts"; type DetectionModeData = "none" | "everything" | "before"; type SpeedUnitData = "feet" | "miles"; @@ -78,7 +79,7 @@ interface FormActorData { speed: number; } -interface TravelFormData { +type TravelFormData = { actors: FormActorData[]; hustleMinutes: number; distance: number; @@ -88,7 +89,7 @@ interface TravelFormData { normalTerrainSlowdown: Fraction; difficultTerrainSlowdown: Fraction; greaterDifficultTerrainSlowdown: Fraction; -} +}; interface SheetActorData extends FormActorData { explorationSpeed: number; @@ -122,7 +123,7 @@ class TravelSpeedSheet extends FormApplication<{}, TravelSpeedSheetOptions> { } async _updateObject(_event: Event, formData: Record): Promise { - const data = expandObject(formData) as TravelFormData; + const data = fu.expandObject(formData) as TravelFormData; data.actors = toArray(data.actors); this.formData = data; this.render(true); @@ -169,9 +170,7 @@ class TravelSpeedSheet extends FormApplication<{}, TravelSpeedSheetOptions> { }, }, ]; - const actorFormData = zip(actors, data.actors, (actor, actorData) => - this.actorFormToSheetData(actor, actorData), - ); + const actorFormData = R.zipWith(actors, data.actors, (a, d) => this.actorFormToSheetData(a, d)); const partySpeedInFeet = Math.min(...actorFormData.map((data) => data.explorationSpeed)); const velocity = speedToVelocity(partySpeedInFeet); return { diff --git a/src/scripts/macros/travel/travel-speed.ts b/src/scripts/macros/travel/travel-speed.ts index c32a5561503..3cd24fbeb0f 100644 --- a/src/scripts/macros/travel/travel-speed.ts +++ b/src/scripts/macros/travel/travel-speed.ts @@ -1,8 +1,6 @@ -import { Fraction, sum } from "@util"; +import * as R from "remeda"; -/** - * Implementation of travel speed https://2e.aonprd.com/Rules.aspx?ID=470 - */ +/** Implementation of travel speed https://2e.aonprd.com/Rules.aspx?ID=470 */ export interface ExplorationOptions { practicedDefender: boolean; swiftSneak: boolean; @@ -53,6 +51,11 @@ export enum DetectionMode { DETECT_BEFORE_WALKING_INTO_IT, } +interface Fraction { + numerator: number; + denominator: number; +} + function sneaksAtFullSpeed(activity: ExplorationActivities, explorationOptions: ExplorationOptions) { return ( activity === ExplorationActivities.AVOID_NOTICE && @@ -145,12 +148,12 @@ export enum TimeUnit { HOUR, } -export interface Velocity { +interface Velocity { distance: Distance; time: TimeUnit; } -export function speedToVelocity(speedInFeet: number): Velocity { +function speedToVelocity(speedInFeet: number): Velocity { return { distance: { unit: LengthUnit.FEET, @@ -279,7 +282,7 @@ function toTravelDuration({ }; } -export function calculateTravelDuration({ +function calculateTravelDuration({ journey, velocity, hustleDurationInMinutes = 0, @@ -290,7 +293,10 @@ export function calculateTravelDuration({ hustleDurationInMinutes?: number; hoursPerDay?: number; }): TravelDuration { - const distanceInFeet = sum(journey.map(increaseDistanceByTerrain)); + const distanceInFeet = R.sumBy(journey, increaseDistanceByTerrain); const feetPerMinute = toFeetPerMinute(velocity); return toTravelDuration({ distanceInFeet, feetPerMinute, hustleDurationInMinutes, hoursPerDay }); } + +export { calculateTravelDuration, speedToVelocity }; +export type { Fraction, Velocity }; diff --git a/src/scripts/macros/treat-wounds.ts b/src/scripts/macros/treat-wounds.ts index eaff645458f..3982915e2ff 100644 --- a/src/scripts/macros/treat-wounds.ts +++ b/src/scripts/macros/treat-wounds.ts @@ -1,4 +1,4 @@ -import { ActorPF2e, CreaturePF2e } from "@actor"; +import type { ActorPF2e, CreaturePF2e } from "@actor"; import { ChatMessagePF2e } from "@module/chat-message/index.ts"; import { eventToRollParams } from "@scripts/sheet-util.ts"; import { ActionDefaultOptions } from "@system/action-macros/index.ts"; @@ -24,7 +24,7 @@ export async function treatWounds(options: ActionDefaultOptions): Promise const medicineName = game.i18n.localize("PF2E.SkillMedicine"); const chirurgeon = CheckFeat(actor, "chirurgeon"); const naturalMedicine = CheckFeat(actor, "natural-medicine"); - const domIdAppend = randomID(); // Attached to element id attributes for DOM uniqueness + const domIdAppend = fu.randomID(); // Attached to element id attributes for DOM uniqueness const dialog = new Dialog({ title: game.i18n.localize("PF2E.Actions.TreatWounds.Label"), content: ` diff --git a/src/scripts/set-game-pf2e.ts b/src/scripts/set-game-pf2e.ts index 5feda919446..1de7f045fc0 100644 --- a/src/scripts/set-game-pf2e.ts +++ b/src/scripts/set-game-pf2e.ts @@ -100,7 +100,7 @@ export const SetGamePF2e = { system: { generateItemName, moduleArt: new ModuleArt(), remigrate, sluggify }, variantRules: { AutomaticBonusProgression }, }; - game.pf2e = mergeObject(game.pf2e ?? {}, initSafe); + game.pf2e = fu.mergeObject(game.pf2e ?? {}, initSafe); game.pf2e.ConditionManager.initialize(); game.pf2e.settings = { campaign: { diff --git a/src/scripts/ui/inline-roll-links.ts b/src/scripts/ui/inline-roll-links.ts index db68b45342c..96aea6c2cb9 100644 --- a/src/scripts/ui/inline-roll-links.ts +++ b/src/scripts/ui/inline-roll-links.ts @@ -358,7 +358,7 @@ export const InlineRollLinks = { foundryDoc instanceof JournalEntry ? { pf2e: { journalEntry: foundryDoc.uuid } } : message?.flags.pf2e.origin - ? { pf2e: { origin: deepClone(message.flags.pf2e.origin) } } + ? { pf2e: { origin: fu.deepClone(message.flags.pf2e.origin) } } : {}; ChatMessagePF2e.create({ diff --git a/src/util/index.ts b/src/util/index.ts index b206cc9afc6..9f2b5e591e7 100644 --- a/src/util/index.ts +++ b/src/util/index.ts @@ -1,4 +1,4 @@ +export * from "./delegated-collection.ts"; export * from "./dom.ts"; export * from "./misc.ts"; export * from "./tags.ts"; -export * from "./delegated-collection.ts"; diff --git a/src/util/misc.ts b/src/util/misc.ts index 98888869a8a..e499a8a6fe3 100644 --- a/src/util/misc.ts +++ b/src/util/misc.ts @@ -24,15 +24,6 @@ function groupBy(array: T[], criterion: (value: T) => R): Map { return result; } -/** Creates a sorting comparator that sorts by the numerical result of a mapping function */ -function sortBy(mapping: (value: T) => J) { - return (a: T, b: T): number => { - const value1 = mapping(a); - const value2 = mapping(b); - return value1 < value2 ? -1 : value1 === value2 ? 0 : 1; - }; -} - /** * Given an array, adds a certain amount of elements to it * until the desired length is being reached @@ -59,48 +50,6 @@ function mapValues( ); } -/** - * Returns true if the string is null, undefined or only consists of 1..n spaces - */ -function isBlank(text: Maybe): text is null | undefined | "" { - return text === null || text === undefined || text.trim() === ""; -} - -/** Returns a formatted number string with a preceding + if non-negative */ -function addSign(number: number): string { - if (number < 0) { - return `${number}`; - } - - return `+${number}`; -} - -/** - * No idea why this isn't built in - */ -function sum(values: number[]): number { - return values.reduce((a, b) => a + b, 0); -} - -/** - * Zip to arrays together based on a given zip function - * @param a - * @param b - * @param zipFunction - */ -function zip(a: A[], b: B[], zipFunction: (a: A, b: B) => R): R[] { - if (a.length > b.length) { - return b.map((elem, index) => zipFunction(a[index], elem)); - } else { - return a.map((elem, index) => zipFunction(elem, b[index])); - } -} - -interface Fraction { - numerator: number; - denominator: number; -} - /** * Continually apply a function on the result of itself until times is reached * @@ -136,19 +85,6 @@ function setHasElement>(set: T, value: unknown): value is return set.has(value); } -/** Returns a subset of an object with explicitly defined keys */ -function pick(obj: T, keys: Iterable): Pick { - return [...keys].reduce( - (result, key) => { - if (key in obj) { - result[key] = obj[key]; - } - return result; - }, - {} as Pick, - ); -} - let intlNumberFormat: Intl.NumberFormat; /** * Return an integer string of a number, always with sign (+/-) @@ -368,7 +304,7 @@ function isObject(value: unknown): boolean { function sortLabeledRecord>(record: T): T { return Object.entries(record) .sort((a, b) => a[1].label.localeCompare(b[1].label, game.i18n.lang)) - .reduce((copy, [key, value]) => mergeObject(copy, { [key]: value }), {} as T); + .reduce((copy, [key, value]) => fu.mergeObject(copy, { [key]: value }), {} as T); } /** Localize the values of a `Record` and sort by those values */ @@ -402,7 +338,7 @@ function sortObjByKey(value: unknown): unknown { /** Walk an object tree and replace any string values found according to a provided function */ function recursiveReplaceString(source: T, replace: (s: string) => string): T; function recursiveReplaceString(source: unknown, replace: (s: string) => string): unknown { - const clone = Array.isArray(source) || R.isObject(source) ? deepClone(source) : source; + const clone = Array.isArray(source) || R.isObject(source) ? fu.deepClone(source) : source; if (typeof clone === "string") { return replace(clone); } else if (Array.isArray(clone)) { @@ -471,7 +407,6 @@ const SORTABLE_BASE_OPTIONS: Sortable.Options = { export { ErrorPF2e, SORTABLE_BASE_OPTIONS, - addSign, applyNTimes, configFromLocalization, fontAwesomeIcon, @@ -479,7 +414,6 @@ export { getActionIcon, getActionTypeLabel, groupBy, - isBlank, isImageFilePath, isImageOrVideoPath, isObject, @@ -491,18 +425,13 @@ export { ordinalString, padArray, parseHTML, - pick, recursiveReplaceString, setHasElement, signedInteger, sluggify, - sortBy, sortLabeledRecord, sortObjByKey, sortStringRecord, - sum, tupleHasValue, - zip, - type Fraction, type SlugCamel, }; diff --git a/src/util/uuid.ts b/src/util/uuid.ts index fc55b1170d0..e32a77fb362 100644 --- a/src/util/uuid.ts +++ b/src/util/uuid.ts @@ -3,11 +3,11 @@ import * as R from "remeda"; class UUIDUtils { /** Retrieve multiple documents by UUID */ static async fromUUIDs(uuids: string[], options?: { relative?: Maybe }): Promise { - const resolvedUUIDs = R.uniq(uuids).flatMap((u) => foundry.utils.parseUuid(u, options).uuid ?? []); + const resolvedUUIDs = R.uniq(uuids).flatMap((u) => fu.parseUuid(u, options).uuid ?? []); // These can't be retrieved via `fromUuidSync`: separate and retrieve directly via `fromUuid` const packEmbeddedLinks = resolvedUUIDs.filter((u) => { - const parsed = foundry.utils.parseUuid(u, options); + const parsed = fu.parseUuid(u, options); return parsed.collection instanceof CompendiumCollection && parsed.embedded.length > 0; }); const packEmbeddedDocs = R.compact(await Promise.all(packEmbeddedLinks.map((u) => fromUuid(u)))); @@ -35,16 +35,16 @@ class UUIDUtils { } static isItemUUID(uuid: unknown): uuid is ItemUUID { - return typeof uuid === "string" && foundry.utils.parseUuid(uuid).documentType === "Item"; + return typeof uuid === "string" && fu.parseUuid(uuid).documentType === "Item"; } static isCompendiumUUID(uuid: unknown): uuid is CompendiumUUID { - return typeof uuid === "string" && foundry.utils.parseUuid(uuid).collection instanceof CompendiumCollection; + return typeof uuid === "string" && fu.parseUuid(uuid).collection instanceof CompendiumCollection; } static isTokenUUID(uuid: unknown): uuid is TokenDocumentUUID { if (typeof uuid !== "string") return false; - const parsed = foundry.utils.parseUuid(uuid); + const parsed = fu.parseUuid(uuid); return parsed.documentType === "Scene" && parsed.embedded[0] === "Token"; } } diff --git a/tests/mocks/actor.ts b/tests/mocks/actor.ts index 7b4aadfd562..8afc6dc54e4 100644 --- a/tests/mocks/actor.ts +++ b/tests/mocks/actor.ts @@ -24,7 +24,7 @@ export class MockActor { data: ActorSourcePF2e, public options: DocumentConstructionContext = {}, ) { - this._source = duplicate(data); + this._source = fu.duplicate(data); this._source.items ??= []; this.prepareData(); } @@ -52,7 +52,7 @@ export class MockActor { for (const source of this._source.items) { const item = this.items.get(source._id ?? ""); if (item) { - (item as { _source: object })._source = duplicate(source); + (item as { _source: object })._source = fu.duplicate(source); } else { this.items.set( source._id ?? "", @@ -65,7 +65,7 @@ export class MockActor { update(changes: Record): void { delete changes.items; for (const [k, v] of Object.entries(changes)) { - global.setProperty(this._source, k, v); + fu.setProperty(this._source, k, v); } this.prepareData(); } @@ -80,14 +80,14 @@ export class MockActor { const itemUpdates = (update.items ?? []) as DeepPartial[]; delete update.items; - mergeObject(actor._source, update); + fu.mergeObject(actor._source, update); for (const partial of itemUpdates) { partial._id ??= "item1"; const source = actor._source.items.find( (maybeSource: ItemSourcePF2e) => maybeSource._id === partial._id, ); if (source) { - mergeObject(source, partial); + fu.mergeObject(source, partial); } else { actor.createEmbeddedDocuments("Item", [partial]); } @@ -101,7 +101,7 @@ export class MockActor { for (const changes of data) { if (type === "Item") { const source = this._source.items.find((i) => i._id === changes._id); - if (source) mergeObject(source, changes); + if (source) fu.mergeObject(source, changes); } } this.prepareData(); @@ -130,6 +130,6 @@ export class MockActor { } toObject(): ActorSourcePF2e { - return duplicate(this._source); + return fu.duplicate(this._source); } } diff --git a/tests/mocks/chat-message.ts b/tests/mocks/chat-message.ts index f0439a154cd..a6a8b121e29 100644 --- a/tests/mocks/chat-message.ts +++ b/tests/mocks/chat-message.ts @@ -2,6 +2,6 @@ export class MockChatMessage { _source: foundry.documents.ChatMessageSource; constructor(data: foundry.documents.ChatMessageSource) { - this._source = duplicate(data); + this._source = fu.duplicate(data); } } diff --git a/tests/mocks/item.ts b/tests/mocks/item.ts index a316979e254..392adba220f 100644 --- a/tests/mocks/item.ts +++ b/tests/mocks/item.ts @@ -1,6 +1,6 @@ import type { ActorPF2e } from "@actor/index.ts"; -import { ItemSystemSource } from "@item/base/data/system.ts"; import { ItemSourcePF2e } from "@item/base/data/index.ts"; +import { ItemSystemSource } from "@item/base/data/system.ts"; import type { ItemPF2e } from "@item/index.ts"; export class MockItem { @@ -12,7 +12,7 @@ export class MockItem { data: ItemSourcePF2e, public options: DocumentConstructionContext = {}, ) { - this._source = duplicate(data); + this._source = fu.duplicate(data); this.parent = options.parent ?? null; } @@ -50,16 +50,16 @@ export class MockItem { ): Promise[]> { return updates.flatMap((update) => { const item = game.items.find((item) => item.id === update._id); - if (item) mergeObject(item._source, update, { performDeletions: true }); + if (item) fu.mergeObject(item._source, update, { performDeletions: true }); return item ?? []; }); } update(changes: object): void { - mergeObject(this._source, changes, { performDeletions: true }); + fu.mergeObject(this._source, changes, { performDeletions: true }); } toObject(): ItemSourcePF2e { - return duplicate(this._source); + return fu.duplicate(this._source); } } diff --git a/tests/mocks/journal-entry.ts b/tests/mocks/journal-entry.ts index 17ccd83edf5..996adc4e303 100644 --- a/tests/mocks/journal-entry.ts +++ b/tests/mocks/journal-entry.ts @@ -4,7 +4,7 @@ export class MockJournalEntry { readonly pages: object[] = []; constructor(source: foundry.documents.JournalEntrySource) { - this._source = duplicate(source); + this._source = fu.duplicate(source); this.pages = source.pages; } } diff --git a/tests/mocks/macro.ts b/tests/mocks/macro.ts index c06aa53fa95..c83f75ffc95 100644 --- a/tests/mocks/macro.ts +++ b/tests/mocks/macro.ts @@ -2,6 +2,6 @@ export class MockMacro { readonly _source: foundry.documents.MacroSource; constructor(data: foundry.documents.MacroSource) { - this._source = duplicate(data); + this._source = fu.duplicate(data); } } diff --git a/tests/mocks/roll-table.ts b/tests/mocks/roll-table.ts index 22191815404..a0bcfd8eed3 100644 --- a/tests/mocks/roll-table.ts +++ b/tests/mocks/roll-table.ts @@ -2,6 +2,6 @@ export class MockRollTable { readonly _source: foundry.documents.RollTableSource; constructor(data: foundry.documents.RollTableSource) { - this._source = duplicate(data); + this._source = fu.duplicate(data); } } diff --git a/tests/mocks/scene.ts b/tests/mocks/scene.ts index ee98558143b..8961efecec1 100644 --- a/tests/mocks/scene.ts +++ b/tests/mocks/scene.ts @@ -1,11 +1,9 @@ -import { FoundryUtils } from "../utils.ts"; - export class MockScene { _source: foundry.documents.SceneSource; constructor(data: Partial) { // eslint-disable-next-line @typescript-eslint/no-explicit-any - this._source = { _id: FoundryUtils.randomID(), name: "", ...data } as any; + this._source = { _id: fu.randomID(), name: "", ...data } as any; this._source.tokens = []; } @@ -62,7 +60,7 @@ export class MockScene { update(changes: object): void { for (const [k, v] of Object.entries(changes)) { - global.setProperty(this._source, k, v); + fu.setProperty(this._source, k, v); } } @@ -72,7 +70,7 @@ export class MockScene { obj = this._source.tokens?.find((t) => t._id === changes._id); } for (const [k, v] of Object.entries(changes)) { - global.setProperty(obj!, k, v); + fu.setProperty(obj!, k, v); } } } diff --git a/tests/mocks/token.ts b/tests/mocks/token.ts index 980551369b7..6274e7b4ff2 100644 --- a/tests/mocks/token.ts +++ b/tests/mocks/token.ts @@ -10,7 +10,7 @@ export class MockToken { data: foundry.documents.TokenSource, context: { parent?: ScenePF2e | null; actor?: ActorPF2e | null } = {}, ) { - this._source = duplicate(data); + this._source = fu.duplicate(data); this.parent = context.parent ?? null; this.actor = context.actor ?? null; } diff --git a/tests/mocks/user.ts b/tests/mocks/user.ts index 8bf503ee2b6..28005379e87 100644 --- a/tests/mocks/user.ts +++ b/tests/mocks/user.ts @@ -4,7 +4,7 @@ export class MockUser { readonly _source: UserSourcePF2e; constructor(data: UserSourcePF2e) { - this._source = duplicate(data); + this._source = fu.duplicate(data); } get name(): string { @@ -13,7 +13,7 @@ export class MockUser { async update(changes: Record): Promise { for (const [k, v] of Object.entries(changes)) { - global.setProperty(this._source, k, v); + fu.setProperty(this._source, k, v); } return this; } diff --git a/tests/module/migration.test.ts b/tests/module/migration.test.ts index 2ee2629f61b..dfaff1078c4 100644 --- a/tests/module/migration.test.ts +++ b/tests/module/migration.test.ts @@ -14,12 +14,10 @@ import { MockMacro } from "tests/mocks/macro.ts"; import { MockRollTable } from "tests/mocks/roll-table.ts"; import { MockScene } from "tests/mocks/scene.ts"; import { MockUser } from "tests/mocks/user.ts"; -import { FoundryUtils } from "tests/utils.ts"; import armorJSON from "../../packs/equipment/scale-mail.json"; import characterJSON from "../../packs/iconics/amiri-level-1.json"; -import { populateFoundryUtilFunctions } from "../fixtures/foundryshim.ts"; -const characterData = FoundryUtils.duplicate(characterJSON) as unknown as CharacterSource; +const characterData = fu.duplicate(characterJSON) as unknown as CharacterSource; characterData.effects = []; characterData.system._migration = { version: 0, previous: null }; for (const item of characterData.items) { @@ -27,13 +25,11 @@ for (const item of characterData.items) { item.system._migration = { version: 0, previous: null }; } -const armorData = FoundryUtils.duplicate(armorJSON) as unknown as ArmorSource; +const armorData = fu.duplicate(armorJSON) as unknown as ArmorSource; armorData.effects = []; armorData.system._migration = { version: 0, previous: null }; describe("test migration runner", () => { - populateFoundryUtilFunctions(); - const settings = { worldSchemaVersion: 10, }; diff --git a/tests/module/utils.test.ts b/tests/module/utils.test.ts index 2caf967e8e3..00320ee56e9 100644 --- a/tests/module/utils.test.ts +++ b/tests/module/utils.test.ts @@ -1,18 +1,4 @@ -import { addSign, applyNTimes, padArray, zip } from "@util"; - -describe("format sign for numbers", () => { - test("0", () => { - expect(addSign(0)).toEqual("+0"); - }); - - test("negative", () => { - expect(addSign(-1)).toEqual("-1"); - }); - - test("positive", () => { - expect(addSign(1)).toEqual("+1"); - }); -}); +import { applyNTimes, padArray } from "@util"; describe("pad array", () => { test("empty", () => { @@ -28,27 +14,6 @@ describe("pad array", () => { }); }); -describe("zip arrays", () => { - test("empty", () => { - expect(zip([], [], (a, b) => [a, b])).toEqual([]); - }); - - test("a larger", () => { - expect(zip([1, 3], [2], (a, b) => [a, b])).toEqual([[1, 2]]); - }); - - test("b larger", () => { - expect(zip([1], [2, 3], (a, b) => [a, b])).toEqual([[1, 2]]); - }); - - test("same length", () => { - expect(zip([1, 3], [2, 4], (a, b) => [a, b])).toEqual([ - [1, 2], - [3, 4], - ]); - }); -}); - describe("repeated assignment", () => { test("0 times is start element", () => { expect(applyNTimes((x) => x + 1, 0, 2)).toEqual(2); diff --git a/tests/setup.ts b/tests/setup.ts index cbc9ad154be..637a27db08e 100644 --- a/tests/setup.ts +++ b/tests/setup.ts @@ -1,3 +1,4 @@ +import "../build/lib/foundry-utils.ts"; import { MockActor } from "./mocks/actor.ts"; import { MockItem } from "./mocks/item.ts"; import { MockToken } from "./mocks/token.ts"; @@ -37,200 +38,6 @@ global.game = Object.freeze({ }, }); -function getType(token: Token | null) { - const tof = typeof token; - if (tof === "object") { - if (token === null) return "null"; - const cn = token.constructor.name; - if (["String", "Number", "Boolean", "Array", "Set"].includes(cn)) return cn; - else if (/^HTML/.test(cn)) return "HTMLElement"; - else return "Object"; - } - return tof; -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function setProperty(object: Record, key: string, value: unknown) { - let target = object; - let changed = false; - // Convert the key to an object reference if it contains dot notation - if (key.indexOf(".") !== -1) { - const parts = key.split("."); - key = parts.pop() ?? ""; - target = parts.reduce((o, i) => { - if (!(i in o)) o[i] = {}; - return o[i]; - }, object); - } - // Update the target - if (target[key] !== value) { - changed = true; - target[key] = value; - } - // Return changed status - return changed; -} - -function duplicate(original: unknown) { - return JSON.parse(JSON.stringify(original)); -} - -/** - * Quickly clone a simple piece of data, returning a copy which can be mutated safely. - * This method DOES support recursive data structures containing inner objects or arrays. - * This method DOES NOT support advanced object types like Set, Map, or other specialized classes. - * @param original Some sort of data - * @return The clone of that data - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function deepClone(original: T): T extends Set | Map | Collection ? never : T { - // Simple types - if (typeof original !== "object" || original === null) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return original as T extends Set | Map | Collection ? never : T; - - // Arrays - if (original instanceof Array) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return original.map(deepClone) as unknown as T extends Set | Map | Collection ? never : T; - - // Dates - if (original instanceof Date) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return new Date(original) as unknown as T extends Set | Map | Collection ? never : T; - - // Unsupported advanced objects - if ((original as { constructor: unknown }).constructor !== Object) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return original as T extends Set | Map | Collection ? never : T; - - // Other objects - const clone: Record = {}; - for (const k of Object.keys(original)) { - clone[k] = deepClone(original[k as keyof typeof original]); - } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return clone as unknown as T extends Set | Map | Collection ? never : T; -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function expandObject(obj: Record, _d = 0) { - const expanded = {}; - if (_d > 10) throw new Error("Maximum depth exceeded"); - for (const entry of Object.entries(obj)) { - const k = entry[0]; - let v = entry[1]; - if (v instanceof Object && !Array.isArray(v)) v = expandObject(v, _d + 1); - setProperty(expanded, k, v); - } - return expanded; -} - -function mergeObject( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - original: any, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - other: any = {}, - { - insertKeys = true, - insertValues = true, - overwrite = true, - recursive = true, - inplace = true, - enforceTypes = false, - } = {}, - _d = 0, - // eslint-disable-next-line @typescript-eslint/no-explicit-any -): any { - other = other || {}; - if (!(original instanceof Object) || !(other instanceof Object)) { - throw Error("One of original or other are not Objects!"); - } - const depth = _d + 1; - - // Maybe copy the original data at depth 0 - if (!inplace && _d === 0) original = duplicate(original); - - // Enforce object expansion at depth 0 - if (_d === 0 && Object.keys(original).some((k) => /\./.test(k))) original = expandObject(original); - if (_d === 0 && Object.keys(other).some((k) => /\./.test(k))) other = expandObject(other); - - // Iterate over the other object - for (let k of Object.keys(other)) { - const v = other[k]; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const tv = getType(v as any); - - // Prepare to delete - let toDelete = false; - if (k.startsWith("-=")) { - k = k.slice(2); - toDelete = v === null; - } - - // Get the existing object - let x = original[k]; - let has = k in original; - let tx = getType(x); - - // Ensure that inner objects exist - if (!has && tv === "Object") { - x = original[k] = {}; - has = true; - tx = "Object"; - } - - // Case 1 - Key exists - if (has) { - // 1.1 - Recursively merge an inner object - if (tv === "Object" && tx === "Object" && recursive) { - mergeObject( - x, - v, - { - insertKeys: insertKeys, - insertValues: insertValues, - overwrite: overwrite, - inplace: true, - enforceTypes: enforceTypes, - }, - depth, - ); - } - - // 1.2 - Remove an existing key - else if (toDelete) { - delete original[k]; - } - - // 1.3 - Overwrite existing value - else if (overwrite) { - if (tx && tv !== tx && enforceTypes) { - throw new Error(`Mismatched data types encountered during object merge.`); - } - original[k] = v; - } - - // 1.4 - Insert new value - else if (x === undefined && insertValues) { - original[k] = v; - } - } - - // Case 2 - Key does not exist - else if (!toDelete) { - const canInsert = (depth === 1 && insertKeys) || (depth > 1 && insertValues); - if (canInsert) original[k] = v; - } - } - - // Return the object for use - return original; -} - -globalThis.mergeObject = mergeObject; -globalThis.duplicate = duplicate; -globalThis.deepClone = deepClone; // eslint-disable-next-line @typescript-eslint/no-explicit-any (global as any).Actor = MockActor; // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/tests/utils.ts b/tests/utils.ts deleted file mode 100644 index dfe0308ba47..00000000000 --- a/tests/utils.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** Utility functions from foundry.js */ -export const FoundryUtils = { - randomID: (length = 16): string => { - const rnd = () => Math.random().toString(36).substr(2); - let id = ""; - while (id.length < length) { - id += rnd(); - } - return id.substr(0, length); - }, - - duplicate: (data: object): ReturnType<(typeof JSON)["parse"]> => { - return JSON.parse(JSON.stringify(data)); - }, -}; diff --git a/types/foundry/client/pixi/core/index.d.ts b/types/foundry/client/pixi/core/index.d.ts index da566ee9e4b..b9b53da7e78 100644 --- a/types/foundry/client/pixi/core/index.d.ts +++ b/types/foundry/client/pixi/core/index.d.ts @@ -1,3 +1,4 @@ +import "./containers/index.d.ts"; import "./interaction/index.d.ts"; +import "./loader.d.ts"; import "./shapes/index.d.ts"; -import "./containers/index.d.ts"; diff --git a/types/foundry/client/pixi/core/loader.d.ts b/types/foundry/client/pixi/core/loader.d.ts new file mode 100644 index 00000000000..da4b4b9b626 --- /dev/null +++ b/types/foundry/client/pixi/core/loader.d.ts @@ -0,0 +1,9 @@ +/** + * Load a single texture and return a Promise which resolves once the texture is ready to use + * @param src The requested texture source + * @param fallback A fallback texture to use if the requested source is unavailable or invalid + */ +declare function loadTexture( + src: string, + { fallback }?: { fallback?: ImageFilePath }, +): Promise; diff --git a/types/foundry/common/module.d.ts b/types/foundry/common/module.d.ts index c32a89111e5..4cb760306fd 100644 --- a/types/foundry/common/module.d.ts +++ b/types/foundry/common/module.d.ts @@ -12,14 +12,18 @@ import "./types.ts"; declare global { const CONST: typeof Constants; namespace globalThis { - export import Color = Utils.Color; - namespace foundry { + /** Constant definitions used throughout the Foundry Virtual Tabletop framework. */ export import CONST = Constants; + /** Abstract class definitions for fundamental concepts used throughout the Foundry Virtual Tabletop framework. */ export import abstract = Abstract; + /** Data schema definitions for data models. */ export import data = Data; + /** Document definitions used throughout the Foundry Virtual Tabletop framework. */ export import documents = Documents; + /** Package data definitions, validations, and schema. */ export import packages = Packages; + /** Utility functions providing helpful functionality. */ export import utils = Utils; } } diff --git a/types/foundry/common/utils/helpers.d.ts b/types/foundry/common/utils/helpers.d.ts index 0205a15a807..7c6b6098f26 100644 --- a/types/foundry/common/utils/helpers.d.ts +++ b/types/foundry/common/utils/helpers.d.ts @@ -111,7 +111,7 @@ export function flattenObject(obj: object, _d?: number): Record * @param _d Recursion depth, to prevent overflow * @return An expanded object */ -export function expandObject(obj: object, _d?: number): T; +export function expandObject>(obj: object, _d?: number): T; /** * A simple function to test whether or not an Object is empty @@ -322,30 +322,4 @@ declare global { */ performDeletions?: boolean; } - - namespace globalThis { - /* eslint-disable no-var */ - var deepClone: typeof foundry.utils.deepClone; - var diffObject: typeof foundry.utils.diffObject; - var duplicate: typeof foundry.utils.duplicate; - var expandObject: typeof foundry.utils.expandObject; - var flattenObject: typeof foundry.utils.flattenObject; - var getType: typeof foundry.utils.getType; - var getProperty: typeof foundry.utils.getProperty; - var isObjectEmpty: typeof foundry.utils.isObjectEmpty; - var mergeObject: typeof foundry.utils.mergeObject; - var setProperty: typeof foundry.utils.setProperty; - var randomID: typeof foundry.utils.randomID; - /* eslint-enable no-var */ - - /** - * Load a single texture and return a Promise which resolves once the texture is ready to use - * @param src The requested texture source - * @param fallback A fallback texture to use if the requested source is unavailable or invalid - */ - function loadTexture( - src: string, - { fallback }?: { fallback?: ImageFilePath }, - ): Promise; - } } diff --git a/vite.config.ts b/vite.config.ts index 07df69c5593..ce35c5607c1 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,4 +1,4 @@ -import { ConditionSource } from "@item/base/data/index.ts"; +import type { ConditionSource } from "@item/base/data/index.ts"; import { execSync } from "child_process"; import esbuild from "esbuild"; import fs from "fs-extra"; @@ -119,6 +119,7 @@ const config = Vite.defineConfig(({ command, mode }): Vite.UserConfig => { CONDITION_SOURCES: JSON.stringify(CONDITION_SOURCES), EN_JSON: JSON.stringify(EN_JSON), ROLL_PARSER: JSON.stringify(ROLL_PARSER), + fu: "foundry.utils", }, esbuild: { keepNames: true }, build: {