Release Healer Man 0.1.12
This commit is contained in:
1 parent
13970d03c8
commit
65e24f644e
201 files changed
+5061
-123
No files matched your search
@@ -7,10 +7,13 @@ import {
|
||||
} from "./manastorm";
|
||||
import { INSTALLED_MANASTORM_RUNTIME_CATALOG } from "./manastormInstalledCatalog";
|
||||
|
||||
export const MANASTORM_ADMIN_CONFIG_SCHEMA_VERSION = 1;
|
||||
export const MANASTORM_ADMIN_CACHE_KEY = "healer-man.manastorm-admin-config.v1";
|
||||
export const MANASTORM_ADMIN_CONFIG_SCHEMA_VERSION = 2;
|
||||
export const MANASTORM_ADMIN_CACHE_KEY = "healer-man.manastorm-admin-config.v2";
|
||||
export const LEGACY_MANASTORM_ADMIN_CACHE_KEY = "healer-man.manastorm-admin-config.v1";
|
||||
export const GM_REVIEW_NOTE_MAX_LENGTH = 1_000;
|
||||
|
||||
export type ManastormBossStatus = "ready" | "needs-tested" | "not-ready";
|
||||
export type GmReadinessStatus = "ready" | "needs-tested" | "not-ready";
|
||||
export type ManastormBossStatus = GmReadinessStatus;
|
||||
|
||||
export interface ManastormGlobalOptions {
|
||||
readonly enabled: boolean;
|
||||
@@ -37,15 +40,24 @@ export interface ManastormAdminStageConfig {
|
||||
readonly requiredKillCount: number;
|
||||
}
|
||||
|
||||
export interface ManastormBossAdminRecord {
|
||||
readonly status: ManastormBossStatus;
|
||||
export interface GmReviewRecord {
|
||||
readonly status: GmReadinessStatus;
|
||||
readonly note?: string;
|
||||
readonly reviewedAt?: number;
|
||||
readonly reviewedBy?: string;
|
||||
}
|
||||
|
||||
export interface ManastormBossAdminRecord extends GmReviewRecord {
|
||||
readonly stage?: ManastormAdminStageConfig;
|
||||
}
|
||||
|
||||
export type DungeonAdminRecord = GmReviewRecord;
|
||||
|
||||
export interface ManastormAdminConfig {
|
||||
readonly schemaVersion: typeof MANASTORM_ADMIN_CONFIG_SCHEMA_VERSION;
|
||||
readonly global: ManastormGlobalOptions;
|
||||
readonly dungeonSpawns: Readonly<Record<string, WorldSpawn>>;
|
||||
readonly dungeons: Readonly<Record<string, DungeonAdminRecord>>;
|
||||
readonly bosses: Readonly<Record<string, ManastormBossAdminRecord>>;
|
||||
}
|
||||
|
||||
@@ -71,6 +83,9 @@ export interface UnifiedBossCatalogEntry {
|
||||
|
||||
export interface ResolvedBossAdminEntry extends UnifiedBossCatalogEntry {
|
||||
readonly status: ManastormBossStatus;
|
||||
readonly reviewNote: string;
|
||||
readonly reviewedAt: number | null;
|
||||
readonly reviewedBy: string | null;
|
||||
readonly stage: ManastormEncounterDefinition | null;
|
||||
readonly runnable: boolean;
|
||||
readonly validationIssues: readonly string[];
|
||||
@@ -88,6 +103,7 @@ export const DEFAULT_MANASTORM_ADMIN_CONFIG: ManastormAdminConfig = Object.freez
|
||||
milestoneEncountersEnabled: true,
|
||||
}),
|
||||
dungeonSpawns: Object.freeze({}),
|
||||
dungeons: Object.freeze({}),
|
||||
bosses: Object.freeze({}),
|
||||
});
|
||||
|
||||
@@ -143,10 +159,37 @@ function normalizedStage(value: unknown): ManastormAdminStageConfig | null {
|
||||
};
|
||||
}
|
||||
|
||||
function normalizedReviewFields(value: Partial<GmReviewRecord>): Omit<GmReviewRecord, "status"> {
|
||||
const rawNote = typeof value.note === "string"
|
||||
? value.note.replace(/\r\n?/g, "\n").slice(0, GM_REVIEW_NOTE_MAX_LENGTH)
|
||||
: "";
|
||||
const reviewedAt = typeof value.reviewedAt === "number"
|
||||
&& Number.isInteger(value.reviewedAt)
|
||||
&& value.reviewedAt > 0
|
||||
? value.reviewedAt
|
||||
: null;
|
||||
const reviewedBy = typeof value.reviewedBy === "string"
|
||||
? value.reviewedBy.trim().slice(0, 256)
|
||||
: "";
|
||||
return {
|
||||
...(rawNote.trim() ? { note: rawNote } : {}),
|
||||
...(reviewedAt ? { reviewedAt } : {}),
|
||||
...(reviewedBy ? { reviewedBy } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
export function normalizeManastormAdminConfig(value: unknown): ManastormAdminConfig {
|
||||
if (!value || typeof value !== "object") return DEFAULT_MANASTORM_ADMIN_CONFIG;
|
||||
const input = value as Partial<ManastormAdminConfig>;
|
||||
if (input.schemaVersion !== MANASTORM_ADMIN_CONFIG_SCHEMA_VERSION) return DEFAULT_MANASTORM_ADMIN_CONFIG;
|
||||
const input = value as {
|
||||
readonly schemaVersion?: unknown;
|
||||
readonly global?: Partial<ManastormGlobalOptions>;
|
||||
readonly dungeonSpawns?: Readonly<Record<string, unknown>>;
|
||||
readonly dungeons?: Readonly<Record<string, unknown>>;
|
||||
readonly bosses?: Readonly<Record<string, unknown>>;
|
||||
};
|
||||
if (input.schemaVersion !== 1 && input.schemaVersion !== MANASTORM_ADMIN_CONFIG_SCHEMA_VERSION) {
|
||||
return DEFAULT_MANASTORM_ADMIN_CONFIG;
|
||||
}
|
||||
const globalInput = input.global as Partial<ManastormGlobalOptions> | undefined;
|
||||
const global: ManastormGlobalOptions = {
|
||||
enabled: globalInput?.enabled !== false,
|
||||
@@ -160,14 +203,42 @@ export function normalizeManastormAdminConfig(value: unknown): ManastormAdminCon
|
||||
const spawn = normalizedSpawn(value);
|
||||
return id && spawn ? [[id, spawn]] : [];
|
||||
}));
|
||||
const dungeons = Object.fromEntries(Object.entries(input.dungeons ?? {}).flatMap(([id, value]) => {
|
||||
if (!id || !value || typeof value !== "object") return [];
|
||||
const candidate = value as Partial<DungeonAdminRecord>;
|
||||
const status: GmReadinessStatus = candidate.status === "ready" || candidate.status === "not-ready"
|
||||
? candidate.status
|
||||
: "needs-tested";
|
||||
return [[id, { status, ...normalizedReviewFields(candidate) }]];
|
||||
}));
|
||||
const bosses = Object.fromEntries(Object.entries(input.bosses ?? {}).flatMap(([key, value]) => {
|
||||
if (!key || !value || typeof value !== "object") return [];
|
||||
const candidate = value as Partial<ManastormBossAdminRecord>;
|
||||
const status: ManastormBossStatus = candidate.status === "ready" || candidate.status === "not-ready" ? candidate.status : "needs-tested";
|
||||
const status: GmReadinessStatus = candidate.status === "ready" || candidate.status === "not-ready" ? candidate.status : "needs-tested";
|
||||
const stage = normalizedStage(candidate.stage);
|
||||
return [[key, { status, ...(stage ? { stage } : {}) }]];
|
||||
return [[key, { status, ...normalizedReviewFields(candidate), ...(stage ? { stage } : {}) }]];
|
||||
}));
|
||||
return { schemaVersion: MANASTORM_ADMIN_CONFIG_SCHEMA_VERSION, global, dungeonSpawns, bosses };
|
||||
return {
|
||||
schemaVersion: MANASTORM_ADMIN_CONFIG_SCHEMA_VERSION,
|
||||
global,
|
||||
dungeonSpawns,
|
||||
dungeons,
|
||||
bosses,
|
||||
};
|
||||
}
|
||||
|
||||
export function publicManastormAdminConfig(config: ManastormAdminConfig): ManastormAdminConfig {
|
||||
return {
|
||||
...config,
|
||||
dungeons: Object.fromEntries(Object.entries(config.dungeons).map(([key, record]) => [
|
||||
key,
|
||||
{ status: record.status },
|
||||
])),
|
||||
bosses: Object.fromEntries(Object.entries(config.bosses).map(([key, record]) => [
|
||||
key,
|
||||
{ status: record.status, ...(record.stage ? { stage: record.stage } : {}) },
|
||||
])),
|
||||
};
|
||||
}
|
||||
|
||||
function dungeonBossEntityId(dungeonId: string, objectiveId: string): string {
|
||||
@@ -351,7 +422,8 @@ export function resolveBossAdminEntry(
|
||||
entry: UnifiedBossCatalogEntry,
|
||||
config: ManastormAdminConfig,
|
||||
): ResolvedBossAdminEntry {
|
||||
const status = config.bosses[entry.key]?.status ?? "needs-tested";
|
||||
const record = config.bosses[entry.key];
|
||||
const status = record?.status ?? "needs-tested";
|
||||
const stage = configuredStageForBoss(entry, config);
|
||||
const issues: string[] = [];
|
||||
if (!stage) issues.push("No runnable Manastorm stage has been configured.");
|
||||
@@ -370,6 +442,9 @@ export function resolveBossAdminEntry(
|
||||
return {
|
||||
...entry,
|
||||
status,
|
||||
reviewNote: record?.note ?? "",
|
||||
reviewedAt: record?.reviewedAt ?? null,
|
||||
reviewedBy: record?.reviewedBy ?? null,
|
||||
stage,
|
||||
runnable,
|
||||
validationIssues: Object.freeze(issues),
|
||||
|
||||
Reference in new issue
Block a user