Honor RoM off-hand and ranged equipment slots

This commit is contained in:
phenom committed 2026-09-23 19:59:21 -04:00
1 parent 0206fda9e2
commit 7449abdad2
15 files changed
+150 -29

No files matched your search

+42 -3
View File
@@ -3,7 +3,7 @@ import { abilitiesForCharacter, learnedAbilityForBuild } from "./abilityCatalog"
import { isValidRomClassPair } from "../app/romDualClass";
import { loadCanonicalBenchmarkBuilds } from "./benchmark/benchmarkBuilds";
import { readinessStatusFor } from "./companionSpecReadiness";
import { classCanEquipItem } from "./equipment";
import { classCanEquipItem, equipInventoryItem } from "./equipment";
import { equipmentStatsForItems, resolvedEquipmentLoadout } from "./heirloomEquipment";
import { partyRolesForClass } from "./partyRoles";
import { createConfiguredPartyMember, createDungeonParty, createSpectatorDungeonParty, usePartyStore } from "./partyStore";
@@ -152,34 +152,72 @@ describe("RoM five class validation scope", () => {
ownerId: `rom-five:${classId}`,
ownerKind: "party",
classId,
secondaryClassId: ROM_FIVE_IDENTITIES.find((identity) => identity.classId === classId)?.secondaryClassId,
specializationId: null,
role,
level: 60,
});
if (classId === "rom-knight") expect(loadout["off-hand"]?.armorType).toBe("shield");
if (classId === "rom-knight") {
expect(loadout["off-hand"]?.armorType).toBe("shield");
expect(loadout.ranged?.weaponType).toBe("bow");
}
if (classId === "rom-priest") {
expect(loadout["main-hand"]?.weaponType).toBe("mace");
expect(loadout["off-hand"]?.armorType).toBe("shield");
expect(loadout.ranged).toBeUndefined();
}
if (classId === "rom-scout") {
expect(loadout["main-hand"]?.weaponType).toBe("dagger");
expect(loadout["main-hand"]?.inventorySlot).toBe("one-hand");
expect(loadout.ranged?.weaponType).toBe("bow");
expect(loadout["off-hand"]?.weaponType).toBe("dagger");
}
if (classId === "rom-rogue") {
expect(loadout["main-hand"]?.weaponType).toBe("dagger");
expect(loadout["off-hand"]?.weaponType).toBe("dagger");
expect(loadout.ranged?.weaponType).toBe("projectile");
}
if (classId === "rom-mage") {
expect(loadout["main-hand"]?.weaponType).toBe("staff");
expect(loadout["main-hand"]?.inventorySlot).toBe("two-hand");
expect(loadout["off-hand"]).toBeUndefined();
expect(loadout.ranged).toBeUndefined();
}
expect(loadout.wrist).toBeUndefined();
for (const item of Object.values(loadout)) expect(classCanEquipItem(classId, item!)).toBe(true);
for (const item of Object.values(loadout)) expect(classCanEquipItem(classId, item!, {
secondaryClassId: ROM_FIVE_IDENTITIES.find((identity) => identity.classId === classId)?.secondaryClassId,
level: 60,
})).toBe(true);
}
});
it("opens the independent ranged and off-hand positions at the source training levels", () => {
const loadout = (classId: "rom-knight" | "rom-scout" | "rom-rogue", secondaryClassId: "rom-scout" | "rom-rogue" | "rom-warrior", level: number) =>
resolvedEquipmentLoadout({}, [], {
ownerId: `rom-slots:${classId}`,
ownerKind: "player",
classId,
secondaryClassId: level >= 10 ? secondaryClassId : null,
specializationId: null,
role: classId === "rom-knight" ? "tank" : "damage",
level,
});
expect(loadout("rom-knight", "rom-scout", 9).ranged).toBeUndefined();
expect(loadout("rom-knight", "rom-scout", 10).ranged?.weaponType).toBe("bow");
expect(loadout("rom-scout", "rom-rogue", 15)["off-hand"]).toBeUndefined();
expect(loadout("rom-scout", "rom-rogue", 16)["off-hand"]?.weaponType).toBe("dagger");
expect(loadout("rom-rogue", "rom-warrior", 11).ranged).toBeUndefined();
expect(loadout("rom-rogue", "rom-warrior", 12).ranged?.weaponType).toBe("projectile");
expect(loadout("rom-rogue", "rom-warrior", 15)["off-hand"]).toBeUndefined();
expect(loadout("rom-rogue", "rom-warrior", 16)["off-hand"]?.weaponType).toBe("dagger");
const knightBow = loadout("rom-knight", "rom-scout", 60).ranged!;
expect(classCanEquipItem("rom-knight", knightBow, { level: 60 })).toBe(false);
expect(equipInventoryItem({}, [knightBow], "knight", "rom-knight", knightBow.instanceId,
{ secondaryClassId: "rom-scout", level: 60 }).ok).toBe(true);
expect(equipInventoryItem({}, [knightBow], "knight", "rom-knight", knightBow.instanceId,
{ secondaryClassId: "rom-priest", level: 60 }).ok).toBe(false);
});
it("grows the five hybrid combat loadouts through the heirloom levels", () => {
for (const { classId, role } of CASES) {
const identity = ROM_FIVE_IDENTITIES.find((entry) => entry.classId === classId && entry.role === role)!;
@@ -190,6 +228,7 @@ describe("RoM five class validation scope", () => {
ownerId: `rom-hybrid:${classId}`,
ownerKind: "player",
classId,
secondaryClassId: level >= 10 ? identity.secondaryClassId : null,
specializationId: null,
role,
level,