forked from phenom/healer-man
169 lines
8.1 KiB
TypeScript
169 lines
8.1 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { abilitiesForClass, type AbilityDefinition } from "./abilityCatalog";
|
|
import { applyAura, type ActiveAura } from "./combatAuras";
|
|
import {
|
|
coaWitchDoctorActiveIngredients,
|
|
coaWitchDoctorBrewingAbilityForCast,
|
|
coaWitchDoctorCanBrewIngredient,
|
|
coaWitchDoctorIngredientCapacity,
|
|
coaWitchDoctorIngredientIdForAbility,
|
|
COA_WITCH_DOCTOR_FROG_BONES_POTION_SHIELD,
|
|
COA_WITCH_DOCTOR_FROG_BONES_SPLASH_SHIELD,
|
|
} from "./coaWitchDoctorBrewingRuntime";
|
|
|
|
const casterId = "witch-doctor-caster";
|
|
|
|
function abilityByTalentEntry(talentEntryId: number): AbilityDefinition {
|
|
const ability = abilitiesForClass("witch-doctor").find((candidate) => candidate.talentEntryId === talentEntryId);
|
|
if (!ability) throw new Error(`Missing Witch Doctor talent action ${talentEntryId}`);
|
|
return ability;
|
|
}
|
|
|
|
function markerFromIngredient(ability: AbilityDefinition): Extract<AbilityDefinition["effects"][number], { kind: "apply-aura" }> {
|
|
const projected = coaWitchDoctorBrewingAbilityForCast(ability, [], casterId, 1_000);
|
|
const marker = projected.effects.find((effect) => effect.kind === "apply-aura"
|
|
&& effect.aura.id.startsWith("coa-witch-doctor:active-ingredient:"));
|
|
if (!marker || marker.kind !== "apply-aura") throw new Error(`Missing marker effect for ${ability.name}`);
|
|
return marker;
|
|
}
|
|
|
|
function activate(
|
|
ability: AbilityDefinition,
|
|
sourceId = casterId,
|
|
targetId = sourceId,
|
|
appliedAt = 1_000,
|
|
): ActiveAura {
|
|
const marker = markerFromIngredient(ability);
|
|
return applyAura([], { definition: marker.aura, sourceId, targetId, now: appliedAt }).aura;
|
|
}
|
|
|
|
describe("CoA Witch Doctor Brewing state and Potion Toss", () => {
|
|
it("uses actor-owned, non-stacking ingredient markers and preserves Frog Bones cauldron pulses", () => {
|
|
const frogBones = abilityByTalentEntry(29741);
|
|
const projected = coaWitchDoctorBrewingAbilityForCast(frogBones, [], casterId, 1_000);
|
|
const cauldronPulse = projected.effects.find((effect) => effect.kind === "apply-aura"
|
|
&& effect.sourceAuraType === 23);
|
|
|
|
expect(projected.effects.some((effect) => effect.kind === "shield")).toBe(false);
|
|
expect(projected.effects.some((effect) => effect.kind === "apply-aura" && effect.sourceAuraType === 4)).toBe(false);
|
|
expect(cauldronPulse).toMatchObject({
|
|
kind: "apply-aura",
|
|
aura: { maxStacks: 1, stackBehavior: "refresh" },
|
|
recipient: "caster",
|
|
});
|
|
expect(projected.effects.filter((effect) => effect.kind === "apply-aura"
|
|
&& effect.aura.id === "coa-witch-doctor:active-ingredient:frog-bones")).toHaveLength(1);
|
|
|
|
const marker = markerFromIngredient(frogBones);
|
|
expect(marker.aura).toMatchObject({
|
|
durationMs: null,
|
|
maxStacks: 1,
|
|
stackBehavior: "refresh",
|
|
hideFromAuraStrip: true,
|
|
utilityTags: ["coa-witch-doctor-active-ingredient:frog-bones"],
|
|
});
|
|
expect(marker.aura.modifiers ?? []).toEqual([]);
|
|
expect(marker.aura.procs ?? []).toEqual([]);
|
|
});
|
|
|
|
it("gates new ingredients at the sourced one-or-two capacity without replacing a full brew", () => {
|
|
const jungleShrooms = abilityByTalentEntry(12645);
|
|
const frogBones = abilityByTalentEntry(29741);
|
|
const bloodthistle = abilityByTalentEntry(6021);
|
|
const jungle = activate(jungleShrooms);
|
|
const ranks = { "coa-entry-6645": 1 };
|
|
|
|
expect(coaWitchDoctorIngredientCapacity({})).toBe(1);
|
|
expect(coaWitchDoctorIngredientCapacity(ranks)).toBe(2);
|
|
expect(coaWitchDoctorActiveIngredients([jungle], casterId, 2_000)).toEqual(new Set(["jungle-shrooms"]));
|
|
expect(coaWitchDoctorCanBrewIngredient(jungleShrooms, [jungle], casterId, {}, 2_000)).toBe(true);
|
|
expect(coaWitchDoctorCanBrewIngredient(frogBones, [jungle], casterId, {}, 2_000)).toBe(false);
|
|
expect(coaWitchDoctorCanBrewIngredient(frogBones, [jungle], casterId, ranks, 2_000)).toBe(true);
|
|
expect(coaWitchDoctorCanBrewIngredient(bloodthistle, [jungle, activate(frogBones)], casterId, ranks, 2_000))
|
|
.toBe(false);
|
|
});
|
|
|
|
it("identifies only the four exact Ingredient cast actions", () => {
|
|
expect(coaWitchDoctorIngredientIdForAbility(abilityByTalentEntry(12645))).toBe("jungle-shrooms");
|
|
expect(coaWitchDoctorIngredientIdForAbility(abilityByTalentEntry(29741))).toBe("frog-bones");
|
|
expect(coaWitchDoctorIngredientIdForAbility(abilityByTalentEntry(6021))).toBe("bloodthistle");
|
|
const knownAbility = abilityByTalentEntry(29741);
|
|
const fishOil = { ...knownAbility, dbcSpellId: 801662, talentEntryId: undefined };
|
|
expect(coaWitchDoctorIngredientIdForAbility(fishOil)).toBe("fish-oil");
|
|
expect(coaWitchDoctorIngredientIdForAbility({ ...fishOil, talentEntryId: 6645 })).toBeNull();
|
|
expect(coaWitchDoctorIngredientIdForAbility({ ...fishOil, classId: "mage" })).toBeNull();
|
|
expect(coaWitchDoctorIngredientIdForAbility({ ...knownAbility, talentEntryId: 29_740 })).toBeNull();
|
|
});
|
|
|
|
it("counts only this caster's live ingredient markers", () => {
|
|
const frogBones = abilityByTalentEntry(29741);
|
|
const marker = activate(frogBones);
|
|
const wrongSource = activate(frogBones, "another-witch-doctor", casterId);
|
|
const wrongTarget = activate(frogBones, casterId, "another-witch-doctor");
|
|
const expired = { ...activate(frogBones), expiresAt: 2_000 };
|
|
|
|
expect(coaWitchDoctorActiveIngredients(
|
|
[marker, wrongSource, wrongTarget, expired], casterId, 2_000,
|
|
)).toEqual(new Set(["frog-bones"]));
|
|
expect(coaWitchDoctorActiveIngredients([wrongSource, wrongTarget], casterId, 1_500).size).toBe(0);
|
|
});
|
|
|
|
it("adds only the sourced eight-second Frog Bones shield to Potion Toss's selected unit heal", () => {
|
|
const toss = abilityByTalentEntry(12646);
|
|
const frogBones = abilityByTalentEntry(29741);
|
|
const baseHeal = toss.effects.find((effect) => effect.kind === "heal");
|
|
const activeMarker = activate(frogBones);
|
|
const projected = coaWitchDoctorBrewingAbilityForCast(toss, [activeMarker], casterId, 2_000);
|
|
const shields = projected.effects.filter((effect) => effect.kind === "shield");
|
|
|
|
expect(projected.target).toBe("friendly");
|
|
expect(projected.effects.find((effect) => effect.kind === "heal")).toBe(baseHeal);
|
|
expect(shields).toEqual([COA_WITCH_DOCTOR_FROG_BONES_POTION_SHIELD]);
|
|
expect(shields[0]).toMatchObject({
|
|
basePoints: 100,
|
|
dieSides: 1,
|
|
coefficient: 0,
|
|
pointsPerLevel: 5,
|
|
sourceLevel: 10,
|
|
spiritCoefficient: 0.35,
|
|
bonusPowerCoefficient: 0.8,
|
|
durationMs: 8_000,
|
|
});
|
|
expect("maxScalingLevel" in shields[0] ? shields[0].maxScalingLevel : undefined).toBeUndefined();
|
|
|
|
const foreignMarker = activate(frogBones, "another-witch-doctor");
|
|
const inactiveProjection = coaWitchDoctorBrewingAbilityForCast(toss, [foreignMarker], casterId, 2_000);
|
|
expect(inactiveProjection.effects.some((effect) => effect.kind === "shield")).toBe(false);
|
|
const otherIngredientMarker = activate(abilityByTalentEntry(6021));
|
|
const nonFrogProjection = coaWitchDoctorBrewingAbilityForCast(toss, [otherIngredientMarker], casterId, 2_000);
|
|
expect(nonFrogProjection.effects.some((effect) => effect.kind === "shield")).toBe(false);
|
|
});
|
|
|
|
it("projects only the verified Frog Bones rider on Splash Potion's area heal", () => {
|
|
const splash = abilityByTalentEntry(7128);
|
|
const frogBones = abilityByTalentEntry(29741);
|
|
const baseHeal = splash.effects.find((effect) => effect.kind === "heal");
|
|
const activeMarker = activate(frogBones);
|
|
const projected = coaWitchDoctorBrewingAbilityForCast(splash, [activeMarker], casterId, 2_000);
|
|
|
|
expect(baseHeal).toMatchObject({
|
|
kind: "heal",
|
|
basePoints: 54,
|
|
dieSides: 3,
|
|
pointsPerLevel: 1.3710700273513794,
|
|
sourceLevel: 17,
|
|
bonusPowerCoefficient: 0.224494,
|
|
spiritCoefficient: 0.08,
|
|
scope: "party",
|
|
radius: 10,
|
|
maximumTargets: 8,
|
|
});
|
|
expect(projected.effects).toEqual([baseHeal, COA_WITCH_DOCTOR_FROG_BONES_SPLASH_SHIELD]);
|
|
expect(projected.effects.some((effect) => effect.kind === "shield"
|
|
&& effect.basePoints === 25 && effect.durationMs === 8_000)).toBe(true);
|
|
|
|
const unmarked = coaWitchDoctorBrewingAbilityForCast(splash, [], casterId, 2_000);
|
|
expect(unmarked.effects).toEqual([baseHeal]);
|
|
});
|
|
});
|