@jgengine/core 0.8.0 → 0.9.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/CHANGELOG.md +16 -0
- package/dist/ai/flock.d.ts +36 -0
- package/dist/ai/flock.js +182 -0
- package/dist/ai/groupAssist.d.ts +19 -0
- package/dist/ai/groupAssist.js +59 -0
- package/dist/ai/laneSelect.d.ts +21 -0
- package/dist/ai/laneSelect.js +37 -0
- package/dist/ai/mobBrain.d.ts +68 -0
- package/dist/ai/mobBrain.js +115 -0
- package/dist/ai/spawnDirector.js +1 -1
- package/dist/ai/threat.d.ts +2 -0
- package/dist/ai/threat.js +34 -0
- package/dist/cartridge/runtime.d.ts +15 -0
- package/dist/cartridge/runtime.js +556 -0
- package/dist/cartridge/spec.d.ts +188 -0
- package/dist/cartridge/spec.js +19 -0
- package/dist/cartridge/validate.d.ts +2 -0
- package/dist/cartridge/validate.js +78 -0
- package/dist/combat/abilityKit.d.ts +17 -2
- package/dist/combat/abilityKit.js +62 -13
- package/dist/combat/castRunner.d.ts +40 -0
- package/dist/combat/castRunner.js +56 -0
- package/dist/combat/comboPoints.d.ts +14 -0
- package/dist/combat/comboPoints.js +52 -0
- package/dist/combat/defensiveWindow.d.ts +1 -1
- package/dist/combat/defensiveWindow.js +5 -3
- package/dist/combat/projectiles.d.ts +33 -12
- package/dist/combat/projectiles.js +201 -129
- package/dist/combat/resistance.d.ts +16 -4
- package/dist/combat/resistance.js +43 -7
- package/dist/combat/resourcePool.d.ts +20 -0
- package/dist/combat/resourcePool.js +55 -0
- package/dist/combat/shotOrigin.d.ts +40 -0
- package/dist/combat/shotOrigin.js +57 -0
- package/dist/combat/telegraph.d.ts +25 -0
- package/dist/combat/telegraph.js +47 -0
- package/dist/crafting/recipe.js +4 -1
- package/dist/devtools/devtools.d.ts +72 -24
- package/dist/devtools/devtools.js +449 -101
- package/dist/devtools/transformTunables.js +3 -3
- package/dist/devtools/tunableSchema.d.ts +140 -0
- package/dist/devtools/tunableSchema.js +476 -0
- package/dist/faction/factions.d.ts +33 -0
- package/dist/faction/factions.js +76 -0
- package/dist/faction/reputation.d.ts +32 -0
- package/dist/faction/reputation.js +82 -0
- package/dist/game/chat.d.ts +2 -0
- package/dist/game/chat.js +9 -1
- package/dist/game/chatFilter.d.ts +15 -0
- package/dist/game/chatFilter.js +55 -0
- package/dist/game/controlGate.d.ts +4 -0
- package/dist/game/controlGate.js +7 -0
- package/dist/game/defineGame.d.ts +2 -0
- package/dist/game/events.d.ts +10 -0
- package/dist/game/gamePhase.d.ts +20 -0
- package/dist/game/gamePhase.js +21 -0
- package/dist/game/lootTable.d.ts +10 -1
- package/dist/game/lootTable.js +27 -7
- package/dist/game/playableGame.d.ts +50 -1
- package/dist/game/playableGame.js +1 -1
- package/dist/game/quest.d.ts +7 -3
- package/dist/game/quest.js +17 -8
- package/dist/game/race.d.ts +39 -1
- package/dist/game/race.js +96 -11
- package/dist/game/recordBook.d.ts +29 -0
- package/dist/game/recordBook.js +86 -0
- package/dist/game/talents.d.ts +47 -0
- package/dist/game/talents.js +140 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/input/axisInput.d.ts +26 -3
- package/dist/input/axisInput.js +44 -4
- package/dist/input/bindingOverrides.d.ts +25 -0
- package/dist/input/bindingOverrides.js +95 -0
- package/dist/input/pointerAxis.d.ts +30 -0
- package/dist/input/pointerAxis.js +31 -0
- package/dist/input/touchScheme.d.ts +6 -0
- package/dist/input/touchScheme.js +45 -7
- package/dist/meta/changelog.d.ts +1 -1
- package/dist/meta/changelog.js +23 -1
- package/dist/movement/dash.d.ts +3 -0
- package/dist/movement/dash.js +19 -5
- package/dist/movement/glideModel.d.ts +44 -0
- package/dist/movement/glideModel.js +54 -0
- package/dist/movement/grappleSwing.d.ts +34 -0
- package/dist/movement/grappleSwing.js +56 -0
- package/dist/movement/leaderTrail.d.ts +29 -0
- package/dist/movement/leaderTrail.js +67 -0
- package/dist/movement/steering.d.ts +27 -0
- package/dist/movement/steering.js +40 -0
- package/dist/multiplayer/feedWriteGate.d.ts +10 -0
- package/dist/multiplayer/feedWriteGate.js +12 -0
- package/dist/multiplayer/presenceModel.d.ts +14 -2
- package/dist/multiplayer/presenceModel.js +15 -3
- package/dist/nav/corridors.d.ts +30 -0
- package/dist/nav/corridors.js +69 -0
- package/dist/nav/navGrid.js +30 -4
- package/dist/nav/railGraph.d.ts +66 -0
- package/dist/nav/railGraph.js +129 -0
- package/dist/nav/timetable.d.ts +41 -0
- package/dist/nav/timetable.js +94 -0
- package/dist/physics/buoyancy.d.ts +6 -0
- package/dist/physics/buoyancy.js +14 -0
- package/dist/physics/flowTube.d.ts +32 -0
- package/dist/physics/flowTube.js +64 -0
- package/dist/physics/forceVolume.d.ts +27 -0
- package/dist/physics/forceVolume.js +38 -0
- package/dist/physics/kinematicVehicle.d.ts +52 -0
- package/dist/physics/kinematicVehicle.js +81 -0
- package/dist/physics/physicsWorld.d.ts +13 -6
- package/dist/physics/physicsWorld.js +169 -40
- package/dist/physics/ragdoll.js +1 -1
- package/dist/physics/vehicleBody.js +2 -1
- package/dist/random/seedLink.d.ts +5 -0
- package/dist/random/seedLink.js +35 -0
- package/dist/runtime/cameraDirector.d.ts +6 -1
- package/dist/runtime/cameraDirector.js +6 -0
- package/dist/runtime/commandRunner.d.ts +2 -2
- package/dist/runtime/commandRunner.js +2 -2
- package/dist/runtime/gameContext.d.ts +21 -0
- package/dist/runtime/gameContext.js +184 -9
- package/dist/runtime/gameRuntime.d.ts +5 -1
- package/dist/runtime/gameRuntime.js +13 -12
- package/dist/runtime/hostPersistence.d.ts +9 -1
- package/dist/runtime/hostPersistence.js +23 -8
- package/dist/runtime/inputSnapshot.d.ts +5 -0
- package/dist/runtime/inputSnapshot.js +5 -0
- package/dist/runtime/motionIntents.d.ts +14 -6
- package/dist/runtime/motionIntents.js +20 -2
- package/dist/runtime/snapshot.d.ts +1 -0
- package/dist/runtime/snapshot.js +1 -1
- package/dist/runtime/visibility.d.ts +74 -0
- package/dist/runtime/visibility.js +127 -0
- package/dist/scene/colliders.d.ts +44 -0
- package/dist/scene/colliders.js +77 -0
- package/dist/scene/entityStore.d.ts +22 -5
- package/dist/scene/entityStore.js +59 -12
- package/dist/scene/objectQuery.d.ts +13 -2
- package/dist/scene/objectQuery.js +34 -2
- package/dist/scene/objectStore.d.ts +2 -0
- package/dist/scene/objectStore.js +31 -12
- package/dist/scene/sceneRaycast.d.ts +80 -0
- package/dist/scene/sceneRaycast.js +284 -0
- package/dist/scene/spatial.d.ts +11 -16
- package/dist/scene/spatial.js +99 -38
- package/dist/sensor/recordingBuffer.d.ts +6 -0
- package/dist/sensor/recordingBuffer.js +7 -0
- package/dist/sensor/replayLoop.d.ts +39 -0
- package/dist/sensor/replayLoop.js +64 -0
- package/dist/sensor/visionCone.d.ts +31 -0
- package/dist/sensor/visionCone.js +72 -0
- package/dist/session/extraction.d.ts +2 -2
- package/dist/session/extraction.js +13 -2
- package/dist/settings/settingsModel.d.ts +97 -0
- package/dist/settings/settingsModel.js +100 -0
- package/dist/stats/statModifiers.d.ts +5 -2
- package/dist/stats/statModifiers.js +32 -14
- package/dist/store/observableKeyedStore.js +8 -2
- package/dist/survival/decayMeter.d.ts +1 -1
- package/dist/survival/decayMeter.js +0 -1
- package/dist/time/beatClock.d.ts +18 -0
- package/dist/time/beatClock.js +24 -0
- package/dist/time/idleProgress.d.ts +29 -0
- package/dist/time/idleProgress.js +31 -0
- package/dist/time/serverTick.d.ts +7 -1
- package/dist/time/serverTick.js +27 -4
- package/dist/time/stateSchedule.d.ts +57 -0
- package/dist/time/stateSchedule.js +121 -0
- package/dist/turn/commit.d.ts +2 -2
- package/dist/turn/commit.js +2 -2
- package/dist/ui/hudLayout.d.ts +58 -0
- package/dist/ui/hudLayout.js +207 -0
- package/dist/ui/hudScale.d.ts +44 -0
- package/dist/ui/hudScale.js +55 -0
- package/dist/visibility/assetStreaming.d.ts +66 -0
- package/dist/visibility/assetStreaming.js +201 -0
- package/dist/visibility/bounds.d.ts +66 -0
- package/dist/visibility/bounds.js +113 -0
- package/dist/visibility/camera.d.ts +21 -0
- package/dist/visibility/camera.js +3 -0
- package/dist/visibility/config.d.ts +21 -0
- package/dist/visibility/config.js +1 -0
- package/dist/visibility/diagnostics.d.ts +26 -0
- package/dist/visibility/diagnostics.js +36 -0
- package/dist/visibility/distance.d.ts +11 -0
- package/dist/visibility/distance.js +30 -0
- package/dist/visibility/frustum.d.ts +47 -0
- package/dist/visibility/frustum.js +186 -0
- package/dist/visibility/occlusion.d.ts +36 -0
- package/dist/visibility/occlusion.js +45 -0
- package/dist/visibility/settings.d.ts +75 -0
- package/dist/visibility/settings.js +49 -0
- package/dist/visibility/simulationCulling.d.ts +34 -0
- package/dist/visibility/simulationCulling.js +36 -0
- package/dist/visibility/spatialIndex.d.ts +45 -0
- package/dist/visibility/spatialIndex.js +198 -0
- package/dist/visibility/visibilitySystem.d.ts +90 -0
- package/dist/visibility/visibilitySystem.js +257 -0
- package/dist/world/buildings.d.ts +6 -0
- package/dist/world/buildings.js +152 -0
- package/dist/world/carve.d.ts +7 -2
- package/dist/world/carve.js +17 -7
- package/dist/world/cellStates.d.ts +45 -0
- package/dist/world/cellStates.js +103 -0
- package/dist/world/environmentSummary.d.ts +10 -2
- package/dist/world/environmentSummary.js +11 -2
- package/dist/world/features.d.ts +56 -8
- package/dist/world/features.js +24 -3
- package/dist/world/gridInstances.d.ts +7 -8
- package/dist/world/gridInstances.js +145 -7
- package/dist/world/lod.d.ts +17 -0
- package/dist/world/lod.js +66 -0
- package/dist/world/mapLayers.d.ts +60 -0
- package/dist/world/mapLayers.js +44 -0
- package/dist/world/massing.d.ts +45 -0
- package/dist/world/massing.js +581 -0
- package/dist/world/minimap.d.ts +13 -1
- package/dist/world/minimap.js +21 -2
- package/dist/world/terrain.d.ts +42 -3
- package/dist/world/terrain.js +197 -16
- package/dist/world/water.d.ts +7 -0
- package/dist/world/water.js +19 -4
- package/dist/world/weather.d.ts +3 -18
- package/dist/world/weather.js +4 -17
- package/dist/world/windZones.d.ts +47 -0
- package/dist/world/windZones.js +59 -0
- package/llms.txt +2585 -1704
- package/package.json +12 -2
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
export const SHAPE_BOX = 0;
|
|
2
2
|
export const SHAPE_SPHERE = 1;
|
|
3
|
+
export const JOINT_FIXED = 0;
|
|
4
|
+
export const JOINT_HINGE = 1;
|
|
5
|
+
export const JOINT_DISTANCE = 2;
|
|
6
|
+
export const JOINT_SPRING = 3;
|
|
3
7
|
const FLAG_SLEEPING = 1;
|
|
4
8
|
const FLAG_STATIC = 2;
|
|
5
9
|
const FLAG_DEAD = 4;
|
|
@@ -65,7 +69,7 @@ export class PhysicsWorld {
|
|
|
65
69
|
awakeList;
|
|
66
70
|
awakeSlot;
|
|
67
71
|
awakeCount = 0;
|
|
68
|
-
|
|
72
|
+
jKind;
|
|
69
73
|
jActive;
|
|
70
74
|
jBodyA;
|
|
71
75
|
jBodyB;
|
|
@@ -75,6 +79,9 @@ export class PhysicsWorld {
|
|
|
75
79
|
jAnchorBX;
|
|
76
80
|
jAnchorBY;
|
|
77
81
|
jAnchorBZ;
|
|
82
|
+
jAxisX;
|
|
83
|
+
jAxisY;
|
|
84
|
+
jAxisZ;
|
|
78
85
|
jRest;
|
|
79
86
|
jStiffness;
|
|
80
87
|
jDamping;
|
|
@@ -168,7 +175,7 @@ export class PhysicsWorld {
|
|
|
168
175
|
this.awakeSlot = new Int32Array(cap).fill(-1);
|
|
169
176
|
this.bodyFree = new Int32Array(cap);
|
|
170
177
|
const jc = this.jointCapacity;
|
|
171
|
-
this.
|
|
178
|
+
this.jKind = new Uint8Array(jc);
|
|
172
179
|
this.jActive = new Uint8Array(jc);
|
|
173
180
|
this.jBodyA = new Int32Array(jc);
|
|
174
181
|
this.jBodyB = new Int32Array(jc);
|
|
@@ -178,6 +185,9 @@ export class PhysicsWorld {
|
|
|
178
185
|
this.jAnchorBX = new Float32Array(jc);
|
|
179
186
|
this.jAnchorBY = new Float32Array(jc);
|
|
180
187
|
this.jAnchorBZ = new Float32Array(jc);
|
|
188
|
+
this.jAxisX = new Float32Array(jc);
|
|
189
|
+
this.jAxisY = new Float32Array(jc);
|
|
190
|
+
this.jAxisZ = new Float32Array(jc);
|
|
181
191
|
this.jRest = new Float32Array(jc);
|
|
182
192
|
this.jStiffness = new Float32Array(jc);
|
|
183
193
|
this.jDamping = new Float32Array(jc);
|
|
@@ -224,18 +234,25 @@ export class PhysicsWorld {
|
|
|
224
234
|
this.collisionMinSpeed = minApproachSpeed;
|
|
225
235
|
}
|
|
226
236
|
hingeJoint(options) {
|
|
227
|
-
|
|
237
|
+
const raw = options.axis ?? [0, 1, 0];
|
|
238
|
+
const len = Math.hypot(raw[0], raw[1], raw[2]);
|
|
239
|
+
if (!(len > 1e-9))
|
|
240
|
+
throw new Error("PhysicsWorld: hingeJoint axis must be non-zero");
|
|
241
|
+
return this.addJoint(options, JOINT_HINGE, 0, [raw[0] / len, raw[1] / len, raw[2] / len]);
|
|
228
242
|
}
|
|
229
243
|
fixedJoint(options) {
|
|
230
|
-
return this.addJoint(options,
|
|
244
|
+
return this.addJoint(options, JOINT_FIXED, 0, null);
|
|
231
245
|
}
|
|
232
246
|
distanceJoint(options) {
|
|
233
|
-
return this.addJoint(options,
|
|
247
|
+
return this.addJoint(options, JOINT_DISTANCE, null, null);
|
|
234
248
|
}
|
|
235
249
|
springJoint(options) {
|
|
236
|
-
return this.addJoint(options,
|
|
250
|
+
return this.addJoint(options, JOINT_SPRING, null, null);
|
|
237
251
|
}
|
|
238
|
-
addJoint(options,
|
|
252
|
+
addJoint(options, kind, restOverride, axis) {
|
|
253
|
+
if (kind !== JOINT_FIXED && kind !== JOINT_HINGE && kind !== JOINT_DISTANCE && kind !== JOINT_SPRING) {
|
|
254
|
+
throw new Error(`PhysicsWorld: joint kind ${kind} is not implemented`);
|
|
255
|
+
}
|
|
239
256
|
let id;
|
|
240
257
|
if (this.jFreeCount > 0) {
|
|
241
258
|
id = this.jFree[--this.jFreeCount];
|
|
@@ -256,7 +273,10 @@ export class PhysicsWorld {
|
|
|
256
273
|
this.jAnchorBX[id] = aB?.[0] ?? 0;
|
|
257
274
|
this.jAnchorBY[id] = aB?.[1] ?? 0;
|
|
258
275
|
this.jAnchorBZ[id] = aB?.[2] ?? 0;
|
|
259
|
-
this.
|
|
276
|
+
this.jKind[id] = kind;
|
|
277
|
+
this.jAxisX[id] = axis?.[0] ?? 0;
|
|
278
|
+
this.jAxisY[id] = axis?.[1] ?? 0;
|
|
279
|
+
this.jAxisZ[id] = axis?.[2] ?? 0;
|
|
260
280
|
this.jStiffness[id] = options.stiffness ?? 40;
|
|
261
281
|
this.jDamping[id] = options.damping ?? 6;
|
|
262
282
|
this.jMaxImpulse[id] = options.maxImpulse ?? Number.POSITIVE_INFINITY;
|
|
@@ -587,28 +607,56 @@ export class PhysicsWorld {
|
|
|
587
607
|
const n = this.bodyHigh;
|
|
588
608
|
const start = this.cellStart;
|
|
589
609
|
start.fill(0);
|
|
590
|
-
// Sleeping bodies never move, so their cell index is cached from addBody / their last awake
|
|
591
|
-
// substep — only awake bodies pay the cell recomputation each substep.
|
|
592
610
|
const awakeCount = this.awakeCount;
|
|
593
611
|
for (let a = 0; a < awakeCount; a += 1) {
|
|
594
612
|
const i = this.awakeList[a];
|
|
595
613
|
this.bodyCell[i] = this.cellOf(i);
|
|
596
614
|
}
|
|
597
|
-
|
|
615
|
+
const nx = this.nx;
|
|
616
|
+
const ny = this.ny;
|
|
598
617
|
for (let i = 0; i < n; i += 1) {
|
|
599
618
|
if ((this.flags[i] & FLAG_DEAD) !== 0)
|
|
600
619
|
continue;
|
|
601
|
-
|
|
620
|
+
const x0 = cellCoord(this.posX[i] - this.halfX[i], this.bounds.min[0], this.cellSize, this.nx);
|
|
621
|
+
const x1 = cellCoord(this.posX[i] + this.halfX[i], this.bounds.min[0], this.cellSize, this.nx);
|
|
622
|
+
const y0 = cellCoord(this.posY[i] - this.halfY[i], this.bounds.min[1], this.cellSize, this.ny);
|
|
623
|
+
const y1 = cellCoord(this.posY[i] + this.halfY[i], this.bounds.min[1], this.cellSize, this.ny);
|
|
624
|
+
const z0 = cellCoord(this.posZ[i] - this.halfZ[i], this.bounds.min[2], this.cellSize, this.nz);
|
|
625
|
+
const z1 = cellCoord(this.posZ[i] + this.halfZ[i], this.bounds.min[2], this.cellSize, this.nz);
|
|
626
|
+
for (let z = z0; z <= z1; z += 1) {
|
|
627
|
+
for (let y = y0; y <= y1; y += 1) {
|
|
628
|
+
const rowBase = (z * ny + y) * nx;
|
|
629
|
+
for (let x = x0; x <= x1; x += 1)
|
|
630
|
+
start[rowBase + x + 1] += 1;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
602
633
|
}
|
|
603
634
|
for (let c = 0; c < this.numCells; c += 1) {
|
|
604
635
|
start[c + 1] += start[c];
|
|
605
636
|
this.cursor[c] = start[c];
|
|
606
637
|
}
|
|
638
|
+
const total = start[this.numCells];
|
|
639
|
+
if (total > this.sorted.length) {
|
|
640
|
+
this.sorted = new Int32Array(Math.max(total, this.sorted.length * 2));
|
|
641
|
+
}
|
|
607
642
|
for (let i = 0; i < n; i += 1) {
|
|
608
643
|
if ((this.flags[i] & FLAG_DEAD) !== 0)
|
|
609
644
|
continue;
|
|
610
|
-
const
|
|
611
|
-
this.
|
|
645
|
+
const x0 = cellCoord(this.posX[i] - this.halfX[i], this.bounds.min[0], this.cellSize, this.nx);
|
|
646
|
+
const x1 = cellCoord(this.posX[i] + this.halfX[i], this.bounds.min[0], this.cellSize, this.nx);
|
|
647
|
+
const y0 = cellCoord(this.posY[i] - this.halfY[i], this.bounds.min[1], this.cellSize, this.ny);
|
|
648
|
+
const y1 = cellCoord(this.posY[i] + this.halfY[i], this.bounds.min[1], this.cellSize, this.ny);
|
|
649
|
+
const z0 = cellCoord(this.posZ[i] - this.halfZ[i], this.bounds.min[2], this.cellSize, this.nz);
|
|
650
|
+
const z1 = cellCoord(this.posZ[i] + this.halfZ[i], this.bounds.min[2], this.cellSize, this.nz);
|
|
651
|
+
for (let z = z0; z <= z1; z += 1) {
|
|
652
|
+
for (let y = y0; y <= y1; y += 1) {
|
|
653
|
+
const rowBase = (z * ny + y) * nx;
|
|
654
|
+
for (let x = x0; x <= x1; x += 1) {
|
|
655
|
+
const c = rowBase + x;
|
|
656
|
+
this.sorted[this.cursor[c]++] = i;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
}
|
|
612
660
|
}
|
|
613
661
|
}
|
|
614
662
|
cellOf(i) {
|
|
@@ -620,26 +668,18 @@ export class PhysicsWorld {
|
|
|
620
668
|
solve(restitutionPass, countPass) {
|
|
621
669
|
const nx = this.nx;
|
|
622
670
|
const ny = this.ny;
|
|
623
|
-
const nz = this.nz;
|
|
624
|
-
const nxny = nx * ny;
|
|
625
|
-
// A contact may wake a sleeping body mid-pass, appending it to the awake list; re-read the
|
|
626
|
-
// count each iteration so it is solved this substep too.
|
|
627
671
|
for (let a = 0; a < this.awakeCount; a += 1) {
|
|
628
672
|
const i = this.awakeList[a];
|
|
629
|
-
const
|
|
630
|
-
const
|
|
631
|
-
const
|
|
632
|
-
const
|
|
633
|
-
const
|
|
634
|
-
const
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
const x0 = cx > 0 ? cx - 1 : cx;
|
|
638
|
-
const x1 = cx < nx - 1 ? cx + 1 : cx;
|
|
639
|
-
for (let z = z0; z <= z1; z += 1) {
|
|
640
|
-
for (let y = y0; y <= y1; y += 1) {
|
|
673
|
+
const ix0 = cellCoord(this.posX[i] - this.halfX[i], this.bounds.min[0], this.cellSize, this.nx);
|
|
674
|
+
const ix1 = cellCoord(this.posX[i] + this.halfX[i], this.bounds.min[0], this.cellSize, this.nx);
|
|
675
|
+
const iy0 = cellCoord(this.posY[i] - this.halfY[i], this.bounds.min[1], this.cellSize, this.ny);
|
|
676
|
+
const iy1 = cellCoord(this.posY[i] + this.halfY[i], this.bounds.min[1], this.cellSize, this.ny);
|
|
677
|
+
const iz0 = cellCoord(this.posZ[i] - this.halfZ[i], this.bounds.min[2], this.cellSize, this.nz);
|
|
678
|
+
const iz1 = cellCoord(this.posZ[i] + this.halfZ[i], this.bounds.min[2], this.cellSize, this.nz);
|
|
679
|
+
for (let z = iz0; z <= iz1; z += 1) {
|
|
680
|
+
for (let y = iy0; y <= iy1; y += 1) {
|
|
641
681
|
const rowBase = (z * ny + y) * nx;
|
|
642
|
-
for (let x =
|
|
682
|
+
for (let x = ix0; x <= ix1; x += 1) {
|
|
643
683
|
const cell = rowBase + x;
|
|
644
684
|
const end = this.cellStart[cell + 1];
|
|
645
685
|
for (let s = this.cellStart[cell]; s < end; s += 1) {
|
|
@@ -649,6 +689,14 @@ export class PhysicsWorld {
|
|
|
649
689
|
const jSleeping = (this.flags[j] & FLAG_SLEEPING) !== 0;
|
|
650
690
|
if (!jSleeping && j < i)
|
|
651
691
|
continue;
|
|
692
|
+
const jx0 = cellCoord(this.posX[j] - this.halfX[j], this.bounds.min[0], this.cellSize, this.nx);
|
|
693
|
+
const jy0 = cellCoord(this.posY[j] - this.halfY[j], this.bounds.min[1], this.cellSize, this.ny);
|
|
694
|
+
const jz0 = cellCoord(this.posZ[j] - this.halfZ[j], this.bounds.min[2], this.cellSize, this.nz);
|
|
695
|
+
const mx0 = ix0 > jx0 ? ix0 : jx0;
|
|
696
|
+
const my0 = iy0 > jy0 ? iy0 : jy0;
|
|
697
|
+
const mz0 = iz0 > jz0 ? iz0 : jz0;
|
|
698
|
+
if (x !== mx0 || y !== my0 || z !== mz0)
|
|
699
|
+
continue;
|
|
652
700
|
this.resolve(i, j, restitutionPass, countPass);
|
|
653
701
|
}
|
|
654
702
|
}
|
|
@@ -819,6 +867,7 @@ export class PhysicsWorld {
|
|
|
819
867
|
for (let id = 0; id < this.jointHigh; id += 1) {
|
|
820
868
|
if (this.jActive[id] !== 1)
|
|
821
869
|
continue;
|
|
870
|
+
const kind = this.jKind[id];
|
|
822
871
|
const bA = this.jBodyA[id];
|
|
823
872
|
const bB = this.jBodyB[id];
|
|
824
873
|
const ax = (bA >= 0 ? this.posX[bA] : 0) + this.jAnchorAX[id];
|
|
@@ -827,9 +876,91 @@ export class PhysicsWorld {
|
|
|
827
876
|
const bx = bB >= 0 ? this.posX[bB] + this.jAnchorBX[id] : this.jAnchorBX[id];
|
|
828
877
|
const by = bB >= 0 ? this.posY[bB] + this.jAnchorBY[id] : this.jAnchorBY[id];
|
|
829
878
|
const bz = bB >= 0 ? this.posZ[bB] + this.jAnchorBZ[id] : this.jAnchorBZ[id];
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
879
|
+
let dx = bx - ax;
|
|
880
|
+
let dy = by - ay;
|
|
881
|
+
let dz = bz - az;
|
|
882
|
+
const imA = bA >= 0 ? this.invMass[bA] : 0;
|
|
883
|
+
const imB = bB >= 0 ? this.invMass[bB] : 0;
|
|
884
|
+
const imSum = imA + imB;
|
|
885
|
+
if (imSum === 0)
|
|
886
|
+
continue;
|
|
887
|
+
if (kind === JOINT_HINGE) {
|
|
888
|
+
const ux = this.jAxisX[id];
|
|
889
|
+
const uy = this.jAxisY[id];
|
|
890
|
+
const uz = this.jAxisZ[id];
|
|
891
|
+
const along = dx * ux + dy * uy + dz * uz;
|
|
892
|
+
dx -= along * ux;
|
|
893
|
+
dy -= along * uy;
|
|
894
|
+
dz -= along * uz;
|
|
895
|
+
const dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
896
|
+
const vax = bA >= 0 ? this.velX[bA] : 0;
|
|
897
|
+
const vay = bA >= 0 ? this.velY[bA] : 0;
|
|
898
|
+
const vaz = bA >= 0 ? this.velZ[bA] : 0;
|
|
899
|
+
const vbx = bB >= 0 ? this.velX[bB] : 0;
|
|
900
|
+
const vby = bB >= 0 ? this.velY[bB] : 0;
|
|
901
|
+
const vbz = bB >= 0 ? this.velZ[bB] : 0;
|
|
902
|
+
let rvx = vbx - vax;
|
|
903
|
+
let rvy = vby - vay;
|
|
904
|
+
let rvz = vbz - vaz;
|
|
905
|
+
const rvAlong = rvx * ux + rvy * uy + rvz * uz;
|
|
906
|
+
rvx -= rvAlong * ux;
|
|
907
|
+
rvy -= rvAlong * uy;
|
|
908
|
+
rvz -= rvAlong * uz;
|
|
909
|
+
if (dist > 1e-9) {
|
|
910
|
+
const nX = dx / dist;
|
|
911
|
+
const nY = dy / dist;
|
|
912
|
+
const nZ = dz / dist;
|
|
913
|
+
if (dist > slop) {
|
|
914
|
+
if (bA >= 0)
|
|
915
|
+
this.wake(bA);
|
|
916
|
+
if (bB >= 0)
|
|
917
|
+
this.wake(bB);
|
|
918
|
+
}
|
|
919
|
+
const rvn = rvx * nX + rvy * nY + rvz * nZ;
|
|
920
|
+
const jn = -rvn / imSum;
|
|
921
|
+
if (bA >= 0) {
|
|
922
|
+
this.velX[bA] -= jn * imA * nX;
|
|
923
|
+
this.velY[bA] -= jn * imA * nY;
|
|
924
|
+
this.velZ[bA] -= jn * imA * nZ;
|
|
925
|
+
}
|
|
926
|
+
if (bB >= 0) {
|
|
927
|
+
this.velX[bB] += jn * imB * nX;
|
|
928
|
+
this.velY[bB] += jn * imB * nY;
|
|
929
|
+
this.velZ[bB] += jn * imB * nZ;
|
|
930
|
+
}
|
|
931
|
+
const corr = (dist * beta) / imSum;
|
|
932
|
+
if (bA >= 0) {
|
|
933
|
+
this.posX[bA] += corr * imA * nX;
|
|
934
|
+
this.posY[bA] += corr * imA * nY;
|
|
935
|
+
this.posZ[bA] += corr * imA * nZ;
|
|
936
|
+
}
|
|
937
|
+
if (bB >= 0) {
|
|
938
|
+
this.posX[bB] -= corr * imB * nX;
|
|
939
|
+
this.posY[bB] -= corr * imB * nY;
|
|
940
|
+
this.posZ[bB] -= corr * imB * nZ;
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
else {
|
|
944
|
+
const rvl = Math.sqrt(rvx * rvx + rvy * rvy + rvz * rvz);
|
|
945
|
+
if (rvl > 1e-9) {
|
|
946
|
+
const nX = rvx / rvl;
|
|
947
|
+
const nY = rvy / rvl;
|
|
948
|
+
const nZ = rvz / rvl;
|
|
949
|
+
const jn = -rvl / imSum;
|
|
950
|
+
if (bA >= 0) {
|
|
951
|
+
this.velX[bA] -= jn * imA * nX;
|
|
952
|
+
this.velY[bA] -= jn * imA * nY;
|
|
953
|
+
this.velZ[bA] -= jn * imA * nZ;
|
|
954
|
+
}
|
|
955
|
+
if (bB >= 0) {
|
|
956
|
+
this.velX[bB] += jn * imB * nX;
|
|
957
|
+
this.velY[bB] += jn * imB * nY;
|
|
958
|
+
this.velZ[bB] += jn * imB * nZ;
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
continue;
|
|
963
|
+
}
|
|
833
964
|
const dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
834
965
|
if (dist < 1e-9)
|
|
835
966
|
continue;
|
|
@@ -837,11 +968,6 @@ export class PhysicsWorld {
|
|
|
837
968
|
const nY = dy / dist;
|
|
838
969
|
const nZ = dz / dist;
|
|
839
970
|
const c = dist - this.jRest[id];
|
|
840
|
-
const imA = bA >= 0 ? this.invMass[bA] : 0;
|
|
841
|
-
const imB = bB >= 0 ? this.invMass[bB] : 0;
|
|
842
|
-
const imSum = imA + imB;
|
|
843
|
-
if (imSum === 0)
|
|
844
|
-
continue;
|
|
845
971
|
const vax = bA >= 0 ? this.velX[bA] : 0;
|
|
846
972
|
const vay = bA >= 0 ? this.velY[bA] : 0;
|
|
847
973
|
const vaz = bA >= 0 ? this.velZ[bA] : 0;
|
|
@@ -849,7 +975,7 @@ export class PhysicsWorld {
|
|
|
849
975
|
const vby = bB >= 0 ? this.velY[bB] : 0;
|
|
850
976
|
const vbz = bB >= 0 ? this.velZ[bB] : 0;
|
|
851
977
|
const rvn = (vbx - vax) * nX + (vby - vay) * nY + (vbz - vaz) * nZ;
|
|
852
|
-
if (
|
|
978
|
+
if (kind === JOINT_SPRING) {
|
|
853
979
|
if (Math.abs(c) > slop) {
|
|
854
980
|
if (bA >= 0)
|
|
855
981
|
this.wake(bA);
|
|
@@ -874,6 +1000,9 @@ export class PhysicsWorld {
|
|
|
874
1000
|
}
|
|
875
1001
|
continue;
|
|
876
1002
|
}
|
|
1003
|
+
if (kind !== JOINT_FIXED && kind !== JOINT_DISTANCE) {
|
|
1004
|
+
throw new Error(`PhysicsWorld: joint kind ${kind} is not implemented`);
|
|
1005
|
+
}
|
|
877
1006
|
if (Math.abs(c) > slop) {
|
|
878
1007
|
if (bA >= 0)
|
|
879
1008
|
this.wake(bA);
|
package/dist/physics/ragdoll.js
CHANGED
|
@@ -88,7 +88,7 @@ export function createRagdoll(world, config) {
|
|
|
88
88
|
(world.posY[a] + world.posY[b]) / 2,
|
|
89
89
|
(world.posZ[a] + world.posZ[b]) / 2,
|
|
90
90
|
];
|
|
91
|
-
links.push(world.
|
|
91
|
+
links.push(world.fixedJoint({
|
|
92
92
|
bodyA: a,
|
|
93
93
|
bodyB: b,
|
|
94
94
|
anchorA: [mid[0] - world.posX[a], mid[1] - world.posY[a], mid[2] - world.posZ[a]],
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { steerYaw } from "../movement/steering.js";
|
|
1
2
|
/**
|
|
2
3
|
* Piecewise-linear tire-grip curve: normalized lateral slip → available grip (0..1). Grip peaks near
|
|
3
4
|
* the breakaway slip then falls off as the tire slides — the shape that separates a planted corner
|
|
@@ -166,7 +167,7 @@ export class VehicleBody {
|
|
|
166
167
|
if (grounded) {
|
|
167
168
|
const steerScale = Math.min(1, Math.abs(speed) / this.turnSpeedRef);
|
|
168
169
|
const dir = speed >= 0 ? 1 : -1;
|
|
169
|
-
this.heading
|
|
170
|
+
this.heading = steerYaw(this.heading, input.steer * steerScale * dir, this.turnRate, dt);
|
|
170
171
|
}
|
|
171
172
|
const [fx, fz] = this.forward;
|
|
172
173
|
if (grounded) {
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const DEFAULT_SEED_PARAM = "seed";
|
|
2
|
+
export declare function withSeedParam(url: string, seed: string | number, param?: string): string;
|
|
3
|
+
export declare function seedFromSearch(search: string, param?: string): string | null;
|
|
4
|
+
export declare function seedFromUrl(url: string, param?: string): string | null;
|
|
5
|
+
export declare function dailySeed(nowMs: number, salt?: string): string;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export const DEFAULT_SEED_PARAM = "seed";
|
|
2
|
+
function splitUrl(url) {
|
|
3
|
+
const hashIndex = url.indexOf("#");
|
|
4
|
+
const hash = hashIndex === -1 ? "" : url.slice(hashIndex + 1);
|
|
5
|
+
const rest = hashIndex === -1 ? url : url.slice(0, hashIndex);
|
|
6
|
+
const queryIndex = rest.indexOf("?");
|
|
7
|
+
const base = queryIndex === -1 ? rest : rest.slice(0, queryIndex);
|
|
8
|
+
const query = queryIndex === -1 ? "" : rest.slice(queryIndex + 1);
|
|
9
|
+
return { base, query, hash };
|
|
10
|
+
}
|
|
11
|
+
export function withSeedParam(url, seed, param = DEFAULT_SEED_PARAM) {
|
|
12
|
+
const { base, query, hash } = splitUrl(url);
|
|
13
|
+
const params = new URLSearchParams(query);
|
|
14
|
+
params.set(param, typeof seed === "number" ? seed.toString() : seed);
|
|
15
|
+
const queryString = params.toString();
|
|
16
|
+
return `${base}${queryString ? `?${queryString}` : ""}${hash ? `#${hash}` : ""}`;
|
|
17
|
+
}
|
|
18
|
+
export function seedFromSearch(search, param = DEFAULT_SEED_PARAM) {
|
|
19
|
+
const normalized = search.startsWith("?") ? search.slice(1) : search;
|
|
20
|
+
const params = new URLSearchParams(normalized);
|
|
21
|
+
const value = params.get(param);
|
|
22
|
+
return value === null || value === "" ? null : value;
|
|
23
|
+
}
|
|
24
|
+
export function seedFromUrl(url, param = DEFAULT_SEED_PARAM) {
|
|
25
|
+
const { query } = splitUrl(url);
|
|
26
|
+
return seedFromSearch(query, param);
|
|
27
|
+
}
|
|
28
|
+
export function dailySeed(nowMs, salt) {
|
|
29
|
+
const date = new Date(nowMs);
|
|
30
|
+
const year = date.getUTCFullYear().toString().padStart(4, "0");
|
|
31
|
+
const month = (date.getUTCMonth() + 1).toString().padStart(2, "0");
|
|
32
|
+
const day = date.getUTCDate().toString().padStart(2, "0");
|
|
33
|
+
const base = `${year}-${month}-${day}`;
|
|
34
|
+
return salt !== undefined && salt.length > 0 ? `${base}:${salt}` : base;
|
|
35
|
+
}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
import type { CinematicCameraConfig } from "../game/playableGame.js";
|
|
1
|
+
import type { ChaseCameraConfig, CinematicCameraConfig } from "../game/playableGame.js";
|
|
2
|
+
/** Runtime patch over the static `camera.chase` config — distance/height/fov retuning from gameplay events (#286.11). */
|
|
3
|
+
export type ChaseCameraTuning = Partial<Pick<ChaseCameraConfig, "distance" | "height" | "lookHeight" | "springDamping" | "fov" | "lead" | "bank">>;
|
|
2
4
|
export interface CameraDirector {
|
|
3
5
|
follow(entityId: string | null): void;
|
|
4
6
|
/** `undefined` means no runtime override — the shell falls back to the static `playable.camera.followEntityId`. `null` means explicitly follow nothing. */
|
|
5
7
|
followedEntityId(): string | null | undefined;
|
|
6
8
|
setCinematic(config: CinematicCameraConfig | null): void;
|
|
7
9
|
cinematic(): CinematicCameraConfig | null;
|
|
10
|
+
/** Overlay a runtime patch on the chase rig's config — a boss-intro pull-back, drift zoom-out; `null` restores the static config. */
|
|
11
|
+
setChaseTuning(tuning: ChaseCameraTuning | null): void;
|
|
12
|
+
chaseTuning(): ChaseCameraTuning | null;
|
|
8
13
|
subscribe(listener: () => void): () => void;
|
|
9
14
|
}
|
|
10
15
|
export declare function createCameraDirector(): CameraDirector;
|
|
@@ -2,6 +2,7 @@ export function createCameraDirector() {
|
|
|
2
2
|
const listeners = new Set();
|
|
3
3
|
let followEntity = undefined;
|
|
4
4
|
let cinematicConfig = null;
|
|
5
|
+
let chaseTuningPatch = null;
|
|
5
6
|
function notify() {
|
|
6
7
|
for (const listener of listeners)
|
|
7
8
|
listener();
|
|
@@ -17,6 +18,11 @@ export function createCameraDirector() {
|
|
|
17
18
|
notify();
|
|
18
19
|
},
|
|
19
20
|
cinematic: () => cinematicConfig,
|
|
21
|
+
setChaseTuning(tuning) {
|
|
22
|
+
chaseTuningPatch = tuning;
|
|
23
|
+
notify();
|
|
24
|
+
},
|
|
25
|
+
chaseTuning: () => chaseTuningPatch,
|
|
20
26
|
subscribe(listener) {
|
|
21
27
|
listeners.add(listener);
|
|
22
28
|
return () => listeners.delete(listener);
|
|
@@ -2,8 +2,8 @@ export type CommandValidationError = {
|
|
|
2
2
|
reason: string;
|
|
3
3
|
};
|
|
4
4
|
export type CommandDef<TInput = unknown> = {
|
|
5
|
-
validate: (snapshot: import("./snapshot.js").GameRuntimeSnapshot, input: TInput) => CommandValidationError | null;
|
|
6
|
-
apply: (snapshot: import("./snapshot.js").GameRuntimeSnapshot, input: TInput) => import("./snapshot.js").GameRuntimeSnapshot;
|
|
5
|
+
validate: (snapshot: import("./snapshot.js").GameRuntimeSnapshot, input: TInput, actorUserId: string) => CommandValidationError | null;
|
|
6
|
+
apply: (snapshot: import("./snapshot.js").GameRuntimeSnapshot, input: TInput, actorUserId: string) => import("./snapshot.js").GameRuntimeSnapshot;
|
|
7
7
|
};
|
|
8
8
|
export type RunCommandResult = {
|
|
9
9
|
ok: true;
|
|
@@ -3,11 +3,11 @@ export function runCommand(snapshot, commands, commandName, input, actorUserId)
|
|
|
3
3
|
if (!command) {
|
|
4
4
|
return { ok: false, reason: `Unknown command: ${commandName}` };
|
|
5
5
|
}
|
|
6
|
-
const validationError = command.validate(snapshot, input);
|
|
6
|
+
const validationError = command.validate(snapshot, input, actorUserId);
|
|
7
7
|
if (validationError) {
|
|
8
8
|
return { ok: false, reason: validationError.reason };
|
|
9
9
|
}
|
|
10
|
-
const next = command.apply(snapshot, input);
|
|
10
|
+
const next = command.apply(snapshot, input, actorUserId);
|
|
11
11
|
return {
|
|
12
12
|
ok: true,
|
|
13
13
|
snapshot: {
|
|
@@ -30,16 +30,19 @@ import { type PaintLayer } from "../scene/paintLayer.js";
|
|
|
30
30
|
import { type EntityStatsApi, type StatCatalog } from "../scene/entityStats.js";
|
|
31
31
|
import type { EntityPose, EntityPosition, EntityStore, SceneEntity, SpawnOptions, SpawnPose } from "../scene/entityStore.js";
|
|
32
32
|
import { type Forms } from "../scene/form.js";
|
|
33
|
+
import type { EntityColliderSet } from "../scene/colliders.js";
|
|
33
34
|
import { type ObjectRaycastHit, type ObjectRaycastInput } from "../scene/objectQuery.js";
|
|
34
35
|
import { type ObjectStore } from "../scene/objectStore.js";
|
|
35
36
|
import { type Roster } from "../scene/roster.js";
|
|
36
37
|
import { type Possession } from "../scene/possession.js";
|
|
38
|
+
import { type SceneRaycastHit, type SceneRaycastInput } from "../scene/sceneRaycast.js";
|
|
37
39
|
import { type SpatialApi } from "../scene/spatial.js";
|
|
38
40
|
import { type CycleTargetOptions } from "../scene/targeting.js";
|
|
39
41
|
import { type Stats } from "../stats/statModifiers.js";
|
|
40
42
|
import { type ObservableKeyedStore } from "../store/observableKeyedStore.js";
|
|
41
43
|
import { type SimClock } from "../time/simClock.js";
|
|
42
44
|
import { type TurnLoop, type TurnLoopConfig } from "../turn/turnLoop.js";
|
|
45
|
+
import { RaceState, type RaceStateConfig } from "../game/race.js";
|
|
43
46
|
import { type CameraDirector } from "./cameraDirector.js";
|
|
44
47
|
import { type InputSnapshot } from "./inputSnapshot.js";
|
|
45
48
|
import { type MotionIntents } from "./motionIntents.js";
|
|
@@ -63,6 +66,8 @@ export interface GameContextEntityEntry {
|
|
|
63
66
|
role?: CatalogEntityRole;
|
|
64
67
|
/** Right-click context-menu verbs for this entity (#31). */
|
|
65
68
|
verbs?: readonly ContextVerb[];
|
|
69
|
+
/** Purpose-specific colliders (physical body + named damage hitboxes). */
|
|
70
|
+
colliders?: EntityColliderSet;
|
|
66
71
|
}
|
|
67
72
|
export interface GameContextObjectEntry {
|
|
68
73
|
proximityPrompt?: ProximityPrompt;
|
|
@@ -72,6 +77,8 @@ export interface GameContextObjectEntry {
|
|
|
72
77
|
slotInventory?: InventoryLayout;
|
|
73
78
|
/** Right-click context-menu verbs for this object (#31). */
|
|
74
79
|
verbs?: readonly ContextVerb[];
|
|
80
|
+
colliders?: EntityColliderSet;
|
|
81
|
+
halfExtents?: readonly [number, number, number];
|
|
75
82
|
}
|
|
76
83
|
export interface GameContextContent {
|
|
77
84
|
itemById?(itemId: string): GameContextItemEntry | null | undefined;
|
|
@@ -86,11 +93,14 @@ export interface GameContextOptions<TAssetRef extends ModelAssetRef = ModelAsset
|
|
|
86
93
|
isNew: boolean;
|
|
87
94
|
};
|
|
88
95
|
now?: () => number;
|
|
96
|
+
occluder?: (from: EntityPosition, to: EntityPosition) => boolean;
|
|
89
97
|
}
|
|
90
98
|
export interface SceneObjectContext extends ObjectStore {
|
|
91
99
|
catalog(instanceId: string): GameContextObjectEntry | null;
|
|
92
100
|
raycast(input: ObjectRaycastInput): ObjectRaycastHit | null;
|
|
93
101
|
raycastAll(input: ObjectRaycastInput): readonly ObjectRaycastHit[];
|
|
102
|
+
setColliders(instanceId: string, colliders: EntityColliderSet | null): void;
|
|
103
|
+
collidersOf(instanceId: string): EntityColliderSet | null;
|
|
94
104
|
}
|
|
95
105
|
export interface FloatTextInput {
|
|
96
106
|
instanceId?: string;
|
|
@@ -129,6 +139,7 @@ export interface SceneEntityContext {
|
|
|
129
139
|
despawn(instanceId: string): boolean;
|
|
130
140
|
update: EntityStore["update"];
|
|
131
141
|
setPose(instanceId: string, pose: EntityPose): boolean;
|
|
142
|
+
setPoseConstraint: EntityStore["setPoseConstraint"];
|
|
132
143
|
get(instanceId: string): SceneEntity | null;
|
|
133
144
|
list(): readonly SceneEntity[];
|
|
134
145
|
spawnPoseOf(instanceId: string): SpawnPose | null;
|
|
@@ -152,6 +163,9 @@ export interface SceneEntityContext {
|
|
|
152
163
|
hasLineOfSight: SpatialApi["hasLineOfSight"];
|
|
153
164
|
queryArc: SpatialApi["queryArc"];
|
|
154
165
|
moveToward: SpatialApi["moveToward"];
|
|
166
|
+
invalidateSpatial: SpatialApi["invalidate"];
|
|
167
|
+
setColliders(instanceId: string, colliders: EntityColliderSet | null): void;
|
|
168
|
+
collidersOf(instanceId: string): EntityColliderSet | null;
|
|
155
169
|
form: Forms;
|
|
156
170
|
paint: PaintLayer;
|
|
157
171
|
}
|
|
@@ -210,11 +224,17 @@ export interface GameContextTurn {
|
|
|
210
224
|
/** Lazily creates (on first call, `config` required) or returns the existing notify-wrapped turn loop for `id`. */
|
|
211
225
|
loop(id: string, config?: TurnLoopConfig): TurnLoop;
|
|
212
226
|
}
|
|
227
|
+
export interface GameContextRace {
|
|
228
|
+
/** Lazily creates (on first call, `config` required) or returns the existing reactive race for `id` — discrete mutations and eventful `update` calls bump `ctx.version()`, so HUDs stop hand-managing `store.set` (#286.2). */
|
|
229
|
+
state(id: string, config?: RaceStateConfig): RaceState;
|
|
230
|
+
}
|
|
213
231
|
export interface GameContext {
|
|
214
232
|
scene: {
|
|
215
233
|
object: SceneObjectContext;
|
|
216
234
|
entity: SceneEntityContext;
|
|
217
235
|
worldItem: SceneWorldItemContext;
|
|
236
|
+
raycast(input: SceneRaycastInput): SceneRaycastHit | null;
|
|
237
|
+
raycastAll(input: SceneRaycastInput): readonly SceneRaycastHit[];
|
|
218
238
|
};
|
|
219
239
|
world: GameContextWorld;
|
|
220
240
|
game: {
|
|
@@ -234,6 +254,7 @@ export interface GameContext {
|
|
|
234
254
|
store: ObservableKeyedStore<unknown>;
|
|
235
255
|
cards: GameContextCards;
|
|
236
256
|
turn: GameContextTurn;
|
|
257
|
+
race: GameContextRace;
|
|
237
258
|
};
|
|
238
259
|
player: {
|
|
239
260
|
userId: string;
|