@woosh/meep-engine 2.146.0 → 2.148.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite.d.ts +4 -4
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite.d.ts.map +1 -1
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite.js +48 -52
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_2d.d.ts +23 -21
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_2d.d.ts.map +1 -1
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_2d.js +41 -406
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_nd.d.ts +5 -4
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_nd.d.ts.map +1 -1
- package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_nd.js +400 -395
- package/src/engine/control/first-person/FirstPersonPlayerController.d.ts +0 -11
- package/src/engine/control/first-person/FirstPersonPlayerController.d.ts.map +1 -1
- package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.d.ts +8 -6
- package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.d.ts.map +1 -1
- package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.js +552 -551
- package/src/engine/control/first-person/abilities/LedgeGrab.d.ts +8 -3
- package/src/engine/control/first-person/abilities/LedgeGrab.d.ts.map +1 -1
- package/src/engine/control/first-person/abilities/LedgeGrab.js +213 -199
- package/src/engine/control/first-person/abilities/Mantle.d.ts.map +1 -1
- package/src/engine/control/first-person/abilities/Mantle.js +195 -188
- package/src/engine/control/first-person/abilities/WallRun.d.ts.map +1 -1
- package/src/engine/control/first-person/abilities/WallRun.js +183 -175
- package/src/engine/control/first-person/sensors/FirstPersonSensors.d.ts +9 -0
- package/src/engine/control/first-person/sensors/FirstPersonSensors.d.ts.map +1 -1
- package/src/engine/control/first-person/sensors/FirstPersonSensors.js +87 -77
- package/src/engine/control/first-person/sensors/FirstPersonSensorsSystem.d.ts +8 -0
- package/src/engine/control/first-person/sensors/FirstPersonSensorsSystem.d.ts.map +1 -1
- package/src/engine/control/first-person/sensors/FirstPersonSensorsSystem.js +229 -196
- package/src/engine/ecs/EntityManager.d.ts +34 -11
- package/src/engine/ecs/EntityManager.d.ts.map +1 -1
- package/src/engine/ecs/EntityManager.js +71 -42
- package/src/engine/interpolation/BinaryInterpolationAdapter.d.ts.map +1 -0
- package/src/engine/interpolation/Interpoland.d.ts +48 -0
- package/src/engine/interpolation/Interpoland.d.ts.map +1 -0
- package/src/engine/interpolation/Interpoland.js +49 -0
- package/src/engine/interpolation/Interpolated.d.ts +101 -0
- package/src/engine/interpolation/Interpolated.d.ts.map +1 -0
- package/src/engine/interpolation/Interpolated.js +149 -0
- package/src/engine/{network/sim → interpolation}/InterpolationLog.d.ts +1 -1
- package/src/engine/interpolation/InterpolationLog.d.ts.map +1 -0
- package/src/engine/{network/sim → interpolation}/InterpolationLog.js +2 -2
- package/src/engine/interpolation/InterpolationSystem.d.ts +116 -0
- package/src/engine/interpolation/InterpolationSystem.d.ts.map +1 -0
- package/src/engine/interpolation/InterpolationSystem.js +233 -0
- package/src/engine/interpolation/PoseInterpolationAdapter.d.ts +17 -0
- package/src/engine/interpolation/PoseInterpolationAdapter.d.ts.map +1 -0
- package/src/engine/interpolation/PoseInterpolationAdapter.js +61 -0
- package/src/engine/interpolation/TransformPoseSerializationAdapter.d.ts +35 -0
- package/src/engine/interpolation/TransformPoseSerializationAdapter.d.ts.map +1 -0
- package/src/engine/interpolation/TransformPoseSerializationAdapter.js +57 -0
- package/src/engine/interpolation/pose_interpoland.d.ts +18 -0
- package/src/engine/interpolation/pose_interpoland.d.ts.map +1 -0
- package/src/engine/interpolation/pose_interpoland.js +27 -0
- package/src/engine/network/NetworkSession.d.ts +2 -2
- package/src/engine/network/NetworkSession.d.ts.map +1 -1
- package/src/engine/network/NetworkSession.js +2 -2
- package/src/engine/network/adapters/QuaternionInterpolationAdapter.d.ts +1 -1
- package/src/engine/network/adapters/QuaternionInterpolationAdapter.d.ts.map +1 -1
- package/src/engine/network/adapters/QuaternionInterpolationAdapter.js +1 -1
- package/src/engine/network/adapters/TransformInterpolationAdapter.d.ts +1 -1
- package/src/engine/network/adapters/TransformInterpolationAdapter.d.ts.map +1 -1
- package/src/engine/network/adapters/TransformInterpolationAdapter.js +1 -1
- package/src/engine/network/adapters/Vector3InterpolationAdapter.d.ts +1 -1
- package/src/engine/network/adapters/Vector3InterpolationAdapter.d.ts.map +1 -1
- package/src/engine/network/adapters/Vector3InterpolationAdapter.js +1 -1
- package/src/engine/physics/INTEPOLATION_SYSTEM_PLAN.md +287 -0
- package/src/engine/physics/PLAN.md +10 -9
- package/src/engine/physics/body/SolverBodyState.d.ts +142 -0
- package/src/engine/physics/body/SolverBodyState.d.ts.map +1 -0
- package/src/engine/physics/body/SolverBodyState.js +251 -0
- package/src/engine/physics/broadphase/generate_pairs.d.ts +2 -1
- package/src/engine/physics/broadphase/generate_pairs.d.ts.map +1 -1
- package/src/engine/physics/broadphase/generate_pairs.js +5 -3
- package/src/engine/physics/constraint/solve_constraints.d.ts.map +1 -1
- package/src/engine/physics/constraint/solve_constraints.js +691 -673
- package/src/engine/physics/ecs/PhysicsSystem.d.ts +82 -15
- package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
- package/src/engine/physics/ecs/PhysicsSystem.js +387 -87
- package/src/engine/physics/inertia/world_inverse_inertia.d.ts +23 -0
- package/src/engine/physics/inertia/world_inverse_inertia.d.ts.map +1 -1
- package/src/engine/physics/inertia/world_inverse_inertia.js +116 -77
- package/src/engine/physics/integration/integrate_position.d.ts +11 -1
- package/src/engine/physics/integration/integrate_position.d.ts.map +1 -1
- package/src/engine/physics/integration/integrate_position.js +97 -79
- package/src/engine/physics/integration/integrate_velocity.d.ts +12 -3
- package/src/engine/physics/integration/integrate_velocity.d.ts.map +1 -1
- package/src/engine/physics/integration/integrate_velocity.js +201 -160
- package/src/engine/physics/narrowphase/box_box_manifold.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/box_box_manifold.js +750 -665
- package/src/engine/physics/narrowphase/box_triangle_contact.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/box_triangle_contact.js +19 -46
- package/src/engine/physics/narrowphase/clip_against_axis_uv.d.ts +16 -0
- package/src/engine/physics/narrowphase/clip_against_axis_uv.d.ts.map +1 -0
- package/src/engine/physics/narrowphase/clip_against_axis_uv.js +49 -0
- package/src/engine/physics/narrowphase/narrowphase_step.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/narrowphase_step.js +52 -4
- package/src/engine/physics/queries/raycast.d.ts.map +1 -1
- package/src/engine/physics/queries/raycast.js +7 -4
- package/src/engine/physics/solver/solve_contacts.d.ts +2 -2
- package/src/engine/physics/solver/solve_contacts.d.ts.map +1 -1
- package/src/engine/physics/solver/solve_contacts.js +1341 -1173
- package/src/engine/network/sim/BinaryInterpolationAdapter.d.ts.map +0 -1
- package/src/engine/network/sim/InterpolationLog.d.ts.map +0 -1
- /package/src/engine/{network/sim → interpolation}/BinaryInterpolationAdapter.d.ts +0 -0
- /package/src/engine/{network/sim → interpolation}/BinaryInterpolationAdapter.js +0 -0
|
@@ -1,175 +1,183 @@
|
|
|
1
|
-
import { DEG_TO_RAD } from "../../../../core/math/DEG_TO_RAD.js";
|
|
2
|
-
import { Ability } from "./Ability.js";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Wall-run ability. Activates when the player is airborne with momentum
|
|
6
|
-
* and a wall is detected to their left or right. While active:
|
|
7
|
-
*
|
|
8
|
-
* - Gravity is reduced to `cfg.wallRun.gravityFactor × base` (typically
|
|
9
|
-
* 25%), letting the player hang on the wall for longer than a normal
|
|
10
|
-
* jump arc.
|
|
11
|
-
* - Horizontal velocity is projected onto the wall's tangent each tick,
|
|
12
|
-
* so the body sticks to the wall and slides along it. Forward
|
|
13
|
-
* momentum is preserved; into-wall and out-of-wall velocity
|
|
14
|
-
* components are zeroed.
|
|
15
|
-
* - Camera tilts into the wall via the shared lean spring (writes the
|
|
16
|
-
* wall-tilt to `runtime.leanTargetRad` each tick — same channel
|
|
17
|
-
* base writes its lat-accel-derived lean to, so transitions in and
|
|
18
|
-
* out share the spring's smoothing).
|
|
19
|
-
*
|
|
20
|
-
* Exits on:
|
|
21
|
-
* - Timer (`cfg.wallRun.maxDuration`)
|
|
22
|
-
* - Lost wall contact (sensor's hit flag becomes false)
|
|
23
|
-
* - Re-grounded (we walked onto a floor at the wall's end)
|
|
24
|
-
* - Wall-jump preempts (higher priority — WallJump declares priority 60)
|
|
25
|
-
*
|
|
26
|
-
* Priority 50 — above Mantle (30), Slide (10), below Wall-jump (60).
|
|
27
|
-
*
|
|
28
|
-
* @author Alex Goldring
|
|
29
|
-
* @copyright Company Named Limited (c) 2026
|
|
30
|
-
*/
|
|
31
|
-
export class WallRun extends Ability {
|
|
32
|
-
constructor() {
|
|
33
|
-
super();
|
|
34
|
-
this.name = "WallRun";
|
|
35
|
-
this.priority = 50;
|
|
36
|
-
|
|
37
|
-
/** @private Seconds since this ability activated. */
|
|
38
|
-
this._elapsed = 0;
|
|
39
|
-
/** @private "L" or "R" — which wall we're running on. */
|
|
40
|
-
this._side = "L";
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
canActivate(controller, runtime, sensors) {
|
|
44
|
-
const cfg = controller.config.wallRun;
|
|
45
|
-
if (!cfg) return false;
|
|
46
|
-
if (sensors === undefined || sensors === null) return false;
|
|
47
|
-
if (controller.state.grounded) return false;
|
|
48
|
-
if (controller.state.airborneTime < cfg.minAirborneTime) return false;
|
|
49
|
-
|
|
50
|
-
const speed = Math.hypot(runtime.velocityX, runtime.velocityZ);
|
|
51
|
-
if (speed < cfg.minSpeed) return false;
|
|
52
|
-
|
|
53
|
-
// Need a wall on left OR right (not front — that's a wall to bump into).
|
|
54
|
-
if (!sensors.wallLeft.hit && !sensors.wallRight.hit) return false;
|
|
55
|
-
|
|
56
|
-
//
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
//
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
// -- Exit:
|
|
123
|
-
if (
|
|
124
|
-
return false;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
//
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
//
|
|
138
|
-
//
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
//
|
|
147
|
-
//
|
|
148
|
-
//
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
//
|
|
155
|
-
//
|
|
156
|
-
//
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
//
|
|
160
|
-
//
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
//
|
|
164
|
-
//
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}
|
|
1
|
+
import { DEG_TO_RAD } from "../../../../core/math/DEG_TO_RAD.js";
|
|
2
|
+
import { Ability } from "./Ability.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Wall-run ability. Activates when the player is airborne with momentum
|
|
6
|
+
* and a wall is detected to their left or right. While active:
|
|
7
|
+
*
|
|
8
|
+
* - Gravity is reduced to `cfg.wallRun.gravityFactor × base` (typically
|
|
9
|
+
* 25%), letting the player hang on the wall for longer than a normal
|
|
10
|
+
* jump arc.
|
|
11
|
+
* - Horizontal velocity is projected onto the wall's tangent each tick,
|
|
12
|
+
* so the body sticks to the wall and slides along it. Forward
|
|
13
|
+
* momentum is preserved; into-wall and out-of-wall velocity
|
|
14
|
+
* components are zeroed.
|
|
15
|
+
* - Camera tilts into the wall via the shared lean spring (writes the
|
|
16
|
+
* wall-tilt to `runtime.leanTargetRad` each tick — same channel
|
|
17
|
+
* base writes its lat-accel-derived lean to, so transitions in and
|
|
18
|
+
* out share the spring's smoothing).
|
|
19
|
+
*
|
|
20
|
+
* Exits on:
|
|
21
|
+
* - Timer (`cfg.wallRun.maxDuration`)
|
|
22
|
+
* - Lost wall contact (sensor's hit flag becomes false)
|
|
23
|
+
* - Re-grounded (we walked onto a floor at the wall's end)
|
|
24
|
+
* - Wall-jump preempts (higher priority — WallJump declares priority 60)
|
|
25
|
+
*
|
|
26
|
+
* Priority 50 — above Mantle (30), Slide (10), below Wall-jump (60).
|
|
27
|
+
*
|
|
28
|
+
* @author Alex Goldring
|
|
29
|
+
* @copyright Company Named Limited (c) 2026
|
|
30
|
+
*/
|
|
31
|
+
export class WallRun extends Ability {
|
|
32
|
+
constructor() {
|
|
33
|
+
super();
|
|
34
|
+
this.name = "WallRun";
|
|
35
|
+
this.priority = 50;
|
|
36
|
+
|
|
37
|
+
/** @private Seconds since this ability activated. */
|
|
38
|
+
this._elapsed = 0;
|
|
39
|
+
/** @private "L" or "R" — which wall we're running on. */
|
|
40
|
+
this._side = "L";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
canActivate(controller, runtime, sensors) {
|
|
44
|
+
const cfg = controller.config.wallRun;
|
|
45
|
+
if (!cfg) return false;
|
|
46
|
+
if (sensors === undefined || sensors === null) return false;
|
|
47
|
+
if (controller.state.grounded) return false;
|
|
48
|
+
if (controller.state.airborneTime < cfg.minAirborneTime) return false;
|
|
49
|
+
|
|
50
|
+
const speed = Math.hypot(runtime.velocityX, runtime.velocityZ);
|
|
51
|
+
if (speed < cfg.minSpeed) return false;
|
|
52
|
+
|
|
53
|
+
// Need a wall on left OR right (not front — that's a wall to bump into).
|
|
54
|
+
if (!sensors.wallLeft.hit && !sensors.wallRight.hit) return false;
|
|
55
|
+
|
|
56
|
+
// Don't (re)grab the wall when a grabbable ledge is right ahead: you've
|
|
57
|
+
// reached its top, so ledge-grab/mantle should take over (hang or
|
|
58
|
+
// vault) instead of the run re-attaching and riding you back DOWN the
|
|
59
|
+
// wall you just climbed. Only bites when facing INTO the wall (the
|
|
60
|
+
// forward probe has to hit for a ledge ahead), so a normal run ALONG
|
|
61
|
+
// the wall — facing down its length — is unaffected.
|
|
62
|
+
if (sensors.ledgeAhead.hit) return false;
|
|
63
|
+
|
|
64
|
+
// Forward intent required — wall-run is a committed action.
|
|
65
|
+
if (controller.intent.move.y < 0.1) return false;
|
|
66
|
+
|
|
67
|
+
// Don't (re)grab a wall you're moving AWAY from. The wall normal
|
|
68
|
+
// points off the wall toward the player, so velocity·normal > 0 means
|
|
69
|
+
// leaving it — most importantly right after a wall-JUMP pushed you off
|
|
70
|
+
// (else the run would re-attach next tick and the tangent projection
|
|
71
|
+
// would cancel the kick). Approaching (dot < 0) or running along it
|
|
72
|
+
// (dot ≈ 0) is fine.
|
|
73
|
+
const hit = sensors.wallLeft.hit
|
|
74
|
+
&& (!sensors.wallRight.hit || sensors.wallLeft.distance <= sensors.wallRight.distance)
|
|
75
|
+
? sensors.wallLeft : sensors.wallRight;
|
|
76
|
+
const awaySpeed = runtime.velocityX * hit.normal.x + runtime.velocityZ * hit.normal.z;
|
|
77
|
+
if (awaySpeed > cfg.minSpeed * 0.5) return false;
|
|
78
|
+
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
onActivate(controller, runtime) {
|
|
83
|
+
const sensors = runtime.sensors;
|
|
84
|
+
this._elapsed = 0;
|
|
85
|
+
|
|
86
|
+
// Pick the closer wall if both detected.
|
|
87
|
+
if (sensors.wallLeft.hit
|
|
88
|
+
&& (!sensors.wallRight.hit
|
|
89
|
+
|| sensors.wallLeft.distance <= sensors.wallRight.distance)) {
|
|
90
|
+
this._side = "L";
|
|
91
|
+
} else {
|
|
92
|
+
this._side = "R";
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Clear any in-progress jump state — the wall-run takes over the
|
|
96
|
+
// vertical model.
|
|
97
|
+
runtime.midJump = false;
|
|
98
|
+
runtime.apexFired = false;
|
|
99
|
+
controller.state.isVariableJumpCut = false;
|
|
100
|
+
controller.state.isAscending = false;
|
|
101
|
+
|
|
102
|
+
controller.signals.onLeaveGround.send1({ reason: "wallrun" });
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Wall-jump (priority 60) should be allowed to preempt freely; that's
|
|
107
|
+
* the canonical chain (wall-run → wall-jump). Any other higher-priority
|
|
108
|
+
* ability is also fine — wall-run is a transient state.
|
|
109
|
+
*/
|
|
110
|
+
canInterrupt() {
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
tick(controller, runtime, bodyTransform, dt, system) {
|
|
115
|
+
const cfg = controller.config.wallRun;
|
|
116
|
+
this._elapsed += dt;
|
|
117
|
+
|
|
118
|
+
// -- Exit: timer.
|
|
119
|
+
if (this._elapsed >= cfg.maxDuration) {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
// -- Exit: re-grounded (we ran onto a floor).
|
|
123
|
+
if (controller.state.grounded) {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const sensors = runtime.sensors;
|
|
128
|
+
const sensorHit = this._side === "L" ? sensors.wallLeft : sensors.wallRight;
|
|
129
|
+
|
|
130
|
+
// -- Exit: lost the wall.
|
|
131
|
+
if (!sensorHit.hit) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// -- Project velocity onto the wall's tangent.
|
|
136
|
+
// The wall's normal is in the XZ plane (vertical wall). Zero the
|
|
137
|
+
// component of velocity along that normal. Forward momentum
|
|
138
|
+
// (along the wall's tangent direction) is preserved.
|
|
139
|
+
const nx = sensorHit.normal.x;
|
|
140
|
+
const nz = sensorHit.normal.z;
|
|
141
|
+
const dotN = runtime.velocityX * nx + runtime.velocityZ * nz;
|
|
142
|
+
runtime.velocityX -= dotN * nx;
|
|
143
|
+
runtime.velocityZ -= dotN * nz;
|
|
144
|
+
|
|
145
|
+
// -- Camera roll: write directly to the shared lean target.
|
|
146
|
+
// Sign convention: positive engine roll = head tilts RIGHT. Wall
|
|
147
|
+
// on LEFT → tilt LEFT → negative roll. Wall on RIGHT → tilt
|
|
148
|
+
// RIGHT → positive roll. L2.f spring-steps from this value each
|
|
149
|
+
// tick; base will overwrite once the ability releases, and the
|
|
150
|
+
// spring smooths the transition.
|
|
151
|
+
const rollSign = this._side === "L" ? -1 : 1;
|
|
152
|
+
runtime.leanTargetRad = rollSign * cfg.cameraRollDeg * DEG_TO_RAD;
|
|
153
|
+
|
|
154
|
+
// -- Reduced gravity. This is wall-run's OWN vertical model (a
|
|
155
|
+
// fraction of base gravity, no fall multiplier), applied here rather
|
|
156
|
+
// than via the system's standard gravity step.
|
|
157
|
+
runtime.velocityY -= runtime.gravity * cfg.gravityFactor * dt;
|
|
158
|
+
|
|
159
|
+
// -- Resolve the move through the shared motor. With the reduced-
|
|
160
|
+
// gravity velocity set above, route it through the same sweep-and-
|
|
161
|
+
// slide + ground-categorize the base locomotion uses (the mover when
|
|
162
|
+
// physics is present, else the flat-ground fallback). _resolveMotion
|
|
163
|
+
// does NOT re-apply gravity. This replaces a raw `position._add` plus
|
|
164
|
+
// a hand-rolled groundResolver catch: the wall-runner now (a) can't
|
|
165
|
+
// drift INTO geometry (the sweep clips it) nor sail OFF a ledge the
|
|
166
|
+
// mover would have caught, and (b) gets the motor's anti-tunnel
|
|
167
|
+
// ground catch for free — the same reason base no longer free-falls
|
|
168
|
+
// past a floor. The motor sets `state.grounded` and fires land/leave.
|
|
169
|
+
system._resolveMotion(controller, runtime, bodyTransform, dt);
|
|
170
|
+
|
|
171
|
+
// -- Exit: the motor caught a floor at the wall's foot (we ran out
|
|
172
|
+
// onto the ground). Release; base resumes next tick.
|
|
173
|
+
if (controller.state.grounded) {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// No onDeactivate cleanup needed: on the tick we release, base runs
|
|
181
|
+
// and overwrites runtime.leanTargetRad with the natural value. The
|
|
182
|
+
// lean spring smooths the transition.
|
|
183
|
+
}
|
|
@@ -51,6 +51,15 @@ export class FirstPersonSensors {
|
|
|
51
51
|
* obstacle's top finds empty space, indicating a grabbable edge.
|
|
52
52
|
*/
|
|
53
53
|
ledgeAhead: SensorHit;
|
|
54
|
+
/**
|
|
55
|
+
* True when `ledgeAhead`'s top surface is wide enough to STAND on
|
|
56
|
+
* (a standing capsule fits forward of the edge), false when it's a
|
|
57
|
+
* knife-edge / thin wall you could only hang from. Mantle reads this
|
|
58
|
+
* to refuse vaulting onto a top it would immediately fall off; ledge
|
|
59
|
+
* grab ignores it (you can hang from a thin edge). Default false so a
|
|
60
|
+
* mantle never fires on stale data.
|
|
61
|
+
*/
|
|
62
|
+
ledgeStandable: boolean;
|
|
54
63
|
/** Clear all sensor slots — call at the start of each tick. */
|
|
55
64
|
clearAll(): void;
|
|
56
65
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FirstPersonSensors.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/control/first-person/sensors/FirstPersonSensors.js"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH;IAEQ,aAAgB;IAChB,4DAA4D;IAC5D,iBAAiB;IACjB,iCAAiC;IACjC,eAA0B;IAC1B,yEAAyE;IACzE,gBAAkC;IAClC,6DAA6D;IAC7D,gBAAsB;IAG1B,mEAAmE;IACnE,cAMC;CACJ;AAED;;;;;;;;;;;;;;GAcG;AACH;IAEQ,2DAA2D;IAC3D,oBAA+B;IAC/B,4DAA4D;IAC5D,qBAAgC;IAChC,0CAA0C;IAC1C,qBAAgC;IAChC;;;;OAIG;IACH,yBAAoC;IACpC;;;;OAIG;IACH,sBAAiC;
|
|
1
|
+
{"version":3,"file":"FirstPersonSensors.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/control/first-person/sensors/FirstPersonSensors.js"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH;IAEQ,aAAgB;IAChB,4DAA4D;IAC5D,iBAAiB;IACjB,iCAAiC;IACjC,eAA0B;IAC1B,yEAAyE;IACzE,gBAAkC;IAClC,6DAA6D;IAC7D,gBAAsB;IAG1B,mEAAmE;IACnE,cAMC;CACJ;AAED;;;;;;;;;;;;;;GAcG;AACH;IAEQ,2DAA2D;IAC3D,oBAA+B;IAC/B,4DAA4D;IAC5D,qBAAgC;IAChC,0CAA0C;IAC1C,qBAAgC;IAChC;;;;OAIG;IACH,yBAAoC;IACpC;;;;OAIG;IACH,sBAAiC;IACjC;;;;;;;OAOG;IACH,wBAA2B;IAG/B,+DAA+D;IAC/D,iBAOC;CACJ;oBAtFmB,kCAAkC"}
|
|
@@ -1,77 +1,87 @@
|
|
|
1
|
-
import Vector3 from "../../../../core/geom/Vector3.js";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Single spatial-query result. Set fields when `hit` is true; ignore them
|
|
5
|
-
* otherwise. `surfaceTag` is optional — populated by future surface-tag
|
|
6
|
-
* systems (grass/wood/metal/etc.) so abilities and audio can react to the
|
|
7
|
-
* material.
|
|
8
|
-
*/
|
|
9
|
-
export class SensorHit {
|
|
10
|
-
constructor() {
|
|
11
|
-
this.hit = false;
|
|
12
|
-
/** Distance from query origin to contact point (meters). */
|
|
13
|
-
this.distance = 0;
|
|
14
|
-
/** World-space contact point. */
|
|
15
|
-
this.point = new Vector3();
|
|
16
|
-
/** Surface normal at contact (unit vector, points away from surface). */
|
|
17
|
-
this.normal = new Vector3(0, 1, 0);
|
|
18
|
-
/** Optional material/surface tag — e.g. "grass", "metal". */
|
|
19
|
-
this.surfaceTag = null;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/** Reset to "no hit" — call before re-using the slot each tick. */
|
|
23
|
-
clear() {
|
|
24
|
-
this.hit = false;
|
|
25
|
-
this.distance = 0;
|
|
26
|
-
this.point.set(0, 0, 0);
|
|
27
|
-
this.normal.set(0, 1, 0);
|
|
28
|
-
this.surfaceTag = null;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Cached spatial-query results for one first-person controller. Populated
|
|
34
|
-
* by {@link FirstPersonSensorsSystem} once per fixed step, read by
|
|
35
|
-
* abilities and (where useful) by the base controller.
|
|
36
|
-
*
|
|
37
|
-
* The point of cacheing is dedup: mantle and ledge-grab both want a
|
|
38
|
-
* "forward + up obstacle probe". Compute once, both read the cached hit.
|
|
39
|
-
*
|
|
40
|
-
* Sensor slots cleared each tick before population. Abilities should
|
|
41
|
-
* treat any `hit === false` slot as "absent" — never read distance/point
|
|
42
|
-
* when hit is false (they're stale or zero, depending on clear-mode).
|
|
43
|
-
*
|
|
44
|
-
* @author Alex Goldring
|
|
45
|
-
* @copyright Company Named Limited (c) 2026
|
|
46
|
-
*/
|
|
47
|
-
export class FirstPersonSensors {
|
|
48
|
-
constructor() {
|
|
49
|
-
/** Wall to the body-local left, probed at chest height. */
|
|
50
|
-
this.wallLeft = new SensorHit();
|
|
51
|
-
/** Wall to the body-local right, probed at chest height. */
|
|
52
|
-
this.wallRight = new SensorHit();
|
|
53
|
-
/** Wall directly in front of the body. */
|
|
54
|
-
this.wallFront = new SensorHit();
|
|
55
|
-
/**
|
|
56
|
-
* Obstacle ahead at chest/waist height — used by mantle/vault.
|
|
57
|
-
* Distance is along the body's forward direction; normal is the
|
|
58
|
-
* face of the obstacle the player is approaching.
|
|
59
|
-
*/
|
|
60
|
-
this.obstacleAhead = new SensorHit();
|
|
61
|
-
/**
|
|
62
|
-
* Ledge edge ahead — for ledge grab. Populated when the forward
|
|
63
|
-
* obstacle probe hits and a downward probe just past the
|
|
64
|
-
* obstacle's top finds empty space, indicating a grabbable edge.
|
|
65
|
-
*/
|
|
66
|
-
this.ledgeAhead = new SensorHit();
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
this.
|
|
76
|
-
}
|
|
77
|
-
|
|
1
|
+
import Vector3 from "../../../../core/geom/Vector3.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Single spatial-query result. Set fields when `hit` is true; ignore them
|
|
5
|
+
* otherwise. `surfaceTag` is optional — populated by future surface-tag
|
|
6
|
+
* systems (grass/wood/metal/etc.) so abilities and audio can react to the
|
|
7
|
+
* material.
|
|
8
|
+
*/
|
|
9
|
+
export class SensorHit {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.hit = false;
|
|
12
|
+
/** Distance from query origin to contact point (meters). */
|
|
13
|
+
this.distance = 0;
|
|
14
|
+
/** World-space contact point. */
|
|
15
|
+
this.point = new Vector3();
|
|
16
|
+
/** Surface normal at contact (unit vector, points away from surface). */
|
|
17
|
+
this.normal = new Vector3(0, 1, 0);
|
|
18
|
+
/** Optional material/surface tag — e.g. "grass", "metal". */
|
|
19
|
+
this.surfaceTag = null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Reset to "no hit" — call before re-using the slot each tick. */
|
|
23
|
+
clear() {
|
|
24
|
+
this.hit = false;
|
|
25
|
+
this.distance = 0;
|
|
26
|
+
this.point.set(0, 0, 0);
|
|
27
|
+
this.normal.set(0, 1, 0);
|
|
28
|
+
this.surfaceTag = null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Cached spatial-query results for one first-person controller. Populated
|
|
34
|
+
* by {@link FirstPersonSensorsSystem} once per fixed step, read by
|
|
35
|
+
* abilities and (where useful) by the base controller.
|
|
36
|
+
*
|
|
37
|
+
* The point of cacheing is dedup: mantle and ledge-grab both want a
|
|
38
|
+
* "forward + up obstacle probe". Compute once, both read the cached hit.
|
|
39
|
+
*
|
|
40
|
+
* Sensor slots cleared each tick before population. Abilities should
|
|
41
|
+
* treat any `hit === false` slot as "absent" — never read distance/point
|
|
42
|
+
* when hit is false (they're stale or zero, depending on clear-mode).
|
|
43
|
+
*
|
|
44
|
+
* @author Alex Goldring
|
|
45
|
+
* @copyright Company Named Limited (c) 2026
|
|
46
|
+
*/
|
|
47
|
+
export class FirstPersonSensors {
|
|
48
|
+
constructor() {
|
|
49
|
+
/** Wall to the body-local left, probed at chest height. */
|
|
50
|
+
this.wallLeft = new SensorHit();
|
|
51
|
+
/** Wall to the body-local right, probed at chest height. */
|
|
52
|
+
this.wallRight = new SensorHit();
|
|
53
|
+
/** Wall directly in front of the body. */
|
|
54
|
+
this.wallFront = new SensorHit();
|
|
55
|
+
/**
|
|
56
|
+
* Obstacle ahead at chest/waist height — used by mantle/vault.
|
|
57
|
+
* Distance is along the body's forward direction; normal is the
|
|
58
|
+
* face of the obstacle the player is approaching.
|
|
59
|
+
*/
|
|
60
|
+
this.obstacleAhead = new SensorHit();
|
|
61
|
+
/**
|
|
62
|
+
* Ledge edge ahead — for ledge grab. Populated when the forward
|
|
63
|
+
* obstacle probe hits and a downward probe just past the
|
|
64
|
+
* obstacle's top finds empty space, indicating a grabbable edge.
|
|
65
|
+
*/
|
|
66
|
+
this.ledgeAhead = new SensorHit();
|
|
67
|
+
/**
|
|
68
|
+
* True when `ledgeAhead`'s top surface is wide enough to STAND on
|
|
69
|
+
* (a standing capsule fits forward of the edge), false when it's a
|
|
70
|
+
* knife-edge / thin wall you could only hang from. Mantle reads this
|
|
71
|
+
* to refuse vaulting onto a top it would immediately fall off; ledge
|
|
72
|
+
* grab ignores it (you can hang from a thin edge). Default false so a
|
|
73
|
+
* mantle never fires on stale data.
|
|
74
|
+
*/
|
|
75
|
+
this.ledgeStandable = false;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Clear all sensor slots — call at the start of each tick. */
|
|
79
|
+
clearAll() {
|
|
80
|
+
this.wallLeft.clear();
|
|
81
|
+
this.wallRight.clear();
|
|
82
|
+
this.wallFront.clear();
|
|
83
|
+
this.obstacleAhead.clear();
|
|
84
|
+
this.ledgeAhead.clear();
|
|
85
|
+
this.ledgeStandable = false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -58,6 +58,14 @@ export class FirstPersonSensorsSystem extends System<any, any, any, any, any> {
|
|
|
58
58
|
* @private
|
|
59
59
|
*/
|
|
60
60
|
private _populateForEntity;
|
|
61
|
+
/**
|
|
62
|
+
* Down-probe a point on (or just past) a ledge top: returns true if a
|
|
63
|
+
* surface is still there near `edgeY` — i.e. the top is wide enough for a
|
|
64
|
+
* standing capsule. A miss (or a much-lower hit) means the surface dropped
|
|
65
|
+
* away: a thin wall / lip you can only hang from, not stand on.
|
|
66
|
+
* @private
|
|
67
|
+
*/
|
|
68
|
+
private _probeStandable;
|
|
61
69
|
/**
|
|
62
70
|
* Raycast through {@link PhysicsSystem.raycast}, populating the slot
|
|
63
71
|
* if anything is hit. Direction is assumed unit (the controller's
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FirstPersonSensorsSystem.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/control/first-person/sensors/FirstPersonSensorsSystem.js"],"names":[],"mappings":"AASA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH;IACI,cAsCC;IAnCG,wEAA4D;IAE5D;;;;;OAKG;IACH,kBAFU,iCAAiC,GAAC,IAAI,CAEpB;IAE5B;;;;;OAKG;IACH,eAFU,aAAa,GAAC,IAAI,CAEH;IAEzB;;;;;;OAMG;IACH,mBAA4B;IAE5B;;;;;;OAMG;IACH,mBAA2C;IAG/C,2CAaC;IAED,2BAIC;IAED;;OAEG;IACH,
|
|
1
|
+
{"version":3,"file":"FirstPersonSensorsSystem.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/control/first-person/sensors/FirstPersonSensorsSystem.js"],"names":[],"mappings":"AASA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH;IACI,cAsCC;IAnCG,wEAA4D;IAE5D;;;;;OAKG;IACH,kBAFU,iCAAiC,GAAC,IAAI,CAEpB;IAE5B;;;;;OAKG;IACH,eAFU,aAAa,GAAC,IAAI,CAEH;IAEzB;;;;;;OAMG;IACH,mBAA4B;IAE5B;;;;;;OAMG;IACH,mBAA2C;IAG/C,2CAaC;IAED,2BAIC;IAED;;OAEG;IACH,2BAmFC;IAED;;;;;;OAMG;IACH,wBASC;IAED;;;;;;;;;;;OAWG;IACH,kBAiBC;CACJ;uBAhOsB,wBAAwB;0BACrB,qCAAqC;4CACnB,mCAAmC;kDAC7B,yCAAyC;8BAL7D,uCAAuC"}
|