diff --git a/src/module/item/base/document.ts b/src/module/item/base/document.ts index 0a3a3001042..4647b2e8349 100644 --- a/src/module/item/base/document.ts +++ b/src/module/item/base/document.ts @@ -21,6 +21,7 @@ import type { ItemOriginFlag } from "@module/chat-message/data.ts"; import { ChatMessagePF2e } from "@module/chat-message/document.ts"; import { preImportJSON } from "@module/doc-helpers.ts"; import { MigrationList, MigrationRunner } from "@module/migration/index.ts"; +import { Migration901ReorganizeBulkData } from "@module/migration/migrations/901-reorganize-bulk-data.ts"; import { MigrationRunnerBase } from "@module/migration/runner/base.ts"; import { RuleElementOptions, RuleElementPF2e, RuleElementSource, RuleElements } from "@module/rules/index.ts"; import { processGrantDeletions } from "@module/rules/rule-element/grant-item/helpers.ts"; @@ -814,6 +815,31 @@ class ItemPF2e extends Item return super.deleteDocuments(ids, operation); } + /** + * Temporary migration to allow certain data models to work despite more recent migrations. + * @todo complete data models and create new migration framework + */ + static override migrateData( + this: ConstructorOf, + source: ItemSourcePF2e, + ): T["_source"] { + const itemType = String(source.type); + const hasTypeDataModel = itemType in CONFIG.Item.dataModels; + if (!hasTypeDataModel) return super.migrateData(source); + + const legacyVersion = R.isPlainObject(source.system.schema) + ? Number(source.system.schema.version) || null + : null; + const version = Number(source.system._migration?.version) || legacyVersion; + if (version && version < 0.901 && itemIsOfType(source, "physical")) { + const migration = new Migration901ReorganizeBulkData(); + migration.updateItem(source); + source = fu.applySpecialKeys(source); + } + + return super.migrateData(source); + } + /* -------------------------------------------- */ /* Event Handlers */ /* -------------------------------------------- */ diff --git a/src/module/item/container/data.ts b/src/module/item/container/data.ts index 20b89ac932a..a050bcfc67e 100644 --- a/src/module/item/container/data.ts +++ b/src/module/item/container/data.ts @@ -1,51 +1,96 @@ -import { EquipmentTrait } from "@item/equipment/data.ts"; +import type { ModelPropsFromSchema } from "@common/data/fields.d.mts"; +import type { EquipmentTrait } from "@item/equipment/data.ts"; +import type { BasePhysicalItemSource, BulkData, PhysicalItemTraits } from "@item/physical/data.ts"; import { - BasePhysicalItemSource, - BulkData, - Investable, - PhysicalItemTraits, - PhysicalSystemData, - PhysicalSystemSource, -} from "@item/physical/data.ts"; + PhysicalItemModelOmission, + PhysicalItemSystemModel, + PhysicalItemSystemSchema, + PhysicalItemSystemSource, + PhysicalItemTraitsSchema, +} from "@item/physical/schema.ts"; +import { RarityField } from "@module/model.ts"; +import { LaxArrayField, SlugField } from "@system/schema-data-fields.ts"; +import type { ContainerPF2e } from "./document.ts"; +import fields = foundry.data.fields; type ContainerSource = BasePhysicalItemSource<"backpack", ContainerSystemSource>; -type ContainerTraits = PhysicalItemTraits; +class ContainerSystemData extends PhysicalItemSystemModel { + declare traits: PhysicalItemTraits; -interface ContainerSystemSource extends Investable { - traits: ContainerTraits; - stowing: boolean; - bulk: ContainerBulkSource; - collapsed: boolean; - usage: { value: string }; - subitems?: never; -} + declare temporary: boolean; + + declare bulk: ContainerBulkData; + + static override defineSchema(): ContainerSystemSchema { + const traits: Record = CONFIG.PF2E.equipmentTraits; + + return { + ...super.defineSchema(), + traits: new fields.SchemaField({ + otherTags: new fields.ArrayField( + new SlugField({ required: true, nullable: false, initial: undefined }), + ), + value: new LaxArrayField( + new fields.StringField({ + required: true, + nullable: false, + choices: traits, + initial: undefined, + }), + ), + rarity: new RarityField(), + }), + bulk: new fields.SchemaField({ + value: new fields.NumberField({ required: true, nullable: false, min: 0, initial: 0 }), + heldOrStowed: new fields.NumberField({ required: true, nullable: false, min: 0, initial: 0.1 }), + capacity: new fields.NumberField({ required: true, nullable: false, min: 0, initial: 10 }), + ignored: new fields.NumberField({ required: true, nullable: false, min: 0, initial: 0 }), + }), + usage: new fields.SchemaField({ + value: new fields.StringField({ required: true, nullable: false, initial: "worn" }), + }), + collapsed: new fields.BooleanField({ required: true, nullable: false, initial: false }), + stowing: new fields.BooleanField({ required: true, nullable: false, initial: false }), + }; + } -interface ContainerBulkSource { - value: number; - heldOrStowed: number; - capacity: number; - ignored: number; + override prepareBaseData(): void { + this.temporary = false; + + // Simple measure to avoid self-recursive containers + if (this.containerId === this.parent.id) { + this.containerId = null; + } + } } interface ContainerSystemData - extends Omit, - Omit, "subitems" | "traits"> { - bulk: ContainerBulkData; - stackGroup: null; -} + extends PhysicalItemSystemModel, + Omit, PhysicalItemModelOmission> {} + +type ContainerSystemSchema = Omit & { + traits: fields.SchemaField>; + bulk: fields.SchemaField; + usage: fields.SchemaField<{ + value: fields.StringField; + }>; + collapsed: fields.BooleanField; + stowing: fields.BooleanField; +}; -type SourceOmission = - | "apex" - | "bulk" - | "description" - | "hp" - | "identification" - | "material" - | "price" - | "temporary" - | "usage"; +type ContainerBulkSchema = { + value: fields.NumberField; + heldOrStowed: fields.NumberField; + capacity: fields.NumberField; + ignored: fields.NumberField; +}; + +type ContainerSystemSource = PhysicalItemSystemSource & { + subitems?: never; +}; -interface ContainerBulkData extends ContainerBulkSource, BulkData {} +interface ContainerBulkData extends ModelPropsFromSchema, BulkData {} -export type { ContainerBulkData, ContainerSource, ContainerSystemData }; +export { ContainerSystemData }; +export type { ContainerBulkData, ContainerSource }; diff --git a/src/module/item/container/document.ts b/src/module/item/container/document.ts index 8e150ad044b..da02152671e 100644 --- a/src/module/item/container/document.ts +++ b/src/module/item/container/document.ts @@ -1,11 +1,11 @@ import type { ActorPF2e } from "@actor"; import { InventoryBulk } from "@actor/inventory/index.ts"; -import type { EnrichmentOptions } from "@client/applications/ux/text-editor.d.mts"; import type { DatabaseUpdateCallbackOptions } from "@common/abstract/_module.d.mts"; import type { RawItemChatData } from "@item/base/data/index.ts"; import type { EquipmentTrait } from "@item/equipment/data.ts"; import { Bulk } from "@item/physical/bulk.ts"; import { PhysicalItemPF2e } from "@item/physical/document.ts"; +import type { EnrichmentOptionsPF2e } from "@system/text-editor.ts"; import type { ContainerSource, ContainerSystemData } from "./data.ts"; import { hasExtraDimensionalParent } from "./helpers.ts"; @@ -52,15 +52,6 @@ class ContainerPF2e extends return super.bulk.plus(this.capacity.value.minus(this.bulkIgnored)); } - override prepareBaseData(): void { - super.prepareBaseData(); - - // Simple measure to avoid self-recursive containers - if (this.system.containerId === this.id) { - this.system.containerId = null; - } - } - /** Reload this container's contents following Actor embedded-document preparation */ override prepareSiblingData(this: ContainerPF2e): void { super.prepareSiblingData(); @@ -85,7 +76,7 @@ class ContainerPF2e extends override async getChatData( this: ContainerPF2e, - htmlOptions: EnrichmentOptions = {}, + htmlOptions: EnrichmentOptionsPF2e = {}, ): Promise { return this.processChatData(htmlOptions, { ...(await super.getChatData()), diff --git a/src/module/item/equipment/data.ts b/src/module/item/equipment/data.ts index 2d1a3c57aba..bfc62ad9c4a 100644 --- a/src/module/item/equipment/data.ts +++ b/src/module/item/equipment/data.ts @@ -1,39 +1,65 @@ +import type { ModelPropsFromSchema } from "@common/data/fields.d.mts"; import type { PhysicalItemSource } from "@item/base/data/index.ts"; -import type { - BasePhysicalItemSource, - Investable, - PhysicalItemTraits, - PhysicalSystemData, - PhysicalSystemSource, -} from "@item/physical/data.ts"; +import type { BasePhysicalItemSource, PhysicalItemTraits } from "@item/physical/data.ts"; +import { + type PhysicalItemModelOmission, + PhysicalItemSystemModel, + PhysicalItemSystemSchema, + PhysicalItemSystemSource, + PhysicalItemTraitsSchema, +} from "@item/physical/schema.ts"; +import { RarityField } from "@module/model.ts"; +import { LaxArrayField, SlugField } from "@system/schema-data-fields.ts"; +import type { EquipmentPF2e } from "./document.ts"; import type { EquipmentTrait } from "./types.ts"; +import fields = foundry.data.fields; type EquipmentSource = BasePhysicalItemSource<"equipment", EquipmentSystemSource>; -interface EquipmentSystemSource extends Investable { - traits: EquipmentTraits; - usage: { value: string }; - /** Doubly-embedded adjustments, attachments, talismans etc. */ - subitems: PhysicalItemSource[]; -} +class EquipmentSystemData extends PhysicalItemSystemModel { + declare traits: PhysicalItemTraits; -interface EquipmentTraits extends PhysicalItemTraits {} + static override defineSchema(): EquipmentSystemSchema { + const traits: Record = CONFIG.PF2E.equipmentTraits; -interface EquipmentSystemData - extends Omit, - Omit, "subitems" | "traits"> { - stackGroup: null; + return { + ...super.defineSchema(), + traits: new fields.SchemaField({ + otherTags: new fields.ArrayField( + new SlugField({ required: true, nullable: false, initial: undefined }), + ), + value: new LaxArrayField( + new fields.StringField({ + required: true, + nullable: false, + choices: traits, + initial: undefined, + }), + ), + rarity: new RarityField(), + }), + usage: new fields.SchemaField({ + value: new fields.StringField({ required: true, nullable: false, initial: "held-in-one-hand" }), + }), + temporary: new fields.BooleanField({ required: true, nullable: false, initial: false }), + subitems: new fields.ArrayField(new fields.ObjectField({ required: true, nullable: false })), + }; + } } +interface EquipmentSystemData + extends PhysicalItemSystemModel, + Omit, PhysicalItemModelOmission> {} + +type EquipmentSystemSchema = Omit & { + traits: fields.SchemaField>; + usage: fields.SchemaField<{ + value: fields.StringField; + }>; + temporary: fields.BooleanField; + subitems: fields.ArrayField>; +}; -type SourceOmission = - | "apex" - | "bulk" - | "description" - | "hp" - | "identification" - | "material" - | "price" - | "temporary" - | "usage"; +type EquipmentSystemSource = PhysicalItemSystemSource; -export type { EquipmentSource, EquipmentSystemData, EquipmentSystemSource, EquipmentTrait }; +export { EquipmentSystemData }; +export type { EquipmentSource, EquipmentSystemSource, EquipmentTrait }; diff --git a/src/module/item/identification.ts b/src/module/item/identification.ts index b57d837c395..33dfdbaaeb4 100644 --- a/src/module/item/identification.ts +++ b/src/module/item/identification.ts @@ -1,4 +1,5 @@ import { SkillSlug } from "@actor/types.ts"; +import type { ImageFilePath } from "@common/constants.d.mts"; import { Rarity } from "@module/data.ts"; import { setHasElement } from "@util"; import { adjustDCByRarity, calculateDC, DCOptions } from "../dc.ts"; @@ -73,7 +74,7 @@ function getItemIdentificationDCs( } } -function getUnidentifiedPlaceholderImage(item: PhysicalItemPF2e): string { +function getUnidentifiedPlaceholderImage(item: PhysicalItemPF2e): ImageFilePath { const iconName = ((): string => { if (item.isOfType("weapon")) { const { traits } = item; diff --git a/src/module/item/physical/data.ts b/src/module/item/physical/data.ts index 817d2198f4f..ffccd9ec7b5 100644 --- a/src/module/item/physical/data.ts +++ b/src/module/item/physical/data.ts @@ -70,7 +70,7 @@ interface PhysicalSystemData extends Omit, temporary: boolean; identification: IdentificationData; usage: UsageDetails; - stackGroup: string | null; + stackGroup?: string | null; } type Investable = TData & { @@ -108,7 +108,7 @@ interface ItemMaterialData extends ItemMaterialSource { type IdentifiedData = DeepPartial; interface IdentificationData extends IdentificationSource { - identified: MystifiedData; + identified: MystifiedData & { img: ImageFilePath | null }; } type EquippedData = { diff --git a/src/module/item/physical/document.ts b/src/module/item/physical/document.ts index 2a02063da54..801fb44027f 100644 --- a/src/module/item/physical/document.ts +++ b/src/module/item/physical/document.ts @@ -8,6 +8,7 @@ import type { DatabaseUpdateCallbackOptions, DatabaseUpdateOperation, } from "@common/abstract/_types.d.mts"; +import { ImageFilePath } from "@common/constants.mjs"; import { ItemPF2e, ItemProxyPF2e, type ContainerPF2e } from "@item"; import type { ItemSourcePF2e, PhysicalItemSource, RawItemChatData, TraitChatData } from "@item/base/data/index.ts"; import { MystifiedTraits } from "@item/base/data/values.ts"; @@ -564,8 +565,11 @@ abstract class PhysicalItemPF2e): MystifiedData { - const mystifiedData: MystifiedData = this.system.identification[status]; + getMystifiedData( + status: IdentificationStatus, + _options?: Record, + ): MystifiedData & { img: ImageFilePath } { + const mystifiedData = this.system.identification[status]; const name = mystifiedData.name || this.generateUnidentifiedName(); const img = mystifiedData.img || getUnidentifiedPlaceholderImage(this); diff --git a/src/module/item/physical/schema.ts b/src/module/item/physical/schema.ts index c84139e7fa3..2f7801616a1 100644 --- a/src/module/item/physical/schema.ts +++ b/src/module/item/physical/schema.ts @@ -1,5 +1,31 @@ +import type { AttributeString } from "@actor/types.ts"; +import { ATTRIBUTE_ABBREVIATIONS } from "@actor/values.ts"; +import type { ImageFilePath } from "@common/constants.d.mts"; +import { ModelPropsFromSchema, SourceFromSchema } from "@common/data/fields.mjs"; +import type { PhysicalItemSource } from "@item/base/data/index.ts"; +import { ItemSystemModel, type ItemSystemSchema } from "@item/base/data/model.ts"; +import type { ItemSystemSource } from "@item/base/data/system.ts"; +import { ITEM_CARRY_TYPES } from "@item/base/data/values.ts"; +import { SIZES, type Size, type ZeroToTwo } from "@module/data.ts"; +import { RarityField } from "@module/model.ts"; +import { SlugField } from "@system/schema-data-fields.ts"; import { CoinsPF2e } from "./coins.ts"; -import type { Price } from "./index.ts"; +import type { + BulkData, + IdentificationData, + IdentificationStatus, + ItemCarryType, + ItemMaterialData, + PhysicalItemHitPoints, + PhysicalItemPF2e, + PhysicalItemTrait, + PhysicalItemTraits, + PreciousMaterialGrade, + PreciousMaterialType, + Price, + UsageDetails, +} from "./index.ts"; +import { PRECIOUS_MATERIAL_GRADES, PRECIOUS_MATERIAL_TYPES } from "./values.ts"; import fields = foundry.data.fields; class PriceField extends fields.SchemaField, Price> { @@ -54,6 +80,128 @@ class PriceField extends fields.SchemaField extends ItemSystemModel { + declare traits: PhysicalItemTraits; + + declare hp: PhysicalItemHitPoints; + + declare bulk: BulkData; + + declare material: ItemMaterialData; + + declare identification: IdentificationData; + + declare usage: UsageDetails; + + declare subitems: PhysicalItemSource[]; + + static override defineSchema(): PhysicalItemSystemSchema { + return { + ...super.defineSchema(), + level: new fields.SchemaField({ + value: new fields.NumberField({ + required: true, + nullable: false, + integer: true, + min: 0, + max: 30, + initial: 0, + }), + }), + quantity: new fields.NumberField({ required: true, nullable: false, initial: 0, min: 0 }), + baseItem: new fields.StringField({ required: true, nullable: true, initial: null }), + bulk: new fields.SchemaField({ + value: new fields.NumberField({ required: true, nullable: false, min: 0, initial: 0 }), + }), + material: new fields.SchemaField({ + grade: new fields.StringField({ + choices: [...PRECIOUS_MATERIAL_GRADES], + required: true, + nullable: true, + initial: null, + }), + type: new fields.StringField({ + choices: [...PRECIOUS_MATERIAL_TYPES], + required: true, + nullable: true, + initial: null, + }), + }), + hp: new fields.SchemaField({ + value: new fields.NumberField({ required: true, nullable: false, initial: 0, min: 0 }), + max: new fields.NumberField({ required: true, nullable: false, initial: 0, min: 0 }), + }), + hardness: new fields.NumberField({ required: true, nullable: false, min: 0, initial: 0 }), + price: new PriceField(), + equipped: new fields.SchemaField({ + carryType: new fields.StringField({ + choices: ITEM_CARRY_TYPES, + required: true, + nullable: false, + initial: "worn", + }), + inSlot: new fields.BooleanField({ required: false, nullable: false }), + handsHeld: new fields.NumberField({ required: false, nullable: false, min: 0, max: 2, integer: true }), + invested: new fields.BooleanField({ required: false, nullable: true, initial: null }), + }), + identification: new fields.SchemaField({ + status: new fields.StringField({ + choices: ["identified", "unidentified"], + required: true, + nullable: false, + initial: "identified", + }), + unidentified: new fields.SchemaField({ + name: new fields.StringField({ required: true, blank: true, nullable: false, initial: "" }), + img: new fields.FilePathField({ + required: true, + categories: ["IMAGE"], + nullable: true, + initial: null, + }), + data: new fields.SchemaField({ + description: new fields.SchemaField({ + value: new fields.StringField({ + required: true, + blank: true, + nullable: false, + initial: "", + }), + }), + }), + }), + misidentified: new fields.ObjectField({ required: true, nullable: false, initial: {} }), + }), + containerId: new fields.StringField({ required: true, nullable: true, blank: false, initial: null }), + size: new fields.StringField({ choices: SIZES, required: true, nullable: false, initial: "med" }), + apex: new fields.SchemaField( + { + attribute: new fields.StringField({ + choices: [...ATTRIBUTE_ABBREVIATIONS], + required: true, + nullable: false, + initial: "str", + }), + selected: new fields.BooleanField({ required: false, nullable: false, initial: false }), + }, + { required: false, nullable: false }, + ), + }; + } + + override prepareBaseData(): void { + this.subitems ??= []; + } +} + +interface PhysicalItemSystemModel + extends ItemSystemModel, + Omit, "description"> {} + type CoinsField = fields.SchemaField, CoinsPF2e, true, false, true>; type CoinsSchema = { @@ -69,4 +217,74 @@ type PriceSchema = { sizeSensitive: fields.BooleanField; }; -export { PriceField }; +type EquippedDataSchema = { + carryType: fields.StringField; + inSlot: fields.BooleanField; + handsHeld: fields.NumberField; + invested: fields.BooleanField; +}; + +type ApexSchema = { + attribute: fields.StringField; + selected: fields.BooleanField; +}; + +type PhysicalItemTraitsSchema = { + value: fields.ArrayField>; + rarity: RarityField; + otherTags: fields.ArrayField, string[], string[], true, false, true>; +}; + +type PhysicalItemSystemSchema = ItemSystemSchema & { + level: fields.SchemaField<{ + value: fields.NumberField; + }>; + quantity: fields.NumberField; + baseItem: fields.StringField; + bulk: fields.SchemaField<{ + value: fields.NumberField; + }>; + material: fields.SchemaField<{ + grade: fields.StringField; + type: fields.StringField; + }>; + hp: fields.SchemaField<{ + value: fields.NumberField; + max: fields.NumberField; + }>; + hardness: fields.NumberField; + price: PriceField; + equipped: fields.SchemaField; + identification: fields.SchemaField<{ + status: fields.StringField; + unidentified: fields.SchemaField<{ + name: fields.StringField; + img: fields.FilePathField; + data: fields.SchemaField<{ + description: fields.SchemaField<{ + value: fields.StringField; + }>; + }>; + }>; + misidentified: fields.ObjectField; + }>; + size: fields.StringField; + containerId: fields.StringField; + apex: fields.SchemaField< + ApexSchema, + fields.SourceFromSchema, + fields.ModelPropsFromSchema, + false, + false + >; +}; + +type PhysicalItemModelOmission = "description" | "bulk" | "hp" | "identification" | "material" | "usage"; + +type PhysicalItemSystemSource = SourceFromSchema & { + schema?: ItemSystemSource["schema"]; + usage?: { value: string }; +}; + +export { PhysicalItemSystemModel, PriceField }; +export type { PhysicalItemModelOmission, PhysicalItemSystemSchema, PhysicalItemSystemSource, PhysicalItemTraitsSchema }; diff --git a/src/module/migration/base.ts b/src/module/migration/base.ts index 5c8520f78ad..0e092e3c01d 100644 --- a/src/module/migration/base.ts +++ b/src/module/migration/base.ts @@ -50,7 +50,7 @@ interface MigrationBase { * @param source Item to update. This should be an `ItemData` from the previous version. * @param actorSource If the item is part of an actor, this is set to the actor. For instance */ - updateItem?(source: ItemSourcePF2e, actorSource?: ActorSourcePF2e): Promise; + updateItem?(source: ItemSourcePF2e, actorSource?: ActorSourcePF2e): Promise | void; /** * Update the macro to the latest schema version. diff --git a/src/module/migration/migrations/901-reorganize-bulk-data.ts b/src/module/migration/migrations/901-reorganize-bulk-data.ts index de1a199cda2..ce7e776b93e 100644 --- a/src/module/migration/migrations/901-reorganize-bulk-data.ts +++ b/src/module/migration/migrations/901-reorganize-bulk-data.ts @@ -9,7 +9,7 @@ import { MigrationBase } from "../base.ts"; export class Migration901ReorganizeBulkData extends MigrationBase { static override version = 0.901; - override async updateItem(source: ItemSourcePF2e): Promise { + override updateItem(source: ItemSourcePF2e): void { this.#migrateRules(source); if (!itemIsOfType(source, "physical")) return; diff --git a/src/scripts/hooks/load.ts b/src/scripts/hooks/load.ts index c4a483f8db2..ca9ade1e118 100644 --- a/src/scripts/hooks/load.ts +++ b/src/scripts/hooks/load.ts @@ -14,7 +14,9 @@ import { AfflictionSystemData } from "@item/affliction/data.ts"; import { CampaignFeatureSystemData } from "@item/campaign-feature/data.ts"; import { ClassSystemData } from "@item/class/data.ts"; import { ConditionSystemData } from "@item/condition/data.ts"; +import { ContainerSystemData } from "@item/container/data.ts"; import { EffectSystemData } from "@item/effect/data.ts"; +import { EquipmentSystemData } from "@item/equipment/data.ts"; import { FeatSystemData } from "@item/feat/data.ts"; import { HeritageSystemData } from "@item/heritage/data.ts"; import { KitSystemData } from "@item/kit/data.ts"; @@ -105,10 +107,12 @@ export const Load = { CONFIG.Item.dataModels.affliction = AfflictionSystemData; } CONFIG.Item.dataModels.action = AbilitySystemData; + CONFIG.Item.dataModels.backpack = ContainerSystemData; CONFIG.Item.dataModels.campaignFeature = CampaignFeatureSystemData; CONFIG.Item.dataModels.class = ClassSystemData; CONFIG.Item.dataModels.condition = ConditionSystemData; CONFIG.Item.dataModels.effect = EffectSystemData; + CONFIG.Item.dataModels.equipment = EquipmentSystemData; CONFIG.Item.dataModels.feat = FeatSystemData; CONFIG.Item.dataModels.heritage = HeritageSystemData; CONFIG.Item.dataModels.kit = KitSystemData; diff --git a/static/template.json b/static/template.json index 8ee34b39e84..5d1969b67fd 100644 --- a/static/template.json +++ b/static/template.json @@ -333,24 +333,6 @@ } } }, - "backpack": { - "templates": [ - "common", - "physical", - "investable" - ], - "bulk": { - "value": 0, - "heldOrStowed": 0.1, - "capacity": 10, - "ignored": 0 - }, - "collapsed": false, - "stowing": false, - "usage": { - "value": "worn" - } - }, "book": { "templates": [ "common", @@ -448,17 +430,6 @@ ] } }, - "equipment": { - "templates": [ - "common", - "physical", - "investable" - ], - "usage": { - "value": "held-in-one-hand" - }, - "subitems": [] - }, "ancestry": { "templates": [ "common", diff --git a/types/foundry/common/utils/helpers.d.mts b/types/foundry/common/utils/helpers.d.mts index c05ef8f7bb1..44cb2d2da3a 100644 --- a/types/foundry/common/utils/helpers.d.mts +++ b/types/foundry/common/utils/helpers.d.mts @@ -142,6 +142,13 @@ export function diffObject = Record