From b8207f5b15eb8d8e2f1a868e8b6187feed6e4d15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=97=D0=B0=D0=B8=D0=B4=20=D0=9E=D0=BC=D0=B0=D1=80=20?= =?UTF-8?q?=D0=9C=D0=B5=D0=B4=D1=85=D0=B0=D1=82?= Date: Sun, 23 Aug 2026 04:59:30 +0500 Subject: [PATCH] Fix enchanted resource IDs in recipes (T4_LEATHER_LEVEL3@3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enchanted refined resources are top-level items carrying their own @enchantmentlevel, and their tradeable/market id is @ (e.g. T4_LEATHER_LEVEL3@3). The parser stored them — and referenced them in enchanted recipes — as just T4_LEATHER_LEVEL3, so those inputs never matched AODP price rows and enchanted-item costs were wrong. Apply canonicalId(name, enchantLevel) to both item ids and craftresource references. Co-Authored-By: Claude Fable 5 --- server/src/gamedata/parse.ts | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/server/src/gamedata/parse.ts b/server/src/gamedata/parse.ts index 2afb369..e29f41d 100644 --- a/server/src/gamedata/parse.ts +++ b/server/src/gamedata/parse.ts @@ -49,10 +49,20 @@ export interface ParseStats { localizedNames: number; } +/** + * Canonical tradeable id: an item with @enchantmentlevel n>0 is `@`. + * This matters for enchanted refined resources, whose base names already carry + * `_LEVELn` (e.g. T4_LEATHER_LEVEL3) AND an @enchantmentlevel — the real id the + * market uses is `T4_LEATHER_LEVEL3@3`, not `T4_LEATHER_LEVEL3`. + */ +function canonicalId(uniqueName: string, enchantLevel: number | null): string { + return enchantLevel && enchantLevel > 0 ? `${uniqueName}@${enchantLevel}` : uniqueName; +} + function parseCraftResources(req: Raw): ParsedRecipe['inputs'] { return asArray(req.craftresource) .map((r) => ({ - item: String(r['@uniquename'] ?? ''), + item: canonicalId(String(r['@uniquename'] ?? ''), num(r['@enchantmentlevel'])), count: num(r['@count']) ?? 0, maxReturnAmount: num(r['@maxreturnamount']), })) @@ -84,10 +94,15 @@ function collectItem(raw: Raw, items: ParsedItem[], recipes: ParsedRecipe[]): vo const uniqueName = raw['@uniquename']; if (!uniqueName || typeof uniqueName !== 'string') return; + // Some items (enchanted refined resources like T4_LEATHER_LEVEL3) carry their + // own @enchantmentlevel and no enchantments node — their tradeable id is @n. + const selfLevel = num(raw['@enchantmentlevel']) ?? 0; + const selfId = canonicalId(uniqueName, selfLevel); + const base: ParsedItem = { - uniqueName, + uniqueName: selfId, baseName: uniqueName, - enchantment: 0, + enchantment: selfLevel, tier: num(raw['@tier']), weight: num(raw['@weight']), itemValue: num(raw['@itemvalue']), @@ -96,7 +111,7 @@ function collectItem(raw: Raw, items: ParsedItem[], recipes: ParsedRecipe[]): vo craftingCategory: raw['@craftingcategory'] ?? null, }; items.push(base); - recipes.push(...parseRecipesForOutput(uniqueName, raw.craftingrequirements)); + recipes.push(...parseRecipesForOutput(selfId, raw.craftingrequirements)); // Enchanted variants live under enchantments.enchantment with their own recipes. for (const ench of asArray(raw.enchantments?.enchantment)) {