94 lines
2.9 KiB
TypeScript
94 lines
2.9 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import {
|
|
ENEMY_SLOW_MOVEMENT_MULTIPLIER,
|
|
enemyControlMovementMultiplier,
|
|
enemyControlPreventsActions,
|
|
enemyControlPreventsMovement,
|
|
normalizeEnemyAttack,
|
|
type EnemyAttackDefinition,
|
|
} from "./enemyAttacks";
|
|
|
|
function attack(overrides: Partial<EnemyAttackDefinition>): EnemyAttackDefinition {
|
|
return {
|
|
id: "test-special",
|
|
name: "Test Special",
|
|
delivery: "area",
|
|
target: "nearby-party",
|
|
school: "physical",
|
|
animation: "attack",
|
|
range: 12,
|
|
cooldownMs: 10_000,
|
|
damageMultiplier: 1,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
describe("enemy attack telegraphs", () => {
|
|
it("infers a warned cone for legacy frontal mechanics", () => {
|
|
const normalized = normalizeEnemyAttack(attack({ name: "Stone Cleave" }));
|
|
expect(normalized.telegraph).toMatchObject({
|
|
shape: "cone",
|
|
anchor: "source",
|
|
warningMs: 1_650,
|
|
});
|
|
expect(normalized.castTimeMs).toBe(1_650);
|
|
});
|
|
|
|
it("preserves authored geometry and extends too-short cast warnings", () => {
|
|
const normalized = normalizeEnemyAttack(attack({
|
|
name: "Falling Ruin",
|
|
castTimeMs: 500,
|
|
telegraph: {
|
|
shape: "circle",
|
|
anchor: "target",
|
|
warningMs: 2_200,
|
|
radius: 7,
|
|
},
|
|
}));
|
|
expect(normalized.castTimeMs).toBe(2_200);
|
|
expect(normalized.telegraph).toEqual({
|
|
shape: "circle",
|
|
anchor: "target",
|
|
warningMs: 2_200,
|
|
radius: 7,
|
|
});
|
|
});
|
|
|
|
it("does not telegraph basic attacks or beneficial mechanics", () => {
|
|
expect(normalizeEnemyAttack(attack({ id: "basic-attack" })).telegraph).toBeUndefined();
|
|
expect(normalizeEnemyAttack(attack({
|
|
effect: { kind: "heal", multiplier: 1 },
|
|
})).telegraph).toBeUndefined();
|
|
expect(normalizeEnemyAttack(attack({
|
|
effect: {
|
|
kind: "composite",
|
|
effects: [{ kind: "summon", creatureEntry: 1, count: 1 }],
|
|
},
|
|
})).telegraph).toBeUndefined();
|
|
expect(normalizeEnemyAttack(attack({
|
|
effect: {
|
|
kind: "composite",
|
|
effects: [
|
|
{ kind: "damage", multiplier: 1 },
|
|
{ kind: "summon", creatureEntry: 1, count: 1 },
|
|
],
|
|
},
|
|
})).telegraph).toMatchObject({ shape: "circle" });
|
|
});
|
|
});
|
|
|
|
describe("enemy control movement", () => {
|
|
it("keeps slowed actors active while reducing movement", () => {
|
|
expect(enemyControlPreventsActions("slow")).toBe(false);
|
|
expect(enemyControlPreventsMovement("slow")).toBe(false);
|
|
expect(enemyControlMovementMultiplier("slow")).toBe(ENEMY_SLOW_MOVEMENT_MULTIPLIER);
|
|
});
|
|
|
|
it("keeps roots movement-only while preserving hard-control action locks", () => {
|
|
expect(enemyControlPreventsActions("root")).toBe(false);
|
|
expect(enemyControlPreventsMovement("root")).toBe(true);
|
|
expect(enemyControlMovementMultiplier("root")).toBe(1);
|
|
expect(enemyControlPreventsActions("stun")).toBe(true);
|
|
});
|
|
});
|