Files
healer-man/src/game/wow335WarlockMetamorphosisRuntime.test.ts
2026-09-30 09:57:13 -04:00

123 lines
5.6 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { applyAura, resolveDamageValue, resolveStatValue } from "./combatAuras";
import {
WOW335_WARLOCK_METAMORPHOSIS_AURA_TAG,
WOW335_WARLOCK_METAMORPHOSIS_FORM_ID,
WOW335_WARLOCK_METAMORPHOSIS_FORM_ABILITY_IDS,
warlockMetamorphosisCanCastAbilityWhileActive,
warlockMetamorphosisAbilityEligible,
warlockMetamorphosisFormActionReady,
wow335WarlockImmolationAttackPowerAura,
wow335WarlockImmolationAttackPowerReduction,
wow335WarlockMetamorphosisAuraForRank,
wow335WarlockMetamorphosisControlDuration,
wow335WarlockMetamorphosisEffectsForRank,
} from "./wow335WarlockMetamorphosisRuntime";
describe("WoW Warlock Metamorphosis profile", () => {
it("uses the generated 3.3.5a rank values and leaves unlearned ranks inactive", () => {
expect(wow335WarlockMetamorphosisEffectsForRank(0)).toBeNull();
expect(wow335WarlockMetamorphosisEffectsForRank(1)).toEqual({
rank: 1,
durationMs: 30_000,
cooldownMs: 180_000,
armorContributionPercent: 130,
damagePercent: 15,
meleeCritChanceTakenReductionPercent: 6,
stunSnareDurationReductionPercent: 25,
allowedSchools: ["fire", "shadow"],
pvpDamageBonusPercent: null,
});
expect(wow335WarlockMetamorphosisEffectsForRank(Number.NaN)).toBeNull();
});
it("allows only Fire/Shadow or explicitly shapeshift-usable abilities while active", () => {
expect(warlockMetamorphosisAbilityEligible(false, {})).toBe(true);
expect(warlockMetamorphosisAbilityEligible(true, { school: "fire" })).toBe(true);
expect(warlockMetamorphosisAbilityEligible(true, { school: "shadow" })).toBe(true);
expect(warlockMetamorphosisAbilityEligible(true, { schools: ["holy", "shadow"] })).toBe(true);
expect(warlockMetamorphosisAbilityEligible(true, {
school: "arcane",
usableWhileShapeshifted: true,
})).toBe(true);
expect(warlockMetamorphosisAbilityEligible(true, { school: "arcane" })).toBe(false);
expect(warlockMetamorphosisAbilityEligible(true, { school: "physical" })).toBe(false);
expect(warlockMetamorphosisAbilityEligible(true, {})).toBe(false);
expect(warlockMetamorphosisCanCastAbilityWhileActive({
id: "physical-strike", description: "Strikes for physical damage.", effects: [{ kind: "damage", coefficient: 1, school: "physical" }],
} as Parameters<typeof warlockMetamorphosisCanCastAbilityWhileActive>[0])).toBe(false);
expect(warlockMetamorphosisCanCastAbilityWhileActive({
id: "explicit-shadow", description: "Deals Shadow damage.", effects: [{ kind: "damage", coefficient: 1 }],
} as Parameters<typeof warlockMetamorphosisCanCastAbilityWhileActive>[0])).toBe(true);
expect(warlockMetamorphosisCanCastAbilityWhileActive({
id: "physical-melee", description: "Usable while shapeshifted.", effects: [{ kind: "damage", coefficient: 1, school: "physical" }],
} as Parameters<typeof warlockMetamorphosisCanCastAbilityWhileActive>[0])).toBe(true);
expect(warlockMetamorphosisFormActionReady(WOW335_WARLOCK_METAMORPHOSIS_FORM_ABILITY_IDS[0], false)).toBe(false);
expect(warlockMetamorphosisFormActionReady(WOW335_WARLOCK_METAMORPHOSIS_FORM_ABILITY_IDS[0], true)).toBe(true);
});
it("creates a timed source-scoped aura with only supported armor and damage modifiers", () => {
const definition = wow335WarlockMetamorphosisAuraForRank(1);
expect(definition).toMatchObject({
id: "wow335:warlock:metamorphosis",
durationMs: 30_000,
uniqueBySource: true,
utilityTags: ["shapeshift", WOW335_WARLOCK_METAMORPHOSIS_AURA_TAG],
});
expect(wow335WarlockMetamorphosisAuraForRank(0)).toBeNull();
expect(WOW335_WARLOCK_METAMORPHOSIS_FORM_ID).toBe(22);
const active = applyAura([], {
definition: definition!,
sourceId: "warlock-a",
targetId: "warlock-a",
now: 10_000,
}).aura;
expect(active.expiresAt).toBe(40_000);
expect(resolveStatValue(100, [active], "armor")).toBeCloseTo(230);
expect(resolveDamageValue(100, [active], {
direction: "dealt",
school: "shadow",
attackKind: "spell",
})).toBeCloseTo(115);
expect(resolveDamageValue(100, [active], {
direction: "dealt",
school: "physical",
attackKind: "melee",
})).toBeCloseTo(115);
expect(definition?.modifiers).toHaveLength(3);
expect(resolveStatValue(100, [active], "incoming-melee-critical-strike")).toBe(94);
});
it("keeps two Warlocks' form auras source-scoped", () => {
const definition = wow335WarlockMetamorphosisAuraForRank(1)!;
const first = applyAura([], {
definition,
sourceId: "warlock-a",
targetId: "warlock-a",
now: 1_000,
});
const second = applyAura(first.auras, {
definition,
sourceId: "warlock-b",
targetId: "warlock-b",
now: 1_500,
});
expect(second.auras).toHaveLength(2);
expect(second.auras.map((aura) => aura.sourceId)).toEqual(["warlock-a", "warlock-b"]);
});
it("uses the Immolation AP formula and reduces only sourced stun, root, and slow durations", () => {
expect(wow335WarlockImmolationAttackPowerReduction(1)).toBe(2);
expect(wow335WarlockImmolationAttackPowerReduction(80)).toBe(357);
expect(wow335WarlockImmolationAttackPowerAura(80).modifiers).toEqual([
{ kind: "stat", stat: "attack-power", operation: "flat", value: -357 },
]);
for (const mechanic of ["stun", "root", "slow"]) {
expect(wow335WarlockMetamorphosisControlDuration(10_000, mechanic, true)).toBe(7_500);
}
expect(wow335WarlockMetamorphosisControlDuration(10_000, "fear", true)).toBe(10_000);
expect(wow335WarlockMetamorphosisControlDuration(10_000, "stun", false)).toBe(10_000);
});
});