176 lines
6.7 KiB
TypeScript
176 lines
6.7 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import type { AbilityDefinition, AbilityEffect } from "./abilityCatalog";
|
|
import {
|
|
ROM_COA_HOLY_POWER_EXPLOSION_ID,
|
|
ROM_COA_HOLY_SEAL_NO_DOT_ABILITY_IDS,
|
|
ROM_COA_BLOCKING_STANCE_ID,
|
|
ROM_COA_RESIST_ID,
|
|
ROM_LOCAL_ACTIVE_BLOCKING_STANCE_AURA_ID,
|
|
romCurrentCoaBlockingStanceModifiers,
|
|
romCurrentCoaHolyPowerExplosionThreatMultiplier,
|
|
romCurrentCoaResistModifiers,
|
|
romCurrentCoaThreatFactors,
|
|
withoutCurrentCoaHolySealPeriodicDamage,
|
|
} from "./romWarriorCurrentCoaMechanics";
|
|
|
|
const damage: AbilityEffect = { kind: "damage", coefficient: 1, school: "physical" };
|
|
const dot: AbilityEffect = { kind: "dot", coefficient: 0.25, ticks: 5, intervalMs: 2_000 };
|
|
const combo: AbilityEffect = { kind: "combo", amount: 1 };
|
|
|
|
const knightAction = { source: "runewaker", classId: "rom-knight" } as Pick<
|
|
AbilityDefinition,
|
|
"source" | "classId"
|
|
>;
|
|
|
|
describe("current CoA Warrior/Knight mechanics", () => {
|
|
it.each(ROM_COA_HOLY_SEAL_NO_DOT_ABILITY_IDS)(
|
|
"removes the obsolete Holy Seal periodic damage from %i while preserving hit and combo effects",
|
|
(sourceSkillId) => {
|
|
const effects: AbilityEffect[] = [damage, dot, combo];
|
|
|
|
expect(withoutCurrentCoaHolySealPeriodicDamage(sourceSkillId, effects)).toEqual([damage, combo]);
|
|
},
|
|
);
|
|
|
|
it("leaves unrelated ability effects and non-Holy-Seal periodic effects unchanged", () => {
|
|
const effects: AbilityEffect[] = [damage, dot, combo];
|
|
expect(withoutCurrentCoaHolySealPeriodicDamage(490152, effects)).toBe(effects);
|
|
});
|
|
|
|
it("applies Holy Power Explosion's 2.98 threat multiplier to Knight actions only when learned", () => {
|
|
expect(romCurrentCoaHolyPowerExplosionThreatMultiplier(knightAction, [ROM_COA_HOLY_POWER_EXPLOSION_ID]))
|
|
.toBe(2.98);
|
|
expect(romCurrentCoaHolyPowerExplosionThreatMultiplier(knightAction, [])).toBe(1);
|
|
expect(romCurrentCoaHolyPowerExplosionThreatMultiplier(
|
|
{ source: "runewaker", classId: "rom-warrior" },
|
|
[ROM_COA_HOLY_POWER_EXPLOSION_ID],
|
|
)).toBe(1);
|
|
expect(romCurrentCoaHolyPowerExplosionThreatMultiplier(
|
|
{ source: "core", classId: "rom-knight" },
|
|
[ROM_COA_HOLY_POWER_EXPLOSION_ID],
|
|
)).toBe(1);
|
|
});
|
|
|
|
it("keeps each CoA aggro factor separate and leaves their overlap unmodeled", () => {
|
|
const ability = { source: "runewaker" as const, classId: "rom-knight" as const };
|
|
const holyPower = romCurrentCoaThreatFactors({
|
|
ability,
|
|
classId: "rom-knight",
|
|
learnedPassiveSpellIds: [490172],
|
|
blockingStanceAuraActive: false,
|
|
});
|
|
expect(holyPower).toMatchObject({ holyPowerExplosionMultiplier: 2.98, resolvedMultiplier: 2.98, overlapUnresolved: false });
|
|
|
|
const blocking = romCurrentCoaThreatFactors({
|
|
ability: { source: "runewaker", classId: "rom-warrior" },
|
|
classId: "rom-warrior",
|
|
secondaryClassId: "rom-knight",
|
|
learnedPassiveSpellIds: [493287],
|
|
blockingStanceAuraActive: true,
|
|
});
|
|
expect(blocking).toMatchObject({ blockingStanceMultiplier: 1.754, resolvedMultiplier: 1.754, overlapUnresolved: false });
|
|
|
|
const resist = romCurrentCoaThreatFactors({
|
|
ability: { source: "runewaker", classId: "rom-warrior" },
|
|
classId: "rom-warrior",
|
|
secondaryClassId: "rom-knight",
|
|
learnedPassiveSpellIds: [493287, 493251],
|
|
blockingStanceAuraActive: true,
|
|
});
|
|
expect(resist).toMatchObject({ resistMultiplier: 1.8, resolvedMultiplier: 1, overlapUnresolved: true });
|
|
|
|
const overlapping = romCurrentCoaThreatFactors({
|
|
ability,
|
|
classId: "rom-warrior",
|
|
secondaryClassId: "rom-knight",
|
|
learnedPassiveSpellIds: [490172, 493287, 493251],
|
|
blockingStanceAuraActive: true,
|
|
});
|
|
expect(overlapping).toMatchObject({
|
|
holyPowerExplosionMultiplier: 2.98,
|
|
blockingStanceMultiplier: 1.754,
|
|
resistMultiplier: 1.8,
|
|
resolvedMultiplier: 1,
|
|
overlapUnresolved: true,
|
|
});
|
|
});
|
|
|
|
it("enables Resist's +80% aggro and 20% physical reduction only for learned Warrior/Knight under CoA stance", () => {
|
|
expect(romCurrentCoaResistModifiers({
|
|
classId: "rom-warrior",
|
|
secondaryClassId: "rom-knight",
|
|
learnedPassiveSpellIds: [ROM_COA_RESIST_ID, ROM_COA_BLOCKING_STANCE_ID],
|
|
blockingStanceAuraActive: true,
|
|
})).toEqual({ threatMultiplier: 1.8, physicalDamageTakenMultiplier: 0.8 });
|
|
});
|
|
|
|
it("applies CoA Blocking Stance's sourced static modifiers only while its passive and active aura are both present", () => {
|
|
const stance = {
|
|
classId: "rom-warrior" as const,
|
|
secondaryClassId: "rom-knight" as const,
|
|
learnedPassiveSpellIds: [ROM_COA_BLOCKING_STANCE_ID],
|
|
blockingStanceAuraActive: true,
|
|
};
|
|
expect(romCurrentCoaBlockingStanceModifiers(stance)).toEqual({
|
|
parryRateBonusPercent: 8,
|
|
receivedDamageReductionPercent: 18.9,
|
|
aggroBonusPercent: 75.4,
|
|
});
|
|
expect(romCurrentCoaBlockingStanceModifiers({
|
|
...stance,
|
|
learnedPassiveSpellIds: [],
|
|
})).toEqual({
|
|
parryRateBonusPercent: 0,
|
|
receivedDamageReductionPercent: 0,
|
|
aggroBonusPercent: 0,
|
|
});
|
|
expect(romCurrentCoaBlockingStanceModifiers({
|
|
...stance,
|
|
blockingStanceAuraActive: false,
|
|
})).toEqual({
|
|
parryRateBonusPercent: 0,
|
|
receivedDamageReductionPercent: 0,
|
|
aggroBonusPercent: 0,
|
|
});
|
|
});
|
|
|
|
it("exposes the stable local 491480 aura ID without treating it as CoA passive 493287", () => {
|
|
expect(ROM_LOCAL_ACTIVE_BLOCKING_STANCE_AURA_ID).toBe("rom-aura-491480-502007-0-parry");
|
|
expect(romCurrentCoaBlockingStanceModifiers({
|
|
classId: "rom-warrior",
|
|
secondaryClassId: "rom-knight",
|
|
learnedPassiveSpellIds: [],
|
|
blockingStanceAuraActive: true,
|
|
})).toEqual({
|
|
parryRateBonusPercent: 0,
|
|
receivedDamageReductionPercent: 0,
|
|
aggroBonusPercent: 0,
|
|
});
|
|
});
|
|
|
|
it("requires learned Resist, learned CoA Blocking Stance, the active stance aura, and Warrior/Knight", () => {
|
|
const baseline = { threatMultiplier: 1, physicalDamageTakenMultiplier: 1 };
|
|
const state = {
|
|
classId: "rom-warrior" as const,
|
|
secondaryClassId: "rom-knight" as const,
|
|
learnedPassiveSpellIds: [ROM_COA_RESIST_ID, ROM_COA_BLOCKING_STANCE_ID],
|
|
blockingStanceAuraActive: true,
|
|
};
|
|
|
|
expect(romCurrentCoaResistModifiers({ ...state, learnedPassiveSpellIds: [] })).toEqual(baseline);
|
|
expect(romCurrentCoaResistModifiers({
|
|
...state,
|
|
learnedPassiveSpellIds: [ROM_COA_RESIST_ID],
|
|
})).toEqual(baseline);
|
|
expect(romCurrentCoaResistModifiers({
|
|
...state,
|
|
blockingStanceAuraActive: false,
|
|
})).toEqual(baseline);
|
|
expect(romCurrentCoaResistModifiers({
|
|
...state,
|
|
classId: "rom-knight",
|
|
secondaryClassId: "rom-warrior",
|
|
})).toEqual(baseline);
|
|
});
|
|
});
|