Update Healer Man server

This commit is contained in:
phenom committed 2026-09-20 17:09:16 -04:00
1 parent 246b4832b0
commit 3e30af4c98
8 files changed
+309 -119

No files matched your search

+12
View File
@@ -3,6 +3,7 @@ import { CLASSES } from "../app/characterCatalog";
import {
SPECIALIZATION_DEFINITIONS,
specializationById,
specializationIconUrl,
specializationsForClass,
} from "./specializationCatalog";
@@ -35,4 +36,15 @@ describe("specialization registry", () => {
expect(coa.every((definition) => ["tank", "healer", "damage"].includes(definition.role))).toBe(true);
expect(specializationById("barbarian:ancestry")?.role).toBe("damage");
});
it("uses dedicated classic spec icons with safe class-icon fallbacks", () => {
expect(specializationIconUrl("priest", "priest:holy"))
.toBe("/assets/ui/talents/icons/spell_holy_guardianspirit.png");
expect(specializationIconUrl("druid", "druid:feral-cat"))
.toBe("/assets/ui/talents/icons/ability_racial_bearform.png");
expect(specializationIconUrl("barbarian", "barbarian:ancestry"))
.toBe("/assets/ui/spells/classicon128_barbarian.png");
expect(specializationIconUrl("priest", "mage:frost"))
.toBe("/assets/ui/spells/classicon128_priest.png");
});
});
+46
View File
@@ -1,6 +1,7 @@
import {
baseClassFor,
classById,
classIconUrl,
type ClassId,
} from "../app/characterCatalog";
import type { ItemStats, NumericItemStatKey } from "./itemStats";
@@ -136,6 +137,40 @@ export const SPECIALIZATION_DEFINITIONS: readonly SpecializationDefinition[] = O
const BY_ID = new Map(SPECIALIZATION_DEFINITIONS.map((definition) => [definition.id, definition]));
const CLASSIC_SPECIALIZATION_ICON_BASENAME: Readonly<Record<string, string>> = Object.freeze({
"warrior:arms": "ability_rogue_eviscerate",
"warrior:fury": "ability_warrior_innerrage",
"warrior:protection": "inv_shield_06",
"paladin:holy": "spell_holy_holybolt",
"paladin:protection": "spell_holy_devotionaura",
"paladin:retribution": "spell_holy_auraoflight",
"hunter:beast-mastery": "ability_hunter_beasttaming",
"hunter:marksmanship": "ability_marksmanship",
"hunter:survival": "ability_hunter_swiftstrike",
"rogue:assassination": "ability_rogue_eviscerate",
"rogue:combat": "ability_backstab",
"rogue:subtlety": "ability_stealth",
"priest:discipline": "spell_holy_wordfortitude",
"priest:holy": "spell_holy_guardianspirit",
"priest:shadow": "spell_shadow_shadowwordpain",
"death-knight:blood": "spell_deathknight_bloodpresence",
"death-knight:frost": "spell_deathknight_frostpresence",
"death-knight:unholy": "spell_deathknight_unholypresence",
"shaman:elemental": "spell_nature_lightning",
"shaman:enhancement": "spell_nature_lightningshield",
"shaman:restoration": "spell_nature_magicimmunity",
"mage:arcane": "spell_holy_magicalsentry",
"mage:fire": "spell_fire_firebolt02",
"mage:frost": "spell_frost_frostbolt02",
"warlock:affliction": "spell_shadow_deathcoil",
"warlock:demonology": "spell_shadow_metamorphosis",
"warlock:destruction": "spell_shadow_rainoffire",
"druid:balance": "spell_nature_starfall",
"druid:feral-bear": "ability_racial_bearform",
"druid:feral-cat": "ability_racial_bearform",
"druid:restoration": "spell_nature_healingtouch",
});
export function specializationsForClass(classId: ClassId): readonly SpecializationDefinition[] {
return SPECIALIZATION_DEFINITIONS.filter((definition) => definition.classId === classId);
}
@@ -144,6 +179,17 @@ export function specializationById(id: string | null | undefined): Specializatio
return id ? BY_ID.get(id) ?? null : null;
}
/** Returns the dedicated WoW talent-tree icon when available, otherwise the class icon. */
export function specializationIconUrl(classId: ClassId, specializationId: string | null | undefined): string {
const specialization = specializationById(specializationId);
const iconBasename = specialization?.classId === classId
? CLASSIC_SPECIALIZATION_ICON_BASENAME[specialization.id]
: undefined;
return iconBasename
? `/assets/ui/talents/icons/${iconBasename}.png`
: classIconUrl(classId);
}
export function normalizeSpecializationId(classId: ClassId, value: unknown): string | null {
if (typeof value !== "string") return null;
const definition = specializationById(value);