Release Healer Man 0.1.11

This commit is contained in:
phenom committed 2026-08-31 14:12:51 -04:00
1 parent 5f1a14d8de
commit 13970d03c8
18 files changed
+514 -19

No files matched your search

+50
View File
@@ -91,6 +91,11 @@ export interface StaticMobSpawnCluster {
readonly spawns: readonly StaticMobSpawnDefinition[];
}
export interface NormalizedMobPopulationPaths {
readonly roamingPacks: readonly MobPackDefinition[];
readonly staticSpawns: readonly StaticMobSpawnDefinition[];
}
/**
* Partitions large imported populations into bounded activation cells. Actors
* in distant cells are not mounted or registered with combat until approached.
@@ -141,6 +146,51 @@ export interface LoopedPath {
const EPSILON = 1e-6;
export function hasUsableLoopedMobPath(waypoints: readonly MobVector3[]): boolean {
if (waypoints.length < 2 || !waypoints.every((point) => point.every(Number.isFinite))) {
return false;
}
const origin = waypoints[0];
return waypoints.some((point) => Math.hypot(
point[0] - origin[0],
point[1] - origin[1],
point[2] - origin[2],
) > EPSILON);
}
/**
* Imported databases occasionally label a stationary spawn as a patrol by
* repeating one waypoint. Keep those actors, but mount them through the static
* path so malformed content can never take down the scene.
*/
export function normalizeMobPopulationPaths(
roamingPacks: readonly MobPackDefinition[],
staticSpawns: readonly StaticMobSpawnDefinition[],
): NormalizedMobPopulationPaths {
const invalidPacks = roamingPacks.filter((pack) => !hasUsableLoopedMobPath(pack.waypoints));
if (!invalidPacks.length) return { roamingPacks, staticSpawns };
const invalidIds = new Set(invalidPacks.map((pack) => pack.id));
const stationarySpawns = invalidPacks.flatMap((pack) => {
const anchor = pack.waypoints.find((point) => point.every(Number.isFinite));
if (!anchor) return [];
return pack.members.map((member): StaticMobSpawnDefinition => ({
id: `${pack.id}:${member.id}`,
entityId: member.entityId,
position: [
anchor[0] + member.formationOffset[0],
anchor[1] + member.formationOffset[1],
anchor[2] - member.formationOffset[2],
],
spawnMask: pack.spawnMask,
}));
});
return {
roamingPacks: roamingPacks.filter((pack) => !invalidIds.has(pack.id)),
staticSpawns: [...staticSpawns, ...stationarySpawns],
};
}
function assertFiniteWaypoint(point: MobVector3, index: number): void {
if (!point.every(Number.isFinite)) {
throw new Error(`Waypoint ${index} contains a non-finite coordinate.`);