Release Healer Man 0.1.39

This commit is contained in:
phenom committed 2026-09-13 11:07:56 -04:00
1 parent b417b7c3c3
commit ed0d2fea3d
51 files changed
+17811 -49

No files matched your search

+42 -8
View File
@@ -6,7 +6,7 @@ import type { DungeonTraversalRamp, Vector3Tuple } from "./dungeonTypes";
const MAX_STAIR_SURFACE_DELTA = 0.85;
const PORTAL_VERTEX_TOLERANCE = 0.002;
function rampHeight(
export function dungeonRampHeight(
ramp: DungeonTraversalRamp,
x: number,
z: number,
@@ -36,10 +36,38 @@ export function smoothDungeonStairGeometry(
surfaceClearance = 0,
): void {
const position = geometry.getAttribute("position");
// Fleet packages can contain many short flights and hundreds of thousands of
// vertices. Index their footprints once rather than checking every flight
// (and validating/recomputing its direction) for every collision vertex.
const cellSize = 16;
const cells = new Map<string, { ramp: DungeonTraversalRamp; dx: number; dz: number; length: number }[]>();
for (const ramp of ramps) {
dungeonRampHeight(ramp, ramp.start[0], ramp.start[2]);
const dx = ramp.end[0] - ramp.start[0];
const dz = ramp.end[2] - ramp.start[2];
const length = Math.hypot(dx, dz);
const xPadding = Math.abs(dz) / length * ramp.width / 2;
const zPadding = Math.abs(dx) / length * ramp.width / 2;
for (let x = Math.floor((Math.min(ramp.start[0], ramp.end[0]) - xPadding) / cellSize);
x <= Math.floor((Math.max(ramp.start[0], ramp.end[0]) + xPadding) / cellSize); x += 1) {
for (let z = Math.floor((Math.min(ramp.start[2], ramp.end[2]) - zPadding) / cellSize);
z <= Math.floor((Math.max(ramp.start[2], ramp.end[2]) + zPadding) / cellSize); z += 1) {
const key = `${x}:${z}`;
const bucket = cells.get(key) ?? [];
bucket.push({ ramp, dx, dz, length });
cells.set(key, bucket);
}
}
}
for (let vertex = 0; vertex < position.count; vertex += 1) {
for (const ramp of ramps) {
const height = rampHeight(ramp, position.getX(vertex), position.getZ(vertex));
if (height === null || Math.abs(position.getY(vertex) - height) > MAX_STAIR_SURFACE_DELTA) continue;
const x = position.getX(vertex); const z = position.getZ(vertex);
for (const { ramp, dx, dz, length } of cells.get(`${Math.floor(x / cellSize)}:${Math.floor(z / cellSize)}`) ?? []) {
const offsetX = x - ramp.start[0]; const offsetZ = z - ramp.start[2];
const along = (offsetX * dx + offsetZ * dz) / length;
const across = (offsetX * dz - offsetZ * dx) / length;
if (along < 0 || along > length || Math.abs(across) > ramp.width / 2) continue;
const height = ramp.start[1] + (ramp.end[1] - ramp.start[1]) * along / length;
if (Math.abs(position.getY(vertex) - height) > MAX_STAIR_SURFACE_DELTA) continue;
position.setY(vertex, height + surfaceClearance);
break;
}
@@ -57,7 +85,7 @@ export function bridgeDungeonStairNavigation(
? Array.from(geometry.index.array)
: Array.from({ length: position.count }, (_, vertex) => vertex);
const portalVertex = (ramp: DungeonTraversalRamp, point: Vector3Tuple): number => {
const height = rampHeight(ramp, point[0], point[2]);
const height = dungeonRampHeight(ramp, point[0], point[2]);
if (height === null) throw new Error(`${ramp.id}: navigation portal is outside the stair ramp.`);
for (let vertex = 0; vertex < position.count; vertex += 1) {
if (Math.hypot(
@@ -69,9 +97,15 @@ export function bridgeDungeonStairNavigation(
throw new Error(`${ramp.id}: reviewed navigation portal no longer matches the source GLB.`);
};
for (const ramp of ramps) {
const [a, b] = ramp.navigationBridge.startPortal.map((point) => portalVertex(ramp, point));
const [c, d] = ramp.navigationBridge.endPortal.map((point) => portalVertex(ramp, point));
indices.push(a, b, d, a, d, c);
for (const bridge of dungeonRampNavigationBridges(ramp)) {
const [a, b] = bridge.startPortal.map((point) => portalVertex(ramp, point));
const [c, d] = bridge.endPortal.map((point) => portalVertex(ramp, point));
indices.push(a, b, d, a, d, c);
}
}
geometry.setIndex(indices);
}
export function dungeonRampNavigationBridges(ramp: DungeonTraversalRamp) {
return [...(ramp.navigationBridge ? [ramp.navigationBridge] : []), ...(ramp.navigationBridges ?? [])];
}