diff --git a/api/src/lib/ancientRunicLeatherHorseStirrupLeathersBench.ts b/api/src/lib/ancientRunicLeatherHorseStirrupLeathersBench.ts new file mode 100644 index 00000000..bb521a61 --- /dev/null +++ b/api/src/lib/ancientRunicLeatherHorseStirrupLeathersBench.ts @@ -0,0 +1,261 @@ +import crypto from "node:crypto"; + +/** + * Ancient Runic Leather Horse Stirrup Leathers Bench, Mithril Tongue Buckle & Celestial Valkyrie Stride Engine for OpenAO MMORPG. + * Simulates stirrup strap stitching benches and tongue buckle tension rigs (Rowan Stirrup Leathers Bench, Runic Hickory Stirrup Rig, Celestial Void Valkyrie Stride Sanctum), + * raw tanned buffalo stirrup straps and tempered mithril tongue buckle sets (Tanned Buffalo Stirrup Strap, Tempered Mithril Tongue Buckle Set, Celestial Void Astral Stride Pelt), + * novice riders suspension stirrup leathers and sovereign aerial stirrup recipes (Novice Riders Suspension Stirrup Leathers, Warmaster Mithril Calf Stirrup Leathers, Celestial Void Valkyrie Sovereign Stirrup Leathers), + * independent steed ankle-strain mitigation & stirrup suspension ratings (scaled across catalog baselines ~16% to 100%), calibrated clamped ankle strain mitigation bonus and rider stride balance scaling, + * upfront leather material deduction on all craft attempts, consistent remainingProvidedLeathers return shapes across all paths, immutable bench cloning for safe rollbacks on both craft and maintain operations, cached static catalog maxima, crypto-secure default gameplay rolls strictly in [0, 1), authoritative catalog power ratio without dead instance fields, and horse stirrup leathers bench maintenance. + */ + +export type StirrupLeathersBenchType = "ROWAN_STIRRUP_LEATHERS_BENCH" | "RUNIC_HICKORY_STIRRUP_RIG" | "CELESTIAL_VOID_VALKYRIE_STRIDE_SANCTUM"; +export type RawLeatherStirrupLeathersType = "TANNED_BUFFALO_STIRRUP_STRAP" | "TEMPERED_MITHRIL_TONGUE_BUCKLE_SET" | "CELESTIAL_VOID_ASTRAL_STRIDE_PELT"; +export type StirrupLeathersRecipeType = "NOVICE_RIDERS_SUSPENSION_STIRRUP_LEATHERS" | "WARMASTER_MITHRIL_CALF_STIRRUP_LEATHERS" | "CELESTIAL_VOID_VALKYRIE_SOVEREIGN_STIRRUP_LEATHERS"; + +export interface StirrupLeathersBenchData { + benchType: StirrupLeathersBenchType; + maxDurability: number; + leathercraftPower: number; + baseSuccessRatePercent: number; // 0 to 100 + stirrupSuspensionBonusPercent: number; +} + +export interface StirrupLeathersRecipeData { + recipeType: StirrupLeathersRecipeType; + requiredLeatherType: RawLeatherStirrupLeathersType; + requiredLeatherCount: number; + baseAnkleStrainMitigationPercent: number; + baseRiderStrideBalanceBonusPercent: number; +} + +export interface ActiveStirrupLeathersBench { + benchId: string; + leatherworkerPlayerId: string; + benchType: StirrupLeathersBenchType; + currentDurability: number; + maxDurability: number; + isFunctional: boolean; +} + +export interface CraftedHorseStirrupLeathers { + stirrupLeathersId: string; + recipeType: StirrupLeathersRecipeType; + finalAnkleStrainMitigationPercent: number; + finalRiderStrideBalanceBonusPercent: number; + stirrupSuspensionPercent: number; // Scaled rating (clamped 0 to 100%, with catalog bench baselines ~16% to 100%) + consumedLeatherCount: number; + consumedLeatherType: RawLeatherStirrupLeathersType; + remainingProvidedLeathers: RawLeatherStirrupLeathersType[]; + craftedEpochMs: number; +} + +export const STIRRUP_LEATHERS_BENCH_CATALOG: Record = { + ROWAN_STIRRUP_LEATHERS_BENCH: { benchType: "ROWAN_STIRRUP_LEATHERS_BENCH", maxDurability: 95, leathercraftPower: 30, baseSuccessRatePercent: 87, stirrupSuspensionBonusPercent: 14 }, + RUNIC_HICKORY_STIRRUP_RIG: { benchType: "RUNIC_HICKORY_STIRRUP_RIG", maxDurability: 200, leathercraftPower: 72, baseSuccessRatePercent: 94, stirrupSuspensionBonusPercent: 24 }, + CELESTIAL_VOID_VALKYRIE_STRIDE_SANCTUM: { benchType: "CELESTIAL_VOID_VALKYRIE_STRIDE_SANCTUM", maxDurability: 350, leathercraftPower: 130, baseSuccessRatePercent: 99, stirrupSuspensionBonusPercent: 40 }, +}; + +export const STIRRUP_LEATHERS_RECIPE_CATALOG: Record = { + NOVICE_RIDERS_SUSPENSION_STIRRUP_LEATHERS: { recipeType: "NOVICE_RIDERS_SUSPENSION_STIRRUP_LEATHERS", requiredLeatherType: "TANNED_BUFFALO_STIRRUP_STRAP", requiredLeatherCount: 2, baseAnkleStrainMitigationPercent: 24, baseRiderStrideBalanceBonusPercent: 14 }, + WARMASTER_MITHRIL_CALF_STIRRUP_LEATHERS: { recipeType: "WARMASTER_MITHRIL_CALF_STIRRUP_LEATHERS", requiredLeatherType: "TEMPERED_MITHRIL_TONGUE_BUCKLE_SET", requiredLeatherCount: 2, baseAnkleStrainMitigationPercent: 50, baseRiderStrideBalanceBonusPercent: 30 }, + CELESTIAL_VOID_VALKYRIE_SOVEREIGN_STIRRUP_LEATHERS: { recipeType: "CELESTIAL_VOID_VALKYRIE_SOVEREIGN_STIRRUP_LEATHERS", requiredLeatherType: "CELESTIAL_VOID_ASTRAL_STRIDE_PELT", requiredLeatherCount: 2, baseAnkleStrainMitigationPercent: 84, baseRiderStrideBalanceBonusPercent: 64 }, +}; + +export class AncientRunicLeatherHorseStirrupLeathersBenchEngine { + public static readonly DURABILITY_COST_PER_CRAFT = 10; + + /** + * Cached static catalog maxima to prevent runtime array reallocation. + */ + public static readonly CATALOG_MAXIMA = { + maxPower: Math.max(...Object.values(STIRRUP_LEATHERS_BENCH_CATALOG).map(b => b.leathercraftPower), 1), + maxBonus: Math.max(...Object.values(STIRRUP_LEATHERS_BENCH_CATALOG).map(b => b.stirrupSuspensionBonusPercent), 1), + }; + + /** + * Generates a crypto-secure UUID or 128-bit hex string using node:crypto. + */ + private static generateSecureId(): string { + if (typeof crypto.randomUUID === "function") { + return crypto.randomUUID(); + } + return crypto.randomBytes(16).toString("hex"); + } + + /** + * Generates a cryptographically secure random float strictly in [0, 1). + */ + public static generateSecureRoll(): number { + if (typeof crypto.randomInt === "function") { + return crypto.randomInt(0, 1000000) / 1000000; + } + return crypto.randomBytes(4).readUInt32LE(0) / 0x100000000; + } + + /** + * Constructs and initializes a horse stirrup leathers stitching bench or rig. + */ + public static constructBench( + leatherworkerPlayerId: string, + benchType: StirrupLeathersBenchType + ): ActiveStirrupLeathersBench { + const data = STIRRUP_LEATHERS_BENCH_CATALOG[benchType]; + if (!data) { + throw new Error(`Unsupported horse stirrup leathers bench type: ${String(benchType)}`); + } + + const uuid = this.generateSecureId(); + + return { + benchId: `bench_${benchType.toLowerCase()}_${uuid}`, + leatherworkerPlayerId, + benchType, + currentDurability: data.maxDurability, + maxDurability: data.maxDurability, + isFunctional: true, + }; + } + + /** + * Stitches and tensions stirrup straps and tempered mithril tongue buckles into horse stirrup leathers. + * Returns an updated clone of `bench` leaving the input instance immutable. + */ + public static craftStirrupLeathers( + bench: ActiveStirrupLeathersBench, + recipeType: StirrupLeathersRecipeType, + providedLeathers: RawLeatherStirrupLeathersType[], + craftRoll?: number, + suspensionRoll?: number, + currentEpochMs = Date.now() + ): { success: boolean; stirrupLeathers?: CraftedHorseStirrupLeathers; updatedBench?: ActiveStirrupLeathersBench; remainingDurability: number; remainingProvidedLeathers: RawLeatherStirrupLeathersType[]; reason?: string } { + const fallbackLeathers = Array.isArray(providedLeathers) ? [...providedLeathers] : []; + + if (!bench || !bench.isFunctional || bench.currentDurability < this.DURABILITY_COST_PER_CRAFT) { + return { + success: false, + updatedBench: bench ? { ...bench } : undefined, + remainingDurability: bench?.currentDurability ?? 0, + remainingProvidedLeathers: fallbackLeathers, + reason: `Horse stirrup leathers bench is warped or lacks durability (requires ${this.DURABILITY_COST_PER_CRAFT}).`, + }; + } + + const benchData = STIRRUP_LEATHERS_BENCH_CATALOG[bench.benchType]; + if (!benchData) { + return { success: false, updatedBench: { ...bench }, remainingDurability: bench.currentDurability, remainingProvidedLeathers: fallbackLeathers, reason: `Unknown bench model: ${String(bench.benchType)}` }; + } + + const recipe = STIRRUP_LEATHERS_RECIPE_CATALOG[recipeType]; + if (!recipe) { + return { success: false, updatedBench: { ...bench }, remainingDurability: bench.currentDurability, remainingProvidedLeathers: fallbackLeathers, reason: `Unknown horse stirrup leathers recipe: ${String(recipeType)}` }; + } + + if (!Array.isArray(providedLeathers)) { + return { success: false, updatedBench: { ...bench }, remainingDurability: bench.currentDurability, remainingProvidedLeathers: [], reason: "Invalid leathers array." }; + } + + // Count matching leather materials + const matchingCount = providedLeathers.filter(l => l === recipe.requiredLeatherType).length; + if (matchingCount < recipe.requiredLeatherCount) { + return { + success: false, + updatedBench: { ...bench }, + remainingDurability: bench.currentDurability, + remainingProvidedLeathers: fallbackLeathers, + reason: `Insufficient stirrup straps/tongue buckles: requires ${recipe.requiredLeatherCount}x ${recipe.requiredLeatherType}, provided ${matchingCount}.`, + }; + } + + // Create updated bench clone + const updatedBench = { ...bench }; + + // Deduct durability on clone + updatedBench.currentDurability -= this.DURABILITY_COST_PER_CRAFT; + if (updatedBench.currentDurability < this.DURABILITY_COST_PER_CRAFT) { + updatedBench.currentDurability = Math.max(0, updatedBench.currentDurability); + updatedBench.isFunctional = false; + } + + // Deduct materials upfront on all craft attempts + const remaining = [...providedLeathers]; + let removed = 0; + for (let i = remaining.length - 1; i >= 0 && removed < recipe.requiredLeatherCount; i--) { + if (remaining[i] === recipe.requiredLeatherType) { + remaining.splice(i, 1); + removed++; + } + } + + const safeRoll = typeof craftRoll === "number" && Number.isFinite(craftRoll) ? Math.max(0, Math.min(1, craftRoll)) : this.generateSecureRoll(); + const rollPercent = safeRoll * 100; + + if (rollPercent > benchData.baseSuccessRatePercent) { + return { + success: false, + updatedBench, + remainingDurability: updatedBench.currentDurability, + remainingProvidedLeathers: remaining, + reason: `Stirrup strap misaligned: mithril tongue buckle distorted during tension clamping, rolled ${rollPercent.toFixed(1)}, needed <= ${benchData.baseSuccessRatePercent}.`, + }; + } + + // Calculate independent stirrup suspension score dynamically using cached catalog maxima & authoritative catalog values (clamped 0% to 100%) + const { maxPower, maxBonus } = this.CATALOG_MAXIMA; + const safeSuspensionRoll = typeof suspensionRoll === "number" && Number.isFinite(suspensionRoll) ? Math.max(0, Math.min(1, suspensionRoll)) : this.generateSecureRoll(); + const powerRatio = Math.min(1.0, benchData.leathercraftPower / maxPower); + const bonusPoints = (benchData.stirrupSuspensionBonusPercent / maxBonus) * 20; + const suspensionScore = Math.max(0, Math.min(100, Math.round( + (safeSuspensionRoll * 40) + (powerRatio * 40) + bonusPoints + ))); + const qualityMultiplier = 0.8 + ((suspensionScore / 100) * 0.4); // 0.8 to 1.2x + + const finalStrainMitigation = Math.max(0, Math.min(100, Math.round(recipe.baseAnkleStrainMitigationPercent * qualityMultiplier))); + const finalBalanceBonus = Math.max(0, Math.min(100, Math.round(recipe.baseRiderStrideBalanceBonusPercent * qualityMultiplier))); + + const uuid = this.generateSecureId(); + + const stirrupLeathers: CraftedHorseStirrupLeathers = { + stirrupLeathersId: `stirrupleathers_${recipeType.toLowerCase()}_${uuid}`, + recipeType, + finalAnkleStrainMitigationPercent: finalStrainMitigation, + finalRiderStrideBalanceBonusPercent: finalBalanceBonus, + stirrupSuspensionPercent: suspensionScore, + consumedLeatherCount: recipe.requiredLeatherCount, + consumedLeatherType: recipe.requiredLeatherType, + remainingProvidedLeathers: remaining, + craftedEpochMs: currentEpochMs, + }; + + return { + success: true, + stirrupLeathers, + updatedBench, + remainingDurability: updatedBench.currentDurability, + remainingProvidedLeathers: remaining, + }; + } + + /** + * Cleans equestrian trail grime and maintains horse stirrup leathers bench. + * Returns an updated clone of `bench` leaving the input instance immutable. + */ + public static maintainBench( + bench: ActiveStirrupLeathersBench, + repairAmount = 50 + ): { success: boolean; updatedBench?: ActiveStirrupLeathersBench; newDurability: number; isFunctional: boolean } { + if (!bench) return { success: false, newDurability: 0, isFunctional: false }; + + const updatedBench = { ...bench }; + const amt = Number.isFinite(repairAmount) ? Math.max(0, repairAmount) : 50; + updatedBench.currentDurability = Math.min(updatedBench.maxDurability, updatedBench.currentDurability + amt); + updatedBench.isFunctional = updatedBench.currentDurability >= this.DURABILITY_COST_PER_CRAFT; + + return { + success: true, + updatedBench, + newDurability: updatedBench.currentDurability, + isFunctional: updatedBench.isFunctional, + }; + } +} diff --git a/api/src/tests/ancientRunicLeatherHorseStirrupLeathersBench.test.ts b/api/src/tests/ancientRunicLeatherHorseStirrupLeathersBench.test.ts new file mode 100644 index 00000000..d75efd9b --- /dev/null +++ b/api/src/tests/ancientRunicLeatherHorseStirrupLeathersBench.test.ts @@ -0,0 +1,154 @@ +import { describe, it, expect } from "vitest"; +import { AncientRunicLeatherHorseStirrupLeathersBenchEngine } from "../lib/ancientRunicLeatherHorseStirrupLeathersBench"; +import type { ActiveStirrupLeathersBench } from "../lib/ancientRunicLeatherHorseStirrupLeathersBench"; + +describe("AncientRunicLeatherHorseStirrupLeathersBenchEngine Stirrup Suspension Benches & Rigs", () => { + it("crafts Celestial Void Valkyrie Sovereign Stirrup Leathers in Stride Sanctum achieving 100% suspension and returns spliced pelts", () => { + const bench = AncientRunicLeatherHorseStirrupLeathersBenchEngine.constructBench("leather_01", "CELESTIAL_VOID_VALKYRIE_STRIDE_SANCTUM"); + expect(bench.benchType).toBe("CELESTIAL_VOID_VALKYRIE_STRIDE_SANCTUM"); + expect(bench.currentDurability).toBe(350); + + const initialLeathers = [ + "CELESTIAL_VOID_ASTRAL_STRIDE_PELT", + "CELESTIAL_VOID_ASTRAL_STRIDE_PELT", + "CELESTIAL_VOID_ASTRAL_STRIDE_PELT" + ] as any[]; + + const craftRes = AncientRunicLeatherHorseStirrupLeathersBenchEngine.craftStirrupLeathers( + bench, + "CELESTIAL_VOID_VALKYRIE_SOVEREIGN_STIRRUP_LEATHERS", + initialLeathers, + 0.1, // Success roll + 1.0, // Suspension roll 1.0 -> 40 + 40 + 20 = 100% + 100000 + ); + + expect(craftRes.success).toBe(true); + expect(craftRes.stirrupLeathers?.recipeType).toBe("CELESTIAL_VOID_VALKYRIE_SOVEREIGN_STIRRUP_LEATHERS"); + expect(craftRes.stirrupLeathers?.stirrupSuspensionPercent).toBe(100); + expect(craftRes.stirrupLeathers?.finalAnkleStrainMitigationPercent).toBe(100); // 84 * 1.20 = 100.8 -> clamped to 100% + expect(craftRes.stirrupLeathers?.finalRiderStrideBalanceBonusPercent).toBe(77); // 64 * 1.20 = 76.8 -> 77% + expect(craftRes.stirrupLeathers?.consumedLeatherCount).toBe(2); + expect(craftRes.stirrupLeathers?.consumedLeatherType).toBe("CELESTIAL_VOID_ASTRAL_STRIDE_PELT"); + expect(craftRes.stirrupLeathers?.remainingProvidedLeathers.length).toBe(1); + expect(craftRes.remainingDurability).toBe(340); // 350 - 10 + }); + + it("verifies mid-range suspension roll and sub-100% quality scaling on Rowan stirrup leathers bench", () => { + const bench = AncientRunicLeatherHorseStirrupLeathersBenchEngine.constructBench("leather_mid", "ROWAN_STIRRUP_LEATHERS_BENCH"); + // powerRatio = 30/130 = 0.23077, bonusPoints = (14/40)*20 = 7.0 + // safeSuspensionRoll = 0.5 -> 0.5 * 40 = 20 + // score = Math.round(20 + 9.23077 + 7.0) = 36 + // qualityMultiplier = 0.8 + (36/100)*0.4 = 0.944 + // finalStrainMitigation = Math.round(24 * 0.944) = 23 + // finalBalanceBonus = Math.round(14 * 0.944) = 13 + const craftRes = AncientRunicLeatherHorseStirrupLeathersBenchEngine.craftStirrupLeathers( + bench, + "NOVICE_RIDERS_SUSPENSION_STIRRUP_LEATHERS", + ["TANNED_BUFFALO_STIRRUP_STRAP", "TANNED_BUFFALO_STIRRUP_STRAP"], + 0.1, + 0.5 + ); + + expect(craftRes.success).toBe(true); + expect(craftRes.stirrupLeathers?.stirrupSuspensionPercent).toBe(36); + expect(craftRes.stirrupLeathers?.finalAnkleStrainMitigationPercent).toBe(23); + expect(craftRes.stirrupLeathers?.finalRiderStrideBalanceBonusPercent).toBe(13); + }); + + it("handles bench becoming non-functional after successful craft when durability falls below threshold", () => { + const bench = AncientRunicLeatherHorseStirrupLeathersBenchEngine.constructBench("leather_wear", "ROWAN_STIRRUP_LEATHERS_BENCH"); + bench.currentDurability = 15; + expect(bench.isFunctional).toBe(true); + + // First craft succeeds: 15 - 10 = 5 (< 10), so isFunctional flips to false in updatedBench + const res1 = AncientRunicLeatherHorseStirrupLeathersBenchEngine.craftStirrupLeathers( + bench, + "NOVICE_RIDERS_SUSPENSION_STIRRUP_LEATHERS", + ["TANNED_BUFFALO_STIRRUP_STRAP", "TANNED_BUFFALO_STIRRUP_STRAP"], + 0.1 + ); + expect(res1.success).toBe(true); + expect(res1.remainingDurability).toBe(5); + expect(res1.updatedBench?.isFunctional).toBe(false); + + // Subsequent craft on updated bench is rejected and returns fallback array + const res2 = AncientRunicLeatherHorseStirrupLeathersBenchEngine.craftStirrupLeathers( + res1.updatedBench!, + "NOVICE_RIDERS_SUSPENSION_STIRRUP_LEATHERS", + ["TANNED_BUFFALO_STIRRUP_STRAP", "TANNED_BUFFALO_STIRRUP_STRAP"] + ); + expect(res2.success).toBe(false); + expect(res2.reason).toContain("warped or lacks durability"); + expect(res2.remainingProvidedLeathers.length).toBe(2); + }); + + it("rejects crafting when insufficient leather is provided and returns provided leathers", () => { + const bench = AncientRunicLeatherHorseStirrupLeathersBenchEngine.constructBench("leather_02", "ROWAN_STIRRUP_LEATHERS_BENCH"); + + const failRes = AncientRunicLeatherHorseStirrupLeathersBenchEngine.craftStirrupLeathers( + bench, + "WARMASTER_MITHRIL_CALF_STIRRUP_LEATHERS", + ["TEMPERED_MITHRIL_TONGUE_BUCKLE_SET"] + ); + + expect(failRes.success).toBe(false); + expect(failRes.reason).toContain("Insufficient stirrup straps/tongue buckles"); + expect(failRes.remainingProvidedLeathers.length).toBe(1); + expect(bench.currentDurability).toBe(95); + }); + + it("handles stirrup strap misaligned failure roll consuming durability and leathers", () => { + const bench = AncientRunicLeatherHorseStirrupLeathersBenchEngine.constructBench("leather_03", "ROWAN_STIRRUP_LEATHERS_BENCH"); // 87% success + + const fail = AncientRunicLeatherHorseStirrupLeathersBenchEngine.craftStirrupLeathers( + bench, + "NOVICE_RIDERS_SUSPENSION_STIRRUP_LEATHERS", + ["TANNED_BUFFALO_STIRRUP_STRAP", "TANNED_BUFFALO_STIRRUP_STRAP", "TANNED_BUFFALO_STIRRUP_STRAP"], + 0.95 + ); + + expect(fail.success).toBe(false); + expect(fail.reason).toContain("misaligned"); + expect(fail.remainingProvidedLeathers?.length).toBe(1); // 3 - 2 = 1 remaining + expect(fail.remainingDurability).toBe(85); // 95 - 10 + }); + + it("gates isFunctional in maintainBench based on DURABILITY_COST_PER_CRAFT threshold and returns clone", () => { + const bench = AncientRunicLeatherHorseStirrupLeathersBenchEngine.constructBench("leather_04", "ROWAN_STIRRUP_LEATHERS_BENCH"); + bench.currentDurability = 0; + bench.isFunctional = false; + + // Maintain 5 (below 10 required) -> isFunctional remains false + const repLow = AncientRunicLeatherHorseStirrupLeathersBenchEngine.maintainBench(bench, 5); + expect(repLow.success).toBe(true); + expect(repLow.newDurability).toBe(5); + expect(repLow.isFunctional).toBe(false); + expect(bench.currentDurability).toBe(0); // input unchanged + + // Maintain 10 more on clone -> 15 (>= 10) -> isFunctional becomes true + const repHigh = AncientRunicLeatherHorseStirrupLeathersBenchEngine.maintainBench(repLow.updatedBench!, 10); + expect(repHigh.success).toBe(true); + expect(repHigh.newDurability).toBe(15); + expect(repHigh.isFunctional).toBe(true); + }); + + it("guards against null inputs and unsupported bench models", () => { + expect(() => AncientRunicLeatherHorseStirrupLeathersBenchEngine.constructBench("l", "PLASTIC_BENCH" as any)).toThrow( + "Unsupported horse stirrup leathers bench type" + ); + + const invalidBench: ActiveStirrupLeathersBench = { + benchId: "bad", + leatherworkerPlayerId: "p", + benchType: "BENCH" as any, + currentDurability: 50, + maxDurability: 50, + isFunctional: true, + }; + + expect(AncientRunicLeatherHorseStirrupLeathersBenchEngine.craftStirrupLeathers(invalidBench, "NOVICE_RIDERS_SUSPENSION_STIRRUP_LEATHERS", ["TANNED_BUFFALO_STIRRUP_STRAP", "TANNED_BUFFALO_STIRRUP_STRAP"]).success).toBe(false); + expect(AncientRunicLeatherHorseStirrupLeathersBenchEngine.craftStirrupLeathers(null as any, "NOVICE_RIDERS_SUSPENSION_STIRRUP_LEATHERS", []).success).toBe(false); + expect(AncientRunicLeatherHorseStirrupLeathersBenchEngine.maintainBench(null as any).success).toBe(false); + }); +});