Exact source used by this build. SHA-256: 9f2bd996240b491602bf658bc59f3610c339c7d6ec65f5e1201cc7fe7f47e09b.
1 import { z } from "zod";
2 import type { SimRules } from "../../types";
3 import { defineSystem } from "../registry";
4
5 export const QualitySchema = z.enum(["regular", "silver", "gold", "iridium"]);
6 export type Quality = z.infer<typeof QualitySchema>;
7 export const ItemDefinitionSchema = z.object({
8 id: z.string(), category: z.enum(["seed", "crop", "forage", "fish", "mineral", "artisan", "cooked", "material", "tool", "equipment", "furniture", "misc"]),
9 baseSellPrice: z.number().int().nonnegative(), stackMax: z.number().int().positive(), qualityAllowed: z.boolean(),
10 edibility: z.object({ value: z.number().int().optional(), energy: z.number(), health: z.number() }).nullable(),
11 tags: z.string().array(), name: z.string(), description: z.string(), cropId: z.string().nullable().optional(), kind: z.string().optional(),
12 legacyIds: z.string().array().optional(),
13 });
14 export type ItemDefinition = z.infer<typeof ItemDefinitionSchema>;
15 const catalogs = new WeakMap<object, Record<string, ItemDefinition>>();
16 export function getItem(rules: SimRules, id: string): ItemDefinition | undefined {
17 const data = rules.content?.items;
18 if (data && typeof data === "object") {
19 let catalog = catalogs.get(data);
20 if (!catalog) { catalog = Object.fromEntries(ItemDefinitionSchema.array().parse(data).map((item) => [item.id, item])); catalogs.set(data, catalog); }
21 return catalog[id];
22 }
23 // Old rules fixtures and old save ids remain usable until migrated to item content.
24 const crop = rules.crops[id], cropId = rules.seedToCrop?.[id];
25 if (!crop && !cropId) return undefined;
26 return { id, category: crop ? "crop" : "seed", baseSellPrice: crop?.sellPrice ?? 0, stackMax: 999, qualityAllowed: !!crop, edibility: null, tags: [], name: "TBD", description: "TBD", cropId: crop?.id ?? cropId };
27 }
28 export function qualityMultiplier(quality: Quality): number { return { regular: 1, silver: 1.25, gold: 1.5, iridium: 2 }[quality]; }
29 export function canonicalItemId(rules: SimRules, id: string): string {
30 const data = rules.content?.items as ItemDefinition[] | undefined;
31 return Array.isArray(data) ? data.find((item) => item.id === id || item.legacyIds?.includes(id))?.id ?? id : id;
32 }
33 export function sellPrice(item: ItemDefinition, quality: Quality, modifier = 1): number { return Math.floor(item.baseSellPrice * qualityMultiplier(quality) * modifier); }
34 /** Quality codes 0/1/2/4, matching the source formula's skipped iridium index. */
35 export function edibleRestoration(item: ItemDefinition, quality: Quality): { energy: number; health: number } | null {
36 if (!item.edibility) return null;
37 const value = item.edibility.value ?? item.edibility.energy / 2.5;
38 const energy = Math.floor(Math.ceil(value * 2.5) + value * { regular: 0, silver: 1, gold: 2, iridium: 4 }[quality]);
39 return { energy, health: Math.max(0, Math.floor(energy * .45)) };
40 }
41 export const itemsSystem = defineSystem({ id: "items", worldDefaults: () => ({}), playerDefaults: () => ({}), intents: [], events: [] });
42