import type { ClassId } from "../app/characterCatalog"; import generated from "./generated/partyTalentRuntimePlans.json"; type TalentRanks = Readonly>; interface PartyTalentRuntimeSnapshot { readonly schemaVersion: 1; readonly plans: Readonly>; } const snapshot = generated as PartyTalentRuntimeSnapshot; function planKey(classId: ClassId, specialization: string): string { return `${classId}:${specialization}`; } export function runtimePartyTalentPlan( classId: ClassId, specialization: string, ): readonly string[] { const plan = snapshot.plans[planKey(classId, specialization)]; if (!plan) throw new Error(`Unsupported party specialization: ${classId}:${specialization}`); return plan; } /** Runtime-only projection of the catalog-validated 71-point party plans. */ export function runtimePartyTalentRanksForLevel( classId: ClassId, specialization: string, level: number, ): TalentRanks { const plan = runtimePartyTalentPlan(classId, specialization); const safeLevel = Number.isFinite(level) ? Math.max(1, Math.min(80, Math.trunc(level))) : 1; const points = Math.max(0, safeLevel - 9); const ranks: Record = {}; for (const nodeId of plan.slice(0, points)) ranks[nodeId] = (ranks[nodeId] ?? 0) + 1; return Object.freeze(ranks); }