Files
2026-08-14 15:56:39 -04:00

53 lines
2.1 KiB
JavaScript

function distanceSquared(left, right) {
const dx = left[0] - right[0];
const dy = left[1] - right[1];
const dz = left[2] - right[2];
return dx * dx + dy * dy + dz * dz;
}
export function orderBossObjectives(rawBosses, entrance, configuredOrder) {
if (!configuredOrder) {
return {
ordered: [...rawBosses].sort((left, right) => (
distanceSquared(left.position, entrance) - distanceSquared(right.position, entrance)
|| left.id.localeCompare(right.id)
)),
provenance: {
authoritative: false,
kind: "healerMan-distance-derived",
evidence: ["scripts/runewaker-pipeline/build-population.mjs"],
},
};
}
if (configuredOrder.kind !== "source-authored") {
throw new Error("bossObjectiveOrder.kind must be source-authored.");
}
if (!Array.isArray(configuredOrder.evidence) || !configuredOrder.evidence.length
|| configuredOrder.evidence.some((entry) => typeof entry !== "string" || !entry.trim())) {
throw new Error("Source-authored bossObjectiveOrder requires nonempty evidence paths.");
}
if (!Array.isArray(configuredOrder.spawnIds)
|| configuredOrder.spawnIds.some((id) => !Number.isInteger(id) || id <= 0)) {
throw new Error("Source-authored bossObjectiveOrder requires positive integer spawnIds.");
}
const configuredIds = new Set(configuredOrder.spawnIds);
if (configuredIds.size !== configuredOrder.spawnIds.length) {
throw new Error("Source-authored bossObjectiveOrder contains duplicate spawnIds.");
}
const bySourceSpawnId = new Map(rawBosses.map((boss) => [boss.sourceSpawnId, boss]));
if (configuredIds.size !== bySourceSpawnId.size
|| [...configuredIds].some((id) => !bySourceSpawnId.has(id))) {
throw new Error("Source-authored bossObjectiveOrder must list every runtime boss spawn exactly once.");
}
return {
ordered: configuredOrder.spawnIds.map((id) => bySourceSpawnId.get(id)),
provenance: {
authoritative: true,
kind: "source-authored",
evidence: [...configuredOrder.evidence],
sourceSpawnIds: [...configuredOrder.spawnIds],
},
};
}