Files
healer-man/src/game/combatAnimation.test.ts
T
2026-08-30 14:16:56 -04:00

176 lines
7.2 KiB
TypeScript

import { describe, expect, it } from "vitest";
import "./abilityCatalog";
import {
clipsForAnimationIds,
parseWoWAnimationName,
selectCharacterBaseClip,
selectCharacterEventClip,
selectCharacterVerticalClip,
selectWoWAnimationVariation,
} from "./combatAnimation";
const clips = [
{ name: "Stand (ID 0 variation 0)" },
{ name: "Walk (ID 4 variation 0)" },
{ name: "Run (ID 5 variation 0)" },
{ name: "Attack1H (ID 17 variation 0)" },
{ name: "Special1H (ID 57 variation 0)" },
{ name: "ReadySpellDirected (ID 51 variation 0)" },
{ name: "SpellCastDirected (ID 53 variation 0)" },
{ name: "CombatWound (ID 9 variation 0)" },
{ name: "Death (ID 1 variation 0)" },
{ name: "AttackUnarmed (ID 16 variation 0)" },
{ name: "AttackUnarmed (ID 16 variation 1)" },
{ name: "JumpStart (ID 37 variation 0)" },
{ name: "Fall (ID 40 variation 0)" },
{ name: "JumpEnd (ID 39 variation 0)" },
{ name: "JumpLandRun (ID 133 variation 0)" },
{ name: "Ready1H (ID 26 variation 0)" },
{ name: "Ready2H (ID 27 variation 0)" },
{ name: "Ready2HL (ID 28 variation 0)" },
{ name: "ReadyBow (ID 29 variation 0)" },
{ name: "HoldBow (ID 109 variation 0)" },
];
describe("WoW character animation selection", () => {
it("parses AnimationData IDs and preserves labels and variations", () => {
expect(parseWoWAnimationName("AttackUnarmed (ID 16 variation 1)")).toEqual({
label: "AttackUnarmed",
id: 16,
variation: 1,
});
expect(parseWoWAnimationName("custom")).toEqual({ label: "custom", id: null, variation: 0 });
});
it("uses native movement IDs instead of fuzzy attack-name matches", () => {
expect(selectCharacterBaseClip(clips, "idle")).toBe(clips[0]);
expect(selectCharacterBaseClip(clips, "walk")).toBe(clips[1]);
expect(selectCharacterBaseClip(clips, "run")).toBe(clips[2]);
});
it("uses the native ready pose for the equipped weapon stance", () => {
expect(selectCharacterBaseClip(clips, "idle", "one-handed")).toBe(clips[15]);
expect(selectCharacterBaseClip(clips, "idle", "two-handed")).toBe(clips[16]);
expect(selectCharacterBaseClip(clips, "idle", "two-handed-left")).toBe(clips[17]);
expect(selectCharacterBaseClip(clips, "idle", "bow")).toBe(clips[19]);
expect(selectCharacterBaseClip(clips, "run", "bow")).toBe(clips[2]);
expect(selectCharacterBaseClip([
clips[0],
clips[15],
], "idle", "bow")).toBe(clips[15]);
const runewakerClips = [
{ name: "Stand - stand_idle01" },
{ name: "Idle - 1h_idle" },
{ name: "Idle - 2h_idle" },
{ name: "Idle - bow_idle" },
];
expect(selectCharacterBaseClip(runewakerClips, "idle", "one-handed"))
.toBe(runewakerClips[1]);
expect(selectCharacterBaseClip(runewakerClips, "idle", "two-handed-left"))
.toBe(runewakerClips[2]);
expect(selectCharacterBaseClip(runewakerClips, "idle", "bow"))
.toBe(runewakerClips[3]);
});
it("rotates every variation of the highest-priority available ID", () => {
expect(selectWoWAnimationVariation(clips, [16], 0)).toBe(clips[9]);
expect(selectWoWAnimationVariation(clips, [16], 1)).toBe(clips[10]);
expect(selectWoWAnimationVariation(clips, [16], 2)).toBe(clips[9]);
expect(clipsForAnimationIds(clips, [57, 17])).toEqual([clips[4], clips[3]]);
});
it("selects native rising, falling, and landing locomotion", () => {
expect(selectCharacterVerticalClip(clips, "rising", false)).toMatchObject({
clip: clips[11],
loop: false,
});
expect(selectCharacterVerticalClip(clips, "falling", false)).toMatchObject({
clip: clips[12],
loop: true,
});
expect(selectCharacterVerticalClip(clips, "landing", false)?.clip).toBe(clips[13]);
expect(selectCharacterVerticalClip(clips, "landing", true)?.clip).toBe(clips[14]);
});
it("maps hard-cast lifecycle events to ready and release clips", () => {
expect(selectCharacterEventClip(clips, "priest", {
revision: 1,
kind: "ability-start",
abilityId: "priest-smite",
})).toMatchObject({ clip: clips[5], loop: true, terminal: false });
expect(selectCharacterEventClip(clips, "priest", {
revision: 2,
kind: "ability-release",
abilityId: "priest-smite",
})).toMatchObject({ clip: clips[6], loop: false, terminal: false });
});
it("maps melee, wound, and terminal death events to native clips", () => {
expect(selectCharacterEventClip(clips, "warrior", { revision: 1, kind: "attack" }))
.toMatchObject({ clip: clips[4], loop: false });
expect(selectCharacterEventClip(clips, "warrior", { revision: 2, kind: "wound" }))
.toMatchObject({ clip: clips[7], loop: false });
expect(selectCharacterEventClip(clips, "warrior", { revision: 3, kind: "death" }))
.toMatchObject({ clip: clips[8], terminal: true });
});
it("uses native RuneWaker spell, bow, and melee clips for RoM characters", () => {
const runewakerClips = [
{ name: "Action - 1h_charge_up" },
{ name: "Attack - 1h_attack01" },
{ name: "Attack - 1h_attack02" },
{ name: "Action - bow_channel" },
{ name: "Action - bow_release" },
{ name: "Cast - casting_delay" },
{ name: "Cast - cast01" },
{ name: "Action - buff01" },
{ name: "Wound - hurt" },
{ name: "Death - death" },
{ name: "Action - jump_up" },
{ name: "Action - jump_down" },
{ name: "Action - jump_end_run" },
];
expect(selectCharacterEventClip(runewakerClips, "rom-mage", {
revision: 1,
kind: "ability-start",
abilityId: "rom-mage-primary-1",
})).toMatchObject({ clip: runewakerClips[5], loop: true, native: true });
expect(selectCharacterEventClip(runewakerClips, "rom-mage", {
revision: 2,
kind: "ability-release",
abilityId: "rom-mage-primary-1",
})).toMatchObject({ clip: runewakerClips[6], loop: false, native: true });
expect(selectCharacterEventClip(runewakerClips, "rom-mage", {
revision: 2,
kind: "ability-release",
abilityId: "rom-mage-primary-0",
})).toMatchObject({ clip: runewakerClips[7], loop: false, native: true });
expect(selectCharacterEventClip(runewakerClips, "rom-scout", {
revision: 3,
kind: "ability-release",
abilityId: "rom-scout-primary-1",
})).toMatchObject({ clip: runewakerClips[4], native: true });
expect(selectCharacterEventClip(runewakerClips, "rom-warrior", {
revision: 4,
kind: "ability-release",
abilityId: "rom-warrior-primary-1",
})).toMatchObject({ clip: runewakerClips[1], native: true });
expect(selectCharacterEventClip(runewakerClips, "rom-warrior", {
revision: 5,
kind: "wound",
})).toMatchObject({ clip: runewakerClips[8], native: true });
expect(selectCharacterEventClip(runewakerClips, "rom-warrior", {
revision: 6,
kind: "death",
})).toMatchObject({ clip: runewakerClips[9], terminal: true, native: true });
expect(selectCharacterVerticalClip(runewakerClips, "rising", false)?.clip)
.toBe(runewakerClips[10]);
expect(selectCharacterVerticalClip(runewakerClips, "falling", false)?.clip)
.toBe(runewakerClips[11]);
expect(selectCharacterVerticalClip(runewakerClips, "landing", true)?.clip)
.toBe(runewakerClips[12]);
});
});