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

195 lines
7.2 KiB
TypeScript

import { beforeEach, describe, expect, it } from "vitest";
import { PLAYER_AGGRO_ID } from "./aggro";
import { applyAura } from "./combatAuras";
import { clearMobRuntimeRegistry, setMobPosition } from "./mobRuntimeRegistry";
import { clearPartyRuntimeRegistry, updatePartyRuntimePosition } from "./partyRuntimeRegistry";
import { useCombatStore, PLAYER_AURA_ENTITY_ID } from "./combatStore";
import { usePartyStore } from "./partyStore";
import type { EnemyAttackDefinition } from "./enemyAttacks";
import { WOW335_DRUID_PREDATORY_INSTINCTS_TALENT_ID } from "./wow335DruidPredatoryInstinctsRuntime";
const TARGET_ID = "druid-predatory-instincts-target";
const ENEMY_ID = "druid-predatory-instincts-area-enemy";
const AREA_ATTACK: EnemyAttackDefinition = {
id: "predatory-area-hit",
name: "Predatory Instincts Area Hit",
delivery: "area",
target: "nearby-party",
school: "fire",
animation: "cast",
castTimeMs: 0,
range: 80,
radius: 80,
cooldownMs: 10_000,
initialCooldownMs: [0, 0],
repeatCooldownMs: [10_000, 10_000],
damageMultiplier: 1,
};
function stableUnit(seed: string): number {
let hash = 2_166_136_261;
for (let index = 0; index < seed.length; index += 1) {
hash ^= seed.charCodeAt(index);
hash = Math.imul(hash, 16_777_619);
}
return (hash >>> 0) / 0x1_0000_0000;
}
function areaHitTime(actorId: string): number {
for (let now = 3_101; now < 20_000; now += 1) {
if (stableUnit(`${ENEMY_ID}:${AREA_ATTACK.id}:${actorId}:0:${now}:outcome`) >= 0.2) return now;
}
throw new Error(`Could not find an area-hit roll for ${actorId}`);
}
function catFormAura(actorId: string, now = 1_000) {
return {
definition: {
id: "wow335:768:cat-form",
name: "Cat Form",
disposition: "buff" as const,
durationMs: null,
},
sourceId: actorId,
targetId: actorId,
now,
};
}
function setCatForm(actorId: string): void {
useCombatStore.setState((state) => ({
auras: [...applyAura(state.auras, catFormAura(actorId)).auras],
}));
}
function registerCriticalTarget(): void {
useCombatStore.getState().registerMob(TARGET_ID, { name: TARGET_ID, maxHealth: 1_000_000, xpReward: 0 });
useCombatStore.setState((state) => ({
mobs: {
...state.mobs,
[TARGET_ID]: {
...state.mobs[TARGET_ID]!,
characterDefender: {
level: 80,
armor: 0,
dodgePercent: 0,
parryPercent: 0,
blockPercent: 0,
blockValue: 0,
},
},
},
}));
setMobPosition(TARGET_ID, [4, 0, 0]);
}
function criticalDamageForSource(sourceActorId: string, abilityId: string): number {
const member = usePartyStore.getState().members.find((candidate) => candidate.id === sourceActorId);
useCombatStore.getState().damageMob(TARGET_ID, 100, 2_000, {
actorId: sourceActorId,
role: member?.role ?? "damage",
abilityId,
}, "physical", {
attackKind: "melee",
seed: `druid-predatory-critical-${sourceActorId}-${abilityId}`,
abilityId,
criticalChanceBonusPercent: 1_000,
rules: { canMiss: false, canCrit: true, canDodge: false, canParry: false, canBlock: false },
});
return useCombatStore.getState().lastCombatResult!.damageBeforeMitigation;
}
function areaDamageForActor(actorKind: "player" | "companion", rank: number): number {
clearMobRuntimeRegistry();
clearPartyRuntimeRegistry();
useCombatStore.getState().initializeCharacter({ classId: "druid", level: 80, xp: 0 });
useCombatStore.getState().setPlayerPosition([0, 0, 0]);
usePartyStore.getState().initializeParty(`druid-predatory-area-${actorKind}`, 1, useCombatStore.getState().health);
const firstMember = usePartyStore.getState().members[0]!;
usePartyStore.setState((state) => ({
members: state.members.map((member, index) => index === 0
? {
...member,
classId: "druid",
talentRanks: { ...member.talentRanks, [WOW335_DRUID_PREDATORY_INSTINCTS_TALENT_ID]: rank },
}
: member),
}));
for (const member of usePartyStore.getState().members) updatePartyRuntimePosition(member.id, 0, 0, 0);
useCombatStore.getState().registerMob(ENEMY_ID, {
name: ENEMY_ID,
level: 80,
maxHealth: 1_000,
xpReward: 0,
attacks: [{
...AREA_ATTACK,
target: actorKind === "player" ? "primary" : "nearby-party",
}],
});
setMobPosition(ENEMY_ID, [2, 0, 0]);
useCombatStore.setState((state) => ({
talentRanks: { ...state.talentRanks, [WOW335_DRUID_PREDATORY_INSTINCTS_TALENT_ID]: rank },
mobs: { ...state.mobs, [ENEMY_ID]: { ...state.mobs[ENEMY_ID]!, attackDamage: 100 } },
}));
const actorId = actorKind === "player" ? PLAYER_AURA_ENTITY_ID : firstMember.id;
if (actorKind === "player") setCatForm(actorId);
else {
useCombatStore.setState((state) => ({ auras: [...applyAura(state.auras, catFormAura(actorId)).auras] }));
}
const healthBefore = actorKind === "player"
? useCombatStore.getState().health
: usePartyStore.getState().members.find((member) => member.id === actorId)!.health;
useCombatStore.getState().damageMob(ENEMY_ID, 1, 1_449);
useCombatStore.getState().advanceMobCombat(1_450);
useCombatStore.getState().advanceMobCombat(areaHitTime(actorId));
const healthAfter = actorKind === "player"
? useCombatStore.getState().health
: usePartyStore.getState().members.find((member) => member.id === actorId)!.health;
return healthBefore - healthAfter;
}
describe("WoW Druid Predatory Instincts integration", () => {
beforeEach(() => {
clearMobRuntimeRegistry();
clearPartyRuntimeRegistry();
useCombatStore.getState().initializeCharacter({ classId: "druid", level: 80, xp: 0 });
usePartyStore.getState().initializeParty("druid-predatory-critical-tests", 1, useCombatStore.getState().health);
});
it("applies the Cat Form critical-damage rank to the player and generated companion", () => {
registerCriticalTarget();
useCombatStore.setState((state) => ({
talentRanks: { ...state.talentRanks, [WOW335_DRUID_PREDATORY_INSTINCTS_TALENT_ID]: 3 },
}));
const playerWithoutCat = criticalDamageForSource(PLAYER_AGGRO_ID, "predatory-player-test");
setCatForm(PLAYER_AURA_ENTITY_ID);
const playerWithCat = criticalDamageForSource(PLAYER_AGGRO_ID, "predatory-player-test-cat");
const member = usePartyStore.getState().members[0]!;
usePartyStore.setState((state) => ({
members: state.members.map((candidate) => candidate.id === member.id ? {
...candidate,
classId: "druid",
talentRanks: { ...candidate.talentRanks, [WOW335_DRUID_PREDATORY_INSTINCTS_TALENT_ID]: 3 },
} : candidate),
}));
const companionWithoutCat = criticalDamageForSource(member.id, "predatory-companion-test");
setCatForm(member.id);
const companionWithCat = criticalDamageForSource(member.id, "predatory-companion-test-cat");
expect(playerWithoutCat).toBe(200);
expect(playerWithCat).toBe(215);
expect(companionWithoutCat).toBe(200);
expect(companionWithCat).toBe(215);
});
it.each(["player", "companion"] as const)("reduces only area attack damage for a Cat Form %s", (actorKind) => {
const withoutTalent = areaDamageForActor(actorKind, 0);
const withTalent = areaDamageForActor(actorKind, 3);
expect(withoutTalent).toBeGreaterThan(0);
expect(withTalent).toBe(Math.round(withoutTalent * 0.9));
});
});