@jgengine/core 0.7.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 +63 -0
- package/README.md +2 -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.d.ts +5 -0
- package/dist/ai/spawnDirector.js +14 -2
- package/dist/ai/threat.d.ts +2 -0
- package/dist/ai/threat.js +34 -0
- package/dist/anim/easing.d.ts +14 -0
- package/dist/anim/easing.js +55 -0
- package/dist/behaviour/behaviour.d.ts +136 -0
- package/dist/behaviour/behaviour.js +388 -0
- package/dist/cards/cardPile.d.ts +1 -0
- package/dist/cards/cardPile.js +12 -3
- 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 +22 -2
- package/dist/combat/abilityKit.js +72 -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/effects.d.ts +1 -1
- package/dist/combat/effects.js +9 -6
- package/dist/combat/projectiles.d.ts +64 -6
- package/dist/combat/projectiles.js +232 -74
- 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 +30 -1
- package/dist/combat/telegraph.js +61 -0
- package/dist/commands/commandRegistry.d.ts +1 -1
- package/dist/commands/commandRegistry.js +2 -1
- package/dist/crafting/recipe.js +4 -1
- package/dist/data/dataSource.d.ts +26 -0
- package/dist/data/dataSource.js +93 -0
- package/dist/data/devProxy.d.ts +11 -0
- package/dist/data/devProxy.js +38 -0
- package/dist/data/fetchJson.d.ts +20 -0
- package/dist/data/fetchJson.js +43 -0
- package/dist/data/jsonDataSource.d.ts +4 -0
- package/dist/data/jsonDataSource.js +6 -0
- package/dist/devtools/devtools.d.ts +205 -0
- package/dist/devtools/devtools.js +832 -0
- package/dist/devtools/transformTunables.d.ts +14 -0
- package/dist/devtools/transformTunables.js +38 -0
- 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 +68 -0
- package/dist/game/chat.js +173 -0
- 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 +10 -2
- package/dist/game/defineGame.js +2 -1
- package/dist/game/events.d.ts +35 -0
- package/dist/game/gamePhase.d.ts +20 -0
- package/dist/game/gamePhase.js +21 -0
- package/dist/game/levelSequence.d.ts +44 -0
- package/dist/game/levelSequence.js +78 -0
- package/dist/game/lootTable.d.ts +10 -1
- package/dist/game/lootTable.js +27 -7
- package/dist/game/playableGame.d.ts +286 -6
- package/dist/game/playableGame.js +6 -1
- package/dist/game/progression.d.ts +1 -0
- package/dist/game/progression.js +23 -2
- package/dist/game/quest.d.ts +7 -3
- package/dist/game/quest.js +17 -8
- package/dist/game/race.d.ts +44 -1
- package/dist/game/race.js +116 -11
- package/dist/game/recordBook.d.ts +29 -0
- package/dist/game/recordBook.js +86 -0
- package/dist/game/social.d.ts +42 -0
- package/dist/game/social.js +122 -0
- package/dist/game/spawnPoints.d.ts +19 -0
- package/dist/game/spawnPoints.js +23 -0
- package/dist/game/talents.d.ts +47 -0
- package/dist/game/talents.js +140 -0
- package/dist/game/worldItem.d.ts +3 -0
- package/dist/game/worldItem.js +12 -6
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/input/actionBindings.d.ts +10 -0
- package/dist/input/actionBindings.js +14 -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/gestureSurface.d.ts +31 -0
- package/dist/input/gestureSurface.js +120 -0
- package/dist/input/pointer.d.ts +45 -0
- package/dist/input/pointer.js +48 -0
- package/dist/input/pointerAxis.d.ts +30 -0
- package/dist/input/pointerAxis.js +31 -0
- package/dist/input/touchScheme.d.ts +89 -0
- package/dist/input/touchScheme.js +153 -0
- package/dist/item/affix.d.ts +1 -1
- package/dist/item/affix.js +1 -19
- package/dist/meta/changelog.d.ts +1 -1
- package/dist/meta/changelog.js +59 -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/movementModel.d.ts +36 -3
- package/dist/movement/movementModel.js +81 -5
- package/dist/movement/steering.d.ts +27 -0
- package/dist/movement/steering.js +40 -0
- package/dist/movement/voxelController.d.ts +61 -0
- package/dist/movement/voxelController.js +229 -0
- package/dist/multiplayer/chatContract.d.ts +42 -0
- package/dist/multiplayer/chatContract.js +57 -0
- package/dist/multiplayer/feedWriteGate.d.ts +10 -0
- package/dist/multiplayer/feedWriteGate.js +12 -0
- package/dist/multiplayer/identity.d.ts +13 -0
- package/dist/multiplayer/identity.js +20 -0
- package/dist/multiplayer/poseSyncGate.d.ts +1 -0
- package/dist/multiplayer/poseSyncGate.js +13 -1
- package/dist/multiplayer/presenceModel.d.ts +17 -2
- package/dist/multiplayer/presenceModel.js +30 -4
- package/dist/multiplayer/voiceContract.d.ts +44 -0
- package/dist/multiplayer/voiceContract.js +108 -0
- package/dist/nav/corridors.d.ts +30 -0
- package/dist/nav/corridors.js +69 -0
- package/dist/nav/navConstrain.d.ts +14 -0
- package/dist/nav/navConstrain.js +26 -0
- package/dist/nav/navFromEnvironment.d.ts +11 -0
- package/dist/nav/navFromEnvironment.js +16 -0
- package/dist/nav/navGrid.d.ts +10 -0
- package/dist/nav/navGrid.js +48 -5
- package/dist/nav/pathFollow.d.ts +4 -2
- package/dist/nav/pathFollow.js +5 -3
- 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/ballisticSweep.d.ts +18 -0
- package/dist/physics/ballisticSweep.js +49 -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 +43 -5
- package/dist/physics/kinematicVehicle.d.ts +52 -0
- package/dist/physics/kinematicVehicle.js +81 -0
- package/dist/physics/physicsWorld.d.ts +54 -8
- package/dist/physics/physicsWorld.js +400 -79
- package/dist/physics/ragdoll.js +1 -1
- package/dist/physics/spatialGrid.d.ts +1 -1
- package/dist/physics/spatialGrid.js +5 -0
- package/dist/physics/vehicleBody.d.ts +6 -0
- package/dist/physics/vehicleBody.js +18 -18
- package/dist/puzzle/cellGrid.d.ts +26 -0
- package/dist/puzzle/cellGrid.js +110 -0
- package/dist/puzzle/fallingPiece.d.ts +24 -0
- package/dist/puzzle/fallingPiece.js +51 -0
- package/dist/random/nameGen.d.ts +18 -0
- package/dist/random/nameGen.js +35 -0
- package/dist/random/rng.d.ts +2 -0
- package/dist/random/rng.js +23 -0
- package/dist/random/seedLink.d.ts +5 -0
- package/dist/random/seedLink.js +35 -0
- package/dist/runtime/adapter.d.ts +35 -0
- package/dist/runtime/adapter.js +37 -1
- package/dist/runtime/cameraDirector.d.ts +15 -0
- package/dist/runtime/cameraDirector.js +31 -0
- package/dist/runtime/commandRunner.d.ts +2 -2
- package/dist/runtime/commandRunner.js +2 -2
- package/dist/runtime/gameContext.d.ts +65 -2
- package/dist/runtime/gameContext.js +280 -9
- package/dist/runtime/gameRuntime.d.ts +5 -1
- package/dist/runtime/gameRuntime.js +13 -12
- package/dist/runtime/hostPersistence.d.ts +30 -1
- package/dist/runtime/hostPersistence.js +70 -5
- package/dist/runtime/inputSnapshot.d.ts +12 -0
- package/dist/runtime/inputSnapshot.js +17 -0
- package/dist/runtime/motionIntents.d.ts +33 -0
- package/dist/runtime/motionIntents.js +43 -0
- package/dist/runtime/snapshot.d.ts +1 -0
- package/dist/runtime/snapshot.js +1 -1
- package/dist/runtime/transport.d.ts +32 -0
- 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 +30 -5
- package/dist/scene/entityStore.js +83 -13
- package/dist/scene/movementSpeed.d.ts +35 -0
- package/dist/scene/movementSpeed.js +20 -0
- package/dist/scene/objectQuery.d.ts +29 -0
- package/dist/scene/objectQuery.js +140 -0
- package/dist/scene/objectStore.d.ts +13 -0
- package/dist/scene/objectStore.js +130 -6
- package/dist/scene/paintLayer.d.ts +15 -0
- package/dist/scene/paintLayer.js +53 -0
- package/dist/scene/sceneRaycast.d.ts +80 -0
- package/dist/scene/sceneRaycast.js +284 -0
- package/dist/scene/spatial.d.ts +17 -1
- package/dist/scene/spatial.js +140 -17
- package/dist/sensor/concealment.d.ts +21 -0
- package/dist/sensor/concealment.js +63 -0
- package/dist/sensor/freezeMonitor.d.ts +17 -0
- package/dist/sensor/freezeMonitor.js +35 -0
- 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/session/roles.d.ts +6 -0
- package/dist/session/roles.js +32 -0
- package/dist/session/roundState.d.ts +41 -17
- package/dist/session/roundState.js +67 -38
- 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/tactics/fallingGrid.d.ts +62 -0
- package/dist/tactics/fallingGrid.js +158 -0
- package/dist/tactics/tacticalGrid.d.ts +11 -0
- package/dist/tactics/tacticalGrid.js +17 -0
- 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/simClock.d.ts +25 -1
- package/dist/time/simClock.js +28 -3
- 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/turn/intent.d.ts +14 -0
- package/dist/turn/intent.js +29 -0
- package/dist/turn/turnLoop.d.ts +2 -0
- package/dist/turn/turnLoop.js +8 -1
- 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 +61 -0
- package/dist/world/environmentSummary.js +138 -0
- package/dist/world/features.d.ts +138 -9
- package/dist/world/features.js +73 -2
- package/dist/world/gridInstances.d.ts +12 -0
- package/dist/world/gridInstances.js +160 -0
- 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/placementController.d.ts +22 -1
- package/dist/world/placementController.js +56 -0
- package/dist/world/terrain.d.ts +62 -1
- package/dist/world/terrain.js +253 -9
- package/dist/world/voxelField.d.ts +54 -0
- package/dist/world/voxelField.js +241 -0
- 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 +4983 -0
- package/package.json +14 -3
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 { MAX_BROADPHASE_CELLS } from "./physicsWorld.js";
|
|
1
2
|
/**
|
|
2
3
|
* A uniform-grid broad-phase over the x/z plane, separate from the rigid-body sim, for cheap
|
|
3
4
|
* same-tick proximity across hundreds–thousands of simple movers (swarm enemies). Rebuild each
|
|
@@ -30,6 +31,10 @@ export class SpatialGrid {
|
|
|
30
31
|
this.nx = Math.max(1, Math.ceil(spanX / config.cellSize));
|
|
31
32
|
this.nz = Math.max(1, Math.ceil(spanZ / config.cellSize));
|
|
32
33
|
this.numCells = this.nx * this.nz;
|
|
34
|
+
if (this.numCells > MAX_BROADPHASE_CELLS) {
|
|
35
|
+
throw new Error(`SpatialGrid: grid too large (${this.nx}x${this.nz} = ${this.numCells} cells, cap ${MAX_BROADPHASE_CELLS}). ` +
|
|
36
|
+
`bounds=[${config.bounds.min}]..[${config.bounds.max}] cellSize=${config.cellSize}; raise cellSize or shrink bounds.`);
|
|
37
|
+
}
|
|
33
38
|
this.cellStart = new Int32Array(this.numCells + 1);
|
|
34
39
|
this.cursor = new Int32Array(this.numCells);
|
|
35
40
|
this.sorted = new Int32Array(config.capacity);
|
|
@@ -77,6 +77,12 @@ export declare class VehicleBody {
|
|
|
77
77
|
private readonly surfaceFriction;
|
|
78
78
|
private groundedCount;
|
|
79
79
|
constructor(world: PhysicsWorld, config: VehicleBodyConfig);
|
|
80
|
+
/**
|
|
81
|
+
* A wheel's local offset rotated by the current heading — the ground-side mount and the
|
|
82
|
+
* chassis-side spring anchor both derive from this, so the suspension axis stays vertical (not a
|
|
83
|
+
* mix of rotated and unrotated ends) as the chassis turns.
|
|
84
|
+
*/
|
|
85
|
+
private rotateOffset;
|
|
80
86
|
private wheelMount;
|
|
81
87
|
get position(): [number, number, number];
|
|
82
88
|
get forward(): [number, number];
|
|
@@ -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
|
|
@@ -101,7 +102,7 @@ export class VehicleBody {
|
|
|
101
102
|
const rest = wheel.restLength ?? this.suspensionRest;
|
|
102
103
|
const id = world.springJoint({
|
|
103
104
|
bodyA: this.chassis,
|
|
104
|
-
anchorA:
|
|
105
|
+
anchorA: this.rotateOffset(wheel.offset),
|
|
105
106
|
anchorB: [worldPos[0], worldPos[1] - rest, worldPos[2]],
|
|
106
107
|
restLength: rest,
|
|
107
108
|
stiffness,
|
|
@@ -118,15 +119,20 @@ export class VehicleBody {
|
|
|
118
119
|
});
|
|
119
120
|
}
|
|
120
121
|
}
|
|
121
|
-
|
|
122
|
+
/**
|
|
123
|
+
* A wheel's local offset rotated by the current heading — the ground-side mount and the
|
|
124
|
+
* chassis-side spring anchor both derive from this, so the suspension axis stays vertical (not a
|
|
125
|
+
* mix of rotated and unrotated ends) as the chassis turns.
|
|
126
|
+
*/
|
|
127
|
+
rotateOffset(offset) {
|
|
122
128
|
const sin = Math.sin(this.heading);
|
|
123
129
|
const cos = Math.cos(this.heading);
|
|
124
|
-
const [ox, oy, oz] =
|
|
125
|
-
return [
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
];
|
|
130
|
+
const [ox, oy, oz] = offset;
|
|
131
|
+
return [ox * cos + oz * sin, oy, -ox * sin + oz * cos];
|
|
132
|
+
}
|
|
133
|
+
wheelMount(wheel) {
|
|
134
|
+
const [dx, dy, dz] = this.rotateOffset(wheel.offset);
|
|
135
|
+
return [this.world.posX[this.chassis] + dx, this.world.posY[this.chassis] + dy, this.world.posZ[this.chassis] + dz];
|
|
130
136
|
}
|
|
131
137
|
get position() {
|
|
132
138
|
return [this.world.posX[this.chassis], this.world.posY[this.chassis], this.world.posZ[this.chassis]];
|
|
@@ -149,16 +155,8 @@ export class VehicleBody {
|
|
|
149
155
|
return this.wheels.length;
|
|
150
156
|
}
|
|
151
157
|
resetTo(position, heading) {
|
|
152
|
-
|
|
153
|
-
const c = this.chassis;
|
|
154
|
-
w.posX[c] = position[0];
|
|
155
|
-
w.posY[c] = position[1];
|
|
156
|
-
w.posZ[c] = position[2];
|
|
157
|
-
w.velX[c] = 0;
|
|
158
|
-
w.velY[c] = 0;
|
|
159
|
-
w.velZ[c] = 0;
|
|
158
|
+
this.world.teleport(this.chassis, position[0], position[1], position[2]);
|
|
160
159
|
this.heading = heading;
|
|
161
|
-
w.wake(c);
|
|
162
160
|
}
|
|
163
161
|
update(dt, input) {
|
|
164
162
|
const w = this.world;
|
|
@@ -169,7 +167,7 @@ export class VehicleBody {
|
|
|
169
167
|
if (grounded) {
|
|
170
168
|
const steerScale = Math.min(1, Math.abs(speed) / this.turnSpeedRef);
|
|
171
169
|
const dir = speed >= 0 ? 1 : -1;
|
|
172
|
-
this.heading
|
|
170
|
+
this.heading = steerYaw(this.heading, input.steer * steerScale * dir, this.turnRate, dt);
|
|
173
171
|
}
|
|
174
172
|
const [fx, fz] = this.forward;
|
|
175
173
|
if (grounded) {
|
|
@@ -212,6 +210,8 @@ export class VehicleBody {
|
|
|
212
210
|
const gap = mount[1] - (groundY + radius);
|
|
213
211
|
const maxTravel = rest + radius;
|
|
214
212
|
const grounded = gap <= maxTravel;
|
|
213
|
+
const anchorA = this.rotateOffset(wheel.offset);
|
|
214
|
+
w.setJointAnchorA(this.springs[i], anchorA[0], anchorA[1], anchorA[2]);
|
|
215
215
|
w.setJointAnchor(this.springs[i], mount[0], groundY + radius, mount[2]);
|
|
216
216
|
state.worldX = mount[0];
|
|
217
217
|
state.worldY = groundY + radius;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface CellGrid<T> {
|
|
2
|
+
readonly width: number;
|
|
3
|
+
readonly height: number;
|
|
4
|
+
readonly cells: readonly (T | null)[];
|
|
5
|
+
}
|
|
6
|
+
export interface CellRun<T> {
|
|
7
|
+
readonly value: T;
|
|
8
|
+
readonly cells: readonly {
|
|
9
|
+
readonly x: number;
|
|
10
|
+
readonly y: number;
|
|
11
|
+
}[];
|
|
12
|
+
readonly direction: "row" | "column";
|
|
13
|
+
}
|
|
14
|
+
export declare function createCellGrid<T>(width: number, height: number): CellGrid<T>;
|
|
15
|
+
export declare function inGridBounds<T>(grid: CellGrid<T>, x: number, y: number): boolean;
|
|
16
|
+
export declare function cellAt<T>(grid: CellGrid<T>, x: number, y: number): T | null;
|
|
17
|
+
export declare function withCell<T>(grid: CellGrid<T>, x: number, y: number, value: T | null): CellGrid<T>;
|
|
18
|
+
export declare function withCells<T>(grid: CellGrid<T>, entries: readonly {
|
|
19
|
+
readonly x: number;
|
|
20
|
+
readonly y: number;
|
|
21
|
+
readonly value: T | null;
|
|
22
|
+
}[]): CellGrid<T>;
|
|
23
|
+
export declare function fullRows<T>(grid: CellGrid<T>): number[];
|
|
24
|
+
export declare function clearRows<T>(grid: CellGrid<T>, rows: readonly number[]): CellGrid<T>;
|
|
25
|
+
export declare function collapseColumns<T>(grid: CellGrid<T>): CellGrid<T>;
|
|
26
|
+
export declare function findRuns<T>(grid: CellGrid<T>, minLength: number, matches?: (a: T, b: T) => boolean): CellRun<T>[];
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
export function createCellGrid(width, height) {
|
|
2
|
+
return { width, height, cells: new Array(width * height).fill(null) };
|
|
3
|
+
}
|
|
4
|
+
export function inGridBounds(grid, x, y) {
|
|
5
|
+
return x >= 0 && x < grid.width && y >= 0 && y < grid.height;
|
|
6
|
+
}
|
|
7
|
+
export function cellAt(grid, x, y) {
|
|
8
|
+
if (!inGridBounds(grid, x, y))
|
|
9
|
+
return null;
|
|
10
|
+
return grid.cells[y * grid.width + x] ?? null;
|
|
11
|
+
}
|
|
12
|
+
export function withCell(grid, x, y, value) {
|
|
13
|
+
if (!inGridBounds(grid, x, y))
|
|
14
|
+
return grid;
|
|
15
|
+
const cells = grid.cells.slice();
|
|
16
|
+
cells[y * grid.width + x] = value;
|
|
17
|
+
return { width: grid.width, height: grid.height, cells };
|
|
18
|
+
}
|
|
19
|
+
export function withCells(grid, entries) {
|
|
20
|
+
const cells = grid.cells.slice();
|
|
21
|
+
for (const entry of entries) {
|
|
22
|
+
if (inGridBounds(grid, entry.x, entry.y))
|
|
23
|
+
cells[entry.y * grid.width + entry.x] = entry.value;
|
|
24
|
+
}
|
|
25
|
+
return { width: grid.width, height: grid.height, cells };
|
|
26
|
+
}
|
|
27
|
+
export function fullRows(grid) {
|
|
28
|
+
const rows = [];
|
|
29
|
+
for (let y = 0; y < grid.height; y += 1) {
|
|
30
|
+
let full = true;
|
|
31
|
+
for (let x = 0; x < grid.width; x += 1) {
|
|
32
|
+
if (cellAt(grid, x, y) === null) {
|
|
33
|
+
full = false;
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (full)
|
|
38
|
+
rows.push(y);
|
|
39
|
+
}
|
|
40
|
+
return rows;
|
|
41
|
+
}
|
|
42
|
+
export function clearRows(grid, rows) {
|
|
43
|
+
const removed = new Set(rows);
|
|
44
|
+
const kept = [];
|
|
45
|
+
for (let y = 0; y < grid.height; y += 1) {
|
|
46
|
+
if (removed.has(y))
|
|
47
|
+
continue;
|
|
48
|
+
kept.push(grid.cells.slice(y * grid.width, y * grid.width + grid.width));
|
|
49
|
+
}
|
|
50
|
+
const clearedCount = grid.height - kept.length;
|
|
51
|
+
const cells = [];
|
|
52
|
+
for (let i = 0; i < clearedCount; i += 1)
|
|
53
|
+
cells.push(...new Array(grid.width).fill(null));
|
|
54
|
+
for (const row of kept)
|
|
55
|
+
cells.push(...row);
|
|
56
|
+
return { width: grid.width, height: grid.height, cells };
|
|
57
|
+
}
|
|
58
|
+
export function collapseColumns(grid) {
|
|
59
|
+
const cells = grid.cells.slice();
|
|
60
|
+
for (let x = 0; x < grid.width; x += 1) {
|
|
61
|
+
const values = [];
|
|
62
|
+
for (let y = 0; y < grid.height; y += 1) {
|
|
63
|
+
const value = cellAt(grid, x, y);
|
|
64
|
+
if (value !== null)
|
|
65
|
+
values.push(value);
|
|
66
|
+
}
|
|
67
|
+
const emptyCount = grid.height - values.length;
|
|
68
|
+
for (let y = 0; y < grid.height; y += 1) {
|
|
69
|
+
cells[y * grid.width + x] = y < emptyCount ? null : values[y - emptyCount];
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return { width: grid.width, height: grid.height, cells };
|
|
73
|
+
}
|
|
74
|
+
function scanRuns(length, cellAtIndex, minLength, matches) {
|
|
75
|
+
const runs = [];
|
|
76
|
+
let start = 0;
|
|
77
|
+
for (let i = 1; i <= length; i += 1) {
|
|
78
|
+
const prev = cellAtIndex(i - 1);
|
|
79
|
+
const cur = i < length ? cellAtIndex(i) : null;
|
|
80
|
+
const continues = prev !== null && cur !== null && matches(prev, cur);
|
|
81
|
+
if (!continues) {
|
|
82
|
+
const runLength = i - start;
|
|
83
|
+
const value = cellAtIndex(start);
|
|
84
|
+
if (runLength >= minLength && value !== null)
|
|
85
|
+
runs.push({ value, start, end: i });
|
|
86
|
+
start = i;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return runs;
|
|
90
|
+
}
|
|
91
|
+
export function findRuns(grid, minLength, matches = (a, b) => a === b) {
|
|
92
|
+
const runs = [];
|
|
93
|
+
for (let y = 0; y < grid.height; y += 1) {
|
|
94
|
+
for (const run of scanRuns(grid.width, (x) => cellAt(grid, x, y), minLength, matches)) {
|
|
95
|
+
const cells = [];
|
|
96
|
+
for (let x = run.start; x < run.end; x += 1)
|
|
97
|
+
cells.push({ x, y });
|
|
98
|
+
runs.push({ value: run.value, cells, direction: "row" });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
for (let x = 0; x < grid.width; x += 1) {
|
|
102
|
+
for (const run of scanRuns(grid.height, (y) => cellAt(grid, x, y), minLength, matches)) {
|
|
103
|
+
const cells = [];
|
|
104
|
+
for (let y = run.start; y < run.end; y += 1)
|
|
105
|
+
cells.push({ x, y });
|
|
106
|
+
runs.push({ value: run.value, cells, direction: "column" });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return runs;
|
|
110
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type CellGrid } from "./cellGrid.js";
|
|
2
|
+
export interface FallingPiece<TShape extends string = string> {
|
|
3
|
+
readonly shape: TShape;
|
|
4
|
+
readonly rotation: number;
|
|
5
|
+
readonly x: number;
|
|
6
|
+
readonly y: number;
|
|
7
|
+
}
|
|
8
|
+
export type ShapeTable<TShape extends string = string> = Record<TShape, readonly (readonly (readonly [number, number])[])[]>;
|
|
9
|
+
export declare function pieceCells<TShape extends string>(table: ShapeTable<TShape>, piece: FallingPiece<TShape>): readonly (readonly [number, number])[];
|
|
10
|
+
export declare function pieceCollides<T, TShape extends string>(grid: CellGrid<T>, table: ShapeTable<TShape>, piece: FallingPiece<TShape>): boolean;
|
|
11
|
+
export declare function mergePiece<T, TShape extends string>(grid: CellGrid<T>, table: ShapeTable<TShape>, piece: FallingPiece<TShape>, value: T): CellGrid<T>;
|
|
12
|
+
export declare function dropDistance<T, TShape extends string>(grid: CellGrid<T>, table: ShapeTable<TShape>, piece: FallingPiece<TShape>): number;
|
|
13
|
+
export declare function gravityInterval(level: number, base?: number, perLevel?: number, min?: number): number;
|
|
14
|
+
export declare function levelForLines(lines: number, linesPerLevel?: number): number;
|
|
15
|
+
export declare function lineScore(cleared: number, level: number, table?: readonly number[]): number;
|
|
16
|
+
export interface LockDelayState {
|
|
17
|
+
readonly delaySeconds: number;
|
|
18
|
+
readonly elapsed: number;
|
|
19
|
+
}
|
|
20
|
+
export declare function createLockDelay(delaySeconds: number): LockDelayState;
|
|
21
|
+
export declare function stepLockDelay(state: LockDelayState, grounded: boolean, dt: number): {
|
|
22
|
+
state: LockDelayState;
|
|
23
|
+
locked: boolean;
|
|
24
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { cellAt } from "./cellGrid.js";
|
|
2
|
+
export function pieceCells(table, piece) {
|
|
3
|
+
const rotations = table[piece.shape];
|
|
4
|
+
const index = ((piece.rotation % rotations.length) + rotations.length) % rotations.length;
|
|
5
|
+
const state = rotations[index];
|
|
6
|
+
return state.map(([ox, oy]) => [piece.x + ox, piece.y + oy]);
|
|
7
|
+
}
|
|
8
|
+
export function pieceCollides(grid, table, piece) {
|
|
9
|
+
for (const [x, y] of pieceCells(table, piece)) {
|
|
10
|
+
if (x < 0 || x >= grid.width || y >= grid.height)
|
|
11
|
+
return true;
|
|
12
|
+
if (y >= 0 && cellAt(grid, x, y) !== null)
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
export function mergePiece(grid, table, piece, value) {
|
|
18
|
+
const cells = grid.cells.slice();
|
|
19
|
+
for (const [x, y] of pieceCells(table, piece)) {
|
|
20
|
+
if (x >= 0 && x < grid.width && y >= 0 && y < grid.height)
|
|
21
|
+
cells[y * grid.width + x] = value;
|
|
22
|
+
}
|
|
23
|
+
return { width: grid.width, height: grid.height, cells };
|
|
24
|
+
}
|
|
25
|
+
export function dropDistance(grid, table, piece) {
|
|
26
|
+
let distance = 0;
|
|
27
|
+
while (!pieceCollides(grid, table, { ...piece, y: piece.y + distance + 1 }))
|
|
28
|
+
distance += 1;
|
|
29
|
+
return distance;
|
|
30
|
+
}
|
|
31
|
+
export function gravityInterval(level, base = 0.8, perLevel = 0.07, min = 0.05) {
|
|
32
|
+
return Math.max(min, base - level * perLevel);
|
|
33
|
+
}
|
|
34
|
+
export function levelForLines(lines, linesPerLevel = 10) {
|
|
35
|
+
return Math.floor(lines / linesPerLevel);
|
|
36
|
+
}
|
|
37
|
+
const CLASSIC_LINE_SCORE = [0, 40, 100, 300, 1200];
|
|
38
|
+
export function lineScore(cleared, level, table = CLASSIC_LINE_SCORE) {
|
|
39
|
+
return (table[cleared] ?? 0) * (level + 1);
|
|
40
|
+
}
|
|
41
|
+
export function createLockDelay(delaySeconds) {
|
|
42
|
+
return { delaySeconds, elapsed: 0 };
|
|
43
|
+
}
|
|
44
|
+
export function stepLockDelay(state, grounded, dt) {
|
|
45
|
+
if (!grounded)
|
|
46
|
+
return { state: { ...state, elapsed: 0 }, locked: false };
|
|
47
|
+
const elapsed = state.elapsed + dt;
|
|
48
|
+
if (elapsed >= state.delaySeconds)
|
|
49
|
+
return { state: { ...state, elapsed: 0 }, locked: true };
|
|
50
|
+
return { state: { ...state, elapsed }, locked: false };
|
|
51
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface SyllableBank {
|
|
2
|
+
onset?: readonly string[];
|
|
3
|
+
nucleus: readonly string[];
|
|
4
|
+
coda?: readonly string[];
|
|
5
|
+
}
|
|
6
|
+
export interface NameGeneratorOptions {
|
|
7
|
+
rng: () => number;
|
|
8
|
+
syllables: SyllableBank;
|
|
9
|
+
minSyllables?: number;
|
|
10
|
+
maxSyllables?: number;
|
|
11
|
+
capitalize?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface NameGenerator {
|
|
14
|
+
name(): string;
|
|
15
|
+
}
|
|
16
|
+
export declare function pickFrom(rng: () => number, bank: readonly string[]): string;
|
|
17
|
+
export declare function createNameGenerator(options: NameGeneratorOptions): NameGenerator;
|
|
18
|
+
export declare function fillTemplate(template: string, vars: Record<string, () => string> | Record<string, string>): string;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export function pickFrom(rng, bank) {
|
|
2
|
+
if (bank.length === 0)
|
|
3
|
+
throw new Error("pickFrom: bank must not be empty");
|
|
4
|
+
return bank[Math.floor(rng() * bank.length)];
|
|
5
|
+
}
|
|
6
|
+
export function createNameGenerator(options) {
|
|
7
|
+
const { rng, syllables } = options;
|
|
8
|
+
const minSyllables = Math.max(1, options.minSyllables ?? 2);
|
|
9
|
+
const maxSyllables = Math.max(minSyllables, options.maxSyllables ?? minSyllables);
|
|
10
|
+
const capitalize = options.capitalize ?? true;
|
|
11
|
+
function syllable() {
|
|
12
|
+
const onset = syllables.onset === undefined || syllables.onset.length === 0 ? "" : pickFrom(rng, syllables.onset);
|
|
13
|
+
const nucleus = pickFrom(rng, syllables.nucleus);
|
|
14
|
+
const coda = syllables.coda === undefined || syllables.coda.length === 0 ? "" : pickFrom(rng, syllables.coda);
|
|
15
|
+
return `${onset}${nucleus}${coda}`;
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
name() {
|
|
19
|
+
const span = maxSyllables - minSyllables;
|
|
20
|
+
const count = minSyllables + (span === 0 ? 0 : Math.floor(rng() * (span + 1)));
|
|
21
|
+
let result = "";
|
|
22
|
+
for (let i = 0; i < count; i++)
|
|
23
|
+
result += syllable();
|
|
24
|
+
return capitalize && result.length > 0 ? result.charAt(0).toUpperCase() + result.slice(1) : result;
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export function fillTemplate(template, vars) {
|
|
29
|
+
return template.replace(/\{(\w+)\}/g, (match, key) => {
|
|
30
|
+
if (!(key in vars))
|
|
31
|
+
return match;
|
|
32
|
+
const value = vars[key];
|
|
33
|
+
return typeof value === "function" ? value() : value;
|
|
34
|
+
});
|
|
35
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
function hashSeed(seed) {
|
|
2
|
+
const text = typeof seed === "number" ? seed.toString() : seed;
|
|
3
|
+
let h = 2166136261 >>> 0;
|
|
4
|
+
for (let i = 0; i < text.length; i++) {
|
|
5
|
+
h ^= text.charCodeAt(i);
|
|
6
|
+
h = Math.imul(h, 16777619);
|
|
7
|
+
}
|
|
8
|
+
return h >>> 0;
|
|
9
|
+
}
|
|
10
|
+
export function seededRng(seed) {
|
|
11
|
+
let a = hashSeed(seed) >>> 0;
|
|
12
|
+
return () => {
|
|
13
|
+
a = (a + 0x6d2b79f5) >>> 0;
|
|
14
|
+
let t = a;
|
|
15
|
+
t = Math.imul(t ^ (t >>> 15), t | 1);
|
|
16
|
+
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
|
17
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export function seededStreams(seed) {
|
|
21
|
+
const base = typeof seed === "number" ? seed.toString() : seed;
|
|
22
|
+
return (stream) => seededRng(`${base}:${stream}`);
|
|
23
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -5,6 +5,20 @@ export type MultiplayerAdapterConfig = {
|
|
|
5
5
|
} | {
|
|
6
6
|
kind: "ws";
|
|
7
7
|
topology?: MultiplayerTopology;
|
|
8
|
+
url?: string;
|
|
9
|
+
} | {
|
|
10
|
+
kind: "socketio";
|
|
11
|
+
topology?: MultiplayerTopology;
|
|
12
|
+
url?: string;
|
|
13
|
+
} | {
|
|
14
|
+
kind: "p2p";
|
|
15
|
+
topology?: MultiplayerTopology;
|
|
16
|
+
room?: string;
|
|
17
|
+
} | {
|
|
18
|
+
kind: "lan";
|
|
19
|
+
topology?: MultiplayerTopology;
|
|
20
|
+
port?: number;
|
|
21
|
+
path?: string;
|
|
8
22
|
} | {
|
|
9
23
|
kind: "offline";
|
|
10
24
|
};
|
|
@@ -13,8 +27,29 @@ export declare function convex(config?: {
|
|
|
13
27
|
}): MultiplayerAdapterConfig;
|
|
14
28
|
export declare function ws(config?: {
|
|
15
29
|
topology?: MultiplayerTopology;
|
|
30
|
+
url?: string;
|
|
31
|
+
}): MultiplayerAdapterConfig;
|
|
32
|
+
export declare function fly(config: {
|
|
33
|
+
app: string;
|
|
34
|
+
topology?: MultiplayerTopology;
|
|
35
|
+
path?: string;
|
|
36
|
+
}): MultiplayerAdapterConfig;
|
|
37
|
+
export declare function socketIo(config?: {
|
|
38
|
+
topology?: MultiplayerTopology;
|
|
39
|
+
url?: string;
|
|
40
|
+
}): MultiplayerAdapterConfig;
|
|
41
|
+
export declare function p2p(config?: {
|
|
42
|
+
topology?: MultiplayerTopology;
|
|
43
|
+
room?: string;
|
|
44
|
+
}): MultiplayerAdapterConfig;
|
|
45
|
+
export declare function lan(config?: {
|
|
46
|
+
topology?: MultiplayerTopology;
|
|
47
|
+
port?: number;
|
|
48
|
+
path?: string;
|
|
16
49
|
}): MultiplayerAdapterConfig;
|
|
17
50
|
export declare function offline(): MultiplayerAdapterConfig;
|
|
51
|
+
export declare function adapterOf(multiplayer: unknown): MultiplayerAdapterConfig | null;
|
|
52
|
+
export declare function multiplayerAdapterKind(multiplayer: unknown): string | null;
|
|
18
53
|
export type ServersPoolConfig = {
|
|
19
54
|
maxServers: number;
|
|
20
55
|
slotsPerServer: number;
|
package/dist/runtime/adapter.js
CHANGED
|
@@ -2,11 +2,47 @@ export function convex(config) {
|
|
|
2
2
|
return { kind: "convex", topology: config?.topology ?? "shared" };
|
|
3
3
|
}
|
|
4
4
|
export function ws(config) {
|
|
5
|
-
return { kind: "ws", topology: config?.topology ?? "shared" };
|
|
5
|
+
return { kind: "ws", topology: config?.topology ?? "shared", url: config?.url };
|
|
6
|
+
}
|
|
7
|
+
export function fly(config) {
|
|
8
|
+
return {
|
|
9
|
+
kind: "ws",
|
|
10
|
+
topology: config.topology ?? "shared",
|
|
11
|
+
url: `wss://${config.app}.fly.dev${config.path ?? "/ws"}`,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export function socketIo(config) {
|
|
15
|
+
return { kind: "socketio", topology: config?.topology ?? "shared", url: config?.url };
|
|
16
|
+
}
|
|
17
|
+
export function p2p(config) {
|
|
18
|
+
return { kind: "p2p", topology: config?.topology ?? "private", room: config?.room };
|
|
19
|
+
}
|
|
20
|
+
export function lan(config) {
|
|
21
|
+
return { kind: "lan", topology: config?.topology ?? "shared", port: config?.port, path: config?.path };
|
|
6
22
|
}
|
|
7
23
|
export function offline() {
|
|
8
24
|
return { kind: "offline" };
|
|
9
25
|
}
|
|
26
|
+
const ADAPTER_KINDS = new Set(["convex", "ws", "socketio", "p2p", "lan", "offline"]);
|
|
27
|
+
function isAdapterConfig(value) {
|
|
28
|
+
if (typeof value !== "object" || value === null)
|
|
29
|
+
return false;
|
|
30
|
+
const kind = value.kind;
|
|
31
|
+
return typeof kind === "string" && ADAPTER_KINDS.has(kind);
|
|
32
|
+
}
|
|
33
|
+
export function adapterOf(multiplayer) {
|
|
34
|
+
if (isAdapterConfig(multiplayer))
|
|
35
|
+
return multiplayer;
|
|
36
|
+
if (typeof multiplayer === "object" && multiplayer !== null) {
|
|
37
|
+
const nested = multiplayer.adapter;
|
|
38
|
+
if (isAdapterConfig(nested))
|
|
39
|
+
return nested;
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
export function multiplayerAdapterKind(multiplayer) {
|
|
44
|
+
return adapterOf(multiplayer)?.kind ?? null;
|
|
45
|
+
}
|
|
10
46
|
export function servers(config) {
|
|
11
47
|
return config;
|
|
12
48
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
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">>;
|
|
4
|
+
export interface CameraDirector {
|
|
5
|
+
follow(entityId: string | null): void;
|
|
6
|
+
/** `undefined` means no runtime override — the shell falls back to the static `playable.camera.followEntityId`. `null` means explicitly follow nothing. */
|
|
7
|
+
followedEntityId(): string | null | undefined;
|
|
8
|
+
setCinematic(config: CinematicCameraConfig | null): void;
|
|
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;
|
|
13
|
+
subscribe(listener: () => void): () => void;
|
|
14
|
+
}
|
|
15
|
+
export declare function createCameraDirector(): CameraDirector;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export function createCameraDirector() {
|
|
2
|
+
const listeners = new Set();
|
|
3
|
+
let followEntity = undefined;
|
|
4
|
+
let cinematicConfig = null;
|
|
5
|
+
let chaseTuningPatch = null;
|
|
6
|
+
function notify() {
|
|
7
|
+
for (const listener of listeners)
|
|
8
|
+
listener();
|
|
9
|
+
}
|
|
10
|
+
return {
|
|
11
|
+
follow(entityId) {
|
|
12
|
+
followEntity = entityId;
|
|
13
|
+
notify();
|
|
14
|
+
},
|
|
15
|
+
followedEntityId: () => followEntity,
|
|
16
|
+
setCinematic(config) {
|
|
17
|
+
cinematicConfig = config;
|
|
18
|
+
notify();
|
|
19
|
+
},
|
|
20
|
+
cinematic: () => cinematicConfig,
|
|
21
|
+
setChaseTuning(tuning) {
|
|
22
|
+
chaseTuningPatch = tuning;
|
|
23
|
+
notify();
|
|
24
|
+
},
|
|
25
|
+
chaseTuning: () => chaseTuningPatch,
|
|
26
|
+
subscribe(listener) {
|
|
27
|
+
listeners.add(listener);
|
|
28
|
+
return () => listeners.delete(listener);
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -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: {
|