Release Healer Man 0.1.59
This commit is contained in:
1 parent
11934a3979
commit
ab1434a3b8
44 files changed
+1242
-103
No files matched your search
@@ -1940,32 +1940,124 @@ describe("combat store", () => {
|
||||
&& aura.definition.name.startsWith("Rupture"))?.expiresAt).toBe(19_000);
|
||||
});
|
||||
|
||||
it("lets Penance damage an enemy and continue pulsing", () => {
|
||||
it("channels Penance damage immediately and at one-second intervals without healing its caster", () => {
|
||||
useCombatStore.getState().initializeCharacter({ classId: "priest", level: 80 });
|
||||
unlockReadySpell("wow335-priest-penance");
|
||||
registerTarget("penance-target", [8, 0, 0], 100_000, 0);
|
||||
useCombatStore.getState().damagePlayer(500);
|
||||
const casterHealth = useCombatStore.getState().health;
|
||||
expect(useCombatStore.getState().castAbility("wow335-priest-penance", [0, 0, 0], 1_000).ok).toBe(true);
|
||||
expect(useCombatStore.getState().activeCast).toMatchObject({ mode: "channel", totalTicks: 3,
|
||||
completesAt: 3_000, nextTickAt: 1_000 });
|
||||
expect(useCombatStore.getState().cooldowns["wow335-priest-penance"]).toBeGreaterThan(1_000);
|
||||
expect(useCombatStore.getState().mobs["penance-target"].health).toBe(100_000);
|
||||
useCombatStore.getState().tick(0, 1_000);
|
||||
const first = useCombatStore.getState().mobs["penance-target"].health;
|
||||
expect(first).toBeLessThan(100_000);
|
||||
expect(useCombatStore.getState().health).toBe(casterHealth);
|
||||
useCombatStore.getState().tick(0, 1_999);
|
||||
expect(useCombatStore.getState().mobs["penance-target"].health).toBe(first);
|
||||
useCombatStore.getState().tick(0, 2_000);
|
||||
expect(useCombatStore.getState().mobs["penance-target"].health).toBeLessThan(first);
|
||||
useCombatStore.getState().tick(0, 3_000);
|
||||
expect(useCombatStore.getState().effects.some((effect) => effect.sourceAbilityId === "wow335-priest-penance")).toBe(false);
|
||||
expect(useCombatStore.getState().activeCast).toBeNull();
|
||||
const after = useCombatStore.getState().mobs["penance-target"].health;
|
||||
useCombatStore.getState().tick(0, 4_000);
|
||||
expect(useCombatStore.getState().mobs["penance-target"].health).toBe(after);
|
||||
});
|
||||
|
||||
it("lets Penance heal a friendly target immediately and twice afterward", () => {
|
||||
it("channels Penance healing and stops future pulses when interrupted", () => {
|
||||
useCombatStore.getState().initializeCharacter({ classId: "priest", level: 80 });
|
||||
unlockReadySpell("wow335-priest-penance");
|
||||
usePartyStore.getState().selectSelf();
|
||||
useCombatStore.getState().damagePlayer(2_000);
|
||||
const before = useCombatStore.getState().health;
|
||||
expect(useCombatStore.getState().castAbility("wow335-priest-penance", [0, 0, 0], 1_000).ok).toBe(true);
|
||||
useCombatStore.getState().tick(0, 1_000);
|
||||
const first = useCombatStore.getState().health;
|
||||
expect(first).toBeGreaterThan(before);
|
||||
useCombatStore.getState().tick(0, 2_000);
|
||||
expect(useCombatStore.getState().health).toBeGreaterThan(first);
|
||||
const interruptedAt = useCombatStore.getState().health;
|
||||
expect(useCombatStore.getState().cancelCast()).toBe(true);
|
||||
useCombatStore.getState().tick(0, 3_000);
|
||||
expect(useCombatStore.getState().effects.some((effect) => effect.sourceAbilityId === "wow335-priest-penance")).toBe(false);
|
||||
expect(useCombatStore.getState().health).toBe(interruptedAt);
|
||||
expect(useCombatStore.getState().activeCast).toBeNull();
|
||||
});
|
||||
|
||||
it("channels ground-area Volley over its selected point without a target or post-channel damage", () => {
|
||||
useCombatStore.getState().initializeCharacter({ classId: "hunter", level: 80 });
|
||||
registerTarget("volley-a", [10, 0, 0], 100_000, 0);
|
||||
registerTarget("volley-b", [12, 0, 0], 100_000, 0);
|
||||
registerTarget("volley-outside", [25, 0, 0], 100_000, 0);
|
||||
expect(useCombatStore.getState().castAbility("wow335-hunter-volley", {
|
||||
now: 1_000, origin: [0, 0, 0], targetActorId: null, groundPosition: [10, 0, 0],
|
||||
}).ok).toBe(true);
|
||||
expect(useCombatStore.getState().activeCast).toMatchObject({ mode: "channel", totalTicks: 6,
|
||||
nextTickAt: 2_000, completesAt: 7_000 });
|
||||
useCombatStore.getState().tick(0, 2_000);
|
||||
expect(useCombatStore.getState().mobs["volley-a"].health).toBeLessThan(100_000);
|
||||
expect(useCombatStore.getState().mobs["volley-b"].health).toBeLessThan(100_000);
|
||||
expect(useCombatStore.getState().mobs["volley-outside"].health).toBe(100_000);
|
||||
useCombatStore.getState().tick(0, 7_000);
|
||||
expect(useCombatStore.getState().activeCast).toBeNull();
|
||||
expect(useCombatStore.getState().areaEffects.some((effect) => effect.sourceAbilityId === "wow335-hunter-volley"))
|
||||
.toBe(false);
|
||||
const finalHealth = useCombatStore.getState().mobs["volley-a"].health;
|
||||
useCombatStore.getState().tick(0, 9_000);
|
||||
expect(useCombatStore.getState().mobs["volley-a"].health).toBe(finalHealth);
|
||||
});
|
||||
|
||||
it("applies Blizzard damage during its eight source-timed channel pulses", () => {
|
||||
useCombatStore.getState().initializeCharacter({ classId: "mage", level: 80 });
|
||||
registerTarget("blizzard-target", [10, 0, 0], 100_000, 0);
|
||||
expect(useCombatStore.getState().castAbility("wow335-mage-blizzard", {
|
||||
now: 1_000, origin: [0, 0, 0], targetActorId: null, groundPosition: [10, 0, 0],
|
||||
}).ok).toBe(true);
|
||||
expect(useCombatStore.getState().activeCast).toMatchObject({ mode: "channel", totalTicks: 8 });
|
||||
useCombatStore.getState().tick(0, 2_000);
|
||||
expect(useCombatStore.getState().mobs["blizzard-target"].health).toBeLessThan(100_000);
|
||||
useCombatStore.getState().tick(0, 9_000);
|
||||
expect(useCombatStore.getState().activeCast).toBeNull();
|
||||
expect(useCombatStore.getState().combatEvents.filter((event) => event.kind === "damage"
|
||||
&& event.abilityId === "wow335-mage-blizzard" && event.targetActorId === "blizzard-target"))
|
||||
.toHaveLength(8);
|
||||
expect(useCombatStore.getState().areaEffects.some((effect) => effect.sourceAbilityId === "wow335-mage-blizzard"))
|
||||
.toBe(false);
|
||||
expect(useCombatStore.getState().auras.some((aura) => aura.definition.name === "Blizzard"))
|
||||
.toBe(false);
|
||||
const finalHealth = useCombatStore.getState().mobs["blizzard-target"].health;
|
||||
useCombatStore.getState().tick(0, 11_000);
|
||||
expect(useCombatStore.getState().mobs["blizzard-target"].health).toBe(finalHealth);
|
||||
});
|
||||
|
||||
it.each([
|
||||
["warlock", "wow335-warlock-rain-of-fire", 4, 2_000],
|
||||
["druid", "wow335-druid-hurricane", 10, 1_000],
|
||||
] as const)("applies %s ground-channel damage only during its pulses", (classId, abilityId, pulses, intervalMs) => {
|
||||
useCombatStore.getState().initializeCharacter({ classId, level: 80 });
|
||||
expect(useCombatStore.getState().abilities.find((ability) => ability.id === abilityId))
|
||||
.toMatchObject({ castMode: "channel", castTimeMs: pulses * intervalMs, channelPulses: pulses });
|
||||
registerTarget("channel-target", [10, 0, 0], 100_000, 0);
|
||||
expect(abilityAtLevel(useCombatStore.getState().abilities.find((ability) => ability.id === abilityId)!, 80))
|
||||
.toMatchObject({ castMode: "channel", castTimeMs: pulses * intervalMs, channelPulses: pulses });
|
||||
expect(useCombatStore.getState().castAbility(abilityId, {
|
||||
now: 1_000, origin: [0, 0, 0], targetActorId: null, groundPosition: [10, 0, 0],
|
||||
}).ok).toBe(true);
|
||||
expect(useCombatStore.getState().activeCast).toMatchObject({ mode: "channel", totalTicks: pulses,
|
||||
nextTickAt: 1_000 + intervalMs });
|
||||
useCombatStore.getState().tick(0, 1_000 + intervalMs);
|
||||
expect(useCombatStore.getState().mobs["channel-target"].health).toBeLessThan(100_000);
|
||||
useCombatStore.getState().tick(0, 1_000 + pulses * intervalMs);
|
||||
expect(useCombatStore.getState().activeCast).toBeNull();
|
||||
expect(useCombatStore.getState().combatEvents.filter((event) => event.kind === "damage"
|
||||
&& event.abilityId === abilityId && event.targetActorId === "channel-target"))
|
||||
.toHaveLength(pulses);
|
||||
expect(useCombatStore.getState().areaEffects.some((effect) => effect.sourceAbilityId === abilityId))
|
||||
.toBe(false);
|
||||
const finalHealth = useCombatStore.getState().mobs["channel-target"].health;
|
||||
useCombatStore.getState().tick(0, 2_000 + pulses * intervalMs);
|
||||
expect(useCombatStore.getState().mobs["channel-target"].health).toBe(finalHealth);
|
||||
});
|
||||
|
||||
it("selects only the three lowest-health nearby Divine Hymn targets per two-second pulse", () => {
|
||||
|
||||
Reference in new issue
Block a user