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