Add source-backed RoM heirloom reference curves

This commit is contained in:
phenom committed 2026-09-23 16:56:48 -04:00
1 parent d3cc27a54a
commit f4a7d33f74
9 files changed
+148524 -8

No files matched your search

File diff suppressed because it is too large. Load diff
+55
View File
@@ -0,0 +1,55 @@
import { describe, expect, it } from "vitest";
import { classById } from "../app/characterCatalog";
import {
romHeirloomReference,
romSourceReferenceItem,
ROM_HEIRLOOM_REFERENCE_CLASSES,
ROM_HEIRLOOM_REFERENCE_SLOTS,
} from "./romHeirloomReference";
describe("RoM source-backed heirloom references", () => {
it("provides finite native stats and source item IDs at every level from 1 through 79", () => {
for (const classId of ROM_HEIRLOOM_REFERENCE_CLASSES) {
for (const slot of ROM_HEIRLOOM_REFERENCE_SLOTS) {
const first = romHeirloomReference(classId, slot, 1);
if (!first) continue;
for (let level = 1; level <= 79; level += 1) {
const reference = romHeirloomReference(classId, slot, level)!;
expect(reference.level).toBe(level);
expect(reference.referenceItemIds.length).toBeGreaterThan(0);
expect(Object.values(reference.stats).length).toBeGreaterThan(0);
for (const value of Object.values(reference.stats)) {
expect(Number.isFinite(value)).toBe(true);
expect(value).toBeGreaterThanOrEqual(0);
}
for (const itemId of reference.referenceItemIds) {
expect(romSourceReferenceItem(itemId)?.quality).toBe(2);
}
}
}
}
});
it("uses source package suit anchors and a leather reference for the Scout", () => {
expect(romHeirloomReference("rom-knight", "chest", 50)?.packageId).toBe(240384);
expect(romHeirloomReference("rom-scout", "chest", 60)?.packageId).toBe(240347);
expect(romHeirloomReference("rom-priest", "chest", 65)?.packageId).toBe(240313);
expect(classById("rom-scout").armor).toBe("leather");
const scoutChest = romHeirloomReference("rom-scout", "chest", 65)!;
expect(scoutChest.referenceItemIds.some((id) => romSourceReferenceItem(id)?.type === 2)).toBe(true);
expect(romHeirloomReference("rom-priest", "off-hand", 65)).toBeNull();
expect(romSourceReferenceItem(213416)?.stats).toMatchObject({ magicalWeaponDamage: 1094, wisdom: 56 });
expect(romSourceReferenceItem(229164)?.stats).toMatchObject({
physicalDefense: 1029,
magicalDefense: 1655,
health: 273,
magicalAttack: 290,
});
});
it("clamps queries to the designed 1–79 heirloom interval", () => {
expect(romHeirloomReference("rom-mage", "main-hand", -20)?.level).toBe(1);
expect(romHeirloomReference("rom-mage", "main-hand", 80)?.level).toBe(79);
expect(romHeirloomReference("rom-mage", "off-hand", 40)).toBeNull();
});
});
+77
View File
@@ -0,0 +1,77 @@
import catalog from "./generated/romHeirloomReference.json";
export const ROM_HEIRLOOM_REFERENCE_CLASSES = [
"rom-knight", "rom-priest", "rom-scout", "rom-rogue", "rom-mage",
] as const;
export type RomHeirloomReferenceClass = (typeof ROM_HEIRLOOM_REFERENCE_CLASSES)[number];
export const ROM_HEIRLOOM_REFERENCE_SLOTS = [
"head", "neck", "shoulder", "back", "chest", "hands", "waist",
"legs", "feet", "finger", "trinket", "main-hand", "off-hand", "ranged",
] as const;
export type RomHeirloomReferenceSlot = (typeof ROM_HEIRLOOM_REFERENCE_SLOTS)[number];
export interface RomNativeGearStats {
readonly strength?: number;
readonly stamina?: number;
readonly intellect?: number;
readonly wisdom?: number;
readonly agility?: number;
readonly health?: number;
readonly mana?: number;
readonly healthRecovery?: number;
readonly manaRecovery?: number;
readonly physicalAttack?: number;
readonly physicalDefense?: number;
readonly magicalDefense?: number;
readonly magicalAttack?: number;
readonly physicalHit?: number;
readonly magicalHit?: number;
readonly dodge?: number;
readonly parry?: number;
readonly physicalCritical?: number;
readonly magicalCritical?: number;
readonly weaponDamage?: number;
readonly magicalDamage?: number;
readonly magicalWeaponDamage?: number;
readonly healingPower?: number;
}
export interface RomHeirloomReference {
readonly level: number;
readonly stats: RomNativeGearStats;
readonly referenceItemIds: readonly number[];
readonly packageId?: number;
readonly speedTenths?: number;
}
interface RomSourceReferenceItem {
readonly itemId: number;
readonly name: string;
readonly level: number;
readonly quality: number;
readonly sourceCatalog: string;
readonly type: number;
readonly position: number;
readonly suitId: number;
readonly stats: RomNativeGearStats;
}
const profiles = catalog.profiles as unknown as Readonly<Record<RomHeirloomReferenceClass, Readonly<Partial<Record<RomHeirloomReferenceSlot, readonly RomHeirloomReference[]>>>>>;
const sourceItems = new Map((catalog.sourceItems as readonly RomSourceReferenceItem[]).map((item) => [item.itemId, item]));
/** Source-backed design values; not yet the combat runtime's equipped item stats. */
export function romHeirloomReference(
classId: RomHeirloomReferenceClass,
slot: RomHeirloomReferenceSlot,
level: number,
): RomHeirloomReference | null {
const curve = profiles[classId]?.[slot];
if (!curve) return null;
const safeLevel = Math.max(1, Math.min(79, Math.trunc(Number.isFinite(level) ? level : 1)));
return curve[safeLevel - 1] ?? null;
}
export function romSourceReferenceItem(itemId: number): RomSourceReferenceItem | null {
return sourceItems.get(itemId) ?? null;
}