Files
healer-man/src/game/enemyChainTargeting.test.ts
T
2026-10-06 22:25:34 -04:00

85 lines
2.6 KiB
TypeScript

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"]);
});
});