Fix aura tooltip healing and cold-start WoW talent gates

Track phenom/healer-man#1 and Codex/healer-man-godot#160.
Coordinator /root; session 01a11cd3-fa7c-7510-ba8f-bc7a3d0310a6.
Source implementation: single_pass_source, GPT-6 Luna xhigh.
Independent source/native validation: single_pass_validation and single_pass_renderer, GPT-6 Luna xhigh.
This commit is contained in:
warren committed 2026-10-09 15:16:21 -04:00
1 parent ff20ab8779
commit fc21cbca9b
3 files changed
+153 -4

No files matched your search

+83
View File
@@ -1,8 +1,12 @@
import { execFileSync } from "node:child_process";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { describe, expect, it } from "vitest";
import {
abilityAtLevel,
abilityById,
isAbilityUnlocked,
learnedAbilityForBuild,
preloadAbilityCatalogsForClasses,
type AbilityDefinition,
type AbilityEffect,
@@ -183,6 +187,85 @@ describe("WoW 3.3.5 executable effect mapping", () => {
expect(livingBomb?.ranks.find((rank) => rank.spellId === 55_359)?.description).toContain("512 Fire damage");
});
it("keeps DBC aura and trigger healing tooltips out of direct-heal semantics", () => {
const vampiricEmbrace = byId("wow335-priest-vampiric-embrace");
const sourceVampiricEmbrace = generatedData.classes.priest.find((chain) => chain.name === "Vampiric Embrace");
const sourceRank = sourceVampiricEmbrace?.ranks.find((rank) => rank.spellId === 15_286);
expect(sourceRank?.effects).toContainEqual(expect.objectContaining({
effectType: 6,
auraType: 4,
implicitTargetA: 6,
triggerSpellId: 0,
}));
expect(vampiricEmbrace).toMatchObject({ target: "hostile", talentNodeId: "talent-484" });
expect(effectsOfKind(vampiricEmbrace, "heal")).toEqual([]);
expect(effectsOfKind(vampiricEmbrace, "damage")).toEqual([]);
expect(effectsOfKind(vampiricEmbrace, "apply-aura")).toContainEqual(expect.objectContaining({
sourceEffectType: 6,
sourceAuraType: 4,
aura: expect.objectContaining({ disposition: "debuff" }),
}));
const judgementOfLight = byId("wow335-paladin-judgement-of-light");
const sourceJudgementOfLight = generatedData.classes.paladin.find((chain) => chain.name === "Judgement of Light");
expect(sourceJudgementOfLight?.ranks[0]?.effects).toContainEqual(expect.objectContaining({
effectType: 6,
auraType: 4,
triggerSpellId: 0,
}));
expect(effectsOfKind(judgementOfLight, "heal")).toEqual([]);
expect(effectsOfKind(judgementOfLight, "damage")).toEqual([]);
expect(effectsOfKind(judgementOfLight, "apply-aura")).toEqual(expect.arrayContaining([
expect.objectContaining({
aura: expect.objectContaining({
procs: expect.arrayContaining([expect.objectContaining({
action: expect.objectContaining({ kind: "heal" }),
})]),
}),
}),
]));
});
it("preserves source-backed direct and party healing alongside aura tooltip filtering", () => {
const flashHeal = byId("wow335-priest-flash-heal");
const rawFlashHeal = generatedData.classes.priest.find((chain) => chain.name === "Flash Heal");
expect(rawFlashHeal?.ranks.every((rank) => rank.effects.some((effect) => effect.effectType === 10))).toBe(true);
expect(flashHeal.ranks?.every((rank) => effectsOfKind(rank, "heal").length === 1)).toBe(true);
const divineHymn = byId("wow335-priest-divine-hymn");
expect(effectsOfKind(divineHymn, "heal")).toEqual([
expect.objectContaining({ scope: "party", maximumTargets: 3, radius: 40 }),
]);
const divineStorm = byId("wow335-paladin-divine-storm");
expect(effectsOfKind(divineStorm, "heal")).toContainEqual(expect.objectContaining({ scope: "party" }));
});
it("preserves talent gates across a cold native import and a later talent preload", () => {
const projectRoot = process.cwd();
const viteNode = path.join(projectRoot, "node_modules", "vite-node", "vite-node.mjs");
const fixture = fileURLToPath(new URL("./wow335AbilityCatalog.cold-start.fixture.ts", import.meta.url));
expect(() => execFileSync(process.execPath, [viteNode, "--mode", "godot-bridge", fixture], {
cwd: projectRoot,
stdio: "pipe",
})).not.toThrow();
});
it("uses legal talent gates for learned builds and leaves source-only nodes unlocked", () => {
const vampiricEmbrace = byId("wow335-priest-vampiric-embrace");
expect(learnedAbilityForBuild(vampiricEmbrace, 80, {})).toBeNull();
expect(learnedAbilityForBuild(vampiricEmbrace, 80, { "talent-484": 1 })).not.toBeNull();
for (const id of [
"wow335-warrior-devastate",
"wow335-rogue-shadowstep",
"wow335-shaman-lava-lash",
]) {
const ability = byId(id);
expect(ability.talentNodeId).toBeUndefined();
expect(learnedAbilityForBuild(ability, 80, {})).not.toBeNull();
}
});
it("uses the same legal talent-node set as the full catalog", () => {
const talentNodeIds = new Set(Object.values(generatedData.classes)
.flatMap((chains) => chains.flatMap((chain) => chain.talentNodeId ? [chain.talentNodeId] : [])));