@jgengine/core 0.8.0 → 0.10.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 +36 -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 +13 -0
- package/dist/ai/spawnDirector.js +41 -17
- package/dist/ai/spawnPoint.d.ts +5 -0
- package/dist/ai/spawnPoint.js +5 -0
- package/dist/ai/threat.d.ts +2 -0
- package/dist/ai/threat.js +34 -0
- package/dist/audio/audioFalloff.d.ts +5 -2
- package/dist/audio/music.d.ts +53 -0
- package/dist/audio/music.js +34 -0
- package/dist/audio/synth.d.ts +60 -0
- package/dist/audio/synth.js +8 -0
- package/dist/authoring.d.ts +14 -0
- package/dist/authoring.js +14 -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/dotField.d.ts +43 -0
- package/dist/combat/dotField.js +48 -0
- package/dist/combat/projectiles.d.ts +33 -12
- package/dist/combat/projectiles.js +214 -129
- package/dist/combat/regenShield.d.ts +35 -0
- package/dist/combat/regenShield.js +45 -0
- 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 +52 -0
- package/dist/combat/shotOrigin.js +86 -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/editor/commands.d.ts +65 -0
- package/dist/editor/commands.js +193 -0
- package/dist/editor/document.d.ts +40 -0
- package/dist/editor/document.js +153 -0
- package/dist/editor/index.d.ts +30 -0
- package/dist/editor/index.js +6 -0
- package/dist/editor/types.d.ts +69 -0
- package/dist/editor/types.js +29 -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/connectedPlayers.d.ts +28 -0
- package/dist/game/connectedPlayers.js +21 -0
- package/dist/game/controlGate.d.ts +4 -0
- package/dist/game/controlGate.js +7 -0
- package/dist/game/defineGame.d.ts +44 -1
- package/dist/game/defineGame.js +1 -0
- package/dist/game/events.d.ts +25 -0
- package/dist/game/gamePhase.d.ts +20 -0
- package/dist/game/gamePhase.js +21 -0
- package/dist/game/keyValueStore.d.ts +25 -0
- package/dist/game/keyValueStore.js +61 -0
- package/dist/game/levelSequence.d.ts +24 -2
- package/dist/game/levelSequence.js +51 -2
- package/dist/game/lootTable.d.ts +14 -1
- package/dist/game/lootTable.js +28 -7
- package/dist/game/modelAnimation.d.ts +8 -0
- package/dist/game/modelAnimation.js +18 -0
- package/dist/game/playableGame.d.ts +116 -4
- 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 +27 -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 +8 -0
- package/dist/index.js +8 -0
- package/dist/input/actionBindings.d.ts +2 -0
- package/dist/input/actionBindings.js +1 -0
- package/dist/input/axisInput.d.ts +33 -3
- package/dist/input/axisInput.js +60 -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/interaction/contextMenu.d.ts +1 -0
- package/dist/interaction/contextMenu.js +1 -0
- package/dist/interaction/proximityPrompt.d.ts +3 -1
- package/dist/interaction/proximityPrompt.js +3 -3
- package/dist/meta/changelog.d.ts +1 -1
- package/dist/meta/changelog.js +44 -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 +4 -0
- package/dist/movement/movementModel.js +13 -0
- package/dist/movement/playerMovement.d.ts +41 -0
- package/dist/movement/playerMovement.js +181 -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/rng.d.ts +2 -0
- package/dist/random/rng.js +2 -0
- package/dist/random/seedLink.d.ts +5 -0
- package/dist/random/seedLink.js +35 -0
- package/dist/render/postProcessing.d.ts +56 -0
- package/dist/render/postProcessing.js +1 -0
- package/dist/runtime/adapter.d.ts +19 -0
- package/dist/runtime/adapter.js +11 -5
- 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 +72 -8
- package/dist/runtime/gameContext.js +374 -61
- 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/hostedGameRunner.d.ts +49 -0
- package/dist/runtime/hostedGameRunner.js +62 -0
- package/dist/runtime/hostedWorldSession.d.ts +65 -0
- package/dist/runtime/hostedWorldSession.js +57 -0
- package/dist/runtime/inputSnapshot.d.ts +18 -0
- package/dist/runtime/inputSnapshot.js +7 -0
- package/dist/runtime/motionIntents.d.ts +16 -6
- package/dist/runtime/motionIntents.js +31 -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/runtime/worldChannel.d.ts +58 -0
- package/dist/runtime/worldChannel.js +69 -0
- package/dist/runtime/worldMirror.d.ts +24 -0
- package/dist/runtime/worldMirror.js +31 -0
- package/dist/runtime/worldReplication.d.ts +46 -0
- package/dist/runtime/worldReplication.js +185 -0
- package/dist/runtime/worldSnapshot.d.ts +17 -0
- package/dist/runtime/worldSnapshot.js +15 -0
- package/dist/scene/assetCatalog.d.ts +1 -0
- package/dist/scene/colliders.d.ts +50 -0
- package/dist/scene/colliders.js +101 -0
- package/dist/scene/entityStats.d.ts +4 -0
- package/dist/scene/entityStats.js +21 -0
- package/dist/scene/entityStore.d.ts +51 -5
- package/dist/scene/entityStore.js +119 -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.d.ts +4 -0
- package/dist/store/observableKeyedStore.js +17 -2
- package/dist/survival/decayMeter.d.ts +1 -1
- package/dist/survival/decayMeter.js +0 -1
- package/dist/tactics/fallingGrid.d.ts +2 -2
- package/dist/tactics/fallingGrid.js +2 -2
- 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/gameLayout.d.ts +125 -0
- package/dist/ui/gameLayout.js +129 -0
- 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/ui/orientation.d.ts +39 -0
- package/dist/ui/orientation.js +49 -0
- package/dist/ui/swingTimer.d.ts +42 -0
- package/dist/ui/swingTimer.js +38 -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 +212 -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 +21 -4
- package/dist/world/environmentSummary.js +22 -3
- package/dist/world/features.d.ts +185 -8
- package/dist/world/features.js +64 -6
- 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/roads.d.ts +47 -0
- package/dist/world/roads.js +146 -0
- package/dist/world/streets.d.ts +80 -0
- package/dist/world/streets.js +144 -0
- package/dist/world/terrain.d.ts +69 -3
- package/dist/world/terrain.js +309 -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 +3013 -1726
- package/package.json +12 -2
|
@@ -24,26 +24,34 @@ import { createItemUse, } from "../item/use.js";
|
|
|
24
24
|
import { createWeaponStats } from "../item/weapon.js";
|
|
25
25
|
import { createPoseState } from "../movement/poseState.js";
|
|
26
26
|
import { createPaintLayer } from "../scene/paintLayer.js";
|
|
27
|
-
import { createEntityStatsApi, seedStatValues, setStatValue, } from "../scene/entityStats.js";
|
|
27
|
+
import { createEntityStatsApi, hydrateEntityStats, seedStatValues, setStatValue, snapshotEntityStats, } from "../scene/entityStats.js";
|
|
28
28
|
import { createForms } from "../scene/form.js";
|
|
29
|
+
import { scaledObjectColliders } from "../scene/colliders.js";
|
|
29
30
|
import { raycastObjects, raycastObjectsAll } from "../scene/objectQuery.js";
|
|
30
|
-
import { createObjectStore } from "../scene/objectStore.js";
|
|
31
|
+
import { createObjectStore, objectVisualScale } from "../scene/objectStore.js";
|
|
31
32
|
import { createRoster } from "../scene/roster.js";
|
|
33
|
+
import { createConnectedPlayers } from "../game/connectedPlayers.js";
|
|
32
34
|
import { createPossession } from "../scene/possession.js";
|
|
35
|
+
import { createSceneRaycast, } from "../scene/sceneRaycast.js";
|
|
33
36
|
import { createSpatialApi } from "../scene/spatial.js";
|
|
34
37
|
import { createTargeting } from "../scene/targeting.js";
|
|
35
38
|
import { createStats } from "../stats/statModifiers.js";
|
|
36
39
|
import { createChangeSignal, notifyAfter } from "../store/changeSignal.js";
|
|
37
40
|
import { createObservableKeyedStore } from "../store/observableKeyedStore.js";
|
|
41
|
+
import { applyWorldSnapshot, composeWorldSnapshot, } from "./worldSnapshot.js";
|
|
38
42
|
import { createSimClock } from "../time/simClock.js";
|
|
39
43
|
import { createTurnLoop } from "../turn/turnLoop.js";
|
|
44
|
+
import { RaceState } from "../game/race.js";
|
|
40
45
|
import { createCameraDirector } from "./cameraDirector.js";
|
|
41
46
|
import { createInputSnapshot } from "./inputSnapshot.js";
|
|
42
47
|
import { createMotionIntents } from "./motionIntents.js";
|
|
43
48
|
export function createGameContext(options) {
|
|
44
49
|
const { definition, content, player } = options;
|
|
45
50
|
const now = options.now ?? Date.now;
|
|
51
|
+
const occluder = options.occluder;
|
|
46
52
|
const signal = createChangeSignal();
|
|
53
|
+
let actingUserId = null;
|
|
54
|
+
const activeUserId = () => actingUserId ?? player.userId;
|
|
47
55
|
const time = createSimClock({ config: definition.time, onChange: signal.notify });
|
|
48
56
|
const ground = groundFieldFor(definition.world);
|
|
49
57
|
const entities = definition.scene;
|
|
@@ -68,12 +76,54 @@ export function createGameContext(options) {
|
|
|
68
76
|
const object = objects.get(instanceId);
|
|
69
77
|
return object === null ? undefined : content.objectById?.(object.catalogId);
|
|
70
78
|
}
|
|
79
|
+
let spatialGeneration = 0;
|
|
80
|
+
const candidateIds = [];
|
|
81
|
+
let candidateIdsDirty = true;
|
|
82
|
+
function refreshCandidateIds() {
|
|
83
|
+
if (!candidateIdsDirty)
|
|
84
|
+
return candidateIds;
|
|
85
|
+
candidateIds.length = 0;
|
|
86
|
+
for (const entity of entities.list())
|
|
87
|
+
candidateIds.push(entity.id);
|
|
88
|
+
candidateIdsDirty = false;
|
|
89
|
+
return candidateIds;
|
|
90
|
+
}
|
|
71
91
|
const spatial = createSpatialApi({
|
|
72
92
|
resolvePosition: (instanceId) => entities.get(instanceId)?.position,
|
|
73
|
-
candidates:
|
|
93
|
+
candidates: refreshCandidateIds,
|
|
94
|
+
grid: { cellSize: 8 },
|
|
95
|
+
getVersion: () => spatialGeneration,
|
|
96
|
+
...(occluder !== undefined ? { occluder } : {}),
|
|
97
|
+
});
|
|
98
|
+
entities.subscribe(() => {
|
|
99
|
+
candidateIdsDirty = true;
|
|
100
|
+
spatialGeneration += 1;
|
|
101
|
+
spatial.invalidate();
|
|
74
102
|
});
|
|
103
|
+
const entityColliders = new Map();
|
|
104
|
+
const objectColliders = new Map();
|
|
105
|
+
function entityCollidersOf(instanceId) {
|
|
106
|
+
const override = entityColliders.get(instanceId);
|
|
107
|
+
if (override !== undefined)
|
|
108
|
+
return override;
|
|
109
|
+
return catalogEntry(instanceId)?.colliders ?? null;
|
|
110
|
+
}
|
|
111
|
+
function objectCollidersOf(instanceId) {
|
|
112
|
+
const override = objectColliders.get(instanceId);
|
|
113
|
+
if (override !== undefined)
|
|
114
|
+
return override;
|
|
115
|
+
const catalog = catalogObject(instanceId);
|
|
116
|
+
if (catalog?.colliders !== undefined)
|
|
117
|
+
return catalog.colliders;
|
|
118
|
+
if (catalog?.halfExtents !== undefined)
|
|
119
|
+
return null;
|
|
120
|
+
const object = objects.get(instanceId);
|
|
121
|
+
if (object === null || object.visual?.scale === undefined)
|
|
122
|
+
return null;
|
|
123
|
+
return scaledObjectColliders(objectVisualScale(object.visual));
|
|
124
|
+
}
|
|
75
125
|
const targeting = notifyAfter(createTargeting({
|
|
76
|
-
candidates: () =>
|
|
126
|
+
candidates: () => [...refreshCandidateIds()],
|
|
77
127
|
classify(_fromId, toId) {
|
|
78
128
|
const role = catalogEntry(toId)?.role;
|
|
79
129
|
return role === "enemy" || role === "hostile" ? "hostile" : "friendly";
|
|
@@ -82,9 +132,43 @@ export function createGameContext(options) {
|
|
|
82
132
|
}), ["setTarget", "cycleTarget"], signal.notify);
|
|
83
133
|
const combatSpatial = {
|
|
84
134
|
inRadius: (center, radius) => spatial.inRadius(center, radius),
|
|
85
|
-
hasLineOfSight: (from, to) =>
|
|
135
|
+
hasLineOfSight: (from, to) => {
|
|
136
|
+
if (typeof from === "string")
|
|
137
|
+
return spatial.hasLineOfSight(from, to);
|
|
138
|
+
const toPos = entities.get(to)?.position;
|
|
139
|
+
if (toPos === undefined)
|
|
140
|
+
return false;
|
|
141
|
+
if (occluder === undefined)
|
|
142
|
+
return true;
|
|
143
|
+
return !occluder(from, toPos);
|
|
144
|
+
},
|
|
86
145
|
positionOf: (instanceId) => entities.get(instanceId)?.position,
|
|
87
146
|
};
|
|
147
|
+
const sceneRaycast = createSceneRaycast({
|
|
148
|
+
entities: {
|
|
149
|
+
list: () => entities.list().map((entity) => ({
|
|
150
|
+
id: entity.id,
|
|
151
|
+
position: entity.position,
|
|
152
|
+
rotationY: entity.rotationY,
|
|
153
|
+
name: entity.name,
|
|
154
|
+
})),
|
|
155
|
+
collidersOf: entityCollidersOf,
|
|
156
|
+
inRadius: (center, radius) => spatial.inRadius(center, radius),
|
|
157
|
+
get: (id) => {
|
|
158
|
+
const entity = entities.get(id);
|
|
159
|
+
if (entity === null)
|
|
160
|
+
return null;
|
|
161
|
+
return { id: entity.id, position: entity.position, rotationY: entity.rotationY, name: entity.name };
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
objects: {
|
|
165
|
+
list: () => objects.list(),
|
|
166
|
+
inBox: (min, max) => objects.inBox(min, max),
|
|
167
|
+
collidersOf: objectCollidersOf,
|
|
168
|
+
halfExtentsOf: (catalogId) => content.objectById?.(catalogId)?.halfExtents ?? null,
|
|
169
|
+
},
|
|
170
|
+
terrain: ground,
|
|
171
|
+
});
|
|
88
172
|
const weapon = createWeaponStats((itemId) => content.itemById?.(itemId));
|
|
89
173
|
const rawEvents = createGameEvents();
|
|
90
174
|
const events = {
|
|
@@ -98,34 +182,55 @@ export function createGameContext(options) {
|
|
|
98
182
|
const feed = createGameFeed(definition.feed);
|
|
99
183
|
const lootRegistry = createLootRegistry();
|
|
100
184
|
const unlocks = notifyAfter(createUnlocks(), ["grant", "hydrate"], signal.notify);
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
185
|
+
const features = definition.features ?? {};
|
|
186
|
+
const rawSocial = features.social || features.chat
|
|
187
|
+
? createSocial({
|
|
188
|
+
events,
|
|
189
|
+
now,
|
|
190
|
+
emotes: {
|
|
191
|
+
entities: { get: (id) => entities.get(id) },
|
|
192
|
+
spatial: { inRadius: (center, radius, filter) => spatial.inRadius(center, radius, filter) },
|
|
193
|
+
},
|
|
194
|
+
})
|
|
195
|
+
: null;
|
|
196
|
+
let social;
|
|
197
|
+
if (features.social && rawSocial !== null) {
|
|
198
|
+
social = {
|
|
199
|
+
friends: notifyAfter(rawSocial.friends, ["request", "accept", "decline", "remove", "block", "hydrate"], signal.notify),
|
|
200
|
+
party: notifyAfter(rawSocial.party, ["invite", "accept", "decline", "kick", "leave", "promote"], signal.notify),
|
|
201
|
+
presence: rawSocial.presence,
|
|
202
|
+
emotes: rawSocial.emotes,
|
|
203
|
+
worldInvites: notifyAfter(rawSocial.worldInvites, ["invite", "accept", "decline"], signal.notify),
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
let chat;
|
|
207
|
+
if (features.chat && rawSocial !== null) {
|
|
208
|
+
chat = notifyAfter(createChat({
|
|
209
|
+
events,
|
|
210
|
+
now,
|
|
211
|
+
party: rawSocial.party,
|
|
212
|
+
proximity: {
|
|
213
|
+
entities: { get: (id) => entities.get(id) },
|
|
214
|
+
spatial: { inRadius: (center, radius, filter) => spatial.inRadius(center, radius, filter) },
|
|
215
|
+
},
|
|
216
|
+
blockedBy: (userId) => rawSocial.friends.snapshot(userId).blocked,
|
|
217
|
+
}), ["register", "send", "whisper", "hydrate"], signal.notify);
|
|
218
|
+
}
|
|
219
|
+
const leaderboard = features.leaderboard
|
|
220
|
+
? notifyAfter(createLeaderboard(), ["increment", "hydrate"], signal.notify)
|
|
221
|
+
: undefined;
|
|
222
|
+
const roster = features.roster
|
|
223
|
+
? notifyAfter(createRoster({ now }), ["capture", "release", "setEquipped", "hydrate"], signal.notify)
|
|
224
|
+
: undefined;
|
|
225
|
+
const playerStatsByUser = new Map();
|
|
226
|
+
function playerStatsFor(userId) {
|
|
227
|
+
let stats = playerStatsByUser.get(userId);
|
|
228
|
+
if (stats === undefined) {
|
|
229
|
+
stats = createStats({});
|
|
230
|
+
playerStatsByUser.set(userId, stats);
|
|
231
|
+
}
|
|
232
|
+
return stats;
|
|
233
|
+
}
|
|
129
234
|
const pose = createPoseState((instanceId) => catalogEntry(instanceId)?.movement);
|
|
130
235
|
const commandRegistry = createCommandRegistry();
|
|
131
236
|
const itemUse = createItemUse((itemId) => content.itemById?.(itemId)?.use);
|
|
@@ -140,7 +245,15 @@ export function createGameContext(options) {
|
|
|
140
245
|
layouts[inventoryId] = { slots: declaration.slots, accepts: declaration.accepts };
|
|
141
246
|
}
|
|
142
247
|
const traits = Object.values(inventoryDeclarations).find((declaration) => declaration.traits !== undefined)?.traits ?? { stackLimit: () => Number.POSITIVE_INFINITY };
|
|
143
|
-
const
|
|
248
|
+
const inventoryByUser = new Map();
|
|
249
|
+
function inventoryFor(userId) {
|
|
250
|
+
let set = inventoryByUser.get(userId);
|
|
251
|
+
if (set === undefined) {
|
|
252
|
+
set = notifyAfter(createInventorySet(layouts, traits), ["put", "take", "move", "replaceState"], signal.notify);
|
|
253
|
+
inventoryByUser.set(userId, set);
|
|
254
|
+
}
|
|
255
|
+
return set;
|
|
256
|
+
}
|
|
144
257
|
const wallets = new Map();
|
|
145
258
|
const walletOf = (userId) => wallets.get(userId) ?? createEmptyWallet();
|
|
146
259
|
const economy = {
|
|
@@ -158,7 +271,8 @@ export function createGameContext(options) {
|
|
|
158
271
|
return null;
|
|
159
272
|
},
|
|
160
273
|
};
|
|
161
|
-
function putIntoAnyInventory(itemId, count) {
|
|
274
|
+
function putIntoAnyInventory(userId, itemId, count) {
|
|
275
|
+
const inventory = inventoryFor(userId);
|
|
162
276
|
for (const inventoryId of inventoryIds) {
|
|
163
277
|
if (inventory.put(inventoryId, itemId, count).status === "ok")
|
|
164
278
|
return;
|
|
@@ -170,7 +284,7 @@ export function createGameContext(options) {
|
|
|
170
284
|
roll: lootRegistry.roll,
|
|
171
285
|
grantToPlayer(userId, drops, source) {
|
|
172
286
|
grantDrops(drops, {
|
|
173
|
-
putItem: (itemId, count) => putIntoAnyInventory(itemId, count),
|
|
287
|
+
putItem: (itemId, count) => putIntoAnyInventory(userId, itemId, count),
|
|
174
288
|
grantCurrency: (currencyId, amount) => economy.grant(userId, currencyId, amount),
|
|
175
289
|
});
|
|
176
290
|
const event = { userId, drops };
|
|
@@ -182,17 +296,17 @@ export function createGameContext(options) {
|
|
|
182
296
|
const trade = createTradeSystem({
|
|
183
297
|
resolveTrade: (itemId) => content.itemById?.(itemId)?.trade,
|
|
184
298
|
wallet: {
|
|
185
|
-
canAfford: (costs) => (walletCanAfford(walletOf(
|
|
299
|
+
canAfford: (costs) => (walletCanAfford(walletOf(activeUserId()), costs) ? null : "insufficient-funds"),
|
|
186
300
|
charge(costs) {
|
|
187
|
-
const result = walletChargeAll(walletOf(
|
|
301
|
+
const result = walletChargeAll(walletOf(activeUserId()), costs);
|
|
188
302
|
if (result.status === "ok") {
|
|
189
|
-
wallets.set(
|
|
303
|
+
wallets.set(activeUserId(), result.state);
|
|
190
304
|
signal.notify();
|
|
191
305
|
}
|
|
192
306
|
},
|
|
193
307
|
grant(gains) {
|
|
194
308
|
for (const [currencyId, amount] of Object.entries(gains)) {
|
|
195
|
-
economy.grant(
|
|
309
|
+
economy.grant(activeUserId(), currencyId, amount);
|
|
196
310
|
}
|
|
197
311
|
},
|
|
198
312
|
},
|
|
@@ -200,16 +314,16 @@ export function createGameContext(options) {
|
|
|
200
314
|
put(inventoryId, itemId, count) {
|
|
201
315
|
if (layouts[inventoryId] === undefined)
|
|
202
316
|
return { reason: `unknown inventory "${inventoryId}"` };
|
|
203
|
-
const result =
|
|
317
|
+
const result = inventoryFor(activeUserId()).put(inventoryId, itemId, count);
|
|
204
318
|
return result.status === "ok" ? null : { reason: result.reason };
|
|
205
319
|
},
|
|
206
320
|
take(inventoryId, itemId, count) {
|
|
207
321
|
if (layouts[inventoryId] === undefined)
|
|
208
322
|
return { reason: `unknown inventory "${inventoryId}"` };
|
|
209
|
-
const result =
|
|
323
|
+
const result = inventoryFor(activeUserId()).take(inventoryId, itemId, count);
|
|
210
324
|
return result.status === "ok" ? null : { reason: result.reason };
|
|
211
325
|
},
|
|
212
|
-
count: (inventoryId, itemId) =>
|
|
326
|
+
count: (inventoryId, itemId) => inventoryFor(activeUserId()).count(inventoryId, itemId),
|
|
213
327
|
},
|
|
214
328
|
});
|
|
215
329
|
function seedUserPool(userId, statId, pool) {
|
|
@@ -227,11 +341,9 @@ export function createGameContext(options) {
|
|
|
227
341
|
},
|
|
228
342
|
grantEconomy: (userId, currencyId, amount) => economy.grant(userId, currencyId, amount),
|
|
229
343
|
grantItem(userId, inventoryId, itemId, count) {
|
|
230
|
-
if (userId !== player.userId)
|
|
231
|
-
return { reason: `unknown user "${userId}"` };
|
|
232
344
|
if (layouts[inventoryId] === undefined)
|
|
233
345
|
return { reason: `unknown inventory "${inventoryId}"` };
|
|
234
|
-
const result =
|
|
346
|
+
const result = inventoryFor(userId).put(inventoryId, itemId, count);
|
|
235
347
|
return result.status === "ok" ? null : { reason: result.reason };
|
|
236
348
|
},
|
|
237
349
|
grantUnlock: (userId, unlockId) => unlocks.grant(userId, unlockId),
|
|
@@ -241,7 +353,8 @@ export function createGameContext(options) {
|
|
|
241
353
|
const quest = notifyAfter(rawQuest, ["accept", "abandon", "progress", "turnIn", "grant", "revoke", "hydrate"], signal.notify);
|
|
242
354
|
const loadouts = notifyAfter(createLoadouts({
|
|
243
355
|
inventory: {
|
|
244
|
-
begin() {
|
|
356
|
+
begin(userId) {
|
|
357
|
+
const inventory = inventoryFor(userId);
|
|
245
358
|
const staged = new Map();
|
|
246
359
|
return {
|
|
247
360
|
put(inventoryId, itemId, count, slot) {
|
|
@@ -282,6 +395,7 @@ export function createGameContext(options) {
|
|
|
282
395
|
statsByInstance.delete(instanceId);
|
|
283
396
|
targeting.clearAll(instanceId);
|
|
284
397
|
pose.clear(instanceId);
|
|
398
|
+
entityColliders.delete(instanceId);
|
|
285
399
|
return existed;
|
|
286
400
|
}
|
|
287
401
|
function resetAllToSpawn(filter) {
|
|
@@ -422,13 +536,14 @@ export function createGameContext(options) {
|
|
|
422
536
|
if (input.dir !== undefined)
|
|
423
537
|
telegraphEvent.dir = input.dir;
|
|
424
538
|
events.emit("combat.telegraph", telegraphEvent);
|
|
539
|
+
const cancelVisual = () => events.emit("combat.telegraphCancelled", { id });
|
|
425
540
|
const bound = input.effect;
|
|
426
541
|
if (bound === undefined)
|
|
427
|
-
return
|
|
542
|
+
return cancelVisual;
|
|
428
543
|
const config = { shape: input.shape, at: input.at, windupMs: input.windupMs };
|
|
429
544
|
if (input.dir !== undefined)
|
|
430
545
|
config.dir = input.dir;
|
|
431
|
-
|
|
546
|
+
const cancelEffect = time.after(input.windupMs / 1000, () => {
|
|
432
547
|
const targets = entities.list().filter((entity) => pointInTelegraph(config, entity.position));
|
|
433
548
|
for (const target of targets) {
|
|
434
549
|
applyEffectAndFloat({
|
|
@@ -439,6 +554,10 @@ export function createGameContext(options) {
|
|
|
439
554
|
});
|
|
440
555
|
}
|
|
441
556
|
});
|
|
557
|
+
return () => {
|
|
558
|
+
cancelEffect();
|
|
559
|
+
cancelVisual();
|
|
560
|
+
};
|
|
442
561
|
}
|
|
443
562
|
function applyHitReaction(input) {
|
|
444
563
|
const attacker = entities.get(input.from);
|
|
@@ -499,10 +618,51 @@ export function createGameContext(options) {
|
|
|
499
618
|
preview: effects.preview,
|
|
500
619
|
applyEffect: applyEffectAndFloat,
|
|
501
620
|
};
|
|
621
|
+
const projectileObstacles = definition.physics?.projectileObstacles === true;
|
|
622
|
+
const projectileSceneRaycast = {
|
|
623
|
+
raycast(input) {
|
|
624
|
+
return sceneRaycast.raycast({
|
|
625
|
+
...input,
|
|
626
|
+
filter: {
|
|
627
|
+
entities: true,
|
|
628
|
+
objects: projectileObstacles,
|
|
629
|
+
terrain: false,
|
|
630
|
+
walls: projectileObstacles,
|
|
631
|
+
...input.filter,
|
|
632
|
+
},
|
|
633
|
+
});
|
|
634
|
+
},
|
|
635
|
+
raycastAll(input) {
|
|
636
|
+
return sceneRaycast.raycastAll({
|
|
637
|
+
...input,
|
|
638
|
+
filter: {
|
|
639
|
+
entities: true,
|
|
640
|
+
objects: projectileObstacles,
|
|
641
|
+
terrain: false,
|
|
642
|
+
walls: projectileObstacles,
|
|
643
|
+
...input.filter,
|
|
644
|
+
},
|
|
645
|
+
});
|
|
646
|
+
},
|
|
647
|
+
};
|
|
502
648
|
const projectiles = notifyAfter(createProjectileSystem({
|
|
503
649
|
effects: floatingEffects,
|
|
504
650
|
spatial: combatSpatial,
|
|
505
651
|
getStat: weapon.getStat,
|
|
652
|
+
sceneRaycast: projectileSceneRaycast,
|
|
653
|
+
objects: projectileObstacles
|
|
654
|
+
? {
|
|
655
|
+
list: () => objects.list(),
|
|
656
|
+
inBox: (min, max) => objects.inBox(min, max),
|
|
657
|
+
halfExtents: (catalogId) => {
|
|
658
|
+
const half = content.objectById?.(catalogId)?.halfExtents;
|
|
659
|
+
return half === undefined ? null : [half[0], half[1], half[2]];
|
|
660
|
+
},
|
|
661
|
+
collidersOf: objectCollidersOf,
|
|
662
|
+
}
|
|
663
|
+
: undefined,
|
|
664
|
+
entityCollidersOf,
|
|
665
|
+
rotationYOf: (instanceId) => entities.get(instanceId)?.rotationY,
|
|
506
666
|
now,
|
|
507
667
|
onSettle(report) {
|
|
508
668
|
events.emit("projectile.settled", {
|
|
@@ -517,10 +677,21 @@ export function createGameContext(options) {
|
|
|
517
677
|
const sceneObjects = {
|
|
518
678
|
...objects,
|
|
519
679
|
catalog: (instanceId) => catalogObject(instanceId) ?? null,
|
|
520
|
-
raycast: (input) => raycastObjects(objects
|
|
521
|
-
raycastAll: (input) => raycastObjectsAll(objects
|
|
680
|
+
raycast: (input) => raycastObjects(objects, input),
|
|
681
|
+
raycastAll: (input) => raycastObjectsAll(objects, input),
|
|
682
|
+
setColliders(instanceId, colliders) {
|
|
683
|
+
if (colliders === null)
|
|
684
|
+
objectColliders.delete(instanceId);
|
|
685
|
+
else
|
|
686
|
+
objectColliders.set(instanceId, colliders);
|
|
687
|
+
signal.notify();
|
|
688
|
+
},
|
|
689
|
+
collidersOf: objectCollidersOf,
|
|
522
690
|
};
|
|
523
|
-
const store = notifyAfter(createObservableKeyedStore(), ["set", "delete"], signal.notify);
|
|
691
|
+
const store = notifyAfter(createObservableKeyedStore(), ["set", "delete", "hydrate"], signal.notify);
|
|
692
|
+
const players = features.players
|
|
693
|
+
? notifyAfter(createConnectedPlayers(), ["join", "leave"], signal.notify)
|
|
694
|
+
: undefined;
|
|
524
695
|
const cardPiles = new Map();
|
|
525
696
|
function pile(id, config) {
|
|
526
697
|
const existing = cardPiles.get(id);
|
|
@@ -542,7 +713,7 @@ export function createGameContext(options) {
|
|
|
542
713
|
throw new Error(`turn loop "${id}" has not been created yet; pass a config on first access`);
|
|
543
714
|
}
|
|
544
715
|
const raw = createTurnLoop(config);
|
|
545
|
-
const wrappedCommit = notifyAfter(raw.commit, ["submit", "expected", "commit", "
|
|
716
|
+
const wrappedCommit = notifyAfter(raw.commit, ["submit", "expected", "commit", "discard", "clear"], signal.notify);
|
|
546
717
|
const wrapped = {
|
|
547
718
|
...notifyAfter(raw, [
|
|
548
719
|
"setOrder",
|
|
@@ -561,9 +732,101 @@ export function createGameContext(options) {
|
|
|
561
732
|
turnLoops.set(id, wrapped);
|
|
562
733
|
return wrapped;
|
|
563
734
|
}
|
|
564
|
-
|
|
735
|
+
class NotifyingRaceState extends RaceState {
|
|
736
|
+
addRacer(racerId, startTime) {
|
|
737
|
+
super.addRacer(racerId, startTime);
|
|
738
|
+
signal.notify();
|
|
739
|
+
}
|
|
740
|
+
removeRacer(racerId) {
|
|
741
|
+
super.removeRacer(racerId);
|
|
742
|
+
signal.notify();
|
|
743
|
+
}
|
|
744
|
+
reset() {
|
|
745
|
+
super.reset();
|
|
746
|
+
signal.notify();
|
|
747
|
+
}
|
|
748
|
+
eliminate(racerId) {
|
|
749
|
+
super.eliminate(racerId);
|
|
750
|
+
signal.notify();
|
|
751
|
+
}
|
|
752
|
+
update(now, positions) {
|
|
753
|
+
const raceEvents = super.update(now, positions);
|
|
754
|
+
if (raceEvents.length > 0)
|
|
755
|
+
signal.notify();
|
|
756
|
+
return raceEvents;
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
const raceStates = new Map();
|
|
760
|
+
function raceState(id, config) {
|
|
761
|
+
const existing = raceStates.get(id);
|
|
762
|
+
if (existing !== undefined)
|
|
763
|
+
return existing;
|
|
764
|
+
if (config === undefined) {
|
|
765
|
+
throw new Error(`race "${id}" has not been created yet; pass a config on first access`);
|
|
766
|
+
}
|
|
767
|
+
const created = new NotifyingRaceState(config);
|
|
768
|
+
raceStates.set(id, created);
|
|
769
|
+
return created;
|
|
770
|
+
}
|
|
771
|
+
const camera = notifyAfter(createCameraDirector(), ["follow", "setCinematic", "setChaseTuning"], signal.notify);
|
|
565
772
|
const input = createInputSnapshot();
|
|
566
|
-
const
|
|
773
|
+
const motionByUser = new Map();
|
|
774
|
+
function motionFor(userId) {
|
|
775
|
+
let queue = motionByUser.get(userId);
|
|
776
|
+
if (queue === undefined) {
|
|
777
|
+
queue = createMotionIntents();
|
|
778
|
+
motionByUser.set(userId, queue);
|
|
779
|
+
}
|
|
780
|
+
return queue;
|
|
781
|
+
}
|
|
782
|
+
const snapshotModules = [
|
|
783
|
+
{ key: "entities", snapshot: () => entities.snapshot(), hydrate: (data) => entities.hydrate(data) },
|
|
784
|
+
{
|
|
785
|
+
key: "stats",
|
|
786
|
+
snapshot: () => snapshotEntityStats(statsByInstance),
|
|
787
|
+
hydrate: (data) => hydrateEntityStats(statsByInstance, data),
|
|
788
|
+
},
|
|
789
|
+
{
|
|
790
|
+
key: "store",
|
|
791
|
+
snapshot: () => store.snapshot(),
|
|
792
|
+
hydrate: (data) => store.hydrate(data),
|
|
793
|
+
},
|
|
794
|
+
{ key: "feed", snapshot: () => feed.snapshot(), hydrate: (data) => feed.hydrate(data) },
|
|
795
|
+
{
|
|
796
|
+
key: "inventory",
|
|
797
|
+
snapshot: () => {
|
|
798
|
+
const byUser = {};
|
|
799
|
+
for (const [userId, set] of inventoryByUser) {
|
|
800
|
+
const states = {};
|
|
801
|
+
for (const inventoryId of inventoryIds)
|
|
802
|
+
states[inventoryId] = set.state(inventoryId);
|
|
803
|
+
byUser[userId] = states;
|
|
804
|
+
}
|
|
805
|
+
return byUser;
|
|
806
|
+
},
|
|
807
|
+
hydrate: (data) => {
|
|
808
|
+
for (const [userId, states] of Object.entries(data)) {
|
|
809
|
+
const set = inventoryFor(userId);
|
|
810
|
+
for (const [inventoryId, state] of Object.entries(states))
|
|
811
|
+
set.replaceState(inventoryId, state);
|
|
812
|
+
}
|
|
813
|
+
},
|
|
814
|
+
},
|
|
815
|
+
];
|
|
816
|
+
if (leaderboard) {
|
|
817
|
+
snapshotModules.push({
|
|
818
|
+
key: "leaderboard",
|
|
819
|
+
snapshot: () => leaderboard.snapshot(),
|
|
820
|
+
hydrate: (data) => leaderboard.hydrate(data),
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
if (chat) {
|
|
824
|
+
snapshotModules.push({
|
|
825
|
+
key: "chat",
|
|
826
|
+
snapshot: () => chat.snapshot(),
|
|
827
|
+
hydrate: (data) => chat.hydrate(data),
|
|
828
|
+
});
|
|
829
|
+
}
|
|
567
830
|
const ctx = {
|
|
568
831
|
scene: {
|
|
569
832
|
object: sceneObjects,
|
|
@@ -572,11 +835,13 @@ export function createGameContext(options) {
|
|
|
572
835
|
despawn: despawnEntity,
|
|
573
836
|
update: entities.update,
|
|
574
837
|
setPose: entities.setPose,
|
|
838
|
+
setPoseConstraint: entities.setPoseConstraint,
|
|
575
839
|
get: entities.get,
|
|
576
840
|
list: entities.list,
|
|
577
841
|
spawnPoseOf: entities.spawnPoseOf,
|
|
578
842
|
resetToSpawn: entities.resetToSpawn,
|
|
579
843
|
resetAllToSpawn,
|
|
844
|
+
blackboard: entities.blackboard,
|
|
580
845
|
stats: entityStats,
|
|
581
846
|
floatText: emitFloatText,
|
|
582
847
|
telegraph: fireTelegraph,
|
|
@@ -595,6 +860,15 @@ export function createGameContext(options) {
|
|
|
595
860
|
hasLineOfSight: spatial.hasLineOfSight,
|
|
596
861
|
queryArc: spatial.queryArc,
|
|
597
862
|
moveToward: spatial.moveToward,
|
|
863
|
+
invalidateSpatial: spatial.invalidate,
|
|
864
|
+
setColliders(instanceId, colliders) {
|
|
865
|
+
if (colliders === null)
|
|
866
|
+
entityColliders.delete(instanceId);
|
|
867
|
+
else
|
|
868
|
+
entityColliders.set(instanceId, colliders);
|
|
869
|
+
signal.notify();
|
|
870
|
+
},
|
|
871
|
+
collidersOf: entityCollidersOf,
|
|
598
872
|
form: forms,
|
|
599
873
|
paint: paintLayer,
|
|
600
874
|
},
|
|
@@ -606,6 +880,8 @@ export function createGameContext(options) {
|
|
|
606
880
|
pickup: pickupWorldItem,
|
|
607
881
|
consume: worldItems.remove,
|
|
608
882
|
},
|
|
883
|
+
raycast: (input) => sceneRaycast.raycast(input),
|
|
884
|
+
raycastAll: (input) => sceneRaycast.raycastAll(input),
|
|
609
885
|
},
|
|
610
886
|
world: {
|
|
611
887
|
ground,
|
|
@@ -621,8 +897,27 @@ export function createGameContext(options) {
|
|
|
621
897
|
signal.notify();
|
|
622
898
|
return result;
|
|
623
899
|
},
|
|
900
|
+
runAs(actorUserId, name, input) {
|
|
901
|
+
const previous = actingUserId;
|
|
902
|
+
actingUserId = actorUserId;
|
|
903
|
+
try {
|
|
904
|
+
const result = commandRegistry.run(ctx, name, input);
|
|
905
|
+
signal.notify();
|
|
906
|
+
return result;
|
|
907
|
+
}
|
|
908
|
+
finally {
|
|
909
|
+
actingUserId = previous;
|
|
910
|
+
}
|
|
911
|
+
},
|
|
912
|
+
actor: () => actingUserId,
|
|
624
913
|
},
|
|
625
914
|
events,
|
|
915
|
+
playEntityAnimation: (instanceId, event) => events.emit("entity.animation", { instanceId, event }),
|
|
916
|
+
audio: {
|
|
917
|
+
play: (sound, at) => events.emit("audio.play", at === undefined ? { sound } : { sound, at }),
|
|
918
|
+
music: (theme, transpose) => events.emit("audio.music", transpose === undefined ? { theme } : { theme, transpose }),
|
|
919
|
+
resume: () => events.emit("audio.resume", {}),
|
|
920
|
+
},
|
|
626
921
|
feed: {
|
|
627
922
|
bind: (action) => feed.bind(action, events),
|
|
628
923
|
push(action, entry) {
|
|
@@ -647,20 +942,33 @@ export function createGameContext(options) {
|
|
|
647
942
|
leaderboard,
|
|
648
943
|
roster,
|
|
649
944
|
store,
|
|
650
|
-
cards: { pile },
|
|
651
|
-
turn: { loop },
|
|
945
|
+
cards: features.cards ? { pile } : undefined,
|
|
946
|
+
turn: features.turn ? { loop } : undefined,
|
|
947
|
+
race: features.race ? { state: raceState } : undefined,
|
|
948
|
+
players,
|
|
652
949
|
},
|
|
653
950
|
player: {
|
|
654
|
-
userId
|
|
951
|
+
get userId() {
|
|
952
|
+
return activeUserId();
|
|
953
|
+
},
|
|
655
954
|
isNew: player.isNew,
|
|
656
|
-
inventory
|
|
657
|
-
|
|
955
|
+
get inventory() {
|
|
956
|
+
return inventoryFor(activeUserId());
|
|
957
|
+
},
|
|
958
|
+
inventoryFor,
|
|
959
|
+
get stats() {
|
|
960
|
+
return playerStatsFor(activeUserId());
|
|
961
|
+
},
|
|
962
|
+
statsFor: playerStatsFor,
|
|
658
963
|
loadout: loadouts,
|
|
659
964
|
applyLoadout: loadouts.applyLoadout,
|
|
660
965
|
movement: pose,
|
|
661
966
|
possession,
|
|
662
967
|
cosmetics,
|
|
663
|
-
motion
|
|
968
|
+
get motion() {
|
|
969
|
+
return motionFor(activeUserId());
|
|
970
|
+
},
|
|
971
|
+
motionFor,
|
|
664
972
|
},
|
|
665
973
|
item: {
|
|
666
974
|
use: {
|
|
@@ -680,6 +988,11 @@ export function createGameContext(options) {
|
|
|
680
988
|
input,
|
|
681
989
|
subscribe: signal.subscribe,
|
|
682
990
|
version: signal.version,
|
|
991
|
+
snapshot: () => composeWorldSnapshot(snapshotModules),
|
|
992
|
+
hydrate(snapshot) {
|
|
993
|
+
applyWorldSnapshot(snapshotModules, snapshot);
|
|
994
|
+
signal.notify();
|
|
995
|
+
},
|
|
683
996
|
};
|
|
684
997
|
return ctx;
|
|
685
998
|
}
|