import { describe, expect, it } from "vitest"; import { dungeonEnemyScaling } from "./dungeonBossScaling"; import { bossDifficultyMultipliers } from "./dungeonDifficulty"; import { requireDungeonDefinition } from "./dungeonRegistry"; import { finalDungeonBoss } from "./dungeonRunStore"; import { useCombatStore } from "./combatStore"; const dungeon = requireDungeonDefinition("barren-caves"); const boss = finalDungeonBoss(dungeon)!; const bossSpawn = dungeon.staticSpawns.find((spawn) => spawn.id === boss.id)!; const trashSpawn = dungeon.staticSpawns.find((spawn) => dungeon.entities[spawn.entityId].kind === "mob")!; describe("native RoM dungeon health budgets", () => { it("calibrates the level-one native Anglo boss without changing trash health", () => { const nativeBoss = dungeonEnemyScaling(dungeon, dungeon.entities[bossSpawn.entityId], boss.id, 1, "normal", 1, "rom", "rom"); const legacyBoss = dungeonEnemyScaling(dungeon, dungeon.entities[bossSpawn.entityId], boss.id, 1, "normal", 1, "rom", "wow"); const nativeTrash = dungeonEnemyScaling(dungeon, dungeon.entities[trashSpawn.entityId], trashSpawn.id, 1, "normal", 1, "rom", "rom"); const legacyTrash = dungeonEnemyScaling(dungeon, dungeon.entities[trashSpawn.entityId], trashSpawn.id, 1, "normal", 1, "rom", "wow"); expect(nativeBoss.maxHealth).toBe(1_848); expect(nativeBoss.maxHealth).toBeCloseTo(legacyBoss.maxHealth * 0.2); expect(nativeTrash.maxHealth).toBeCloseTo(legacyTrash.maxHealth * 0.4); expect(nativeTrash.nativeDamageBudgetScale).toBe(1); }); it("keeps native training budgets independent of legacy stats and authored difficulty", () => { for (const level of [1, 30, 66, 79, 80]) for (const spawn of [trashSpawn, bossSpawn]) { const entity = dungeon.entities[spawn.entityId]; const legacy = dungeonEnemyScaling(dungeon, entity, spawn.id, level, "normal", 1, "rom"); expect(dungeonEnemyScaling(dungeon, entity, spawn.id, level, "normal", 1, "rom", "wow")).toEqual(legacy); const native = dungeonEnemyScaling(dungeon, entity, spawn.id, level, "normal", 1, "rom", "rom"); expect(native.maxHealth).not.toBe(legacy.maxHealth); expect(native.damageMultiplier).toBe(legacy.damageMultiplier); expect(legacy.nativeDamageBudgetScale).toBe(1); expect(native.nativeDamageBudgetScale).toBeGreaterThanOrEqual(1); expect(native.level).toBe(legacy.level); for (const source of ["wow", "coa"] as const) { expect(dungeonEnemyScaling(dungeon, entity, spawn.id, level, "normal", 1, source, "rom")) .toEqual(dungeonEnemyScaling(dungeon, entity, spawn.id, level, "normal", 1, source)); } } }); it("retains the level-80 cap and 79-to-80 native boss health plateau", () => { const entity = dungeon.entities[bossSpawn.entityId]; const at = (level: number) => dungeonEnemyScaling(dungeon, entity, boss.id, level, "normal", 1, "rom", "rom"); expect(at(79)).toEqual(at(80)); expect(at(120)).toEqual(at(80)); expect(at(80).level).toBe(80); }); it("does not compound damage during live registration or retain native pressure after a mode switch", () => { const entity = dungeon.entities[bossSpawn.entityId]; const native = dungeonEnemyScaling(dungeon, entity, boss.id, 80, "normal", 1, "rom", "rom"); const legacy = dungeonEnemyScaling(dungeon, entity, boss.id, 80, "normal", 1, "rom", "wow"); const combat = useCombatStore.getState(); combat.initializeCharacter({ classId: "rom-priest", raceId: "rom-human", level: 80, statRuleset: "rom" }); combat.registerMob("legacy-reference", { name: "Anglo", boss: true, ...legacy, xpReward: 0 }); combat.registerMob("live-registration", { name: "Anglo", boss: true, ...native, xpReward: 0 }); const first = useCombatStore.getState().mobs["live-registration"]; combat.registerMob("live-registration", { name: "Anglo", boss: true, ...native, xpReward: 0 }); expect(useCombatStore.getState().mobs["live-registration"].attackDamage).toBe(first.attackDamage); expect(useCombatStore.getState().mobs["live-registration"].attackPowerDamageContribution).toBe(first.attackPowerDamageContribution); combat.registerMob("live-registration", { name: "Anglo", boss: true, ...legacy, xpReward: 0 }); const switched = useCombatStore.getState().mobs["live-registration"]; const reference = useCombatStore.getState().mobs["legacy-reference"]; expect(switched.attackDamage).toBe(reference.attackDamage); expect(switched.attackPowerDamageContribution).toBe(reference.attackPowerDamageContribution); expect(switched.nativeDamageBudgetScale).toBe(1); }); it("keeps each difficulty multiplier and leaves trash independent of the boss slider", () => { const entity = dungeon.entities[bossSpawn.entityId]; const easy = dungeonEnemyScaling(dungeon, entity, boss.id, 30, "normal", 1, "rom", "rom"); for (let difficulty = 2; difficulty <= 10; difficulty += 1) { const scaled = dungeonEnemyScaling(dungeon, entity, boss.id, 30, "normal", difficulty, "rom", "rom"); expect(scaled.maxHealth).toBeCloseTo(easy.maxHealth * bossDifficultyMultipliers(difficulty).health); expect(scaled.damageMultiplier).toBeCloseTo(easy.damageMultiplier * bossDifficultyMultipliers(difficulty).damage); expect(dungeonEnemyScaling(dungeon, dungeon.entities[trashSpawn.entityId], trashSpawn.id, 30, "normal", difficulty, "rom", "rom")) .toEqual(dungeonEnemyScaling(dungeon, dungeon.entities[trashSpawn.entityId], trashSpawn.id, 30, "normal", 1, "rom", "rom")); } }); });