Update Healer Man server
This commit is contained in:
1 parent
f8314e7de1
commit
1fedb9368d
76 files changed
+20556
-2523
No files matched your search
@@ -0,0 +1,130 @@
|
||||
import { BASE_CLASS_IDS } from "../app/characterCatalog";
|
||||
import type { PartyRole } from "./partyRoles";
|
||||
import {
|
||||
SPECIALIZATION_DEFINITIONS,
|
||||
type SpecializationDefinition,
|
||||
} from "./specializationCatalog";
|
||||
|
||||
export type CompanionSpecReadinessStatus = "ready" | "beta" | "needs-testing";
|
||||
|
||||
export interface CompanionSpecReadinessPolicy {
|
||||
readonly schemaVersion: 1;
|
||||
readonly statuses: Readonly<Record<string, CompanionSpecReadinessStatus>>;
|
||||
}
|
||||
|
||||
export interface PublishedCompanionSpecReadinessPolicy {
|
||||
readonly revision: number;
|
||||
readonly updatedAt: number | null;
|
||||
readonly updatedBy?: string | null;
|
||||
readonly config: CompanionSpecReadinessPolicy;
|
||||
}
|
||||
|
||||
export const READY_COMPANION_SPECIALIZATION_IDS = Object.freeze([
|
||||
"warrior:protection",
|
||||
"priest:discipline",
|
||||
"hunter:marksmanship",
|
||||
"mage:fire",
|
||||
"rogue:assassination",
|
||||
] as const);
|
||||
|
||||
const CLASSIC_CLASS_IDS = new Set<string>(BASE_CLASS_IDS);
|
||||
const READY_IDS = new Set<string>(READY_COMPANION_SPECIALIZATION_IDS);
|
||||
const VALID_STATUSES = new Set<CompanionSpecReadinessStatus>(["ready", "beta", "needs-testing"]);
|
||||
|
||||
export const WOW_COMPANION_SPECIALIZATIONS: readonly SpecializationDefinition[] = Object.freeze(
|
||||
SPECIALIZATION_DEFINITIONS.filter((definition) => CLASSIC_CLASS_IDS.has(definition.classId)),
|
||||
);
|
||||
|
||||
const WOW_SPECIALIZATION_IDS = new Set(WOW_COMPANION_SPECIALIZATIONS.map((definition) => definition.id));
|
||||
|
||||
export const DEFAULT_COMPANION_SPEC_READINESS_POLICY: CompanionSpecReadinessPolicy = Object.freeze({
|
||||
schemaVersion: 1,
|
||||
statuses: Object.freeze(Object.fromEntries(
|
||||
WOW_COMPANION_SPECIALIZATIONS.map((definition) => [
|
||||
definition.id,
|
||||
(READY_IDS.has(definition.id) ? "ready" : "needs-testing") as CompanionSpecReadinessStatus,
|
||||
]),
|
||||
)),
|
||||
});
|
||||
|
||||
function invalid(message: string): Error {
|
||||
return new Error(message);
|
||||
}
|
||||
|
||||
export function readinessStatusFor(
|
||||
specializationId: string | null | undefined,
|
||||
policy: CompanionSpecReadinessPolicy = DEFAULT_COMPANION_SPEC_READINESS_POLICY,
|
||||
): CompanionSpecReadinessStatus {
|
||||
if (!specializationId || !WOW_SPECIALIZATION_IDS.has(specializationId)) return "needs-testing";
|
||||
return policy.statuses[specializationId] ?? "needs-testing";
|
||||
}
|
||||
|
||||
export function validateReadyRoleCoverage(policy: CompanionSpecReadinessPolicy): void {
|
||||
const readyByRole = (role: PartyRole) => WOW_COMPANION_SPECIALIZATIONS.filter((definition) => (
|
||||
definition.role === role && readinessStatusFor(definition.id, policy) === "ready"
|
||||
));
|
||||
if (readyByRole("tank").length < 1) throw invalid("At least one tank specialization must be Ready.");
|
||||
if (readyByRole("healer").length < 1) throw invalid("At least one healer specialization must be Ready.");
|
||||
if (new Set(readyByRole("damage").map((definition) => definition.id)).size < 3) {
|
||||
throw invalid("At least three distinct damage specializations must be Ready.");
|
||||
}
|
||||
}
|
||||
|
||||
export function normalizeCompanionSpecReadinessPolicy(value: unknown): CompanionSpecReadinessPolicy {
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
||||
throw invalid("Companion readiness policy must be an object.");
|
||||
}
|
||||
const input = value as Partial<CompanionSpecReadinessPolicy>;
|
||||
if (input.schemaVersion !== 1) throw invalid("Companion readiness policy schemaVersion must be 1.");
|
||||
if (!input.statuses || typeof input.statuses !== "object" || Array.isArray(input.statuses)) {
|
||||
throw invalid("Companion readiness statuses are required.");
|
||||
}
|
||||
for (const [specializationId, status] of Object.entries(input.statuses)) {
|
||||
if (!WOW_SPECIALIZATION_IDS.has(specializationId)) {
|
||||
throw invalid(`Unknown WoW specialization: ${specializationId}.`);
|
||||
}
|
||||
if (!VALID_STATUSES.has(status as CompanionSpecReadinessStatus)) {
|
||||
throw invalid(`Invalid readiness status for ${specializationId}.`);
|
||||
}
|
||||
}
|
||||
const policy: CompanionSpecReadinessPolicy = Object.freeze({
|
||||
schemaVersion: 1,
|
||||
statuses: Object.freeze(Object.fromEntries(WOW_COMPANION_SPECIALIZATIONS.map((definition) => [
|
||||
definition.id,
|
||||
(input.statuses as Readonly<Record<string, CompanionSpecReadinessStatus>>)[definition.id] ?? "needs-testing",
|
||||
]))),
|
||||
});
|
||||
validateReadyRoleCoverage(policy);
|
||||
return policy;
|
||||
}
|
||||
|
||||
export function companionSpecializationsForPolicy(
|
||||
policy: CompanionSpecReadinessPolicy,
|
||||
includeBetaSpecs: boolean,
|
||||
role?: PartyRole,
|
||||
): readonly SpecializationDefinition[] {
|
||||
return WOW_COMPANION_SPECIALIZATIONS.filter((definition) => {
|
||||
if (role && definition.role !== role) return false;
|
||||
const status = readinessStatusFor(definition.id, policy);
|
||||
return status === "ready" || (includeBetaSpecs && status === "beta");
|
||||
});
|
||||
}
|
||||
|
||||
export function cloneDefaultCompanionSpecReadinessPolicy(): CompanionSpecReadinessPolicy {
|
||||
return JSON.parse(JSON.stringify(DEFAULT_COMPANION_SPEC_READINESS_POLICY)) as CompanionSpecReadinessPolicy;
|
||||
}
|
||||
|
||||
export function companionSpecReadinessFromCache(value: string | null): PublishedCompanionSpecReadinessPolicy {
|
||||
try {
|
||||
const parsed = JSON.parse(value ?? "null") as Partial<PublishedCompanionSpecReadinessPolicy> | null;
|
||||
if (!parsed || !Number.isInteger(parsed.revision)) throw new Error("missing cache");
|
||||
return {
|
||||
revision: parsed.revision!,
|
||||
updatedAt: parsed.updatedAt ?? null,
|
||||
updatedBy: parsed.updatedBy ?? null,
|
||||
config: normalizeCompanionSpecReadinessPolicy(parsed.config),
|
||||
};
|
||||
} catch {
|
||||
return { revision: 0, updatedAt: null, updatedBy: null, config: DEFAULT_COMPANION_SPEC_READINESS_POLICY };
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user