Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/module/item/base/document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -814,6 +815,31 @@ class ItemPF2e<TParent extends ActorPF2e | null = ActorPF2e | null> 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<T extends foundry.abstract.DataModel>(
this: ConstructorOf<T>,
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 */
/* -------------------------------------------- */
Expand Down
123 changes: 84 additions & 39 deletions src/module/item/container/data.ts
Original file line number Diff line number Diff line change
@@ -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<EquipmentTrait>;
class ContainerSystemData extends PhysicalItemSystemModel<ContainerPF2e, ContainerSystemSchema> {
declare traits: PhysicalItemTraits<EquipmentTrait>;

interface ContainerSystemSource extends Investable<PhysicalSystemSource> {
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<EquipmentTrait, string> = 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<ContainerSystemSource, SourceOmission>,
Omit<Investable<PhysicalSystemData>, "subitems" | "traits"> {
bulk: ContainerBulkData;
stackGroup: null;
}
extends PhysicalItemSystemModel<ContainerPF2e, ContainerSystemSchema>,
Omit<ModelPropsFromSchema<ContainerSystemSchema>, PhysicalItemModelOmission> {}

type ContainerSystemSchema = Omit<PhysicalItemSystemSchema, "traits" | "bulk"> & {
traits: fields.SchemaField<PhysicalItemTraitsSchema<EquipmentTrait>>;
bulk: fields.SchemaField<ContainerBulkSchema>;
usage: fields.SchemaField<{
value: fields.StringField<string, string, true, false>;
}>;
collapsed: fields.BooleanField<boolean, boolean, true, false, true>;
stowing: fields.BooleanField<boolean, boolean, true, false, true>;
};

type SourceOmission =
| "apex"
| "bulk"
| "description"
| "hp"
| "identification"
| "material"
| "price"
| "temporary"
| "usage";
type ContainerBulkSchema = {
value: fields.NumberField<number, number, true, false, true>;
heldOrStowed: fields.NumberField<number, number, true, false, true>;
capacity: fields.NumberField<number, number, true, false, true>;
ignored: fields.NumberField<number, number, true, false, true>;
};

type ContainerSystemSource = PhysicalItemSystemSource<ContainerSystemSchema> & {
subitems?: never;
};

interface ContainerBulkData extends ContainerBulkSource, BulkData {}
interface ContainerBulkData extends ModelPropsFromSchema<ContainerBulkSchema>, BulkData {}

export type { ContainerBulkData, ContainerSource, ContainerSystemData };
export { ContainerSystemData };
export type { ContainerBulkData, ContainerSource };
13 changes: 2 additions & 11 deletions src/module/item/container/document.ts
Original file line number Diff line number Diff line change
@@ -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";

Expand Down Expand Up @@ -52,15 +52,6 @@ class ContainerPF2e<TParent extends ActorPF2e | null = ActorPF2e | null> 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<ActorPF2e>): void {
super.prepareSiblingData();
Expand All @@ -85,7 +76,7 @@ class ContainerPF2e<TParent extends ActorPF2e | null = ActorPF2e | null> extends

override async getChatData(
this: ContainerPF2e<TParent>,
htmlOptions: EnrichmentOptions = {},
htmlOptions: EnrichmentOptionsPF2e = {},
): Promise<RawItemChatData> {
return this.processChatData(htmlOptions, {
...(await super.getChatData()),
Expand Down
84 changes: 55 additions & 29 deletions src/module/item/equipment/data.ts
Original file line number Diff line number Diff line change
@@ -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<PhysicalSystemSource> {
traits: EquipmentTraits;
usage: { value: string };
/** Doubly-embedded adjustments, attachments, talismans etc. */
subitems: PhysicalItemSource[];
}
class EquipmentSystemData extends PhysicalItemSystemModel<EquipmentPF2e, EquipmentSystemSchema> {
declare traits: PhysicalItemTraits<EquipmentTrait>;

interface EquipmentTraits extends PhysicalItemTraits<EquipmentTrait> {}
static override defineSchema(): EquipmentSystemSchema {
const traits: Record<EquipmentTrait, string> = CONFIG.PF2E.equipmentTraits;

interface EquipmentSystemData
extends Omit<EquipmentSystemSource, SourceOmission>,
Omit<Investable<PhysicalSystemData>, "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<EquipmentPF2e, EquipmentSystemSchema>,
Omit<ModelPropsFromSchema<EquipmentSystemSchema>, PhysicalItemModelOmission> {}

type EquipmentSystemSchema = Omit<PhysicalItemSystemSchema, "traits"> & {
traits: fields.SchemaField<PhysicalItemTraitsSchema<EquipmentTrait>>;
usage: fields.SchemaField<{
value: fields.StringField<string, string, true, false>;
}>;
temporary: fields.BooleanField<boolean, boolean, true, false, true>;
subitems: fields.ArrayField<fields.ObjectField<PhysicalItemSource, PhysicalItemSource, true, false>>;
};

type SourceOmission =
| "apex"
| "bulk"
| "description"
| "hp"
| "identification"
| "material"
| "price"
| "temporary"
| "usage";
type EquipmentSystemSource = PhysicalItemSystemSource<EquipmentSystemSchema>;

export type { EquipmentSource, EquipmentSystemData, EquipmentSystemSource, EquipmentTrait };
export { EquipmentSystemData };
export type { EquipmentSource, EquipmentSystemSource, EquipmentTrait };
3 changes: 2 additions & 1 deletion src/module/item/identification.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions src/module/item/physical/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ interface PhysicalSystemData extends Omit<PhysicalSystemSource, "description">,
temporary: boolean;
identification: IdentificationData;
usage: UsageDetails;
stackGroup: string | null;
stackGroup?: string | null;
}

type Investable<TData extends PhysicalSystemData | PhysicalSystemSource> = TData & {
Expand Down Expand Up @@ -108,7 +108,7 @@ interface ItemMaterialData extends ItemMaterialSource {
type IdentifiedData = DeepPartial<MystifiedData>;

interface IdentificationData extends IdentificationSource {
identified: MystifiedData;
identified: MystifiedData & { img: ImageFilePath | null };
}

type EquippedData = {
Expand Down
8 changes: 6 additions & 2 deletions src/module/item/physical/document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -564,8 +565,11 @@ abstract class PhysicalItemPF2e<TParent extends ActorPF2e | null = ActorPF2e | n
}

/* Retrieve subtitution data for an unidentified or misidentified item, generating defaults as necessary */
getMystifiedData(status: IdentificationStatus, _options?: Record<string, boolean>): MystifiedData {
const mystifiedData: MystifiedData = this.system.identification[status];
getMystifiedData(
status: IdentificationStatus,
_options?: Record<string, boolean>,
): MystifiedData & { img: ImageFilePath } {
const mystifiedData = this.system.identification[status];

const name = mystifiedData.name || this.generateUnidentifiedName();
const img = mystifiedData.img || getUnidentifiedPlaceholderImage(this);
Expand Down
Loading