Files
healer-man/src/game/partyTalentUi.ts
T
2026-08-22 15:53:43 -04:00

74 lines
2.3 KiB
TypeScript

import type { ClassId } from "../app/characterCatalog";
import { clampPlayerLevel } from "./progression";
import { runtimePartyTalentPlan } from "./partyTalentRuntime";
import {
talentUiDescriptionForRank,
talentUiNodeById,
talentUiTreeById,
type TalentUiCatalog,
} from "./talentUiCatalog";
export interface PartyTalentSelection {
readonly point: number;
readonly level: number;
readonly nodeId: string;
readonly treeId: string;
readonly treeName: string;
readonly name: string;
readonly rank: number;
readonly maxRank: number;
readonly icon: string;
readonly description: string;
}
export function partyTalentSelections(
catalog: TalentUiCatalog,
classId: ClassId,
specialization: string,
): readonly PartyTalentSelection[] {
if (catalog.classId !== classId) return [];
const ranks: Record<string, number> = {};
return runtimePartyTalentPlan(classId, specialization).map((nodeId, index) => {
const node = talentUiNodeById(catalog, nodeId);
if (!node) throw new Error(`Party talent plan references missing UI node ${nodeId}.`);
const tree = talentUiTreeById(catalog, node.treeId);
if (!tree) throw new Error(`Party talent plan references missing UI tree ${node.treeId}.`);
const rank = (ranks[nodeId] ?? 0) + 1;
ranks[nodeId] = rank;
return Object.freeze({
point: index + 1,
level: index + 10,
nodeId,
treeId: tree.id,
treeName: tree.name,
name: node.name,
rank,
maxRank: node.maxRank,
icon: node.icon,
description: talentUiDescriptionForRank(node, rank),
});
});
}
export function learnedPartyTalentSelections(
catalog: TalentUiCatalog,
classId: ClassId,
specialization: string,
level: number,
): readonly PartyTalentSelection[] {
const points = Math.max(0, clampPlayerLevel(level) - 9);
return partyTalentSelections(catalog, classId, specialization).slice(0, points);
}
export function upcomingPartyTalentSelections(
catalog: TalentUiCatalog,
classId: ClassId,
specialization: string,
level: number,
limit = 5,
): readonly PartyTalentSelection[] {
const start = Math.max(0, clampPlayerLevel(level) - 9);
const safeLimit = Number.isFinite(limit) ? Math.max(0, Math.trunc(limit)) : 0;
return partyTalentSelections(catalog, classId, specialization).slice(start, start + safeLimit);
}