import { describe, expect, it } from "vitest"; import { selectEnemyChainTargets, type EnemyChainTargetCandidate, } from "./enemyChainTargeting"; const target = ( id: string, x: number, alive = true, ): EnemyChainTargetCandidate => ({ id, alive, position: [x, 0, 0], }); describe("enemy chain target selection", () => { it("keeps the primary first and selects the nearest target from each prior hit", () => { const primary = target("primary", 0); const hits = selectEnemyChainTargets({ primaryTarget: primary, candidates: [target("east", 6), target("west", -5), target("west-next", -13)], maximumTargets: 3, jumpRange: 10, damageMultiplierPerBounce: 0.6, hasLineOfSight: () => true, }); expect(hits.map(({ target: hit, jumpIndex, damageMultiplier }) => ({ id: hit.id, jumpIndex, damageMultiplier, }))).toEqual([ { id: "primary", jumpIndex: 0, damageMultiplier: 1 }, { id: "west", jumpIndex: 1, damageMultiplier: 0.6 }, { id: "west-next", jumpIndex: 2, damageMultiplier: 0.36 }, ]); }); it("skips dead, out-of-pool, and blocked candidates without repeating actor IDs", () => { const primary = target("primary", 0); const hits = selectEnemyChainTargets({ primaryTarget: primary, candidates: [ target("blocked", 1), target("first", 3), target("first", 3.1), target("dead", 3.2, false), target("second", 7), target("outside-search-pool", 41), ], maximumTargets: 4, jumpRange: 10, damageMultiplierPerBounce: 0.7, hasLineOfSight: (_source, destination) => destination[0] !== 1, }); expect(hits.map(({ target: hit }) => hit.id)).toEqual(["primary", "first", "second"]); expect(hits.map(({ damageMultiplier }) => damageMultiplier)).toEqual([1, 0.7, 0.7 * 0.7]); }); it("aborts for a dead primary and never replaces a living primary with a nearer candidate", () => { const deadPrimary = target("dead-primary", 0, false); expect(selectEnemyChainTargets({ primaryTarget: deadPrimary, candidates: [target("nearby", 0.1)], maximumTargets: 3, jumpRange: 10, damageMultiplierPerBounce: 0.6, hasLineOfSight: () => true, })).toEqual([]); const primary = target("primary", 0); const hits = selectEnemyChainTargets({ primaryTarget: primary, candidates: [target("nearby", 0.1)], maximumTargets: 3, jumpRange: 10, damageMultiplierPerBounce: 0.6, hasLineOfSight: () => true, }); expect(hits.map(({ target: hit }) => hit.id)).toEqual(["primary", "nearby"]); }); });