114 lines
5.9 KiB
TypeScript
114 lines
5.9 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { applyAura, dispelAuras, resolveDamageValue } from "./combatAuras";
|
|
import {
|
|
enemyDisarmBlocksCombatChannel,
|
|
enemyDisarmCanApplyToTarget,
|
|
enemyDisarmInvalidatesAbilityRequirement,
|
|
enemyDisarmWeaponChannels,
|
|
enemyWeaponDebuffAuraDefinition,
|
|
enemyWeaponDebuffContract,
|
|
} from "./enemyWeaponDebuffs";
|
|
|
|
describe("pinned enemy weapon debuffs", () => {
|
|
it("pins Disarm's main-hand/ranged rows and per-profile hit and duration contract", () => {
|
|
const ascension = enemyWeaponDebuffContract(6713, "ascension")!;
|
|
const epoch = enemyWeaponDebuffContract(6713, "epoch")!;
|
|
|
|
expect(ascension).toMatchObject({
|
|
spellMechanic: 3,
|
|
signature: {
|
|
schoolMask: 1,
|
|
damageClass: 2,
|
|
durationMs: 5_000,
|
|
effects: [
|
|
{ effectType: 6, auraType: 67, basePoints: 0, implicitTargetA: 6, miscValue: 0 },
|
|
{ effectType: 6, auraType: 278, basePoints: 1, implicitTargetA: 6, miscValue: 0 },
|
|
],
|
|
},
|
|
durationMs: 5_000,
|
|
dispelType: 0,
|
|
stackAmount: 0,
|
|
casterWeaponRequirement: { itemClass: -1, subclassMask: 0, inventoryMask: 0 },
|
|
applicationGate: "landed-melee-hit",
|
|
weaponChannels: ["main-hand", "ranged"],
|
|
});
|
|
expect(epoch.signature.effects).toEqual([
|
|
{ effectType: 6, auraType: 67, basePoints: 0, mechanic: 0, implicitTargetA: 6, implicitTargetB: 0, radius: 0, miscValue: 0, periodMs: 0 },
|
|
]);
|
|
expect(enemyDisarmWeaponChannels("ascension")).toEqual(["main-hand", "ranged"]);
|
|
expect(enemyDisarmWeaponChannels("epoch")).toEqual(["main-hand"]);
|
|
expect(enemyWeaponDebuffContract(999_999, "ascension")).toBeUndefined();
|
|
});
|
|
|
|
it("requires an affected target weapon and leaves off-hand and casting channels alone", () => {
|
|
const offHandOnly = { mainHand: false, offHand: true, ranged: false };
|
|
expect(enemyDisarmCanApplyToTarget("ascension", offHandOnly)).toBe(false);
|
|
expect(enemyDisarmCanApplyToTarget("epoch", offHandOnly)).toBe(false);
|
|
expect(enemyDisarmCanApplyToTarget("ascension", { ...offHandOnly, ranged: true })).toBe(true);
|
|
expect(enemyDisarmCanApplyToTarget("epoch", { ...offHandOnly, ranged: true })).toBe(false);
|
|
expect(enemyDisarmCanApplyToTarget("ascension", { mainHand: true, offHand: false, ranged: false, feralForm: true })).toBe(false);
|
|
|
|
expect(enemyDisarmBlocksCombatChannel("ascension", "main-hand")).toBe(true);
|
|
expect(enemyDisarmBlocksCombatChannel("ascension", "ranged")).toBe(true);
|
|
expect(enemyDisarmBlocksCombatChannel("ascension", "off-hand")).toBe(false);
|
|
expect(enemyDisarmBlocksCombatChannel("ascension", "spell-cast")).toBe(false);
|
|
expect(enemyDisarmBlocksCombatChannel("epoch", "ranged")).toBe(false);
|
|
});
|
|
|
|
it("blocks an explicit weapon requirement only when Disarm disables every matching equipped slot", () => {
|
|
const daggerRequirement = { equippedItemClass: 2, equippedItemSubclassMask: 1 << 15 };
|
|
const mainHandDagger = [{ channel: "main-hand" as const, itemClass: 2, subclass: 15 }];
|
|
const mainHandAndOffHandDaggers = [
|
|
...mainHandDagger,
|
|
{ channel: "off-hand" as const, itemClass: 2, subclass: 15 },
|
|
];
|
|
|
|
expect(enemyDisarmInvalidatesAbilityRequirement("ascension", daggerRequirement, mainHandDagger)).toBe(true);
|
|
expect(enemyDisarmInvalidatesAbilityRequirement("epoch", daggerRequirement, mainHandDagger)).toBe(true);
|
|
expect(enemyDisarmInvalidatesAbilityRequirement("ascension", daggerRequirement, mainHandAndOffHandDaggers)).toBe(false);
|
|
expect(enemyDisarmInvalidatesAbilityRequirement("ascension", { ...daggerRequirement, equippedItemSubclassMask: 1 << 4 }, mainHandDagger)).toBe(false);
|
|
expect(enemyDisarmInvalidatesAbilityRequirement("ascension", { equippedItemClass: -1, equippedItemSubclassMask: 0 }, mainHandDagger)).toBe(false);
|
|
});
|
|
|
|
it("pins Infected Wound as flat physical damage taken, pre-mitigation, disease dispel, and refresh", () => {
|
|
for (const profile of ["ascension", "epoch"] as const) {
|
|
const contract = enemyWeaponDebuffContract(17230, profile)!;
|
|
expect(contract).toMatchObject({
|
|
signature: {
|
|
schoolMask: 8,
|
|
damageClass: 0,
|
|
durationMs: 300_000,
|
|
effects: [{ effectType: 6, auraType: 14, basePoints: 6, implicitTargetA: 6, miscValue: 1 }],
|
|
},
|
|
durationMs: 300_000,
|
|
dispelType: 3,
|
|
dispelCategory: "disease",
|
|
stackAmount: 0,
|
|
casterWeaponRequirement: { itemClass: -1, subclassMask: 0, inventoryMask: 0 },
|
|
applicationGate: "aura-only-no-miss-roll",
|
|
modifiers: [{ kind: "damage", direction: "taken", school: "physical", operation: "flat", value: 6, phase: "pre-mitigation" }],
|
|
});
|
|
}
|
|
|
|
const definition = enemyWeaponDebuffAuraDefinition(enemyWeaponDebuffContract(17230, "ascension")!);
|
|
let auras = applyAura([], { definition, sourceId: "infected-caster-a", targetId: "player", now: 100 }).auras;
|
|
auras = applyAura(auras, { definition, sourceId: "infected-caster-b", targetId: "player", now: 1_000 }).auras;
|
|
expect(auras).toHaveLength(1);
|
|
expect(auras[0]).toMatchObject({ sourceId: "infected-caster-b", appliedAt: 1_000, expiresAt: 301_000 });
|
|
expect(resolveDamageValue(100, auras, {
|
|
direction: "taken", school: "physical", attackKind: "melee", phase: "pre-mitigation",
|
|
})).toBe(106);
|
|
expect(resolveDamageValue(100, auras, {
|
|
direction: "taken", school: "fire", attackKind: "spell", phase: "pre-mitigation",
|
|
})).toBe(100);
|
|
expect(resolveDamageValue(100, auras, {
|
|
direction: "taken", school: "physical", attackKind: "melee",
|
|
})).toBe(100);
|
|
|
|
expect(dispelAuras(auras, { relationship: "friendly", categories: ["poison"] }).removed).toHaveLength(0);
|
|
const diseaseDispel = dispelAuras(auras, { relationship: "friendly", categories: ["disease"] });
|
|
expect(diseaseDispel.removed).toHaveLength(1);
|
|
expect(diseaseDispel.auras).toHaveLength(0);
|
|
});
|
|
});
|