Files
healer-man/src/game/romDruidRogueCurrentCoa.test.ts
T
2026-10-02 10:38:12 -04:00

139 lines
6.2 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { ROM_ALL_ABILITIES, ROM_ELITE_ABILITIES } from "./romAbilityCatalog";
import { channelAmountEffects, channelPulseAt, channelPulseCount } from "./combatChannelAmounts";
import { romAbilityRequiredTargetStateTag, romEffectsForCast, type RomCastMechanics } from "./romCombatMechanics";
import { ROM_DRUID_ROGUE_CORROSIVE_POISON_TAG } from "./romDruidRogueCurrentCoa";
const empty: RomCastMechanics = {
sealStacks: 0,
bleedCount: 0,
charged: false,
mainHandDagger: false,
casterMaxHealth: 10_000,
targetHealthPercent: 1,
};
function ability(spellId: number) {
const found = ROM_ALL_ABILITIES.find((entry) => entry.dbcSpellId === spellId
&& entry.classId === "rom-druid" && entry.secondaryClassId === "rom-rogue");
expect(found, `Druid/Rogue ability ${spellId}`).toBeDefined();
return found!;
}
describe("current CoA Druid/Rogue Necrotic Wound profile", () => {
it("uses the sourced gate, costs, channel, range, radius, and three full per-hit values", () => {
const wound = ability(494321);
expect(wound).toMatchObject({
name: "Necrotic Wound",
target: "hostile",
range: { min: 0, max: 15 },
radius: 8,
targeting: { shape: "circle", radius: 8 },
castMode: "channel",
castTimeMs: 1_500,
channelPulses: 3,
channelTickMs: 500,
cooldownMs: 0,
costs: [
{ resource: "mana", amount: 4, percentage: true },
{ resource: "nature-power", amount: 1 },
],
});
expect(romAbilityRequiredTargetStateTag(wound)).toBe(ROM_DRUID_ROGUE_CORROSIVE_POISON_TAG);
expect(romEffectsForCast(wound, { ...empty, naturePower: 1 })).toEqual([]);
const currentEffects = romEffectsForCast(wound, {
...empty,
naturePower: 1,
targetTags: [ROM_DRUID_ROGUE_CORROSIVE_POISON_TAG],
});
expect(currentEffects.filter((effect) => effect.kind === "apply-aura"
&& effect.aura.modifiers?.some((modifier) => modifier.kind === "healing" && modifier.direction === "taken")))
.toHaveLength(0);
expect(currentEffects.filter((effect) => effect.kind === "apply-aura" && effect.control?.kind === "root"))
.toHaveLength(0);
expect(currentEffects.filter((effect) => effect.kind === "damage"))
.toEqual([{ kind: "damage", coefficient: 3.297, school: "shadow" }]);
expect(currentEffects.filter((effect) => effect.kind === "utility" && effect.action === "class-script"))
.toEqual([expect.objectContaining({ triggerSpellId: 494024, durationMs: 12_000 })]);
expect(currentEffects.filter((effect) => effect.kind === "cooldown-reset"))
.toEqual([{ kind: "cooldown-reset", mode: "reduce", abilityName: "Sinister Request", amountMs: 1_000 }]);
const channelAmounts = channelAmountEffects(wound, 3);
expect(channelAmounts).toEqual([{ effect: currentEffects.find((effect) => effect.kind === "damage"), multiplier: 1 }]);
expect(channelPulseCount(wound)).toBe(3);
expect([0, 1, 2].map((pulse) => channelPulseAt(wound, 1_000, 1_500, pulse)))
.toEqual([1_500, 2_000, 2_500]);
});
it("replaces Corrosive Poison with the current +105, one-second Dark DoT and adds the spread prerequisite", () => {
const poison = ability(494024);
const dot = poison.effects.find((effect) => effect.kind === "dot");
expect(poison).toMatchObject({
range: { max: 15 },
castMode: "instant",
castTimeMs: 0,
cooldownMs: 1_000,
costs: [{ resource: "energy", amount: 25 }],
});
expect(dot).toMatchObject({
kind: "dot",
school: "shadow",
basePoints: 11_342,
sourceLevel: 105,
ticks: 12,
intervalMs: 1_000,
tag: ROM_DRUID_ROGUE_CORROSIVE_POISON_TAG,
});
});
it("adds the exact one-second cooldown rider to both successful casts", () => {
const cooldownRiders = [494022, 494321].map((spellId) => romEffectsForCast(ability(spellId), {
...empty,
naturePower: 1,
targetTags: spellId === 494321 ? [ROM_DRUID_ROGUE_CORROSIVE_POISON_TAG] : [],
}).filter((effect) => effect.kind === "cooldown-reset"));
expect(cooldownRiders).toEqual([
[{ kind: "cooldown-reset", mode: "reduce", abilityName: "Sinister Request", amountMs: 1_000 }],
[{ kind: "cooldown-reset", mode: "reduce", abilityName: "Sinister Request", amountMs: 1_000 }],
]);
const kneeUlcer = ROM_ELITE_ABILITIES.find((entry) => entry.dbcSpellId === 499944);
expect(kneeUlcer).toBeDefined();
const passiveTag = kneeUlcer!.effects.find((effect) => effect.kind === "apply-aura");
expect(passiveTag?.kind).toBe("apply-aura");
const withObsoletePassive = romEffectsForCast(ability(494321), {
...empty,
naturePower: 1,
targetTags: [ROM_DRUID_ROGUE_CORROSIVE_POISON_TAG],
passiveTags: passiveTag?.kind === "apply-aura" ? passiveTag.aura.utilityTags : [],
});
expect(withObsoletePassive.some((effect) => effect.kind === "apply-aura" && effect.control?.kind === "root"))
.toBe(false);
});
it("uses the current Sinister Request identity and does not leak the old Speed Catalysis aura", () => {
const request = ability(494023);
expect(request).toMatchObject({
name: "Sinister Request",
target: "self",
range: { max: 5 },
radius: 15,
cooldownMs: 15_000,
costs: [],
effects: expect.arrayContaining([
{ kind: "resource", amount: 100, resource: "mana", percentage: true, scope: "party" },
{ kind: "resource", amount: 100, resource: "rage", percentage: true, scope: "party" },
{ kind: "resource", amount: 100, resource: "focus", percentage: true, scope: "party" },
{ kind: "resource", amount: 100, resource: "energy", percentage: true, scope: "party" },
{ kind: "resource", amount: 100, resource: "psi", percentage: true, scope: "party" },
{ kind: "resource", amount: 100, resource: "nature-power", percentage: true, scope: "party" },
]),
});
expect(request.description).toContain("restores the MP, Rage, Focus, Energy, Psi, and Nature's Power");
expect(request.description).toContain("within radius 150");
expect(request.description).toContain("Cooldown: 15 seconds");
expect(request.description).not.toContain("attack and casting speed");
});
});