@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
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type CardPile, type CardPileConfig } from "../cards/cardPile.js";
|
|
1
2
|
import { type OnDeathSpec } from "../combat/death.js";
|
|
2
3
|
import { type EffectInput, type EffectResult, type EffectVia, type ReceiveMap, type SingleTargetEffectInput } from "../combat/effects.js";
|
|
3
4
|
import { type ProjectileSystem } from "../combat/projectiles.js";
|
|
@@ -6,6 +7,7 @@ import { type TelegraphShape } from "../combat/telegraph.js";
|
|
|
6
7
|
import { type CommandDefinition, type CommandResult } from "../commands/commandRegistry.js";
|
|
7
8
|
import { type Cosmetics } from "../game/cosmetics.js";
|
|
8
9
|
import type { GameDefinition } from "../game/defineGame.js";
|
|
10
|
+
import { type TerrainField } from "../world/terrain.js";
|
|
9
11
|
import { type GameEventMap, type GameEvents } from "../game/events.js";
|
|
10
12
|
import { type GameFeed } from "../game/feed.js";
|
|
11
13
|
import { type Leaderboard } from "../game/leaderboard.js";
|
|
@@ -13,6 +15,7 @@ import { type Loadouts } from "../game/loadout.js";
|
|
|
13
15
|
import { type Drop, type LootTableDef } from "../game/lootTable.js";
|
|
14
16
|
import { type QuestJournal } from "../game/quest.js";
|
|
15
17
|
import { type WorldItemRecord, type WorldItemSpawnInput } from "../game/worldItem.js";
|
|
18
|
+
import { type Chat } from "../game/chat.js";
|
|
16
19
|
import { type Social } from "../game/social.js";
|
|
17
20
|
import { type TradeField, type TradeSystem } from "../game/trade.js";
|
|
18
21
|
import { type Unlocks } from "../game/unlocks.js";
|
|
@@ -23,16 +26,26 @@ import { type ItemUseHandler, type ItemUseInput, type ItemUseRejection, type Ite
|
|
|
23
26
|
import { type WeaponStats } from "../item/weapon.js";
|
|
24
27
|
import { type PoseAllowedStates, type PoseState } from "../movement/poseState.js";
|
|
25
28
|
import type { ModelAssetRef } from "../scene/assetCatalog.js";
|
|
29
|
+
import { type PaintLayer } from "../scene/paintLayer.js";
|
|
26
30
|
import { type EntityStatsApi, type StatCatalog } from "../scene/entityStats.js";
|
|
27
|
-
import type { EntityPose, EntityPosition, SceneEntity, SpawnOptions } from "../scene/entityStore.js";
|
|
31
|
+
import type { EntityPose, EntityPosition, EntityStore, SceneEntity, SpawnOptions, SpawnPose } from "../scene/entityStore.js";
|
|
28
32
|
import { type Forms } from "../scene/form.js";
|
|
33
|
+
import type { EntityColliderSet } from "../scene/colliders.js";
|
|
34
|
+
import { type ObjectRaycastHit, type ObjectRaycastInput } from "../scene/objectQuery.js";
|
|
29
35
|
import { type ObjectStore } from "../scene/objectStore.js";
|
|
30
36
|
import { type Roster } from "../scene/roster.js";
|
|
31
37
|
import { type Possession } from "../scene/possession.js";
|
|
38
|
+
import { type SceneRaycastHit, type SceneRaycastInput } from "../scene/sceneRaycast.js";
|
|
32
39
|
import { type SpatialApi } from "../scene/spatial.js";
|
|
33
40
|
import { type CycleTargetOptions } from "../scene/targeting.js";
|
|
34
41
|
import { type Stats } from "../stats/statModifiers.js";
|
|
42
|
+
import { type ObservableKeyedStore } from "../store/observableKeyedStore.js";
|
|
35
43
|
import { type SimClock } from "../time/simClock.js";
|
|
44
|
+
import { type TurnLoop, type TurnLoopConfig } from "../turn/turnLoop.js";
|
|
45
|
+
import { RaceState, type RaceStateConfig } from "../game/race.js";
|
|
46
|
+
import { type CameraDirector } from "./cameraDirector.js";
|
|
47
|
+
import { type InputSnapshot } from "./inputSnapshot.js";
|
|
48
|
+
import { type MotionIntents } from "./motionIntents.js";
|
|
36
49
|
export interface GameContextItemEntry {
|
|
37
50
|
use?: string;
|
|
38
51
|
weapon?: Record<string, unknown>;
|
|
@@ -53,6 +66,8 @@ export interface GameContextEntityEntry {
|
|
|
53
66
|
role?: CatalogEntityRole;
|
|
54
67
|
/** Right-click context-menu verbs for this entity (#31). */
|
|
55
68
|
verbs?: readonly ContextVerb[];
|
|
69
|
+
/** Purpose-specific colliders (physical body + named damage hitboxes). */
|
|
70
|
+
colliders?: EntityColliderSet;
|
|
56
71
|
}
|
|
57
72
|
export interface GameContextObjectEntry {
|
|
58
73
|
proximityPrompt?: ProximityPrompt;
|
|
@@ -62,6 +77,8 @@ export interface GameContextObjectEntry {
|
|
|
62
77
|
slotInventory?: InventoryLayout;
|
|
63
78
|
/** Right-click context-menu verbs for this object (#31). */
|
|
64
79
|
verbs?: readonly ContextVerb[];
|
|
80
|
+
colliders?: EntityColliderSet;
|
|
81
|
+
halfExtents?: readonly [number, number, number];
|
|
65
82
|
}
|
|
66
83
|
export interface GameContextContent {
|
|
67
84
|
itemById?(itemId: string): GameContextItemEntry | null | undefined;
|
|
@@ -76,9 +93,14 @@ export interface GameContextOptions<TAssetRef extends ModelAssetRef = ModelAsset
|
|
|
76
93
|
isNew: boolean;
|
|
77
94
|
};
|
|
78
95
|
now?: () => number;
|
|
96
|
+
occluder?: (from: EntityPosition, to: EntityPosition) => boolean;
|
|
79
97
|
}
|
|
80
98
|
export interface SceneObjectContext extends ObjectStore {
|
|
81
99
|
catalog(instanceId: string): GameContextObjectEntry | null;
|
|
100
|
+
raycast(input: ObjectRaycastInput): ObjectRaycastHit | null;
|
|
101
|
+
raycastAll(input: ObjectRaycastInput): readonly ObjectRaycastHit[];
|
|
102
|
+
setColliders(instanceId: string, colliders: EntityColliderSet | null): void;
|
|
103
|
+
collidersOf(instanceId: string): EntityColliderSet | null;
|
|
82
104
|
}
|
|
83
105
|
export interface FloatTextInput {
|
|
84
106
|
instanceId?: string;
|
|
@@ -115,9 +137,14 @@ export interface HitReactionInput {
|
|
|
115
137
|
export interface SceneEntityContext {
|
|
116
138
|
spawn(name: string, options?: SpawnOptions): string;
|
|
117
139
|
despawn(instanceId: string): boolean;
|
|
140
|
+
update: EntityStore["update"];
|
|
118
141
|
setPose(instanceId: string, pose: EntityPose): boolean;
|
|
142
|
+
setPoseConstraint: EntityStore["setPoseConstraint"];
|
|
119
143
|
get(instanceId: string): SceneEntity | null;
|
|
120
144
|
list(): readonly SceneEntity[];
|
|
145
|
+
spawnPoseOf(instanceId: string): SpawnPose | null;
|
|
146
|
+
resetToSpawn(instanceId: string): boolean;
|
|
147
|
+
resetAllToSpawn(filter?: (entity: SceneEntity) => boolean): number;
|
|
121
148
|
stats: EntityStatsApi;
|
|
122
149
|
floatText(input: FloatTextInput): void;
|
|
123
150
|
telegraph(input: TelegraphInput): () => void;
|
|
@@ -125,7 +152,7 @@ export interface SceneEntityContext {
|
|
|
125
152
|
setTarget(fromId: string, toId: string | null): void;
|
|
126
153
|
getTarget(fromId: string): string | null;
|
|
127
154
|
cycleTarget(fromId: string, options?: CycleTargetOptions): string | null;
|
|
128
|
-
canReceive(instanceId: string, effect: string): string | null;
|
|
155
|
+
canReceive(instanceId: string, effect: string, magnitude?: number): string | null;
|
|
129
156
|
preview(input: SingleTargetEffectInput): number;
|
|
130
157
|
effect(input: EffectInput): EffectResult[];
|
|
131
158
|
willHitProjectile: ProjectileSystem["willHitProjectile"];
|
|
@@ -136,7 +163,11 @@ export interface SceneEntityContext {
|
|
|
136
163
|
hasLineOfSight: SpatialApi["hasLineOfSight"];
|
|
137
164
|
queryArc: SpatialApi["queryArc"];
|
|
138
165
|
moveToward: SpatialApi["moveToward"];
|
|
166
|
+
invalidateSpatial: SpatialApi["invalidate"];
|
|
167
|
+
setColliders(instanceId: string, colliders: EntityColliderSet | null): void;
|
|
168
|
+
collidersOf(instanceId: string): EntityColliderSet | null;
|
|
139
169
|
form: Forms;
|
|
170
|
+
paint: PaintLayer;
|
|
140
171
|
}
|
|
141
172
|
export type WorldItemPickupResult = {
|
|
142
173
|
status: "ok";
|
|
@@ -151,6 +182,7 @@ export interface SceneWorldItemContext {
|
|
|
151
182
|
list(): readonly WorldItemRecord[];
|
|
152
183
|
nearestInRadius(from: EntityPosition, radius: number, filter?: (record: WorldItemRecord) => boolean): string | null;
|
|
153
184
|
pickup(instanceId: string, userId: string): WorldItemPickupResult;
|
|
185
|
+
consume(instanceId: string): WorldItemRecord | null;
|
|
154
186
|
}
|
|
155
187
|
export interface GameContextCommands {
|
|
156
188
|
define<TInput>(name: string, definition: CommandDefinition<GameContext, TInput>): void;
|
|
@@ -180,12 +212,31 @@ export interface GameContextItemUse {
|
|
|
180
212
|
can(input: ItemUseInput): ItemUseRejection | null;
|
|
181
213
|
use(input: ItemUseInput): ItemUseResult<GameContext>;
|
|
182
214
|
}
|
|
215
|
+
export interface GameContextWorld {
|
|
216
|
+
ground: TerrainField;
|
|
217
|
+
groundHeightAt(x: number, z: number): number;
|
|
218
|
+
}
|
|
219
|
+
export interface GameContextCards {
|
|
220
|
+
/** Lazily creates (on first call, `config` required) or returns the existing notify-wrapped pile for `id`. */
|
|
221
|
+
pile(id: string, config?: CardPileConfig): CardPile;
|
|
222
|
+
}
|
|
223
|
+
export interface GameContextTurn {
|
|
224
|
+
/** Lazily creates (on first call, `config` required) or returns the existing notify-wrapped turn loop for `id`. */
|
|
225
|
+
loop(id: string, config?: TurnLoopConfig): TurnLoop;
|
|
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
|
+
}
|
|
183
231
|
export interface GameContext {
|
|
184
232
|
scene: {
|
|
185
233
|
object: SceneObjectContext;
|
|
186
234
|
entity: SceneEntityContext;
|
|
187
235
|
worldItem: SceneWorldItemContext;
|
|
236
|
+
raycast(input: SceneRaycastInput): SceneRaycastHit | null;
|
|
237
|
+
raycastAll(input: SceneRaycastInput): readonly SceneRaycastHit[];
|
|
188
238
|
};
|
|
239
|
+
world: GameContextWorld;
|
|
189
240
|
game: {
|
|
190
241
|
commands: GameContextCommands;
|
|
191
242
|
events: GameEvents;
|
|
@@ -194,10 +245,16 @@ export interface GameContext {
|
|
|
194
245
|
trade: TradeSystem;
|
|
195
246
|
quest: QuestJournal;
|
|
196
247
|
social: Social;
|
|
248
|
+
chat: Chat;
|
|
197
249
|
unlocks: Unlocks;
|
|
198
250
|
economy: GameContextEconomy;
|
|
199
251
|
leaderboard: Leaderboard;
|
|
200
252
|
roster: Roster;
|
|
253
|
+
/** Game-defined keyed reactive store slot (#163.1); mutations bump `ctx.version()`/notify `ctx.subscribe`. */
|
|
254
|
+
store: ObservableKeyedStore<unknown>;
|
|
255
|
+
cards: GameContextCards;
|
|
256
|
+
turn: GameContextTurn;
|
|
257
|
+
race: GameContextRace;
|
|
201
258
|
};
|
|
202
259
|
player: {
|
|
203
260
|
userId: string;
|
|
@@ -211,12 +268,18 @@ export interface GameContext {
|
|
|
211
268
|
movement: PoseState;
|
|
212
269
|
possession: Possession;
|
|
213
270
|
cosmetics: Cosmetics;
|
|
271
|
+
/** Vertical-motion seam into the shell-owned FrameDriver (#162.4); see `MotionIntents`. */
|
|
272
|
+
motion: MotionIntents;
|
|
214
273
|
};
|
|
215
274
|
item: {
|
|
216
275
|
use: GameContextItemUse;
|
|
217
276
|
weapon: WeaponStats;
|
|
218
277
|
};
|
|
219
278
|
time: SimClock;
|
|
279
|
+
/** Runtime camera-follow/cinematic override (#196.2); the shell reads `followedEntityId()` each frame. */
|
|
280
|
+
camera: CameraDirector;
|
|
281
|
+
/** Held-input snapshot published by the shell each frame before `onTick` (#164.1). */
|
|
282
|
+
input: InputSnapshot;
|
|
220
283
|
subscribe(listener: () => void): () => void;
|
|
221
284
|
version(): number;
|
|
222
285
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createCardPile } from "../cards/cardPile.js";
|
|
1
2
|
import { createDeathSystem, deathReasonFromEffect, normalizeOnDeath } from "../combat/death.js";
|
|
2
3
|
import { createEffectSystem, } from "../combat/effects.js";
|
|
3
4
|
import { createProjectileSystem } from "../combat/projectiles.js";
|
|
@@ -6,6 +7,7 @@ import { pointInTelegraph, } from "../combat/telegraph.js";
|
|
|
6
7
|
import { createCommandRegistry, } from "../commands/commandRegistry.js";
|
|
7
8
|
import { balance as walletBalance, canAfford as walletCanAfford, charge as walletCharge, chargeAll as walletChargeAll, createEmptyWallet, grant as walletGrant, } from "../economy/wallet.js";
|
|
8
9
|
import { createCosmetics } from "../game/cosmetics.js";
|
|
10
|
+
import { groundFieldFor } from "../world/terrain.js";
|
|
9
11
|
import { createGameEvents } from "../game/events.js";
|
|
10
12
|
import { createGameFeed } from "../game/feed.js";
|
|
11
13
|
import { createLeaderboard } from "../game/leaderboard.js";
|
|
@@ -13,6 +15,7 @@ import { createLoadouts } from "../game/loadout.js";
|
|
|
13
15
|
import { createLootRegistry, grantDrops } from "../game/lootTable.js";
|
|
14
16
|
import { createQuestJournal } from "../game/quest.js";
|
|
15
17
|
import { createWorldItemStore, resolveDeathDrops, DEFAULT_RARITY, WORLD_ITEM_ENTITY_NAME, } from "../game/worldItem.js";
|
|
18
|
+
import { createChat } from "../game/chat.js";
|
|
16
19
|
import { createSocial } from "../game/social.js";
|
|
17
20
|
import { createTradeSystem } from "../game/trade.js";
|
|
18
21
|
import { createUnlocks } from "../game/unlocks.js";
|
|
@@ -20,21 +23,32 @@ import { createInventorySet, putItem, } from "../inventory/inventoryModel.js";
|
|
|
20
23
|
import { createItemUse, } from "../item/use.js";
|
|
21
24
|
import { createWeaponStats } from "../item/weapon.js";
|
|
22
25
|
import { createPoseState } from "../movement/poseState.js";
|
|
26
|
+
import { createPaintLayer } from "../scene/paintLayer.js";
|
|
23
27
|
import { createEntityStatsApi, seedStatValues, setStatValue, } from "../scene/entityStats.js";
|
|
24
28
|
import { createForms } from "../scene/form.js";
|
|
29
|
+
import { raycastObjects, raycastObjectsAll } from "../scene/objectQuery.js";
|
|
25
30
|
import { createObjectStore } from "../scene/objectStore.js";
|
|
26
31
|
import { createRoster } from "../scene/roster.js";
|
|
27
32
|
import { createPossession } from "../scene/possession.js";
|
|
33
|
+
import { createSceneRaycast, } from "../scene/sceneRaycast.js";
|
|
28
34
|
import { createSpatialApi } from "../scene/spatial.js";
|
|
29
35
|
import { createTargeting } from "../scene/targeting.js";
|
|
30
36
|
import { createStats } from "../stats/statModifiers.js";
|
|
31
37
|
import { createChangeSignal, notifyAfter } from "../store/changeSignal.js";
|
|
38
|
+
import { createObservableKeyedStore } from "../store/observableKeyedStore.js";
|
|
32
39
|
import { createSimClock } from "../time/simClock.js";
|
|
40
|
+
import { createTurnLoop } from "../turn/turnLoop.js";
|
|
41
|
+
import { RaceState } from "../game/race.js";
|
|
42
|
+
import { createCameraDirector } from "./cameraDirector.js";
|
|
43
|
+
import { createInputSnapshot } from "./inputSnapshot.js";
|
|
44
|
+
import { createMotionIntents } from "./motionIntents.js";
|
|
33
45
|
export function createGameContext(options) {
|
|
34
46
|
const { definition, content, player } = options;
|
|
35
47
|
const now = options.now ?? Date.now;
|
|
48
|
+
const occluder = options.occluder;
|
|
36
49
|
const signal = createChangeSignal();
|
|
37
50
|
const time = createSimClock({ config: definition.time, onChange: signal.notify });
|
|
51
|
+
const ground = groundFieldFor(definition.world);
|
|
38
52
|
const entities = definition.scene;
|
|
39
53
|
const objects = createObjectStore();
|
|
40
54
|
entities.subscribe(signal.notify);
|
|
@@ -57,12 +71,46 @@ export function createGameContext(options) {
|
|
|
57
71
|
const object = objects.get(instanceId);
|
|
58
72
|
return object === null ? undefined : content.objectById?.(object.catalogId);
|
|
59
73
|
}
|
|
74
|
+
let spatialGeneration = 0;
|
|
75
|
+
const candidateIds = [];
|
|
76
|
+
let candidateIdsDirty = true;
|
|
77
|
+
function refreshCandidateIds() {
|
|
78
|
+
if (!candidateIdsDirty)
|
|
79
|
+
return candidateIds;
|
|
80
|
+
candidateIds.length = 0;
|
|
81
|
+
for (const entity of entities.list())
|
|
82
|
+
candidateIds.push(entity.id);
|
|
83
|
+
candidateIdsDirty = false;
|
|
84
|
+
return candidateIds;
|
|
85
|
+
}
|
|
60
86
|
const spatial = createSpatialApi({
|
|
61
87
|
resolvePosition: (instanceId) => entities.get(instanceId)?.position,
|
|
62
|
-
candidates:
|
|
88
|
+
candidates: refreshCandidateIds,
|
|
89
|
+
grid: { cellSize: 8 },
|
|
90
|
+
getVersion: () => spatialGeneration,
|
|
91
|
+
...(occluder !== undefined ? { occluder } : {}),
|
|
63
92
|
});
|
|
93
|
+
entities.subscribe(() => {
|
|
94
|
+
candidateIdsDirty = true;
|
|
95
|
+
spatialGeneration += 1;
|
|
96
|
+
spatial.invalidate();
|
|
97
|
+
});
|
|
98
|
+
const entityColliders = new Map();
|
|
99
|
+
const objectColliders = new Map();
|
|
100
|
+
function entityCollidersOf(instanceId) {
|
|
101
|
+
const override = entityColliders.get(instanceId);
|
|
102
|
+
if (override !== undefined)
|
|
103
|
+
return override;
|
|
104
|
+
return catalogEntry(instanceId)?.colliders ?? null;
|
|
105
|
+
}
|
|
106
|
+
function objectCollidersOf(instanceId) {
|
|
107
|
+
const override = objectColliders.get(instanceId);
|
|
108
|
+
if (override !== undefined)
|
|
109
|
+
return override;
|
|
110
|
+
return catalogObject(instanceId)?.colliders ?? null;
|
|
111
|
+
}
|
|
64
112
|
const targeting = notifyAfter(createTargeting({
|
|
65
|
-
candidates: () =>
|
|
113
|
+
candidates: () => [...refreshCandidateIds()],
|
|
66
114
|
classify(_fromId, toId) {
|
|
67
115
|
const role = catalogEntry(toId)?.role;
|
|
68
116
|
return role === "enemy" || role === "hostile" ? "hostile" : "friendly";
|
|
@@ -71,9 +119,43 @@ export function createGameContext(options) {
|
|
|
71
119
|
}), ["setTarget", "cycleTarget"], signal.notify);
|
|
72
120
|
const combatSpatial = {
|
|
73
121
|
inRadius: (center, radius) => spatial.inRadius(center, radius),
|
|
74
|
-
hasLineOfSight: (from, to) =>
|
|
122
|
+
hasLineOfSight: (from, to) => {
|
|
123
|
+
if (typeof from === "string")
|
|
124
|
+
return spatial.hasLineOfSight(from, to);
|
|
125
|
+
const toPos = entities.get(to)?.position;
|
|
126
|
+
if (toPos === undefined)
|
|
127
|
+
return false;
|
|
128
|
+
if (occluder === undefined)
|
|
129
|
+
return true;
|
|
130
|
+
return !occluder(from, toPos);
|
|
131
|
+
},
|
|
75
132
|
positionOf: (instanceId) => entities.get(instanceId)?.position,
|
|
76
133
|
};
|
|
134
|
+
const sceneRaycast = createSceneRaycast({
|
|
135
|
+
entities: {
|
|
136
|
+
list: () => entities.list().map((entity) => ({
|
|
137
|
+
id: entity.id,
|
|
138
|
+
position: entity.position,
|
|
139
|
+
rotationY: entity.rotationY,
|
|
140
|
+
name: entity.name,
|
|
141
|
+
})),
|
|
142
|
+
collidersOf: entityCollidersOf,
|
|
143
|
+
inRadius: (center, radius) => spatial.inRadius(center, radius),
|
|
144
|
+
get: (id) => {
|
|
145
|
+
const entity = entities.get(id);
|
|
146
|
+
if (entity === null)
|
|
147
|
+
return null;
|
|
148
|
+
return { id: entity.id, position: entity.position, rotationY: entity.rotationY, name: entity.name };
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
objects: {
|
|
152
|
+
list: () => objects.list(),
|
|
153
|
+
inBox: (min, max) => objects.inBox(min, max),
|
|
154
|
+
collidersOf: objectCollidersOf,
|
|
155
|
+
halfExtentsOf: (catalogId) => content.objectById?.(catalogId)?.halfExtents ?? null,
|
|
156
|
+
},
|
|
157
|
+
terrain: ground,
|
|
158
|
+
});
|
|
77
159
|
const weapon = createWeaponStats((itemId) => content.itemById?.(itemId));
|
|
78
160
|
const rawEvents = createGameEvents();
|
|
79
161
|
const events = {
|
|
@@ -84,7 +166,7 @@ export function createGameContext(options) {
|
|
|
84
166
|
signal.notify();
|
|
85
167
|
},
|
|
86
168
|
};
|
|
87
|
-
const feed = createGameFeed();
|
|
169
|
+
const feed = createGameFeed(definition.feed);
|
|
88
170
|
const lootRegistry = createLootRegistry();
|
|
89
171
|
const unlocks = notifyAfter(createUnlocks(), ["grant", "hydrate"], signal.notify);
|
|
90
172
|
const rawSocial = createSocial({
|
|
@@ -96,11 +178,22 @@ export function createGameContext(options) {
|
|
|
96
178
|
},
|
|
97
179
|
});
|
|
98
180
|
const social = {
|
|
99
|
-
friends: notifyAfter(rawSocial.friends, ["request", "accept", "remove", "block", "hydrate"], signal.notify),
|
|
100
|
-
party: notifyAfter(rawSocial.party, ["invite", "accept", "kick", "leave", "promote"], signal.notify),
|
|
181
|
+
friends: notifyAfter(rawSocial.friends, ["request", "accept", "decline", "remove", "block", "hydrate"], signal.notify),
|
|
182
|
+
party: notifyAfter(rawSocial.party, ["invite", "accept", "decline", "kick", "leave", "promote"], signal.notify),
|
|
101
183
|
presence: rawSocial.presence,
|
|
102
184
|
emotes: rawSocial.emotes,
|
|
185
|
+
worldInvites: notifyAfter(rawSocial.worldInvites, ["invite", "accept", "decline"], signal.notify),
|
|
103
186
|
};
|
|
187
|
+
const chat = notifyAfter(createChat({
|
|
188
|
+
events,
|
|
189
|
+
now,
|
|
190
|
+
party: rawSocial.party,
|
|
191
|
+
proximity: {
|
|
192
|
+
entities: { get: (id) => entities.get(id) },
|
|
193
|
+
spatial: { inRadius: (center, radius, filter) => spatial.inRadius(center, radius, filter) },
|
|
194
|
+
},
|
|
195
|
+
blockedBy: (userId) => rawSocial.friends.snapshot(userId).blocked,
|
|
196
|
+
}), ["register", "send", "whisper", "hydrate"], signal.notify);
|
|
104
197
|
const leaderboard = notifyAfter(createLeaderboard(), ["increment", "hydrate"], signal.notify);
|
|
105
198
|
const roster = notifyAfter(createRoster({ now }), ["capture", "release", "setEquipped", "hydrate"], signal.notify);
|
|
106
199
|
const playerStats = createStats({});
|
|
@@ -110,6 +203,7 @@ export function createGameContext(options) {
|
|
|
110
203
|
const possession = notifyAfter(createPossession({ entities, events }), ["possess", "own", "disown"], signal.notify);
|
|
111
204
|
const forms = notifyAfter(createForms({ entities, time, events }), ["shapeshift", "revert"], signal.notify);
|
|
112
205
|
const cosmetics = notifyAfter(createCosmetics({ events }), ["apply", "equip", "hydrate"], signal.notify);
|
|
206
|
+
const paintLayer = notifyAfter(createPaintLayer(), ["paint", "clear"], signal.notify);
|
|
113
207
|
const inventoryDeclarations = definition.inventories ?? {};
|
|
114
208
|
const inventoryIds = Object.keys(inventoryDeclarations);
|
|
115
209
|
const layouts = {};
|
|
@@ -259,13 +353,24 @@ export function createGameContext(options) {
|
|
|
259
353
|
statsByInstance.delete(instanceId);
|
|
260
354
|
targeting.clearAll(instanceId);
|
|
261
355
|
pose.clear(instanceId);
|
|
356
|
+
entityColliders.delete(instanceId);
|
|
262
357
|
return existed;
|
|
263
358
|
}
|
|
359
|
+
function resetAllToSpawn(filter) {
|
|
360
|
+
let count = 0;
|
|
361
|
+
for (const entity of entities.list()) {
|
|
362
|
+
if (filter !== undefined && !filter(entity))
|
|
363
|
+
continue;
|
|
364
|
+
if (entities.resetToSpawn(entity.id))
|
|
365
|
+
count += 1;
|
|
366
|
+
}
|
|
367
|
+
return count;
|
|
368
|
+
}
|
|
264
369
|
const worldItems = notifyAfter(createWorldItemStore({
|
|
265
370
|
spawnEntity: (position) => entities.spawn(WORLD_ITEM_ENTITY_NAME, { position, role: "prop" }),
|
|
266
371
|
despawnEntity,
|
|
267
372
|
resolvePosition: (instanceId) => entities.get(instanceId)?.position,
|
|
268
|
-
}), ["spawn", "take"], signal.notify);
|
|
373
|
+
}), ["spawn", "take", "remove"], signal.notify);
|
|
269
374
|
function spawnWorldItem(input) {
|
|
270
375
|
const record = worldItems.spawn(input);
|
|
271
376
|
events.emit("worldItem.dropped", {
|
|
@@ -389,13 +494,14 @@ export function createGameContext(options) {
|
|
|
389
494
|
if (input.dir !== undefined)
|
|
390
495
|
telegraphEvent.dir = input.dir;
|
|
391
496
|
events.emit("combat.telegraph", telegraphEvent);
|
|
497
|
+
const cancelVisual = () => events.emit("combat.telegraphCancelled", { id });
|
|
392
498
|
const bound = input.effect;
|
|
393
499
|
if (bound === undefined)
|
|
394
|
-
return
|
|
500
|
+
return cancelVisual;
|
|
395
501
|
const config = { shape: input.shape, at: input.at, windupMs: input.windupMs };
|
|
396
502
|
if (input.dir !== undefined)
|
|
397
503
|
config.dir = input.dir;
|
|
398
|
-
|
|
504
|
+
const cancelEffect = time.after(input.windupMs / 1000, () => {
|
|
399
505
|
const targets = entities.list().filter((entity) => pointInTelegraph(config, entity.position));
|
|
400
506
|
for (const target of targets) {
|
|
401
507
|
applyEffectAndFloat({
|
|
@@ -406,6 +512,10 @@ export function createGameContext(options) {
|
|
|
406
512
|
});
|
|
407
513
|
}
|
|
408
514
|
});
|
|
515
|
+
return () => {
|
|
516
|
+
cancelEffect();
|
|
517
|
+
cancelVisual();
|
|
518
|
+
};
|
|
409
519
|
}
|
|
410
520
|
function applyHitReaction(input) {
|
|
411
521
|
const attacker = entities.get(input.from);
|
|
@@ -466,10 +576,51 @@ export function createGameContext(options) {
|
|
|
466
576
|
preview: effects.preview,
|
|
467
577
|
applyEffect: applyEffectAndFloat,
|
|
468
578
|
};
|
|
579
|
+
const projectileObstacles = definition.physics?.projectileObstacles === true;
|
|
580
|
+
const projectileSceneRaycast = {
|
|
581
|
+
raycast(input) {
|
|
582
|
+
return sceneRaycast.raycast({
|
|
583
|
+
...input,
|
|
584
|
+
filter: {
|
|
585
|
+
entities: true,
|
|
586
|
+
objects: projectileObstacles,
|
|
587
|
+
terrain: false,
|
|
588
|
+
walls: projectileObstacles,
|
|
589
|
+
...input.filter,
|
|
590
|
+
},
|
|
591
|
+
});
|
|
592
|
+
},
|
|
593
|
+
raycastAll(input) {
|
|
594
|
+
return sceneRaycast.raycastAll({
|
|
595
|
+
...input,
|
|
596
|
+
filter: {
|
|
597
|
+
entities: true,
|
|
598
|
+
objects: projectileObstacles,
|
|
599
|
+
terrain: false,
|
|
600
|
+
walls: projectileObstacles,
|
|
601
|
+
...input.filter,
|
|
602
|
+
},
|
|
603
|
+
});
|
|
604
|
+
},
|
|
605
|
+
};
|
|
469
606
|
const projectiles = notifyAfter(createProjectileSystem({
|
|
470
607
|
effects: floatingEffects,
|
|
471
608
|
spatial: combatSpatial,
|
|
472
609
|
getStat: weapon.getStat,
|
|
610
|
+
sceneRaycast: projectileSceneRaycast,
|
|
611
|
+
objects: projectileObstacles
|
|
612
|
+
? {
|
|
613
|
+
list: () => objects.list(),
|
|
614
|
+
inBox: (min, max) => objects.inBox(min, max),
|
|
615
|
+
halfExtents: (catalogId) => {
|
|
616
|
+
const half = content.objectById?.(catalogId)?.halfExtents;
|
|
617
|
+
return half === undefined ? null : [half[0], half[1], half[2]];
|
|
618
|
+
},
|
|
619
|
+
collidersOf: objectCollidersOf,
|
|
620
|
+
}
|
|
621
|
+
: undefined,
|
|
622
|
+
entityCollidersOf,
|
|
623
|
+
rotationYOf: (instanceId) => entities.get(instanceId)?.rotationY,
|
|
473
624
|
now,
|
|
474
625
|
onSettle(report) {
|
|
475
626
|
events.emit("projectile.settled", {
|
|
@@ -484,16 +635,111 @@ export function createGameContext(options) {
|
|
|
484
635
|
const sceneObjects = {
|
|
485
636
|
...objects,
|
|
486
637
|
catalog: (instanceId) => catalogObject(instanceId) ?? null,
|
|
638
|
+
raycast: (input) => raycastObjects(objects, input),
|
|
639
|
+
raycastAll: (input) => raycastObjectsAll(objects, input),
|
|
640
|
+
setColliders(instanceId, colliders) {
|
|
641
|
+
if (colliders === null)
|
|
642
|
+
objectColliders.delete(instanceId);
|
|
643
|
+
else
|
|
644
|
+
objectColliders.set(instanceId, colliders);
|
|
645
|
+
signal.notify();
|
|
646
|
+
},
|
|
647
|
+
collidersOf: objectCollidersOf,
|
|
487
648
|
};
|
|
649
|
+
const store = notifyAfter(createObservableKeyedStore(), ["set", "delete"], signal.notify);
|
|
650
|
+
const cardPiles = new Map();
|
|
651
|
+
function pile(id, config) {
|
|
652
|
+
const existing = cardPiles.get(id);
|
|
653
|
+
if (existing !== undefined)
|
|
654
|
+
return existing;
|
|
655
|
+
if (config === undefined) {
|
|
656
|
+
throw new Error(`cardPile "${id}" has not been created yet; pass a config on first access`);
|
|
657
|
+
}
|
|
658
|
+
const created = notifyAfter(createCardPile(config), ["shuffle", "draw", "discard", "exhaust", "move", "reset"], signal.notify);
|
|
659
|
+
cardPiles.set(id, created);
|
|
660
|
+
return created;
|
|
661
|
+
}
|
|
662
|
+
const turnLoops = new Map();
|
|
663
|
+
function loop(id, config) {
|
|
664
|
+
const existing = turnLoops.get(id);
|
|
665
|
+
if (existing !== undefined)
|
|
666
|
+
return existing;
|
|
667
|
+
if (config === undefined) {
|
|
668
|
+
throw new Error(`turn loop "${id}" has not been created yet; pass a config on first access`);
|
|
669
|
+
}
|
|
670
|
+
const raw = createTurnLoop(config);
|
|
671
|
+
const wrappedCommit = notifyAfter(raw.commit, ["submit", "expected", "commit", "discard", "clear"], signal.notify);
|
|
672
|
+
const wrapped = {
|
|
673
|
+
...notifyAfter(raw, [
|
|
674
|
+
"setOrder",
|
|
675
|
+
"addParticipant",
|
|
676
|
+
"removeParticipant",
|
|
677
|
+
"advancePhase",
|
|
678
|
+
"advanceTurn",
|
|
679
|
+
"advanceRound",
|
|
680
|
+
"spend",
|
|
681
|
+
"gain",
|
|
682
|
+
"refill",
|
|
683
|
+
"restore",
|
|
684
|
+
], signal.notify),
|
|
685
|
+
commit: wrappedCommit,
|
|
686
|
+
};
|
|
687
|
+
turnLoops.set(id, wrapped);
|
|
688
|
+
return wrapped;
|
|
689
|
+
}
|
|
690
|
+
class NotifyingRaceState extends RaceState {
|
|
691
|
+
addRacer(racerId, startTime) {
|
|
692
|
+
super.addRacer(racerId, startTime);
|
|
693
|
+
signal.notify();
|
|
694
|
+
}
|
|
695
|
+
removeRacer(racerId) {
|
|
696
|
+
super.removeRacer(racerId);
|
|
697
|
+
signal.notify();
|
|
698
|
+
}
|
|
699
|
+
reset() {
|
|
700
|
+
super.reset();
|
|
701
|
+
signal.notify();
|
|
702
|
+
}
|
|
703
|
+
eliminate(racerId) {
|
|
704
|
+
super.eliminate(racerId);
|
|
705
|
+
signal.notify();
|
|
706
|
+
}
|
|
707
|
+
update(now, positions) {
|
|
708
|
+
const raceEvents = super.update(now, positions);
|
|
709
|
+
if (raceEvents.length > 0)
|
|
710
|
+
signal.notify();
|
|
711
|
+
return raceEvents;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
const raceStates = new Map();
|
|
715
|
+
function raceState(id, config) {
|
|
716
|
+
const existing = raceStates.get(id);
|
|
717
|
+
if (existing !== undefined)
|
|
718
|
+
return existing;
|
|
719
|
+
if (config === undefined) {
|
|
720
|
+
throw new Error(`race "${id}" has not been created yet; pass a config on first access`);
|
|
721
|
+
}
|
|
722
|
+
const created = new NotifyingRaceState(config);
|
|
723
|
+
raceStates.set(id, created);
|
|
724
|
+
return created;
|
|
725
|
+
}
|
|
726
|
+
const camera = notifyAfter(createCameraDirector(), ["follow", "setCinematic", "setChaseTuning"], signal.notify);
|
|
727
|
+
const input = createInputSnapshot();
|
|
728
|
+
const motion = createMotionIntents();
|
|
488
729
|
const ctx = {
|
|
489
730
|
scene: {
|
|
490
731
|
object: sceneObjects,
|
|
491
732
|
entity: {
|
|
492
733
|
spawn: spawnEntity,
|
|
493
734
|
despawn: despawnEntity,
|
|
735
|
+
update: entities.update,
|
|
494
736
|
setPose: entities.setPose,
|
|
737
|
+
setPoseConstraint: entities.setPoseConstraint,
|
|
495
738
|
get: entities.get,
|
|
496
739
|
list: entities.list,
|
|
740
|
+
spawnPoseOf: entities.spawnPoseOf,
|
|
741
|
+
resetToSpawn: entities.resetToSpawn,
|
|
742
|
+
resetAllToSpawn,
|
|
497
743
|
stats: entityStats,
|
|
498
744
|
floatText: emitFloatText,
|
|
499
745
|
telegraph: fireTelegraph,
|
|
@@ -512,7 +758,17 @@ export function createGameContext(options) {
|
|
|
512
758
|
hasLineOfSight: spatial.hasLineOfSight,
|
|
513
759
|
queryArc: spatial.queryArc,
|
|
514
760
|
moveToward: spatial.moveToward,
|
|
761
|
+
invalidateSpatial: spatial.invalidate,
|
|
762
|
+
setColliders(instanceId, colliders) {
|
|
763
|
+
if (colliders === null)
|
|
764
|
+
entityColliders.delete(instanceId);
|
|
765
|
+
else
|
|
766
|
+
entityColliders.set(instanceId, colliders);
|
|
767
|
+
signal.notify();
|
|
768
|
+
},
|
|
769
|
+
collidersOf: entityCollidersOf,
|
|
515
770
|
form: forms,
|
|
771
|
+
paint: paintLayer,
|
|
516
772
|
},
|
|
517
773
|
worldItem: {
|
|
518
774
|
spawn: spawnWorldItem,
|
|
@@ -520,7 +776,14 @@ export function createGameContext(options) {
|
|
|
520
776
|
list: worldItems.list,
|
|
521
777
|
nearestInRadius: worldItems.nearestInRadius,
|
|
522
778
|
pickup: pickupWorldItem,
|
|
779
|
+
consume: worldItems.remove,
|
|
523
780
|
},
|
|
781
|
+
raycast: (input) => sceneRaycast.raycast(input),
|
|
782
|
+
raycastAll: (input) => sceneRaycast.raycastAll(input),
|
|
783
|
+
},
|
|
784
|
+
world: {
|
|
785
|
+
ground,
|
|
786
|
+
groundHeightAt: ground.sampleHeight,
|
|
524
787
|
},
|
|
525
788
|
game: {
|
|
526
789
|
commands: {
|
|
@@ -552,10 +815,15 @@ export function createGameContext(options) {
|
|
|
552
815
|
trade,
|
|
553
816
|
quest,
|
|
554
817
|
social,
|
|
818
|
+
chat,
|
|
555
819
|
unlocks,
|
|
556
820
|
economy,
|
|
557
821
|
leaderboard,
|
|
558
822
|
roster,
|
|
823
|
+
store,
|
|
824
|
+
cards: { pile },
|
|
825
|
+
turn: { loop },
|
|
826
|
+
race: { state: raceState },
|
|
559
827
|
},
|
|
560
828
|
player: {
|
|
561
829
|
userId: player.userId,
|
|
@@ -567,6 +835,7 @@ export function createGameContext(options) {
|
|
|
567
835
|
movement: pose,
|
|
568
836
|
possession,
|
|
569
837
|
cosmetics,
|
|
838
|
+
motion,
|
|
570
839
|
},
|
|
571
840
|
item: {
|
|
572
841
|
use: {
|
|
@@ -582,6 +851,8 @@ export function createGameContext(options) {
|
|
|
582
851
|
weapon,
|
|
583
852
|
},
|
|
584
853
|
time,
|
|
854
|
+
camera,
|
|
855
|
+
input,
|
|
585
856
|
subscribe: signal.subscribe,
|
|
586
857
|
version: signal.version,
|
|
587
858
|
};
|
|
@@ -5,7 +5,7 @@ import { type GameRuntimeSnapshot, type RuntimeChunkRow, type RuntimePlayerRow,
|
|
|
5
5
|
export type ServerLoopHooks = {
|
|
6
6
|
onInit?: (ctx: RuntimeInitContext) => void;
|
|
7
7
|
onNewPlayer?: (ctx: RuntimeLoopContext) => void;
|
|
8
|
-
onTick?: (ctx:
|
|
8
|
+
onTick?: (ctx: RuntimeWorldContext, dtSeconds: number) => void;
|
|
9
9
|
};
|
|
10
10
|
export type RuntimeInitContext = {
|
|
11
11
|
snapshot: GameRuntimeSnapshot;
|
|
@@ -17,6 +17,9 @@ export type RuntimeLoopContext = RuntimeInitContext & {
|
|
|
17
17
|
isNew: boolean;
|
|
18
18
|
};
|
|
19
19
|
};
|
|
20
|
+
export type RuntimeWorldContext = RuntimeInitContext & {
|
|
21
|
+
playerIds: readonly string[];
|
|
22
|
+
};
|
|
20
23
|
export type GameRuntimeDefinition = {
|
|
21
24
|
gameId: string;
|
|
22
25
|
save: SaveConfig;
|
|
@@ -29,6 +32,7 @@ export type HydrateInput = {
|
|
|
29
32
|
serverRow: RuntimeServerRow;
|
|
30
33
|
playersByUserId: Record<string, RuntimePlayerRow>;
|
|
31
34
|
chunksByKey: Record<string, RuntimeChunkRow>;
|
|
35
|
+
revision?: number;
|
|
32
36
|
};
|
|
33
37
|
export type GameRuntime = {
|
|
34
38
|
gameId: string;
|