Release Healer Man 0.1.34
This commit is contained in:
1 parent
05b25c4341
commit
46c272f889
19 files changed
+568
-121
No files matched your search
@@ -0,0 +1,80 @@
|
||||
import type RAPIER from "@dimforge/rapier3d-compat";
|
||||
import type { World } from "@dimforge/rapier3d-compat";
|
||||
import type { PartyWorldPosition } from "../game/partyRuntimeRegistry";
|
||||
|
||||
const COLLISION_RADIUS = 0.34;
|
||||
const COLLISION_HALF_HEIGHT = 0.48;
|
||||
const COLLISION_CENTER_HEIGHT = COLLISION_RADIUS + COLLISION_HALF_HEIGHT + 0.04;
|
||||
const WALKABLE_STEP_HEIGHT = 0.32;
|
||||
export const PARTY_FLOOR_RAY_HEIGHT = 1.25;
|
||||
export const PARTY_FLOOR_RAY_DEPTH = 2;
|
||||
const FLOOR_SAMPLE_SPACING = 0.6;
|
||||
const MAX_FLOOR_DELTA = 0.55;
|
||||
|
||||
export function partyTerrainQueryFlags(rapier: typeof RAPIER) {
|
||||
// Actor capsules are not terrain. Crowd spacing is handled separately by
|
||||
// party separation; counting the player here blocks recalls and replans.
|
||||
return rapier.QueryFilterFlags.EXCLUDE_SENSORS
|
||||
| rapier.QueryFilterFlags.EXCLUDE_DYNAMIC
|
||||
| rapier.QueryFilterFlags.EXCLUDE_KINEMATIC;
|
||||
}
|
||||
|
||||
/** Shared by live party routing and collision-backed traversal regressions. */
|
||||
export function createPartyDirectSegmentValidator(rapier: typeof RAPIER, world: World) {
|
||||
const capsule = new rapier.Capsule(
|
||||
COLLISION_HALF_HEIGHT - WALKABLE_STEP_HEIGHT * 0.5,
|
||||
COLLISION_RADIUS,
|
||||
);
|
||||
const rotation = { x: 0, y: 0, z: 0, w: 1 };
|
||||
const flags = partyTerrainQueryFlags(rapier);
|
||||
return (start: PartyWorldPosition, end: PartyWorldPosition): boolean => {
|
||||
const dx = end[0] - start[0];
|
||||
const dy = end[1] - start[1];
|
||||
const dz = end[2] - start[2];
|
||||
const distance = Math.hypot(dx, dy, dz);
|
||||
if (!Number.isFinite(distance)) return false;
|
||||
if (Math.hypot(dx, dz) <= 0.04) return Math.abs(dy) <= WALKABLE_STEP_HEIGHT;
|
||||
const obstacle = world.castShape(
|
||||
{ x: start[0], y: start[1] + COLLISION_CENTER_HEIGHT + WALKABLE_STEP_HEIGHT * 0.5, z: start[2] },
|
||||
rotation,
|
||||
{ x: dx / distance, y: dy / distance, z: dz / distance },
|
||||
capsule,
|
||||
0.025,
|
||||
distance,
|
||||
false,
|
||||
flags,
|
||||
);
|
||||
if (obstacle && obstacle.time_of_impact < distance - 0.04) return false;
|
||||
const sampleCount = Math.max(1, Math.ceil(distance / FLOOR_SAMPLE_SPACING));
|
||||
for (let sample = 0; sample <= sampleCount; sample += 1) {
|
||||
const progress = sample / sampleCount;
|
||||
const expectedY = start[1] + dy * progress;
|
||||
const ray = new rapier.Ray({
|
||||
x: start[0] + dx * progress,
|
||||
y: expectedY + PARTY_FLOOR_RAY_HEIGHT,
|
||||
z: start[2] + dz * progress,
|
||||
}, { x: 0, y: -1, z: 0 });
|
||||
const floor = world.castRay(ray, PARTY_FLOOR_RAY_DEPTH, true, flags);
|
||||
if (!floor || Math.abs(ray.origin.y - floor.timeOfImpact - expectedY) > MAX_FLOOR_DELTA) return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
/** Triangle corridors need wall/overhang validation, not a second radius erosion. */
|
||||
export function createPartyRouteSegmentValidator(rapier: typeof RAPIER, world: World) {
|
||||
const flags = partyTerrainQueryFlags(rapier);
|
||||
return (start: PartyWorldPosition, end: PartyWorldPosition): boolean => {
|
||||
const dx = end[0] - start[0];
|
||||
const dy = end[1] - start[1];
|
||||
const dz = end[2] - start[2];
|
||||
const distance = Math.hypot(dx, dy, dz);
|
||||
if (!Number.isFinite(distance)) return false;
|
||||
if (distance <= 0.04) return true;
|
||||
const ray = new rapier.Ray({
|
||||
x: start[0], y: start[1] + 0.9, z: start[2],
|
||||
}, { x: dx / distance, y: dy / distance, z: dz / distance });
|
||||
const hit = world.castRay(ray, distance, true, flags);
|
||||
return !hit || hit.timeOfImpact >= distance - 0.04;
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user