import { describe, expect, it } from "vitest"; import { createClassInventory } from "./healers"; import { AVAILABLE_BOSS_IDS } from "./bossCatalog"; import { canAddBossToEncounter } from "./bossSelection"; import { useGameStore } from "./store"; import type { BossId } from "./types"; interface BattleResult { phase: ReturnType["phase"]; time: number; damageBySource: ReturnType["partyCombat"]["combatants"]; } function simulateControlledBattle(bossIds: readonly [BossId, BossId], maxSeconds = 200): BattleResult { useGameStore.getState().configureHealer("priest", "Aelia", createClassInventory("priest"), bossIds); useGameStore.getState().startEncounter(); while (useGameStore.getState().phase === "combat" && useGameStore.getState().time < maxSeconds) { useGameStore.setState((state) => ({ party: state.party.map((member) => ({ ...member, hp: member.maxHp, absorb: 10_000 })), })); useGameStore.getState().tick(0.1); } const state = useGameStore.getState(); return { phase: state.phase, time: state.time, damageBySource: state.partyCombat.combatants }; } describe("full-mechanics dual-boss battle simulations", () => { const combinations: readonly (readonly [BossId, BossId])[] = AVAILABLE_BOSS_IDS.flatMap((first, index) => AVAILABLE_BOSS_IDS.slice(index + 1) .filter((second) => canAddBossToEncounter([first], second)) .map((second) => [first, second] as const), ); it.each(combinations)("party rotations defeat %s + %s", (first, second) => { const result = simulateControlledBattle([first, second]); expect(result.phase).toBe("victory"); expect(result.time).toBeGreaterThanOrEqual(50); expect(result.time).toBeLessThanOrEqual(100); for (const memberId of ["brann", "nia", "orin", "vale"] as const) expect(result.damageBySource[memberId].damageDone).toBeGreaterThan(0); }); });