forked from phenom/healer-man
Update Healer Man server
This commit is contained in:
1 parent
f8314e7de1
commit
1fedb9368d
76 files changed
+20556
-2523
No files matched your search
@@ -3,12 +3,52 @@ import generated from "./generated/partyTalentRuntimePlans.json";
|
||||
|
||||
type TalentRanks = Readonly<Record<string, number>>;
|
||||
|
||||
export interface PartyTalentRuntimeMilestone {
|
||||
readonly level: number;
|
||||
readonly link: string;
|
||||
}
|
||||
|
||||
export interface PartyTalentRuntimeLevelPlan {
|
||||
readonly minimumLevel: number;
|
||||
readonly sourceLevels: readonly number[];
|
||||
readonly sourcePointCount: number;
|
||||
readonly sourcePointFlags: readonly boolean[];
|
||||
readonly plan: readonly string[];
|
||||
}
|
||||
|
||||
export interface PartyTalentRuntimeProfile {
|
||||
readonly id: string;
|
||||
readonly classId: ClassId;
|
||||
readonly specialization: string;
|
||||
readonly name: string;
|
||||
readonly characterName: string;
|
||||
readonly source: "azerothcore-playerbots" | "healer-man-generated";
|
||||
readonly sourceVersion: string;
|
||||
readonly defaultForSpecialization: boolean;
|
||||
readonly playerbotsSpecName: string | null;
|
||||
readonly milestones: readonly PartyTalentRuntimeMilestone[];
|
||||
readonly sourcePointCount: number | null;
|
||||
readonly sourcePointsRetained: number | null;
|
||||
readonly fallbackPointCount: number;
|
||||
readonly plan: readonly string[];
|
||||
readonly levelPlans: readonly PartyTalentRuntimeLevelPlan[];
|
||||
}
|
||||
|
||||
export interface PartyTalentRuntimeProvenance {
|
||||
readonly sourcePointCount: number | null;
|
||||
readonly sourcePointsRetained: number | null;
|
||||
readonly fallbackPointCount: number;
|
||||
readonly sourceLevels: readonly number[];
|
||||
}
|
||||
|
||||
interface PartyTalentRuntimeSnapshot {
|
||||
readonly schemaVersion: 1;
|
||||
readonly plans: Readonly<Record<string, readonly string[]>>;
|
||||
readonly schemaVersion: 2;
|
||||
readonly playerbotsCommit: string;
|
||||
readonly profiles: readonly PartyTalentRuntimeProfile[];
|
||||
}
|
||||
|
||||
const snapshot = generated as PartyTalentRuntimeSnapshot;
|
||||
const profileById = new Map(snapshot.profiles.map((profile) => [profile.id, profile]));
|
||||
|
||||
function planKey(classId: ClassId, specialization: string): string {
|
||||
return `${classId}:${specialization}`;
|
||||
@@ -17,10 +57,73 @@ function planKey(classId: ClassId, specialization: string): string {
|
||||
export function runtimePartyTalentPlan(
|
||||
classId: ClassId,
|
||||
specialization: string,
|
||||
profileId?: string,
|
||||
): readonly string[] {
|
||||
const plan = snapshot.plans[planKey(classId, specialization)];
|
||||
if (!plan) throw new Error(`Unsupported party specialization: ${classId}:${specialization}`);
|
||||
return plan;
|
||||
return runtimePartyTalentProfile(classId, specialization, profileId).plan;
|
||||
}
|
||||
|
||||
export function runtimePartyTalentProfiles(
|
||||
classId: ClassId,
|
||||
specialization: string,
|
||||
): readonly PartyTalentRuntimeProfile[] {
|
||||
const key = planKey(classId, specialization);
|
||||
return snapshot.profiles.filter((profile) => planKey(profile.classId, profile.specialization) === key);
|
||||
}
|
||||
|
||||
export function runtimePartyTalentProfile(
|
||||
classId: ClassId,
|
||||
specialization: string,
|
||||
profileId?: string,
|
||||
): PartyTalentRuntimeProfile {
|
||||
const profiles = runtimePartyTalentProfiles(classId, specialization);
|
||||
const profile = profileId
|
||||
? profileById.get(profileId)
|
||||
: profiles.find((candidate) => candidate.defaultForSpecialization);
|
||||
if (!profile || profile.classId !== classId || profile.specialization !== specialization) {
|
||||
throw new Error(`Unsupported party talent profile: ${classId}:${specialization}:${profileId ?? "default"}`);
|
||||
}
|
||||
return profile;
|
||||
}
|
||||
|
||||
export function runtimeDefaultPartyTalentProfile(
|
||||
classId: ClassId,
|
||||
specialization: string,
|
||||
): PartyTalentRuntimeProfile {
|
||||
return runtimePartyTalentProfile(classId, specialization);
|
||||
}
|
||||
|
||||
function levelPlanFor(
|
||||
profile: PartyTalentRuntimeProfile,
|
||||
level: number,
|
||||
): PartyTalentRuntimeLevelPlan | null {
|
||||
const safeLevel = Number.isFinite(level) ? Math.max(1, Math.min(80, Math.trunc(level))) : 1;
|
||||
return [...profile.levelPlans]
|
||||
.reverse()
|
||||
.find((candidate) => candidate.minimumLevel <= safeLevel) ?? null;
|
||||
}
|
||||
|
||||
export function runtimePartyTalentProvenanceForLevel(
|
||||
profile: PartyTalentRuntimeProfile,
|
||||
level: number,
|
||||
): PartyTalentRuntimeProvenance {
|
||||
const selected = levelPlanFor(profile, level);
|
||||
if (!selected) {
|
||||
return {
|
||||
sourcePointCount: profile.sourcePointCount,
|
||||
sourcePointsRetained: profile.sourcePointsRetained,
|
||||
fallbackPointCount: profile.fallbackPointCount,
|
||||
sourceLevels: [],
|
||||
};
|
||||
}
|
||||
const safeLevel = Number.isFinite(level) ? Math.max(1, Math.min(80, Math.trunc(level))) : 1;
|
||||
const points = Math.max(0, safeLevel - 9);
|
||||
const sourcePointsRetained = selected.sourcePointFlags.slice(0, points).filter(Boolean).length;
|
||||
return {
|
||||
sourcePointCount: selected.sourcePointCount,
|
||||
sourcePointsRetained,
|
||||
fallbackPointCount: Math.max(0, points - sourcePointsRetained),
|
||||
sourceLevels: selected.sourceLevels,
|
||||
};
|
||||
}
|
||||
|
||||
/** Runtime-only projection of the catalog-validated 71-point party plans. */
|
||||
@@ -28,9 +131,11 @@ export function runtimePartyTalentRanksForLevel(
|
||||
classId: ClassId,
|
||||
specialization: string,
|
||||
level: number,
|
||||
profileId?: string,
|
||||
): TalentRanks {
|
||||
const plan = runtimePartyTalentPlan(classId, specialization);
|
||||
const safeLevel = Number.isFinite(level) ? Math.max(1, Math.min(80, Math.trunc(level))) : 1;
|
||||
const profile = runtimePartyTalentProfile(classId, specialization, profileId);
|
||||
const plan = levelPlanFor(profile, safeLevel)?.plan ?? profile.plan;
|
||||
const points = Math.max(0, safeLevel - 9);
|
||||
const ranks: Record<string, number> = {};
|
||||
for (const nodeId of plan.slice(0, points)) ranks[nodeId] = (ranks[nodeId] ?? 0) + 1;
|
||||
|
||||
Reference in new issue
Block a user