@jgengine/shell 0.18.1 → 0.21.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.
Files changed (261) hide show
  1. package/CHANGELOG.md +504 -7
  2. package/dist/GameHost.d.ts +12 -1
  3. package/dist/GameHost.js +14 -1
  4. package/dist/GamePlayerShell.js +132 -17
  5. package/dist/Shell3dPresentation.d.ts +5 -11
  6. package/dist/Shell3dPresentation.js +73 -11
  7. package/dist/ShellChrome.d.ts +2 -1
  8. package/dist/ShellChrome.js +22 -6
  9. package/dist/ShellHudPresentation.d.ts +20 -2
  10. package/dist/ShellHudPresentation.js +41 -6
  11. package/dist/audio/AudioComponents.js +6 -2
  12. package/dist/audio/audioEngine.d.ts +24 -0
  13. package/dist/audio/audioEngine.js +231 -83
  14. package/dist/audio/audioWire.d.ts +3 -0
  15. package/dist/audio/audioWire.js +9 -3
  16. package/dist/audio/loopParams.d.ts +39 -0
  17. package/dist/audio/loopParams.js +43 -0
  18. package/dist/audio/synthEngine.d.ts +7 -1
  19. package/dist/audio/synthEngine.js +103 -38
  20. package/dist/camera/GameCameraRig.js +3 -2
  21. package/dist/camera/GameFirstPersonCamera.d.ts +6 -4
  22. package/dist/camera/GameFirstPersonCamera.js +68 -27
  23. package/dist/camera/Viewports.d.ts +34 -0
  24. package/dist/camera/Viewports.js +123 -0
  25. package/dist/camera/cameraRigs.js +76 -62
  26. package/dist/camera/chaseHeading.d.ts +9 -0
  27. package/dist/camera/chaseHeading.js +10 -0
  28. package/dist/camera/customMuzzle.d.ts +14 -0
  29. package/dist/camera/customMuzzle.js +68 -0
  30. package/dist/camera/index.d.ts +1 -0
  31. package/dist/camera/index.js +1 -0
  32. package/dist/camera/orbitCameraMath.js +2 -2
  33. package/dist/camera/rigMath.d.ts +82 -4
  34. package/dist/camera/rigMath.js +133 -15
  35. package/dist/camera/rigResolve.d.ts +3 -0
  36. package/dist/camera/rigResolve.js +11 -0
  37. package/dist/combat/ProjectileModels.d.ts +31 -0
  38. package/dist/combat/ProjectileModels.js +70 -0
  39. package/dist/commandSink.d.ts +1 -2
  40. package/dist/commandSink.js +3 -2
  41. package/dist/defineGame.d.ts +19 -3
  42. package/dist/defineGame.js +39 -7
  43. package/dist/devtools/DevtoolsOverlay.d.ts +2 -69
  44. package/dist/devtools/DevtoolsOverlay.js +15 -167
  45. package/dist/devtools/DevtoolsRuntime.d.ts +7 -0
  46. package/dist/devtools/DevtoolsRuntime.js +61 -0
  47. package/dist/devtools/agentBridge.d.ts +6 -0
  48. package/dist/devtools/agentBridge.js +16 -2
  49. package/dist/devtools/devtoolsReports.d.ts +68 -0
  50. package/dist/devtools/devtoolsReports.js +100 -0
  51. package/dist/devtools/latencyInstrumentation.d.ts +3 -0
  52. package/dist/devtools/latencyInstrumentation.js +11 -0
  53. package/dist/devtools/playtestProbe.d.ts +4 -0
  54. package/dist/devtools/playtestProbe.js +22 -0
  55. package/dist/diagnostics/FpsCounter.d.ts +17 -0
  56. package/dist/diagnostics/FpsCounter.js +32 -0
  57. package/dist/drivers/FrameDriver.js +35 -20
  58. package/dist/drivers/FrameRateLimiter.d.ts +9 -0
  59. package/dist/drivers/FrameRateLimiter.js +39 -0
  60. package/dist/drivers/HudOnlyDriver.d.ts +6 -1
  61. package/dist/drivers/HudOnlyDriver.js +32 -14
  62. package/dist/environment/DayNightSky.js +5 -5
  63. package/dist/environment/Daylight.d.ts +35 -1
  64. package/dist/environment/Daylight.js +98 -28
  65. package/dist/environment/EnvironmentScene.js +11 -10
  66. package/dist/environment/VolumetricClouds.js +20 -2
  67. package/dist/environment/daylightCycle.d.ts +20 -0
  68. package/dist/environment/daylightCycle.js +59 -7
  69. package/dist/environment/sunShadowMath.d.ts +45 -0
  70. package/dist/environment/sunShadowMath.js +50 -0
  71. package/dist/input/gamepadPoll.d.ts +47 -0
  72. package/dist/input/gamepadPoll.js +142 -0
  73. package/dist/input/gamepadSource.d.ts +11 -1
  74. package/dist/input/gamepadSource.js +117 -71
  75. package/dist/input/mouseLook.js +2 -1
  76. package/dist/input/padHaptics.d.ts +17 -0
  77. package/dist/input/padHaptics.js +32 -0
  78. package/dist/input/pointerLock.d.ts +11 -0
  79. package/dist/input/pointerLock.js +19 -0
  80. package/dist/inputSink.d.ts +6 -6
  81. package/dist/inputSink.js +60 -21
  82. package/dist/lazyShell3dPresentation.d.ts +9 -0
  83. package/dist/lazyShell3dPresentation.js +24 -0
  84. package/dist/materialOverride.d.ts +42 -0
  85. package/dist/materialOverride.js +95 -36
  86. package/dist/multiplayer.d.ts +33 -3
  87. package/dist/multiplayer.js +105 -22
  88. package/dist/pointer/pointerService.d.ts +2 -0
  89. package/dist/pointer/pointerService.js +12 -2
  90. package/dist/postfx/PostProcessing.d.ts +11 -1
  91. package/dist/postfx/PostProcessing.js +14 -42
  92. package/dist/postfx/gradeShader.d.ts +1 -1
  93. package/dist/postfx/gradeShader.js +30 -2
  94. package/dist/postfx/shadowPrepass.d.ts +3 -0
  95. package/dist/postfx/shadowPrepass.js +18 -0
  96. package/dist/postfx/stylizePass.d.ts +23 -0
  97. package/dist/postfx/stylizePass.js +216 -0
  98. package/dist/presentationRecovery.d.ts +75 -0
  99. package/dist/presentationRecovery.js +204 -0
  100. package/dist/presentationRecovery3d.d.ts +4 -0
  101. package/dist/presentationRecovery3d.js +32 -0
  102. package/dist/presentationResolve.d.ts +8 -1
  103. package/dist/presentationResolve.js +5 -2
  104. package/dist/render/CascadedShadows.d.ts +7 -2
  105. package/dist/render/CascadedShadows.js +98 -20
  106. package/dist/render/EnvironmentLighting.js +74 -35
  107. package/dist/render/HairCards.d.ts +7 -0
  108. package/dist/render/HairCards.js +23 -0
  109. package/dist/render/SceneModels.js +52 -78
  110. package/dist/render/SkinnedInstances.d.ts +91 -0
  111. package/dist/render/SkinnedInstances.js +249 -0
  112. package/dist/render/TextureFiltering.d.ts +16 -0
  113. package/dist/render/TextureFiltering.js +64 -0
  114. package/dist/render/authoredSurfaceMaterial.d.ts +43 -0
  115. package/dist/render/authoredSurfaceMaterial.js +108 -0
  116. package/dist/render/displayColor.d.ts +33 -0
  117. package/dist/render/displayColor.js +138 -0
  118. package/dist/render/materialAppearanceSignals.d.ts +23 -0
  119. package/dist/render/materialAppearanceSignals.js +58 -0
  120. package/dist/render/materialAsset.d.ts +98 -0
  121. package/dist/render/materialAsset.js +454 -0
  122. package/dist/render/measureBounds.js +16 -3
  123. package/dist/render/measureCollisionMesh.js +8 -1
  124. package/dist/render/modelAssets.d.ts +8 -0
  125. package/dist/render/modelAssets.js +43 -0
  126. package/dist/render/modelBindPose.d.ts +5 -0
  127. package/dist/render/modelBindPose.js +35 -0
  128. package/dist/render/modelLoad.d.ts +32 -0
  129. package/dist/render/modelLoad.js +70 -1
  130. package/dist/render/modelNodeVisibility.d.ts +6 -0
  131. package/dist/render/modelNodeVisibility.js +18 -0
  132. package/dist/render/modelRender.d.ts +18 -1
  133. package/dist/render/modelRender.js +81 -3
  134. package/dist/render/projectedOverdraw.d.ts +35 -0
  135. package/dist/render/projectedOverdraw.js +204 -0
  136. package/dist/render/resolveModel.d.ts +12 -2
  137. package/dist/render/resolveModel.js +25 -4
  138. package/dist/render/rigNode.d.ts +26 -0
  139. package/dist/render/rigNode.js +32 -0
  140. package/dist/render/spriteRender.d.ts +4 -0
  141. package/dist/render/spriteRender.js +5 -0
  142. package/dist/render/staticShapeInstances.d.ts +34 -0
  143. package/dist/render/staticShapeInstances.js +78 -0
  144. package/dist/render/useFootIk.d.ts +53 -4
  145. package/dist/render/useFootIk.js +262 -70
  146. package/dist/render/useModelAnimation.d.ts +50 -0
  147. package/dist/render/useModelAnimation.js +245 -99
  148. package/dist/render/useModelInstance.d.ts +25 -0
  149. package/dist/render/useModelInstance.js +43 -0
  150. package/dist/scatter/scatterModels.d.ts +3 -2
  151. package/dist/scatter/scatterModels.js +15 -7
  152. package/dist/scene/AuthoredScene.d.ts +23 -6
  153. package/dist/scene/AuthoredScene.js +46 -9
  154. package/dist/scene/CityRenderer.js +6 -6
  155. package/dist/scene/GeneratedAssetRenderer.js +11 -42
  156. package/dist/settings/QuickControls.js +1 -1
  157. package/dist/settings/SettingsMenu.js +11 -18
  158. package/dist/settings/SettingsRuntime.d.ts +5 -0
  159. package/dist/settings/SettingsRuntime.js +29 -2
  160. package/dist/settings/appliedSettings.d.ts +9 -3
  161. package/dist/settings/appliedSettings.js +6 -5
  162. package/dist/settings/settingsController.d.ts +3 -0
  163. package/dist/settings/settingsController.js +47 -3
  164. package/dist/shellControlSuspension.d.ts +18 -0
  165. package/dist/shellControlSuspension.js +28 -0
  166. package/dist/shellInputPublication.d.ts +28 -0
  167. package/dist/shellInputPublication.js +49 -0
  168. package/dist/shellPointerInput.d.ts +18 -0
  169. package/dist/shellPointerInput.js +49 -0
  170. package/dist/structures/BuildingKitBatch.d.ts +3 -1
  171. package/dist/structures/BuildingKitBatch.js +35 -56
  172. package/dist/structures/BuildingRenderBudget.d.ts +23 -0
  173. package/dist/structures/BuildingRenderBudget.js +54 -0
  174. package/dist/structures/GeneratedBuilding.d.ts +3 -15
  175. package/dist/structures/GeneratedBuilding.js +33 -22
  176. package/dist/structures/buildingKitMaterials.d.ts +20 -0
  177. package/dist/structures/buildingKitMaterials.js +70 -0
  178. package/dist/structures/buildingSpatialBatch.d.ts +11 -0
  179. package/dist/structures/buildingSpatialBatch.js +45 -0
  180. package/dist/structures/buildingSurface.d.ts +21 -0
  181. package/dist/structures/buildingSurface.js +134 -0
  182. package/dist/terrain/GrassField.d.ts +2 -1
  183. package/dist/terrain/GrassField.js +59 -5
  184. package/dist/terrain/grassGeometry.d.ts +10 -0
  185. package/dist/terrain/grassGeometry.js +55 -0
  186. package/dist/terrain/grassMaterial.js +2 -2
  187. package/dist/terrain/terrainDetailMaterial.d.ts +3 -3
  188. package/dist/terrain/terrainDetailMaterial.js +7 -7
  189. package/dist/touch/TouchControlsOverlay.js +4 -4
  190. package/dist/useShellMultiplayerSync.d.ts +1 -1
  191. package/dist/useShellMultiplayerSync.js +9 -3
  192. package/dist/vfx/ParticleField.d.ts +23 -20
  193. package/dist/vfx/ParticleField.js +168 -50
  194. package/dist/visibility/CullingProvider.js +2 -62
  195. package/dist/visibility/cullingDriver.d.ts +9 -0
  196. package/dist/visibility/cullingDriver.js +68 -0
  197. package/dist/weather/AuthoredWeatherLayer.d.ts +16 -0
  198. package/dist/weather/AuthoredWeatherLayer.js +31 -0
  199. package/dist/weather/DustField.d.ts +2 -1
  200. package/dist/weather/DustField.js +17 -3
  201. package/dist/weather/FireSpreadLayer.d.ts +4 -2
  202. package/dist/weather/FireSpreadLayer.js +5 -11
  203. package/dist/weather/LightningStrike.d.ts +4 -1
  204. package/dist/weather/LightningStrike.js +13 -17
  205. package/dist/weather/RainField.d.ts +2 -1
  206. package/dist/weather/RainField.js +17 -3
  207. package/dist/weather/RainImpactField.d.ts +16 -0
  208. package/dist/weather/RainImpactField.js +89 -0
  209. package/dist/weather/SnowField.d.ts +2 -1
  210. package/dist/weather/SnowField.js +17 -3
  211. package/dist/weather/WeatherLayer.d.ts +11 -2
  212. package/dist/weather/WeatherLayer.js +30 -6
  213. package/dist/weather/index.d.ts +2 -0
  214. package/dist/weather/index.js +2 -0
  215. package/dist/weather/weatherMath.d.ts +2 -0
  216. package/dist/weather/weatherMath.js +8 -0
  217. package/dist/weather/weatherShelter.d.ts +18 -0
  218. package/dist/weather/weatherShelter.js +75 -0
  219. package/dist/weather/weatherUniforms.d.ts +40 -1
  220. package/dist/weather/weatherUniforms.js +29 -9
  221. package/dist/world/AuthoredFlocks.d.ts +22 -0
  222. package/dist/world/AuthoredFlocks.js +87 -0
  223. package/dist/world/StaticObjectModels.d.ts +8 -0
  224. package/dist/world/StaticObjectModels.js +75 -0
  225. package/dist/world/WorldHud.d.ts +14 -6
  226. package/dist/world/WorldHud.js +25 -12
  227. package/dist/world/WorldParticles.d.ts +10 -23
  228. package/dist/world/WorldParticles.js +210 -62
  229. package/dist/world/WorldScene.d.ts +3 -1
  230. package/dist/world/WorldScene.js +41 -26
  231. package/dist/world/staticObjectBatches.d.ts +35 -0
  232. package/dist/world/staticObjectBatches.js +163 -0
  233. package/dist/world/worldBarSamples.d.ts +37 -33
  234. package/dist/world/worldBarSamples.js +117 -75
  235. package/package.json +4 -4
  236. package/skills/ce-handoff/SKILL.md +2 -2
  237. package/skills/game-design/SKILL.md +2 -2
  238. package/skills/jgengine/SKILL.md +28 -6
  239. package/skills/jgengine/api.md +139 -99
  240. package/skills/jgengine/capabilities.md +28 -0
  241. package/skills/jgengine/recipes/minimal-game.md +10 -2
  242. package/skills/jgengine-ui/SKILL.md +16 -17
  243. package/skills/jgengine-ui/api.md +603 -365
  244. package/skills/jgengine-ui/capabilities.md +131 -1
  245. package/skills/jgengine-ui/reference-primitives.md +179 -0
  246. package/skills/jgengine-ui/reference.md +4 -81
  247. package/skills/jgengine-world/SKILL.md +25 -4
  248. package/skills/jgengine-world/api.md +1045 -655
  249. package/skills/jgengine-world/capabilities.md +246 -9
  250. package/skills/jgengine-world/recipes/character-animation.md +62 -0
  251. package/skills/jgengine-world/recipes/controls.md +48 -0
  252. package/skills/jgengine-world/recipes/flight-feel.md +76 -0
  253. package/skills/jgengine-world/recipes/npc-senses.md +55 -0
  254. package/skills/jgengine-world/recipes/vehicle-feel.md +87 -0
  255. package/skills/jgengine-world/reference.md +180 -4
  256. package/skills/jgengine-world/visibility.md +139 -0
  257. package/skills/level-design/SKILL.md +1 -1
  258. package/dist/render/testFixtures/proceduralSpriteAtlas.d.ts +0 -15
  259. package/dist/render/testFixtures/proceduralSpriteAtlas.js +0 -30
  260. package/dist/render/testFixtures/proceduralTileLayer.d.ts +0 -13
  261. package/dist/render/testFixtures/proceduralTileLayer.js +0 -30
@@ -4,11 +4,11 @@
4
4
 
5
5
  ## @jgengine/core/ai/crowd
6
6
 
7
- - `CrowdField` (interface): interface CrowdField — ⚠ undocumented
8
- - `FlowField` (interface): interface FlowField — ⚠ undocumented
9
- - `FlowFieldOptions` (interface): interface FlowFieldOptions — ⚠ undocumented
10
- - `Poi` (interface): interface Poi — ⚠ undocumented
11
- - `SelectPoiOptions` (interface): interface SelectPoiOptions — ⚠ undocumented
7
+ - `CrowdField` (interface): interface CrowdField { enter(point: NavPoint): void; leave(point: NavPoint): void; count(point: NavPoint): number; cellCount(col: number, row: number): number; penalty(weight?: number): (col: number, row: number) => number; reset(): void } — ⚠ undocumented
8
+ - `FlowField` (interface): interface FlowField { readonly cols: number; readonly rows: number; cost(col: number, row: number): number; reachable(point: NavPoint): boolean; direction(point: NavPoint): NavPoint; next(point: NavPoint): NavPoint | null } — ⚠ undocumented
9
+ - `FlowFieldOptions` (interface): interface FlowFieldOptions { clearance?: number; congestion?: (col: number, row: number) => number } — ⚠ undocumented
10
+ - `Poi` (interface): interface Poi { id: string; point: NavPoint; appeal?: number; capacity?: number } — ⚠ undocumented
11
+ - `SelectPoiOptions` (interface): interface SelectPoiOptions { roll: number; occupancy?: (id: string) => number; distanceBias?: number; distance?: (from: NavPoint, poi: Poi) => number } — ⚠ undocumented
12
12
  - `VisitorLoop` (interface): interface VisitorLoop — Handle returned by {@link createVisitorLoop}: per-agent seek/travel/dwell/depart state machine.
13
13
  - `VisitorLoopOptions` (interface): interface VisitorLoopOptions — Config for {@link createVisitorLoop}: the POI catalog, dwell duration, and exit point every agent shares.
14
14
  - `VisitorPhase` (type): type VisitorPhase = "seeking" | "traveling" | "dwelling" | "departing" | "done" — A many-agent visitor's current step of the seek→travel→arrive→dwell→depart loop.
@@ -18,14 +18,17 @@
18
18
 
19
19
  - `Blackboard` (type): type Blackboard = Record<string, BlackboardValue> — Named facts available to conditions, utilities, and actions.
20
20
  - `BlackboardValue` (type): type BlackboardValue = number | boolean | string — Scalar values stored in a decision graph blackboard.
21
+ - `DecisionAbort` (type): type DecisionAbort<Context = unknown> = ( action: string, ctx: Context, params: Record<string, BlackboardValue> | undefined, blackboard: Blackboard, ) => void — Called when an action that was running last tick is not reached this tick, so it can release what it holds.
21
22
  - `DecisionAction` (type): type DecisionAction<Context = unknown> = ( ctx: Context, params: Record<string, BlackboardValue> | undefined, blackboard: Blackboard, ) => DecisionStatus — Callback implementation for an action node.
22
23
  - `DecisionGraph` (type): type DecisionGraph = DecisionNode — Root node for a serializable AI decision graph.
24
+ - `DecisionGraphOptions` (interface): interface DecisionGraphOptions<Context = unknown> — Optional hooks and injected randomness for {@link createDecisionGraphRuntime}.
23
25
  - `DecisionGraphRuntime` (interface): interface DecisionGraphRuntime<Context = unknown> — Stateful evaluator for a serializable decision graph.
24
- - `DecisionGraphSnapshot` (interface): interface DecisionGraphSnapshot — Serializable state retained by an active decision graph.
25
- - `DecisionNode` (type): type DecisionNode = | { kind: "selector"; children: DecisionNode[] } | { kind: "sequence"; children: DecisionNode[] } | { kind: "condition"; key: string; op: DecisionOperator; value: BlackboardValue } | { kind: "action"; action: string; params?: Record<string, BlackboardValue> } | { kind: "utility";… — Serializable selector, sequence, condition, action, or utility node.
26
+ - `DecisionGraphSnapshot` (interface): interface DecisionGraphSnapshot — Serializable state retained by an active decision graph. Fields besides `runningPath` are absent in older saves.
27
+ - `DecisionNode` (type): type DecisionNode = | { kind: "selector"; children: DecisionNode[]; memory?: boolean } | { kind: "sequence"; children: DecisionNode[]; memory?: boolean } | { kind: "condition"; key: string; op: DecisionOperator; value: BlackboardValue } | { kind: "action"; action: string; params?: Record<string, Bla… — Serializable decision node. - `selector` runs children in order until one does not fail; `sequence` until one does not finish. With `memory`, a composite whose child was running last tick resumes at that child instead of re-checking earlier ones. - `condition` compares a blackboard key; `action` calls a registered action. - `utility` runs the option with the highest weighted blackboard score. - `invert` swaps `done` and `failed`; `cooldown` fails for `seconds` after its child finishes; `wait` stays running for `seconds`; `random` picks a child by `weights` from the runtime's `rng` and keeps it while it runs.
28
+ - `DecisionNodeMemory` (interface): interface DecisionNodeMemory — Progress of a memory composite, `random` choice, or `wait` timer, keyed by node id (depth-first order).
26
29
  - `DecisionOperator` (type): type DecisionOperator = "=" | "==" | "!=" | "<" | "<=" | ">" | ">=" | "eq" | "ne" | "lt" | "lte" | "gt" | "gte" — Comparison operators supported by decision conditions.
27
30
  - `DecisionStatus` (type): type DecisionStatus = "running" | "done" | "failed" — Outcome reported when a decision graph evaluates.
28
- - `createDecisionGraphRuntime` (function): function createDecisionGraphRuntime<Context = unknown>(graph: DecisionGraph, actions: Record<string, DecisionAction<Context>>): DecisionGraphRuntime<Context> — Creates a deterministic runtime for a serializable decision graph.
31
+ - `createDecisionGraphRuntime` (function): function createDecisionGraphRuntime<Context = unknown>(graph: DecisionGraph, actions: Record<string, DecisionAction<Context>>, options: DecisionGraphOptions<Context> = {}): DecisionGraphRuntime<Context> — Creates a deterministic runtime for a serializable decision graph.
29
32
 
30
33
  ## @jgengine/core/ai/difficulty
31
34
 
@@ -57,16 +60,17 @@
57
60
 
58
61
  ## @jgengine/core/ai/flock
59
62
 
60
- - `FlockAgent` (interface): interface FlockAgent — ⚠ undocumented
61
- - `FlockConfig` (interface): interface FlockConfig — ⚠ undocumented
62
- - `FlockStepAgent` (interface): interface FlockStepAgent — ⚠ undocumented
63
+ - `FlockAgent` (interface): interface FlockAgent { position: FlockVec3; velocity: FlockVec3 } — ⚠ undocumented
64
+ - `FlockConfig` (interface): interface FlockConfig { maxSpeed: number; accel?: number; separationRadius: number; separationWeight?: number; neighborRadius: number; cohesionWeight?: number; alignmentWeight?: number; seekWeight?: number; stragglerRadius?: number; stragglerBoost?: number } — ⚠ undocumented
65
+ - `FlockStepAgent` (interface): interface FlockStepAgent { position: FlockVec3; velocity: FlockVec3 } — ⚠ undocumented
63
66
  - `FlockVec3` (type): type FlockVec3 = readonly [number, number, number] — ⚠ undocumented
64
67
 
65
68
  ## @jgengine/core/ai/groupAssist
66
69
 
67
- - `AssistMember` (interface): interface AssistMember — ⚠ undocumented
68
- - `AssistNetwork` (interface): interface AssistNetwork — ⚠ undocumented
69
- - `AssistNetworkConfig` (interface): interface AssistNetworkConfig — ⚠ undocumented
70
+ - `AssistMember` (interface): interface AssistMember { id: string; groupId: string; table: ThreatTable } — ⚠ undocumented
71
+ - `AssistNetwork` (interface): interface AssistNetwork { register(member: AssistMember): void; remove(memberId: string): void; memberIds(groupId?: string): string[]; assistersOf(memberId: string): string[]; addThreat(memberId: string, sourceId: string, amount: number): string[]; snapshot(): Ass… — ⚠ undocumented
72
+ - `AssistNetworkConfig` (interface): interface AssistNetworkConfig { radius?: number; shareFraction?: number; distanceBetween?: (a: string, b: string) => number } — ⚠ undocumented
73
+ - `AssistNetworkState` (interface): interface AssistNetworkState — Plain JSON membership of an {@link AssistNetwork}, in registration order; threat lives in each member's own table.
70
74
 
71
75
  ## @jgengine/core/ai/heatSystem
72
76
 
@@ -95,28 +99,28 @@
95
99
 
96
100
  ## @jgengine/core/ai/jobBoard
97
101
 
98
- - `Job` (interface): interface Job — ⚠ undocumented
99
- - `JobBoard` (interface): interface JobBoard — ⚠ undocumented
100
- - `JobDef` (interface): interface JobDef — ⚠ undocumented
102
+ - `Job` (interface): interface Job { id: string; station: NavPoint; work: number; priority: number; arriveRadius: number; repeat: boolean; worker: string | null } — ⚠ undocumented · used by `activeJobs` (@jgengine/core/gameplay): Jobs currently progressing.
103
+ - `JobBoard` (interface): interface JobBoard { post(job: JobDef): string; cancel(jobId: string): boolean; assign(workerId: string, jobId: string): boolean; claim(workerId: string): string | null; release(workerId: string): void; advance(workerId: string, dt: number, ctx: JobTickContex… — ⚠ undocumented
104
+ - `JobDef` (interface): interface JobDef { id?: string; station: NavPoint; work: number; priority?: number; arriveRadius?: number; repeat?: boolean } — ⚠ undocumented
101
105
  - `JobPhase` (type): type JobPhase = "idle" | "travelling" | "working" | "done" — ⚠ undocumented
102
- - `JobReport` (interface): interface JobReport — ⚠ undocumented
103
- - `JobTickContext` (interface): interface JobTickContext — ⚠ undocumented
104
- - `WorkerState` (interface): interface WorkerState — ⚠ undocumented
106
+ - `JobReport` (interface): interface JobReport { jobId: string; workerId: string; cycle: number } — ⚠ undocumented
107
+ - `JobTickContext` (interface): interface JobTickContext { distanceToStation: number } — ⚠ undocumented
108
+ - `WorkerState` (interface): interface WorkerState { jobId: string | null; phase: JobPhase; worked: number; cycles: number } — ⚠ undocumented
105
109
 
106
110
  ## @jgengine/core/ai/laneSelect
107
111
 
108
- - `LaneCandidate` (interface): interface LaneCandidate<TId extends string = string> — ⚠ undocumented
109
- - `PickLaneOptions` (interface): interface PickLaneOptions<TId extends string = string> — ⚠ undocumented
112
+ - `LaneCandidate` (interface): interface LaneCandidate<TId extends string = string> { id: TId; cost: number } — ⚠ undocumented
113
+ - `PickLaneOptions` (interface): interface PickLaneOptions<TId extends string = string> { current?: TId | null; stickiness?: number; tieEpsilon?: number; rng?: () => number } — ⚠ undocumented
110
114
 
111
115
  ## @jgengine/core/ai/mobBrain
112
116
 
113
117
  - `MobBrain` (interface): interface MobBrain — The wander → aggro → chase → engage → leash-evade loop every MMO-shaped game hand-rolls, composed over `ai/threat`. The brain decides intent; the game executes it (`moveToward`, ground-snap, facing, swings) and routes damage into `addThreat`. Pack aggro is game-side: call `addThreat` on nearby packmates' brains when one aggros.
114
- - `MobBrainConfig` (interface): interface MobBrainConfig — ⚠ undocumented
115
- - `MobBrainDeps` (interface): interface MobBrainDeps — ⚠ undocumented
118
+ - `MobBrainConfig` (interface): interface MobBrainConfig { aggroRadius: number; attackRange: number; leashDistance: number; wander?: MobWanderConfig | false; evadeSpeedScale?: number; stickiness?: number; homeArriveRadius?: number; threat?: ThreatTableConfig } — ⚠ undocumented
119
+ - `MobBrainDeps` (interface): interface MobBrainDeps { home: MobVec3; position(): MobVec3 | null; targetPosition(targetId: string): MobVec3 | null; candidates(): readonly string[]; rng?: () => number } — ⚠ undocumented
116
120
  - `MobBrainMode` (type): type MobBrainMode = "idle" | "wander" | "chase" | "engage" | "evade" — ⚠ undocumented
117
- - `MobBrainStep` (interface): interface MobBrainStep — ⚠ undocumented
121
+ - `MobBrainStep` (interface): interface MobBrainStep { mode: MobBrainMode; targetId: string | null; moveTo: MobVec3 | null; speedScale: number; inAttackRange: boolean; arrivedHome: boolean } — ⚠ undocumented
118
122
  - `MobVec3` (type): type MobVec3 = readonly [number, number, number] — ⚠ undocumented
119
- - `MobWanderConfig` (interface): interface MobWanderConfig — ⚠ undocumented
123
+ - `MobWanderConfig` (interface): interface MobWanderConfig { radius: number; intervalSeconds?: number; speedScale?: number; arriveRadius?: number } — ⚠ undocumented
120
124
 
121
125
  ## @jgengine/core/ai/mobBrainGraph
122
126
 
@@ -145,16 +149,16 @@
145
149
 
146
150
  ## @jgengine/core/ai/spawnDirector
147
151
 
148
- - `DirectorContext` (interface): interface DirectorContext — ⚠ undocumented
149
- - `DirectorStep` (interface): interface DirectorStep — ⚠ undocumented
150
- - `SpawnDirectorConfig` (interface): interface SpawnDirectorConfig — ⚠ undocumented
151
- - `SpawnDirectorState` (interface): interface SpawnDirectorState — ⚠ undocumented
152
- - `SpawnEntry` (interface): interface SpawnEntry — ⚠ undocumented
152
+ - `DirectorContext` (interface): interface DirectorContext { alive: number; players?: number; playerPositions?: readonly NavPoint[] } — ⚠ undocumented
153
+ - `DirectorStep` (interface): interface DirectorStep { state: SpawnDirectorState; spawns: SpawnRequest[] } — ⚠ undocumented
154
+ - `SpawnDirectorConfig` (interface): interface SpawnDirectorConfig { waves: readonly WaveManifest[]; maxAlive?: number; escalationPerSecond?: number; alertBudgetPerSecond?: number; alertDecayPerSecond?: number; playerBudgetPerSecond?: number; maxSpawnsPerTick?: number; loop?: boolean; seed?: number; spawnP… — ⚠ undocumented
155
+ - `SpawnDirectorState` (interface): interface SpawnDirectorState { wave: number; elapsed: number; waveElapsed: number; budget: number; alert: number; spawnedThisWave: number; spawnedTotal: number; rng: RandomSeed; done: boolean } — ⚠ undocumented
156
+ - `SpawnEntry` (interface): interface SpawnEntry { id: string; cost: number; weight?: number; minWave?: number } — ⚠ undocumented
153
157
  - `SpawnPointBiasStrength` (type): type SpawnPointBiasStrength = "subtle" | "moderate" | "strong" — How strongly `distanceBias` weights candidates by distance from `avoid` — a named intent, not a weighting exponent.
154
158
  - `SpawnPointDistanceBias` (type): type SpawnPointDistanceBias = "near" | "far" | "none" — Preference for picking a spawn point relative to `avoid` positions: closer, farther, or unweighted.
155
159
  - `SpawnPointSelectionOptions` (interface): interface SpawnPointSelectionOptions — Semantic options for selecting a spawn point without exposing weighting internals.
156
- - `SpawnRequest` (interface): interface SpawnRequest — ⚠ undocumented
157
- - `WaveManifest` (interface): interface WaveManifest — ⚠ undocumented
160
+ - `SpawnRequest` (interface): interface SpawnRequest { entryId: string; cost: number; wave: number; point?: NavPoint; laneId?: number } — ⚠ undocumented
161
+ - `WaveManifest` (interface): interface WaveManifest { budget: number; entries: readonly SpawnEntry[]; duration?: number; budgetPerSecond?: number } — ⚠ undocumented
158
162
  - `advanceSpawnDirector` (function): function advanceSpawnDirector(config: SpawnDirectorConfig, state: SpawnDirectorState, dt: number, ctx: DirectorContext): DirectorStep — ⚠ undocumented
159
163
  - `advanceWave` (function): function advanceWave(config: SpawnDirectorConfig, state: SpawnDirectorState): SpawnDirectorState — ⚠ undocumented
160
164
  - `createSpawnDirectorState` (function): function createSpawnDirectorState(config: SpawnDirectorConfig): SpawnDirectorState — ⚠ undocumented
@@ -186,10 +190,11 @@
186
190
 
187
191
  ## @jgengine/core/ai/threat
188
192
 
189
- - `HighestThreatOptions` (interface): interface HighestThreatOptions — ⚠ undocumented
190
- - `ThreatEntry` (interface): interface ThreatEntry — ⚠ undocumented
191
- - `ThreatTable` (interface): interface ThreatTable — ⚠ undocumented
192
- - `ThreatTableConfig` (interface): interface ThreatTableConfig — ⚠ undocumented
193
+ - `HighestThreatOptions` (interface): interface HighestThreatOptions { current?: string | null; stickiness?: number } — ⚠ undocumented
194
+ - `ThreatEntry` (interface): interface ThreatEntry { sourceId: string; threat: number } — ⚠ undocumented
195
+ - `ThreatTable` (interface): interface ThreatTable { add(sourceId: string, amount: number): number; set(sourceId: string, amount: number): void; threatOf(sourceId: string): number; decay(dt: number): void; highest(options?: HighestThreatOptions): string | null; ranked(): ThreatEntry[]; taun… — ⚠ undocumented
196
+ - `ThreatTableConfig` (interface): interface ThreatTableConfig { decayPerSecond?: number; max?: number; forgetBelow?: number } — ⚠ undocumented
197
+ - `ThreatTableState` (interface): interface ThreatTableState — Plain JSON state of a {@link ThreatTable}; `entries` keeps insertion order, which breaks ties in `highest`.
193
198
  - `createThreatTable` (function): function createThreatTable(config: ThreatTableConfig = {}): ThreatTable — ⚠ undocumented
194
199
 
195
200
  ## @jgengine/core/ai/waveRunner
@@ -209,21 +214,46 @@
209
214
  - `AnimEvent` (interface): interface AnimEvent — A named moment inside a clip (foot plant, hit frame, reload point).
210
215
  - `AnimGraph` (interface): interface AnimGraph — Serializable animation graph. Clip durations come from the rig at runtime (see {@link AnimGraphClipInfo}).
211
216
  - `AnimGraphClipInfo` (type): type AnimGraphClipInfo = Readonly<Record<string, number | { duration: number; rootTrack?: { times: Float32Array; values: Float32Array } }>> — Per-clip duration in seconds, read from the loaded rig.
217
+ - `AnimGraphDiagnostic` (interface): interface AnimGraphDiagnostic — A located, repairable error in an authored animation graph; paths are relative to the graph value.
212
218
  - `AnimGraphOutput` (interface): interface AnimGraphOutput — What one advance asks the rig to show.
213
219
  - `AnimGraphRuntime` (interface): interface AnimGraphRuntime — The evaluator handle: arm triggers, advance, inspect, snapshot and restore.
220
+ - `AnimGraphRuntimeOptions` (interface): interface AnimGraphRuntimeOptions — Randomness used only when entering a clip state with variants. Omit to choose the first variant.
214
221
  - `AnimGraphState` (interface): interface AnimGraphState — Serializable evaluator state.
222
+ - `AnimGraphValidation` (interface): interface AnimGraphValidation — The whole authored graph is retained unchanged only when every supported field is valid.
215
223
  - `AnimLayer` (interface): interface AnimLayer — A blend layer with its own state machine. Masked layers apply only to bones whose track names start with a prefix.
216
224
  - `AnimParamValue` (type): type AnimParamValue = number | boolean — Parameter value a graph reads: floats for blends and comparisons, booleans for gates.
217
225
  - `AnimParams` (type): type AnimParams = Readonly<Record<string, AnimParamValue>> — The parameter set a graph evaluates against each advance.
218
- - `AnimState` (type): type AnimState = | { kind: "clip"; clip: string; speed?: number; loop?: boolean; rootMotion?: boolean } | { kind: "blend1D"; param: string; points: readonly { at: number; clip: string }[]; speed?: number; loop?: boolean; rootMotion?: boolean } | { kind: "blend2D"; params: readonly [string, string]; … — A state plays one clip, or blends clips by one or two parameters.
226
+ - `AnimState` (type): type AnimState = | { kind: "clip"; clip: string; variants?: readonly string[]; speed?: number; loop?: boolean; rootMotion?: boolean } | { kind: "blend1D"; param: string; points: readonly { at: number; clip: string }[]; speed?: number; loop?: boolean; rootMotion?: boolean } | { kind: "blend2D"; param… — A state plays one clip, or blends clips by one or two parameters.
219
227
  - `AnimTransition` (interface): interface AnimTransition — Edge between states. `from: "*"` matches any state except `to`.
220
- - `createAnimGraphRuntime` (function): function createAnimGraphRuntime(initial: AnimGraph): AnimGraphRuntime — Headless animation state machine and blend evaluator. It owns every clip's playback time and weight, so the renderer only seeks and weights actions on a mixer, and headless hosts, replays, and tests advance the same graph without three.js. Transitions are data (parameter comparisons and consumed triggers), layers can be masked or additive, and events fire by clip time, including across loop wraps.
228
+ - `createAnimGraphRuntime` (function): function createAnimGraphRuntime(initial: AnimGraph, options: AnimGraphRuntimeOptions = {}): AnimGraphRuntime — Headless animation state machine and blend evaluator. It owns every clip's playback time and weight, so the renderer only seeks and weights actions on a mixer, and headless hosts, replays, and tests advance the same graph without three.js. Transitions are data (parameter comparisons and consumed triggers), layers can be masked or additive, and events fire by clip time, including across loop wraps.
229
+ - `parseAnimGraph` (function): function parseAnimGraph(value: unknown): AnimGraph | undefined — Validates untrusted JSON (a saved scene document, a network payload) as an {@link AnimGraph}. Malformed states, transitions to unknown states, and bad conditions are dropped; a layer whose entry state is missing is dropped; the result is `undefined` when no layer survives.
221
230
  - `stateClipWeights` (function): function stateClipWeights(state: AnimState, params: AnimParams): Record<string, number> — Static clip weights of a state at `params`, before any crossfade.
231
+ - `validateAnimGraph` (function): function validateAnimGraph(value: unknown): AnimGraphValidation — Validate an authored graph without dropping broken combat states or weakening transition conditions. Known malformed fields and dangling references reject the whole graph with relative repair locations; valid data, including authored object/array order, is returned unchanged. Unknown extension fields are retained. Clip availability belongs to the imported-rig diagnostics. Use {@link parseAnimGraph} when permissive repair is intentional.
232
+
233
+ ## @jgengine/core/anim/boneTexture
234
+
235
+ - `BoneTextureClip` (interface): interface BoneTextureClip — One baked clip's rows in a {@link BoneTextureLayout}.
236
+ - `BoneTextureClipInput` (interface): interface BoneTextureClipInput — A clip to bake: its name and length in seconds.
237
+ - `BoneTextureInput` (interface): interface BoneTextureInput — Inputs for {@link planBoneTexture}.
238
+ - `BoneTextureLayout` (interface): interface BoneTextureLayout — Where every bone matrix of every baked frame lives in a vertex-animation texture: one row per frame, four RGBA texels (matrix columns) per bone. Plain data, so a baked crowd can be cached, shipped or checked in a test without a renderer.
239
+ - `BoneTextureSample` (interface): interface BoneTextureSample — Two texture rows around a playback time and the blend between them.
240
+ - `planBoneTexture` (function): function planBoneTexture(input: BoneTextureInput): BoneTextureLayout — Plans a bone-matrix texture for a rig's clips. Throws when the rig or the clip set does not fit `maxTextureSize`; lower `fps` or bake fewer clips.
241
+ - `sampleBoneTexture` (function): function sampleBoneTexture(clip: BoneTextureClip, fps: number, time: number, loop: boolean, out?: BoneTextureSample): BoneTextureSample — Finds the baked rows around `time` in a clip, wrapping when `loop` and holding the last pose otherwise. This is the CPU mirror of the crowd shader's lookup.
222
242
 
223
243
  ## @jgengine/core/anim/easing
224
244
 
225
245
  - `Easing` (type): type Easing = (t: number) => number — ⚠ undocumented
226
246
 
247
+ ## @jgengine/core/anim/footPlacement
248
+
249
+ - `FootGroundSample` (interface): interface FootGroundSample — One foot's animated ankle height and the ground found under it, both in world units.
250
+ - `FootPlacement` (interface): interface FootPlacement — Result of {@link placeFeet}: where each ankle should go and how far the pelvis drops.
251
+ - `FootPlacementInput` (interface): interface FootPlacementInput — Inputs for {@link placeFeet}. All heights are world units.
252
+ - `LegChain` (interface): interface LegChain — Thigh → shin → foot bone names for one leg.
253
+ - `RigBone` (interface): interface RigBone — A bone as {@link inferLegChains} sees it: its name and its parent's name.
254
+ - `inferLegChains` (function): function inferLegChains(bones: readonly RigBone[]): LegChain[] — Finds leg chains on a humanoid rig by bone name: a thigh-like bone (`UpLeg`, `UpperLeg`, `Thigh`) with its first child and grandchild as shin and foot. Control, IK-target and twist bones are skipped. Returns chains in rig order; an empty array means the rig has no recognizable legs.
255
+ - `placeFeet` (function): function placeFeet(input: FootPlacementInput, out?: FootPlacement): FootPlacement — Resolves foot placement for a rig whose clips were authored on flat ground at its origin. Each foot keeps its animated lift and moves by its ground's height above or below the origin, the sole is never left under the ground, and the pelvis drops by the deepest reach so both legs stay within length. Pure and allocation-light; the renderer applies the targets with {@link solveTwoBone}.
256
+
227
257
  ## @jgengine/core/anim/ikSolver
228
258
 
229
259
  - `FabrikOptions` (interface): interface FabrikOptions — Iteration budget and convergence threshold for {@link solveFabrik}.
@@ -239,9 +269,13 @@
239
269
 
240
270
  ## @jgengine/core/anim/locomotionGraph
241
271
 
272
+ - `LOCOMOTION_CROUCHED_PARAM` (const): const LOCOMOTION_CROUCHED_PARAM: "crouched" — Physically resolved crouch stance from the shared player motor.
273
+ - `LOCOMOTION_GROUNDED_PARAM` (const): const LOCOMOTION_GROUNDED_PARAM: "grounded" — Ground contact reported by the shared player motor; absent for custom movers without authored parameters.
242
274
  - `LOCOMOTION_LAYER` (const): const LOCOMOTION_LAYER: "base" — Layer id the locomotion graph uses; query `runtime.stateOf(LOCOMOTION_LAYER)`.
243
275
  - `LOCOMOTION_SPEED_PARAM` (const): const LOCOMOTION_SPEED_PARAM: "speed" — The parameter name the shell feeds with the entity's smoothed ground speed.
276
+ - `LOCOMOTION_VERTICAL_SPEED_PARAM` (const): const LOCOMOTION_VERTICAL_SPEED_PARAM: "verticalSpeed" — Resolved vertical velocity from the shared player motor, in world units/sec.
244
277
  - `LocomotionGraphInput` (interface): interface LocomotionGraphInput — Inputs for {@link locomotionGraph}: the idle/walk/run clip names and the one-shot table a rig config already carries.
278
+ - `animGraphFromConfig` (function): function animGraphFromConfig(config: ModelAnimationConfig): AnimGraph | undefined — The graph a model animation config plays: its `graph`, or the {@link locomotionGraph} its `states` and `oneShots` describe, retaining every one-shot variant. `undefined` for a single-clip config or incomplete locomotion roles. Idle must name a clip; an absent or blank walk role holds that idle clip at walking speeds until configured. The shell plays this and the editor inspects it, so both see the same graph.
245
279
  - `locomotionGraph` (function): function locomotionGraph(input: LocomotionGraphInput): AnimGraph — The engine's default locomotion as an authored graph: a speed-driven blend between idle, walk, and run, plus a state per one-shot that plays once and returns (or clamps for `death`). What `useModelAnimation` used to hardcode.
246
280
 
247
281
  ## @jgengine/core/area/areaEffectField
@@ -269,15 +303,32 @@
269
303
 
270
304
  ## @jgengine/core/audio/audioFalloff
271
305
 
272
- - `AudioBusDef` (interface): interface AudioBusDef — ⚠ undocumented
306
+ - `AudioBusDef` (interface): interface AudioBusDef { id: AudioBusId; gain?: number } — ⚠ undocumented
273
307
  - `AudioBusId` (type): type AudioBusId = string — ⚠ undocumented
274
- - `AudioFalloffConfig` (interface): interface AudioFalloffConfig — ⚠ undocumented
308
+ - `AudioFalloffConfig` (interface): interface AudioFalloffConfig { minDistance?: number; maxDistance?: number; curve?: FalloffCurve } — ⚠ undocumented
275
309
  - `FalloffCurve` (type): type FalloffCurve = "linear" | "inverse" | "none" — ⚠ undocumented
276
- - `SoundDef` (interface): interface SoundDef — ⚠ undocumented
310
+ - `SoundDef` (interface): interface SoundDef { id: string; url?: string; synth?: SynthPatch; bus: AudioBusId; gain?: number; loop?: boolean; maxVoices?: number; priority?: number; positional?: boolean; falloff?: AudioFalloffConfig; doppler?: number; spatial?: {panning: "hrtf" | "equal… — ⚠ undocumented
277
311
  - `computeFalloffGain` (function): function computeFalloffGain(distance: number, config: AudioFalloffConfig = {}): number — ⚠ undocumented
278
312
  - `distance3` (function): function distance3(a: { x: number; y: number; z: number }, b: { x: number; y: number; z: number }): number — ⚠ undocumented
279
313
  - `resolveEmitterGain` (function): function resolveEmitterGain(distance: number, sound: Pick<SoundDef, "gain" | "positional" | "falloff">, busGain: number): number — ⚠ undocumented
280
314
 
315
+ ## @jgengine/core/audio/doppler
316
+
317
+ - `DopplerOptions` (interface): interface DopplerOptions — Options for {@link dopplerRate}.
318
+ - `SPEED_OF_SOUND` (const): const SPEED_OF_SOUND: 343 — Speed of sound in air at 20 °C, metres per second — the default for {@link dopplerRate}.
319
+ - `dopplerRate` (function): function dopplerRate(listenerPosition: Vec3Tuple, listenerVelocity: Vec3Tuple, emitterPosition: Vec3Tuple, emitterVelocity: Vec3Tuple, options: DopplerOptions = {}): number — Doppler pitch multiplier for an emitter heard by a listener: `(c + vListener) / (c - vEmitter)`, where each speed is the component along the line between them, positive when closing. Multiply a loop's playback rate by it. Pure and allocation-free.
320
+
321
+ ## @jgengine/core/audio/engineLayers
322
+
323
+ - `EngineLayer` (interface): interface EngineLayer — One recorded engine sample, keyed by the rpm it was recorded at.
324
+ - `EngineLayerMix` (interface): interface EngineLayerMix — One layer's live mix.
325
+ - `EngineLayers` (interface): interface EngineLayers — A layered engine loop: N rpm-keyed samples crossfaded by rpm, with on- and off-load sets picked by engine load.
326
+ - `EngineLayersAudio` (interface): interface EngineLayersAudio — The part of `ctx.game.audio` {@link EngineLayers} drives.
327
+ - `EngineLayersConfig` (interface): interface EngineLayersConfig — Config for {@link createEngineLayers}.
328
+ - `EngineLayersPlayOptions` (interface): interface EngineLayersPlayOptions — Per-tick parameters {@link EngineLayers.play} forwards to every layer's loop.
329
+ - `EngineLayersState` (interface): interface EngineLayersState — Serializable {@link EngineLayers} state.
330
+ - `createEngineLayers` (function): function createEngineLayers(initial: EngineLayersConfig): EngineLayers — Layered engine sound: each set of rpm-keyed samples is equal-power crossfaded between the two samples bracketing the current rpm and pitched by `rpm / layer.rpm`; the on- and off-load sets are equal-power crossfaded by smoothed `engineLoad`. A config with only one load set plays it at every load. `play` drives `ctx.game.audio` directly, so a game needs no per-layer glue.
331
+
281
332
  ## @jgengine/core/audio/music
282
333
 
283
334
  - `MusicInstrument` (type): type MusicInstrument = | "strings" | "flute" | "harp" | "horn" | "choir" | "bell" | "timpani" | "bass" | "stacc" | "pad" | "lute" | "dulcimer" | "frameDrum" | "warDrum" | "reed" | "pipe" | "squareLead" | "woodBlock" | "tinyBell" | "piano" | "shaker" | "brassStab" | "cymSwell" | "oboe" — Named synthesised instrument. Each maps to a voice in the shell's instrument library (`@jgengine/shell/audio/musicVoices`); an unknown name falls back to a plain sine voice so a theme is never silent.
@@ -317,23 +368,34 @@
317
368
  - `ToneVoice` (interface): interface ToneVoice — A pitched oscillator voice: a 12ms linear attack to `gain`, then an exponential decay to silence across `duration`, with an optional exponential pitch slide from `freq` to `slideTo`.
318
369
  - `patchDuration` (function): function patchDuration(patch: SynthPatch): number — Total wall-clock length of a patch in seconds — the latest voice end across all voices.
319
370
 
371
+ ## @jgengine/core/audio/voiceAllocator
372
+
373
+ - `AudioVoice` (interface): interface AudioVoice — One reserved playback slot, including sources still loading.
374
+ - `VoiceAllocation` (type): type VoiceAllocation = { ok: false } | { ok: true; voiceId: number; stolen: number[] } — Admission result; stop every stolen source before starting the admitted voice.
375
+ - `VoiceAllocator` (interface): interface VoiceAllocator — Bounded reservations with higher-priority protection and oldest-first ties.
376
+ - `VoiceAllocatorConfig` (interface): interface VoiceAllocatorConfig — Playback budget. Zero disables playback; default 64.
377
+ - `VoiceAllocatorSnapshot` (interface): interface VoiceAllocatorSnapshot — Detached allocator state; ids increase monotonically for deterministic oldest-first ties.
378
+ - `VoiceAllocatorStorage` (interface): interface VoiceAllocatorStorage — Caller-owned reservation storage. Reads and writes use detached state.
379
+ - `createVoiceAllocator` (function): function createVoiceAllocator(config: VoiceAllocatorConfig = {}): VoiceAllocator — Reserve bounded sound playback before fetching or constructing a graph. Equal priorities steal the oldest reservation; lower priorities cannot interrupt higher priorities. Policy is retunable.
380
+
320
381
  ## @jgengine/core/faction/factions
321
382
 
322
- - `FactionDef` (interface): interface FactionDef — ⚠ undocumented
323
- - `FactionGraph` (interface): interface FactionGraph — ⚠ undocumented
324
- - `FactionGraphConfig` (interface): interface FactionGraphConfig — ⚠ undocumented
383
+ - `FactionDef` (interface): interface FactionDef { id: string; relations?: Readonly<Record<string, FactionRelation>>; towardSelf?: FactionRelation; towardOthers?: FactionRelation } — ⚠ undocumented
384
+ - `FactionGraph` (interface): interface FactionGraph { relationBetween(observer: string | null | undefined, other: string | null | undefined): FactionRelation; isHostile(observer: string | null | undefined, other: string | null | undefined): boolean; isFriendly(observer: string | null | undef… — ⚠ undocumented
385
+ - `FactionGraphConfig` (interface): interface FactionGraphConfig { factions: readonly FactionDef[]; symmetric?: boolean; unaligned?: FactionRelation } — ⚠ undocumented
325
386
  - `FactionRelation` (type): type FactionRelation = "hostile" | "neutral" | "friendly" — ⚠ undocumented
326
- - `FactionRoster` (interface): interface FactionRoster — ⚠ undocumented
387
+ - `FactionRoster` (interface): interface FactionRoster { assign(entityId: string, factionId: string | null): void; factionOf(entityId: string): string | null; relationBetweenEntities(observer: string, other: string): FactionRelation; isHostile(observer: string, other: string): boolean; isFriend… — ⚠ undocumented
327
388
  - `createFactionGraph` (function): function createFactionGraph(config: FactionGraphConfig): FactionGraph — ⚠ undocumented
328
389
  - `createFactionRoster` (function): function createFactionRoster(graph: FactionGraph): FactionRoster — ⚠ undocumented
329
390
 
330
391
  ## @jgengine/core/faction/reputation
331
392
 
332
393
  - `DEFAULT_REPUTATION_TIERS` (const): const DEFAULT_REPUTATION_TIERS: readonly ReputationTier[] — ⚠ undocumented
333
- - `EffectiveRelationInput` (interface): interface EffectiveRelationInput — ⚠ undocumented
334
- - `ReputationLedger` (interface): interface ReputationLedger — ⚠ undocumented
335
- - `ReputationLedgerConfig` (interface): interface ReputationLedgerConfig — ⚠ undocumented
336
- - `ReputationTier` (interface): interface ReputationTier — ⚠ undocumented
394
+ - `EffectiveRelationInput` (interface): interface EffectiveRelationInput { base: FactionRelation; ledger: ReputationLedger; actorId: string; factionId: string } — ⚠ undocumented
395
+ - `ReputationLedger` (interface): interface ReputationLedger { standing(actorId: string, factionId: string): number; hasStanding(actorId: string, factionId: string): boolean; gain(actorId: string, factionId: string, amount: number): number; set(actorId: string, factionId: string, standing: number): n… — ⚠ undocumented
396
+ - `ReputationLedgerConfig` (interface): interface ReputationLedgerConfig { tiers?: readonly ReputationTier[]; initial?: Readonly<Record<string, number>>; min?: number; max?: number } — ⚠ undocumented
397
+ - `ReputationLedgerState` (interface): interface ReputationLedgerState — Plain JSON standings of a {@link ReputationLedger}: stored values only, `initial` stays config.
398
+ - `ReputationTier` (interface): interface ReputationTier { id: string; min: number; relation: FactionRelation } — ⚠ undocumented · used by `tierForStanding`: Map a faction standing value to its named reputation tier.
337
399
  - `createReputationLedger` (function): function createReputationLedger(config: ReputationLedgerConfig = {}): ReputationLedger — ⚠ undocumented
338
400
  - `effectiveRelation` (function): function effectiveRelation(input: EffectiveRelationInput): FactionRelation — ⚠ undocumented
339
401
  - `tierForStanding` (function): function tierForStanding(tiers: readonly ReputationTier[], standing: number): ReputationTier — Map a faction standing value to its named reputation tier.
@@ -343,34 +405,48 @@
343
405
  - `ActionBinding` (interface): interface ActionBinding<TCode extends string = string> — Action-binding model: games bind semantic actions ("jump", "interact") to physical control codes; capture layers resolve raw events through this map so gameplay code never sees keycodes. Control codes are plain strings, so the same model serves keyboard codes, mouse buttons, touch controls, or gamepad inputs.
344
406
  - `ActionBindingConfig` (type): type ActionBindingConfig<TCode extends string = string> = ActionBinding<TCode>[] | ActionBindingModes<TCode> — ⚠ undocumented
345
407
  - `ActionBindingMap` (type): type ActionBindingMap<TAction extends string, TCode extends string = string> = Record< TAction, ActionBinding<TCode> > — ⚠ undocumented
346
- - `ActionBindingModes` (interface): interface ActionBindingModes<TCode extends string = string> — ⚠ undocumented
408
+ - `ActionBindingModes` (interface): interface ActionBindingModes<TCode extends string = string> { hold?: ActionBinding<TCode>[]; toggle?: ActionBinding<TCode>[] } — ⚠ undocumented
347
409
  - `ActionCodes` (type): type ActionCodes<TCode extends string = string> = | readonly TCode[] | { hold?: readonly TCode[]; toggle?: readonly TCode[]; repeatMs?: number } — ⚠ undocumented
348
- - `ActionCodesMap` (type): type ActionCodesMap<TAction extends string = string, TCode extends string = string> = Record< TAction, ActionCodes<TCode> > — Maps each game action name to the input codes (hold/toggle keys, repeat rate) that trigger it.
410
+ - `ActionCodesMap` (type): type ActionCodesMap<TAction extends string = string, TCode extends string = string> = Record< TAction, ActionCodes<TCode> > — Maps each game action name to the input codes (hold/toggle keys, repeat rate) that trigger it. In a `defineGame({ input })`, pressing an action runs the same-named command with `{ yaw, pitch, aim }`; `repeatMs` re-fires it while held (automatic weapons).
349
411
  - `ActionStateBindingMap` (type): type ActionStateBindingMap<TAction extends string, TCode extends string = string> = Record< TAction, ActionBindingConfig<TCode> > — ⚠ undocumented
350
- - `ActionStateTracker` (interface): interface ActionStateTracker<TAction extends string> — ⚠ undocumented
351
- - `ShouldDispatchActionInput` (interface): interface ShouldDispatchActionInput — ⚠ undocumented
412
+ - `ActionStateTracker` (interface): interface ActionStateTracker<TAction extends string> { handleDown(code: string): TAction | null; handleUp(code: string): TAction | null; isDown(action: TAction): boolean; wasPressed(action: TAction): boolean; endFrame(): void; reset(): void; rebind(map: ActionStateBindingMap<TAction, string>)… — ⚠ undocumented · used by `rebindGamepadPoll` (@jgengine/shell/input/gamepadPoll): Swap the pad bindings mid-game (a context push) without touching keyboard state: release the codes of pad-held actions whose pad codes chang…
413
+ - `ShouldDispatchActionInput` (interface): interface ShouldDispatchActionInput { pressed: boolean; down: boolean; repeatMs: number | undefined; lastFiredAt: number | null; now: number } — ⚠ undocumented
352
414
 
353
415
  ## @jgengine/core/input/actionContexts
354
416
 
355
417
  - `ActionContext` (interface): interface ActionContext — Named action-binding layer that may optionally expose lower layers.
418
+ - `ActionContextAxisShape` (type): type ActionContextAxisShape = Omit<AxisShapeConfig, "scale"> — Serializable axis shaping a context may carry: {@link AxisShapeConfig} without the `scale` callback.
356
419
  - `ActionContextStack` (interface): interface ActionContextStack — Mutable layered action-map stack with snapshot and restore support.
357
420
  - `ActionContextStackSnapshot` (interface): interface ActionContextStackSnapshot — Serializable state for an action-context stack.
421
+ - `ActiveContextAxes` (interface): interface ActiveContextAxes — Axis bindings and shaping merged across the active contexts.
358
422
  - `createActionContextStack` (function): function createActionContextStack(): ActionContextStack — Creates a serializable stack of layered action maps for menus and gameplay modes.
359
423
 
360
424
  ## @jgengine/core/input/axisInput
361
425
 
362
426
  - `AXIS_RANGE` (const): const AXIS_RANGE: Record<AxisName, AxisRange> — ⚠ undocumented
363
- - `AxisBinding` (interface): interface AxisBinding — ⚠ undocumented
364
- - `AxisBindingMap` (type): type AxisBindingMap = Record<AxisName, AxisBinding> — ⚠ undocumented
365
- - `AxisChannelConfig` (interface): interface AxisChannelConfig — ⚠ undocumented
366
- - `AxisInput` (interface): interface AxisInput — ⚠ undocumented
427
+ - `AxisBinding` (interface): interface AxisBinding { positive: readonly string[]; negative?: readonly string[]; pointer?: PointerAxisBinding } — ⚠ undocumented · used by `analogAxes` (@jgengine/core/input/axisShaper): Which axes an analog source is driving this frame: an axis counts as analog when any action bound to it has a published analog magnitude (`c…
428
+ - `AxisBindingMap` (type): type AxisBindingMap = Record<AxisName, AxisBinding> — ⚠ undocumented · used by `DRIVE_AXIS_BINDINGS`: Car axes bound to raw key codes, for an {@link AxisChannel} sampled with a key-code `isDown`.
429
+ - `AxisChannelConfig` (interface): interface AxisChannelConfig { bindings: AxisBindingMap; smoothing?: number } — ⚠ undocumented · used by `useAxisChannel` (@jgengine/react): Wires useHeldKeys into a fresh AxisChannel, ready for a per-frame `channel.sample(dt, isDown)`.
430
+ - `AxisInput` (interface): interface AxisInput { throttle: number; brake: number; steer: number; handbrake: number } — ⚠ undocumented · used by `tickDrivableVehicle` (@jgengine/core/physics/drivableVehicle): Connects an `AxisInput` sample straight through a ground-vehicle sim (`KinematicVehicle` or `VehicleDynamics`) to a scene entity's pose for …
367
431
  - `AxisName` (type): type AxisName = keyof AxisInput — ⚠ undocumented
368
- - `AxisRange` (interface): interface AxisRange — ⚠ undocumented
369
- - `DRIVE_AXIS_BINDINGS` (const): const DRIVE_AXIS_BINDINGS: AxisBindingMap — ⚠ undocumented
432
+ - `AxisRange` (interface): interface AxisRange { min: number; max: number } — ⚠ undocumented
433
+ - `DRIVE_AXIS_BINDINGS` (const): const DRIVE_AXIS_BINDINGS: AxisBindingMap — Car axes bound to raw key codes, for an {@link AxisChannel} sampled with a key-code `isDown`. `ctx.input.axis` samples action names instead, so bind it to the game's input actions (e.g. `moveForward`), not this map.
370
434
  - `GenericAxisChannel` (interface): interface GenericAxisChannel<TAxes extends string> — The held-key-ramping analog channel for any axis schema (#282.7) — drones (pitch/roll/strafe), boats, mechs — not just the four car axes `AxisChannel` hardcodes. Same semantics: keys ramp, `setAnalog` overrides, a binding's `pointer` source takes over while a pointer is active.
371
- - `GenericAxisConfig` (interface): interface GenericAxisConfig<TAxes extends string> — ⚠ undocumented
435
+ - `GenericAxisConfig` (interface): interface GenericAxisConfig<TAxes extends string> { bindings: Record<TAxes, AxisBinding>; ranges?: Partial<Record<TAxes, AxisRange>>; smoothing?: number } — ⚠ undocumented
372
436
  - `NEUTRAL_AXIS` (const): const NEUTRAL_AXIS: AxisInput — ⚠ undocumented
373
437
 
438
+ ## @jgengine/core/input/axisShaper
439
+
440
+ - `AxisShapeConfig` (interface): interface AxisShapeConfig — Shaping for one named axis.
441
+ - `AxisShapeProfile` (interface): interface AxisShapeProfile — How one axis responds to one kind of source. Keys want ramps (a held key should feel like a pedal being pressed, not a switch); sticks want deadzone and curve but no lag.
442
+ - `AxisShapeSample` (interface): interface AxisShapeSample<TAxes extends string> — Per-sample options for {@link AxisShaper.shape}.
443
+ - `AxisShaper` (interface): interface AxisShaper<TAxes extends string> — Stateful per-axis response shaping between raw input and a sim.
444
+ - `AxisShaperConfig` (interface): interface AxisShaperConfig<TAxes extends string> — Config for {@link createAxisShaper}: one entry per axis.
445
+ - `AxisValues` (type): type AxisValues<TAxes extends string> = Record<TAxes, number> — Shaped axis output, one number per axis.
446
+ - `analogAxes` (function): function analogAxes<TAxes extends string>(bindings: Readonly<Record<TAxes, AxisBinding>>, analog: Readonly<Record<string, number>> | null): Set<TAxes> — Which axes an analog source is driving this frame: an axis counts as analog when any action bound to it has a published analog magnitude (`ctx.input.analog()`). Feed the result to {@link AxisShaper.shape}.
447
+ - `createAxisShaper` (function): function createAxisShaper<TAxes extends string>(initial: AxisShaperConfig<TAxes>): AxisShaper<TAxes> — Creates an {@link AxisShaper}: deadzone, curve and ramp rates per axis, chosen per frame by whether the source was a key or a stick. It sits between `ctx.input.axis(...)` and any sim (a vehicle, an aircraft, aim), so a held key eases in and self-centres while a stick stays direct. Deterministic and serializable.
448
+ - `shapeAxisValue` (function): function shapeAxisValue(raw: number, profile: AxisShapeProfile | undefined): number — Deadzone, saturation and curve applied to one raw value; sign is preserved.
449
+
374
450
  ## @jgengine/core/input/bindingOverrides
375
451
 
376
452
  - `BINDING_OVERRIDES_STORAGE_PREFIX` (const): const BINDING_OVERRIDES_STORAGE_PREFIX: "jgengine:keybinds:" — ⚠ undocumented
@@ -392,41 +468,62 @@
392
468
 
393
469
  ## @jgengine/core/input/gamepadModel
394
470
 
471
+ - `DEFAULT_GAMEPAD_DEADZONE` (const): const DEFAULT_GAMEPAD_DEADZONE: GamepadDeadzone — Shell default stick deadzone.
395
472
  - `GAMEPAD_GLYPH_SETS` (const): const GAMEPAD_GLYPH_SETS: Record<GamepadGlyphName, GamepadGlyphSet> — Built-in short labels for the common controller families.
396
473
  - `GamepadBindings` (type): type GamepadBindings<TAction extends string = string> = ActionCodesMap<TAction, GamepadCode> — Action bindings whose codes identify gamepad buttons or axes.
397
474
  - `GamepadCode` (type): type GamepadCode = `pad:${number}` | `padaxis:${number}${"+" | "-"}` — A gamepad button or signed axis binding code.
398
475
  - `GamepadDeadzone` (interface): interface GamepadDeadzone — Deadzone policy applied to gamepad axes.
476
+ - `GamepadFeelConfig` (interface): interface GamepadFeelConfig — Game-level pad feel read by the shell's gamepad poll. A number `deadzone` is the axial inner deadzone with a `0.95` outer edge.
399
477
  - `GamepadFrame` (interface): interface GamepadFrame — The digital and analog action state produced by a gamepad frame.
400
478
  - `GamepadGlyphName` (type): type GamepadGlyphName = "xbox" | "playstation" | "nintendo" | "generic" — Names of the built-in controller glyph sets.
401
479
  - `GamepadGlyphSet` (interface): interface GamepadGlyphSet — Button labels used by one controller family.
480
+ - `GamepadSample` (interface): interface GamepadSample — Read-only view of one sampled gamepad. The browser `Gamepad` satisfies it structurally, so a poll loop can resolve `navigator.getGamepads()` entries without copying them.
402
481
  - `GamepadSnapshot` (interface): interface GamepadSnapshot — A serializable gamepad state sampled from the platform input API.
403
482
  - `ResolveGamepadFrameOptions` (interface): interface ResolveGamepadFrameOptions — Options for resolving one gamepad snapshot into action state.
483
+ - `gamepadFeelOptions` (function): function gamepadFeelOptions(config: GamepadFeelConfig | undefined): ResolveGamepadFrameOptions — Resolve a {@link GamepadFeelConfig} into the options {@link resolveGamepadFrame} takes.
404
484
  - `gamepadGlyphSets` (const): const gamepadGlyphSets: Record<GamepadGlyphName, GamepadGlyphSet> — Lowercase alias for consumers that prefer data-oriented naming.
405
- - `resolveGamepadFrame` (function): function resolveGamepadFrame(snapshot: GamepadSnapshot, bindings: GamepadBindings, options: ResolveGamepadFrameOptions): GamepadFrame — Resolve one sampled gamepad into held actions and shaped analog action values.
485
+ - `resolveGamepadFrame` (function): function resolveGamepadFrame(snapshot: GamepadSample, bindings: GamepadBindings, options: ResolveGamepadFrameOptions, out: GamepadFrame = { held: [], analog: {} }): GamepadFrame — Resolve one sampled gamepad into held actions and shaped analog action values. Sticks go through the deadzone and curve; analog buttons (triggers) through `triggerDeadzone` and the same curve. Pass `out` to reuse a frame across polls; it is cleared and returned.
406
486
 
407
487
  ## @jgengine/core/input/gestureSurface
408
488
 
409
489
  - `DEFAULT_GESTURE_TUNING` (const): const DEFAULT_GESTURE_TUNING: GestureSurfaceTuning — ⚠ undocumented
410
- - `GestureSurfaceTracker` (interface): interface GestureSurfaceTracker — ⚠ undocumented
411
- - `GestureSurfaceTuning` (interface): interface GestureSurfaceTuning — ⚠ undocumented
490
+ - `GestureSurfaceTracker` (interface): interface GestureSurfaceTracker { begin(x: number, y: number, nowMs: number): void; move(x: number, y: number): readonly string[]; end(x: number, y: number, nowMs: number): readonly string[]; cancel(): void; isActive(): boolean } — ⚠ undocumented
491
+ - `GestureSurfaceTuning` (interface): interface GestureSurfaceTuning { tapMoveThresholdPx: number; tapMaxMs: number; swipeMinPx: number; swipeMinVelocity: number; dragStepPx: number } — ⚠ undocumented
412
492
  - `createGestureSurfaceTracker` (function): function createGestureSurfaceTracker(bindings: TouchGestureBindings, tuning: GestureSurfaceTuning = DEFAULT_GESTURE_TUNING): GestureSurfaceTracker — ⚠ undocumented
413
493
 
494
+ ## @jgengine/core/input/haptics
495
+
496
+ - `HapticChannels` (interface): interface HapticChannels — Named continuous rumble channels for one player, mixed by priority each frame. Game code sets levels from its own telemetry (engine rpm, road surface, impacts); the shell mixes and drives the pad.
497
+ - `HapticChannelsOptions` (interface): interface HapticChannelsOptions — Options for {@link createHapticChannels}.
498
+ - `HapticChannelsSnapshot` (interface): interface HapticChannelsSnapshot — Serializable channel state.
499
+ - `HapticLevel` (interface): interface HapticLevel — Motor intensities, each `0..1`: `strong` is the low-frequency motor, `weak` the high-frequency one.
500
+ - `HapticPulse` (interface): interface HapticPulse extends HapticLevel — A decaying one-shot on a channel: starts at `strong`/`weak` and fades to zero over `ms`.
501
+ - `createHapticChannels` (function): function createHapticChannels(options: HapticChannelsOptions = {}): HapticChannels — Per-player haptic channels mixed by priority.
502
+
503
+ ## @jgengine/core/input/inputBuffer
504
+
505
+ - `BufferedAction` (interface): interface BufferedAction — One action's press history inside an {@link InputBuffer}.
506
+ - `InputBuffer` (interface): interface InputBuffer — Remembers recent action presses so a press slightly early (a jump before landing) still counts. Times are caller-supplied ms, so it runs the same on a client, a host or in a replay.
507
+ - `InputBufferSnapshot` (interface): interface InputBufferSnapshot — Serializable state for an {@link InputBuffer}.
508
+ - `createInputBuffer` (function): function createInputBuffer(options: { windowMs: number }): InputBuffer — Creates an input buffer for jump buffering, coyote time, hold duration and double taps.
509
+
414
510
  ## @jgengine/core/input/lookChannel
415
511
 
416
- - `LookChannel` (interface): interface LookChannel — ⚠ undocumented
417
- - `LookChannelOptions` (interface): interface LookChannelOptions — ⚠ undocumented
512
+ - `LookChannel` (interface): interface LookChannel { accumulate(dx: number, dy: number): void; consume(): LookDeltas; setYaw(yaw: number): void; readYaw(): number; setPitch(pitch: number): void; readPitch(): number; setVerticalOffset(offset: number): void; readVerticalOffset(): number; snap… — ⚠ undocumented
513
+ - `LookChannelOptions` (interface): interface LookChannelOptions { sensitivity: number; maxVerticalOffset?: number } — ⚠ undocumented
514
+ - `LookChannelState` (interface): interface LookChannelState — Plain JSON state of a {@link LookChannel}: the pending pixel deltas and the committed pose.
418
515
  - `LookDeltas` (interface): interface LookDeltas — Per-frame look channel shared between an event-driven capture layer (writer of raw pointer deltas) and a frame-driven controller (consumer), plus the latest committed pose for same-frame readers such as presence sync. Kept as plain mutable state on purpose: routing per-frame deltas through a reactive store would notify subscribers every frame for state no UI reads.
419
516
  - `createLookChannel` (function): function createLookChannel({ sensitivity, maxVerticalOffset = Infinity }: LookChannelOptions): LookChannel — ⚠ undocumented
420
517
 
421
518
  ## @jgengine/core/input/pointer
422
519
 
423
520
  - `DragCapture` (interface): interface DragCapture — Renderer-agnostic drag-capture / pull-vector state machine for slingshot-style aiming, drawback abilities, and similar gestures.
424
- - `DragCaptureConfig` (interface): interface DragCaptureConfig — ⚠ undocumented
521
+ - `DragCaptureConfig` (interface): interface DragCaptureConfig { maxPull?: number; grabRadius?: number } — ⚠ undocumented
425
522
  - `DragResult` (type): type DragResult = DragState — ⚠ undocumented
426
523
  - `DragState` (interface): interface DragState — Snapshot of an in-progress or just-released drag: origin/current cursor points plus the clamped pull vector.
427
524
  - `PointerButton` (type): type PointerButton = "primary" | "secondary" | "middle" — ⚠ undocumented
428
525
  - `PointerHit` (interface): interface PointerHit — Renderer-free result of a screen→world raycast. The shell's pointer service produces this from the cursor; core-side gameplay (item.use aim, click-to-move, ground-target abilities, pings) consumes it without touching three.js.
429
- - `PointerVec3` (type): type PointerVec3 = readonly [number, number, number] — ⚠ undocumented
526
+ - `PointerVec3` (type): type PointerVec3 = readonly [number, number, number] — ⚠ undocumented · used by `aimToPoint`: Build an `origin → point` aim for `item.use` / projectiles, firing toward the cursor.
430
527
  - `aimToPoint` (function): function aimToPoint(origin: PointerVec3, point: PointerVec3): Aim — Build an `origin → point` aim for `item.use` / projectiles, firing toward the cursor.
431
528
  - `createDragCapture` (function): function createDragCapture(config: DragCaptureConfig = {}): DragCapture — ⚠ undocumented
432
529
  - `groundOf` (function): function groundOf(hit: PointerHit): readonly [number, number] — Project a pointer hit onto the XZ plane for navmesh routing (`findPath` takes `[x, z]`).
@@ -435,8 +532,8 @@
435
532
  ## @jgengine/core/input/pointerAxis
436
533
 
437
534
  - `PointerAxisBinding` (interface): interface PointerAxisBinding — Maps one pointer axis onto an analog axis target — the pointer-backed counterpart of an `AxisBinding`'s key lists.
438
- - `PointerAxisState` (interface): interface PointerAxisState — ⚠ undocumented
439
- - `PointerSurfaceRect` (interface): interface PointerSurfaceRect — ⚠ undocumented
535
+ - `PointerAxisState` (interface): interface PointerAxisState { x: number; y: number; active: boolean } — ⚠ undocumented · used by `normalizePointerToAxis`: Normalize client coordinates against a surface rect into a `PointerAxisState`, clamped to `[-1, 1]` per axis.
536
+ - `PointerSurfaceRect` (interface): interface PointerSurfaceRect { left: number; top: number; width: number; height: number } — ⚠ undocumented · used by `normalizePointerToAxis`: Normalize client coordinates against a surface rect into a `PointerAxisState`, clamped to `[-1, 1]` per axis.
440
537
  - `normalizePointerToAxis` (function): function normalizePointerToAxis(clientX: number, clientY: number, rect: PointerSurfaceRect): PointerAxisState — Normalize client coordinates against a surface rect into a `PointerAxisState`, clamped to `[-1, 1]` per axis.
441
538
  - `pointerAxisValue` (function): function pointerAxisValue(binding: PointerAxisBinding, state: PointerAxisState | null | undefined): number | null — Resolve a pointer binding against the current pointer state: `null` when no pointer is active (callers fall back to their digital target), otherwise the deadzone/curve-shaped value in `[-1, 1]`.
442
539
 
@@ -460,7 +557,7 @@
460
557
  ## @jgengine/core/input/touchGestures
461
558
 
462
559
  - `TouchGestureEnd` (type): type TouchGestureEnd = "tap" | "hold-end" | null — ⚠ undocumented
463
- - `TouchGestureTracker` (interface): interface TouchGestureTracker — ⚠ undocumented
560
+ - `TouchGestureTracker` (interface): interface TouchGestureTracker { begin(x: number, y: number, nowMs: number): void; move(x: number, y: number): {dx: number; dy: number} | null; beginHold(): boolean; end(nowMs: number): TouchGestureEnd; cancel(): TouchGestureEnd; isActive(): boolean; isHolding(): boolean… — ⚠ undocumented
464
561
  - `TouchGestureTuning` (interface): interface TouchGestureTuning — Single-finger gesture disambiguation for touch look controls: one touch stream drives look-drag, tap, and long-press-hold without fighting. Movement past the tap threshold commits the gesture to "look" and cancels tap/hold; a still finger that lifts quickly is a tap; a still finger held past the long-press delay is a hold. Timestamps are injected so the state machine stays pure; the capture layer owns timers and touch identifiers.
465
562
  - `createTouchGestureTracker` (function): function createTouchGestureTracker(tuning: TouchGestureTuning): TouchGestureTracker — ⚠ undocumented
466
563
 
@@ -468,7 +565,7 @@
468
565
 
469
566
  - `DEFAULT_TOUCH_JOYSTICK_VARIANT` (const): const DEFAULT_TOUCH_JOYSTICK_VARIANT: TouchJoystickVariant — Variant used when the player has not picked one.
470
567
  - `DEFAULT_TOUCH_STYLE` (const): const DEFAULT_TOUCH_STYLE: TouchStyle — Skin used when neither the game nor the player picks one.
471
- - `DeriveTouchSchemeOptions` (interface): interface DeriveTouchSchemeOptions — ⚠ undocumented
568
+ - `DeriveTouchSchemeOptions` (interface): interface DeriveTouchSchemeOptions { reserved: ReadonlySet<string>; firstPerson: boolean; config?: TouchControlsConfig | false; mode?: string | null } — ⚠ undocumented · used by `deriveTouchScheme`: Null means "render no touch controls" — either the game opted out or there is nothing to synthesize.
472
569
  - `ResolvedTouchLayout` (interface): interface ResolvedTouchLayout — The dock zone each cluster resolved to, after applying game config over the bottom-layout defaults.
473
570
  - `TOUCH_CODE_PREFIX` (const): const TOUCH_CODE_PREFIX: "touch:" — ⚠ undocumented
474
571
  - `TOUCH_JOYSTICK_VARIANTS` (const): const TOUCH_JOYSTICK_VARIANTS: readonly TouchJoystickVariant[] — Every joystick variant id, in menu order.
@@ -476,19 +573,19 @@
476
573
  - `TOUCH_STYLES` (const): const TOUCH_STYLES: readonly TouchStyle[] — Every touch skin id, in menu order.
477
574
  - `TOUCH_STYLE_OPTIONS` (const): const TOUCH_STYLE_OPTIONS: readonly { value: TouchStyle; label: string }[] — Touch skins as `{ value, label }` rows for the Settings → Controls selector.
478
575
  - `TouchAnchor` (type): type TouchAnchor = | "bottom-left" | "bottom-center" | "bottom-right" | "left" | "right" | "top-left" | "top-center" | "top-right" — Screen zone a touch cluster or button docks to. The four corners plus the mid `left`/`right` rails (vertical stacks, MMO-style hotbars) and the `bottom-center` / `top-center` strips let controls use the whole viewport instead of piling into one bottom bar.
479
- - `TouchButton` (interface): interface TouchButton — ⚠ undocumented
576
+ - `TouchButton` (interface): interface TouchButton { action: string; label: string; icon: string | false | null; kind: TouchButtonKind; shape: TouchButtonShape; anchor: TouchAnchor | null; image: string | null } — ⚠ undocumented
480
577
  - `TouchButtonKind` (type): type TouchButtonKind = "primary" | "utility" — ⚠ undocumented
481
578
  - `TouchButtonShape` (type): type TouchButtonShape = "circle" | "square" | "pedal" | "lever" | "trigger" | "wheel" | "tab" — Physical silhouette a touch button wears. The capture layer draws each as its own shape — a `pedal` reads as a foot pedal, a `lever` as a pull handle, a `trigger` as a firing paddle — so a control looks like the thing it does instead of a labelled circle. `circle`/`square` are the neutral fallbacks.
482
- - `TouchButtonSpec` (interface): interface TouchButtonSpec — ⚠ undocumented
579
+ - `TouchButtonSpec` (interface): interface TouchButtonSpec { action: string; label?: string; icon?: string | false; kind?: TouchButtonKind; shape?: TouchButtonShape; anchor?: TouchAnchor; image?: string } — ⚠ undocumented
483
580
  - `TouchControlsConfig` (interface): interface TouchControlsConfig — Game-authored refinement of the derived touch scheme on `defineGame({ touch })` — gestures, curated buttons, hidden actions, cluster layout, skin, and per-context `modes`.
484
581
  - `TouchControlsModeConfig` (type): type TouchControlsModeConfig = Omit<TouchControlsConfig, "modes"> — One named control context's touch config — the same shape as the base config, minus nested modes.
485
- - `TouchDragBinding` (interface): interface TouchDragBinding — ⚠ undocumented
486
- - `TouchGestureBindings` (interface): interface TouchGestureBindings — ⚠ undocumented
487
- - `TouchJoystick` (interface): interface TouchJoystick — ⚠ undocumented
582
+ - `TouchDragBinding` (interface): interface TouchDragBinding { left?: string; right?: string; up?: string; down?: string; stepPx?: number } — ⚠ undocumented
583
+ - `TouchGestureBindings` (interface): interface TouchGestureBindings { tap?: string; swipeUp?: string; swipeDown?: string; swipeLeft?: string; swipeRight?: string; drag?: TouchDragBinding } — ⚠ undocumented
584
+ - `TouchJoystick` (interface): interface TouchJoystick { up: string | null; down: string | null; left: string | null; right: string | null } — ⚠ undocumented
488
585
  - `TouchJoystickVariant` (type): type TouchJoystickVariant = "fixed" | "floating" — How the movement joystick behaves — the two shipped variants, player-selectable in Settings → Controls. `fixed` is the classic always-visible stick in its dock corner; `floating` keeps the corner as an empty capture zone and spawns the stick centered under wherever the thumb lands, so the hand never has to find a fixed target mid-play.
489
586
  - `TouchLayoutConfig` (interface): interface TouchLayoutConfig — Where each touch cluster docks; unset falls back to the classic bottom layout.
490
587
  - `TouchMovementConfig` (interface): interface TouchMovementConfig — Restricts the virtual joystick to one axis — a `horizontal` zone reads as a steering control, freeing throttle/brake to become pedal buttons.
491
- - `TouchScheme` (interface): interface TouchScheme — ⚠ undocumented
588
+ - `TouchScheme` (interface): interface TouchScheme { joystick: TouchJoystick | null; buttons: readonly TouchButton[]; gestures: TouchGestureBindings | null; look: boolean; lookSensitivity: number; layout: ResolvedTouchLayout; style: TouchStyle } — ⚠ undocumented · used by `deriveTouchScheme`: Null means "render no touch controls" — either the game opted out or there is nothing to synthesize.
492
589
  - `TouchStyle` (type): type TouchStyle = "glass" | "arcade" | "mechanical" | "minimal" — Player-selectable skin for the whole touch layer. A style is a material + geometry preset (not just colours), chosen in Settings → Controls and persisted; `glass` is the translucent default, the rest are opt-in looks.
493
590
  - `deriveTouchScheme` (function): function deriveTouchScheme(input: ActionCodesMap | undefined, { reserved, firstPerson, config: rawConfig, mode }: DeriveTouchSchemeOptions): TouchScheme | null — Null means "render no touch controls" — either the game opted out or there is nothing to synthesize.
494
591
  - `touchActionLabel` (function): function touchActionLabel(action: string): string — ⚠ undocumented
@@ -499,8 +596,8 @@
499
596
 
500
597
  ## @jgengine/core/interaction/contextMenu
501
598
 
502
- - `BuildContextMenuInput` (interface): interface BuildContextMenuInput — ⚠ undocumented
503
- - `ContextMenu` (interface): interface ContextMenu — ⚠ undocumented
599
+ - `BuildContextMenuInput` (interface): interface BuildContextMenuInput { kind: ContextTargetKind; targetId: string; verbs: readonly ContextVerb[] | undefined; point?: readonly [number, number, number] } — ⚠ undocumented · used by `buildContextMenu`: Assemble a menu from a target's catalog verbs; null when the target lists none.
600
+ - `ContextMenu` (interface): interface ContextMenu { kind: ContextTargetKind; targetId: string; point?: readonly [number, number, number]; verbs: readonly ContextVerb[] } — ⚠ undocumented · used by `buildContextMenu`: Assemble a menu from a target's catalog verbs; null when the target lists none.
504
601
  - `ContextTargetKind` (type): type ContextTargetKind = "entity" | "object" — ⚠ undocumented
505
602
  - `ContextVerb` (interface): interface ContextVerb — One right-click verb: a label plus the command it dispatches (walk-then-act supported by args).
506
603
  - `buildContextMenu` (function): function buildContextMenu(input: BuildContextMenuInput): ContextMenu | null — Assemble a menu from a target's catalog verbs; null when the target lists none.
@@ -532,14 +629,14 @@
532
629
 
533
630
  ## @jgengine/core/interaction/proximityPrompt
534
631
 
535
- - `GaugePromptDisplay` (interface): interface GaugePromptDisplay — ⚠ undocumented
536
- - `KeybindPromptDisplay` (interface): interface KeybindPromptDisplay — ⚠ undocumented
537
- - `LabelPromptDisplay` (interface): interface LabelPromptDisplay — ⚠ undocumented
538
- - `PositionedPrompt` (interface): interface PositionedPrompt — ⚠ undocumented
539
- - `PromptCommand` (interface): interface PromptCommand — ⚠ undocumented
540
- - `PromptPoint` (interface): interface PromptPoint — ⚠ undocumented
541
- - `ProximityPrompt` (interface): interface ProximityPrompt — ⚠ undocumented
542
- - `ProximityPromptConfig` (interface): interface ProximityPromptConfig — ⚠ undocumented
632
+ - `GaugePromptDisplay` (interface): interface GaugePromptDisplay { kind: "gauge"; gaugeId: string } — ⚠ undocumented
633
+ - `KeybindPromptDisplay` (interface): interface KeybindPromptDisplay { kind: "keybind"; actionId: string; label?: string } — ⚠ undocumented
634
+ - `LabelPromptDisplay` (interface): interface LabelPromptDisplay { kind: "label"; text: string } — ⚠ undocumented
635
+ - `PositionedPrompt` (interface): interface PositionedPrompt { id: string; position: PromptPoint; priority?: number; prompt: ProximityPrompt } — ⚠ undocumented · used by `resolveActivePrompt`: Nearest prompt strictly within its radius wins; a higher-priority prompt in range beats any lower-priority one regardless of distance; equal…
636
+ - `PromptCommand` (interface): interface PromptCommand { name: string; input: unknown } — ⚠ undocumented
637
+ - `PromptPoint` (interface): interface PromptPoint { x: number; z: number } — ⚠ undocumented · used by `resolveActivePrompt`: Nearest prompt strictly within its radius wins; a higher-priority prompt in range beats any lower-priority one regardless of distance; equal…
638
+ - `ProximityPrompt` (interface): interface ProximityPrompt { radius: number; display: ProximityPromptDisplay; invoke: PromptCommand | null } — ⚠ undocumented
639
+ - `ProximityPromptConfig` (interface): interface ProximityPromptConfig { radius: number; display: ProximityPromptDisplay; invoke?: PromptCommand | null } — ⚠ undocumented
543
640
  - `ProximityPromptDisplay` (type): type ProximityPromptDisplay = KeybindPromptDisplay | GaugePromptDisplay | LabelPromptDisplay — ⚠ undocumented
544
641
  - `command` (function): function command(name: string, input?: unknown): PromptCommand — ⚠ undocumented
545
642
  - `gauge` (function): function gauge(gaugeId: string): GaugePromptDisplay — ⚠ undocumented
@@ -553,26 +650,36 @@
553
650
 
554
651
  ## @jgengine/core/interaction/qte
555
652
 
556
- - `QteInputEvent` (interface): interface QteInputEvent — ⚠ undocumented
557
- - `QteOutcome` (type): type QteOutcome = | { status: "success" } | { status: "fail"; atStep: string; reason: "missed-window" | "wrong-action" | "too-early" } — ⚠ undocumented
558
- - `QteStep` (interface): interface QteStep — ⚠ undocumented
653
+ - `QteInputEvent` (interface): interface QteInputEvent { action: string; at: number } — ⚠ undocumented · used by `evaluateQteSequence`: Evaluate a quick-time-event input sequence against timed hit windows.
654
+ - `QteOutcome` (type): type QteOutcome = | { status: "success" } | { status: "fail"; atStep: string; reason: "missed-window" | "wrong-action" | "too-early" } — ⚠ undocumented · used by `evaluateQteSequence`: Evaluate a quick-time-event input sequence against timed hit windows.
655
+ - `QteStep` (interface): interface QteStep { id: string; action: string; windowStart: number; windowEnd: number } — ⚠ undocumented · used by `evaluateQteSequence`: Evaluate a quick-time-event input sequence against timed hit windows.
559
656
  - `evaluateQteSequence` (function): function evaluateQteSequence(steps: readonly QteStep[], inputs: readonly QteInputEvent[]): QteOutcome — Evaluate a quick-time-event input sequence against timed hit windows.
560
657
  - `pendingQteStep` (function): function pendingQteStep(steps: readonly QteStep[], elapsedSeconds: number): QteStep | null — ⚠ undocumented
561
658
  - `qteProgress` (function): function qteProgress(steps: readonly QteStep[], elapsedSeconds: number): number — ⚠ undocumented
562
659
 
563
660
  ## @jgengine/core/interaction/skillCheck
564
661
 
565
- - `SkillCheckConfig` (interface): interface SkillCheckConfig — ⚠ undocumented
566
- - `SkillCheckResult` (interface): interface SkillCheckResult — ⚠ undocumented
567
- - `SkillCheckZone` (interface): interface SkillCheckZone — ⚠ undocumented
662
+ - `SkillCheckConfig` (interface): interface SkillCheckConfig { trackWidth: number; zone: SkillCheckZone; markerPeriod: number; window: number; zoneDriftPerSecond?: number } — ⚠ undocumented · used by `skillCheckZoneAt`: A timing-bar skill check that succeeds when the moving marker is released inside the target zone.
663
+ - `SkillCheckResult` (interface): interface SkillCheckResult { success: boolean; timedOut: boolean; markerPosition: number; zone: SkillCheckZone } — ⚠ undocumented
664
+ - `SkillCheckZone` (interface): interface SkillCheckZone { start: number; end: number } — ⚠ undocumented · used by `skillCheckZoneAt`: A timing-bar skill check that succeeds when the moving marker is released inside the target zone.
568
665
  - `evaluateSkillCheck` (function): function evaluateSkillCheck(config: SkillCheckConfig, elapsedSeconds: number): SkillCheckResult — ⚠ undocumented
569
666
  - `skillCheckMarkerPosition` (function): function skillCheckMarkerPosition(config: SkillCheckConfig, elapsedSeconds: number): number — ⚠ undocumented
570
667
  - `skillCheckZoneAt` (function): function skillCheckZoneAt(config: SkillCheckConfig, elapsedSeconds: number): SkillCheckZone — A timing-bar skill check that succeeds when the moving marker is released inside the target zone.
571
668
 
669
+ ## @jgengine/core/material/appearanceSignals
670
+
671
+ - `MaterialAppearanceBaseline` (interface): interface MaterialAppearanceBaseline — Resolved dry values from one assigned surface, after imported and instance edits. RGB is linear.
672
+ - `MaterialAppearancePolicy` (interface): interface MaterialAppearancePolicy — Local appearance only. Wind is a separate geometry/simulation input, and no field creates motion.
673
+ - `MaterialAppearanceResponse` (interface): interface MaterialAppearanceResponse — Sparse edits for one sample. Unrequested imported parameters, textures, coverage and transmission stay intact.
674
+ - `MaterialAppearanceTarget` (interface): interface MaterialAppearanceTarget — Explicit sample position; callers schedule a bounded set of visible or relevant targets.
675
+ - `MaterialWetnessPolicy` (interface): interface MaterialWetnessPolicy — Opt-in wet endpoints. Color retention multiplies linear albedo; it never replaces the authored hue.
676
+ - `resolveMaterialAppearance` (function): function resolveMaterialAppearance(sample: EnvironmentSample, baseline: MaterialAppearanceBaseline, policy: MaterialAppearancePolicy = {}): MaterialAppearanceResponse — Resolve one surface from an immutable dry baseline, avoiding cumulative drift. Wetness already includes field shelter; neither exposure nor sheltered is multiplied into it again. No accumulation or drying simulation.
677
+ - `sampleMaterialAppearance` (function): function sampleMaterialAppearance(field: Pick<EnvironmentField, "sample">, target: MaterialAppearanceTarget, baseline: MaterialAppearanceBaseline, policy: MaterialAppearancePolicy = {}): MaterialAppearanceResponse — Exactly one field sample per target; no traversal, subscription, hidden tick, or full-world scan.
678
+
572
679
  ## @jgengine/core/movement/avatarGait
573
680
 
574
681
  - `DEFAULT_GAIT_TUNING` (const): const DEFAULT_GAIT_TUNING: GaitTuning — ⚠ undocumented
575
- - `GaitTuning` (interface): interface GaitTuning — ⚠ undocumented
682
+ - `GaitTuning` (interface): interface GaitTuning { stridesPerUnit: number; bobAmplitude: number; swayAmplitude: number; fullIntensitySpeed: number } — ⚠ undocumented · used by `gaitBobOffset`: Vertical hop per footfall — two peaks per stride cycle.
576
683
  - `advanceGaitPhase` (function): function advanceGaitPhase(phase: number, speedUnitsPerSec: number, dtSec: number, tuning: GaitTuning = DEFAULT_GAIT_TUNING): number — ⚠ undocumented
577
684
  - `gaitBobOffset` (function): function gaitBobOffset(phase: number, speedUnitsPerSec: number, tuning: GaitTuning = DEFAULT_GAIT_TUNING): number — Vertical hop per footfall — two peaks per stride cycle.
578
685
  - `gaitSwayAngle` (function): function gaitSwayAngle(phase: number, speedUnitsPerSec: number, tuning: GaitTuning = DEFAULT_GAIT_TUNING): number — Signed side-to-side lean alternating with each footfall.
@@ -594,15 +701,15 @@
594
701
  - `CharacterControllerState` (interface): interface CharacterControllerState — The controller's serializable state. `position` is the feet point; the capsule stands above it.
595
702
  - `CharacterMoveInput` (interface): interface CharacterMoveInput — One step of intent for {@link CharacterController.move}.
596
703
  - `CharacterMoveResult` (interface): interface CharacterMoveResult — What one move did.
597
- - `createCharacterController` (function): function createCharacterController(initial: CharacterControllerConfig): CharacterController — Collide-and-slide capsule controller over any {@link PhysicsBackend}: horizontal slide along walls, step-up over ledges, slope limit, ceiling test, crouch with headroom check, ground snapping, and moving-platform carry read from the ground body's velocity. Pure over the backend's `shapecast`/`overlap`; the caller owns input and gravity policy.
704
+ - `createCharacterController` (function): function createCharacterController(initial: CharacterControllerConfig): CharacterController — Collide-and-slide capsule controller over any {@link PhysicsBackend}: horizontal slide along walls, step-up over ledges, slope limit, ceiling test, crouch with headroom check, ground snapping, and moving-platform carry read from the ground body's velocity. Pure over the backend's `shapecast`/`overlap`; the caller owns input and gravity policy. Invalid dimensions or control ranges throw before configuration changes.
598
705
 
599
706
  ## @jgengine/core/movement/dash
600
707
 
601
- - `DashBurst` (interface): interface DashBurst — ⚠ undocumented
602
- - `DashConfig` (interface): interface DashConfig — ⚠ undocumented
603
- - `DashDirection` (interface): interface DashDirection — ⚠ undocumented
708
+ - `DashBurst` (interface): interface DashBurst { direction: DashDirection; durationMs: number; distance: number } — ⚠ undocumented
709
+ - `DashConfig` (interface): interface DashConfig { distance: number; durationMs: number; iframes: {fromMs: number; toMs: number}; staminaCost: number; staminaMax: number; staminaRegenPerSecond: number; cooldownMs: number } — ⚠ undocumented
710
+ - `DashDirection` (interface): interface DashDirection { x: number; z: number } — ⚠ undocumented
604
711
  - `DashRejection` (type): type DashRejection = { reason: "no-stamina" | "cooldown" | "dashing" } — ⚠ undocumented
605
- - `DashState` (interface): interface DashState — ⚠ undocumented
712
+ - `DashState` (interface): interface DashState { tryDash(direction: DashDirection, nowMs: number): DashRejection | DashBurst; tick(dtSeconds: number, nowMs: number): void; stamina(): number; staminaFraction(): number; isDashing(nowMs: number): boolean; isInvulnerable(nowMs: number): boo… — ⚠ undocumented
606
713
 
607
714
  ## @jgengine/core/movement/formation
608
715
 
@@ -634,7 +741,7 @@
634
741
  - `HOVER_FLIGHT_TUNING` (const): const HOVER_FLIGHT_TUNING: FreeFlightTuning — Preset for hover/jetpack — gravity + thrust, useful for short bursts.
635
742
  - `NOCLIP_FLIGHT_TUNING` (const): const NOCLIP_FLIGHT_TUNING: FreeFlightTuning — Preset for noclip — weightless, noclips, yaw-relative with independent vertical.
636
743
  - `SPECTATOR_FLIGHT_TUNING` (const): const SPECTATOR_FLIGHT_TUNING: FreeFlightTuning — Preset for spectator — weightless, noclips, flies where the camera looks.
637
- - `advanceFreeFlight` (function): function advanceFreeFlight(state: FreeFlightState, intent: FreeFlightIntent, yaw: number, pitch: number | undefined, dt: number, tuning: FreeFlightTuning): FreeFlightStep — Advance one frame of free-flight kinematics. Horizontal uses yaw-relative strafe (A = left, D = right) — never roll/bank. Vertical is independent of pitch unless `alignWithLook` is true (spectator).
744
+ - `advanceFreeFlight` (function): function advanceFreeFlight(state: FreeFlightState, intent: FreeFlightIntent, yaw: number, pitch: number | undefined, dt: number, tuning: FreeFlightTuning, options?: Pick<MotionFrameOptions, "authoritativeStep">): FreeFlightStep — Advance one frame of free-flight kinematics. Horizontal uses yaw-relative strafe (A = left, D = right) — never roll/bank. Vertical is independent of pitch unless `alignWithLook` is true (spectator).
638
745
  - `createFreeFlightController` (function): function createFreeFlightController(initialTuning: FreeFlightTuning, options: { yaw?: number; pitch?: number; state?: FreeFlightState } = {}): FreeFlightController — Totally free flight for any actor — character, camera, drone, or debug rig. Horizontal is always yaw-relative strafe; vertical is jump/crouch. Use `alignWithLook` for a 6DOF spectator that flies where the camera looks.
639
746
  - `createFreeFlightState` (function): function createFreeFlightState(): FreeFlightState — Create an empty flight velocity state.
640
747
  - `resolveFlightStep` (function): function resolveFlightStep(position: readonly [number, number, number], stepX: number, stepY: number, stepZ: number, obstacles: readonly CollisionObstacle[], radius: number = DEFAULT_OBSTACLE_PLAYER_RADIUS): { stepX: number; stepY: number; stepZ: number } — Resolve a flight step against world solids (XZ slide + Y clamp).
@@ -643,28 +750,28 @@
643
750
 
644
751
  ## @jgengine/core/movement/glideModel
645
752
 
646
- - `GlideInput` (interface): interface GlideInput — ⚠ undocumented
753
+ - `GlideInput` (interface): interface GlideInput { yaw?: number; throttle?: number; control?: number } — ⚠ undocumented
647
754
  - `GlideModel` (interface): interface GlideModel — A kinematic glider (#282.6) — the walk-controller-compatible sibling of `physics/traversal`'s rigid-body `Glide`, with the yaw and powered-climb authority that one lacks. External flows — `physics/flowTube` corridors, `world/windZones` schedules — compose per tick through `externalVelocity`, and turbulence degrades control through the `control` scalar.
648
- - `GlideModelConfig` (interface): interface GlideModelConfig — ⚠ undocumented
649
- - `GlideStep` (interface): interface GlideStep — ⚠ undocumented
755
+ - `GlideModelConfig` (interface): interface GlideModelConfig { gravityScale?: number; gravity?: number; thrustAccel?: number; forwardSpeed?: number; yawRate?: number; climbAccel?: number; maxFallSpeed?: number; control?: number } — ⚠ undocumented · used by `createGlideModel`: Gliding/wingsuit descent control — lift, drag, and steering from a launch.
756
+ - `GlideStep` (interface): interface GlideStep { position: GlideVec3; velocity: GlideVec3; heading: number } — ⚠ undocumented
650
757
  - `GlideVec3` (type): type GlideVec3 = readonly [number, number, number] — ⚠ undocumented
651
758
  - `createGlideModel` (function): function createGlideModel(config: GlideModelConfig = {}): GlideModel — Gliding/wingsuit descent control — lift, drag, and steering from a launch.
652
759
 
653
760
  ## @jgengine/core/movement/grappleSwing
654
761
 
655
762
  - `GrappleSwing` (interface): interface GrappleSwing — A rope-swing constraint for kinematic controllers (#282.5) — the walk-controller-compatible sibling of `physics/traversal`'s rigid-body `Grapple`. Fire at an anchor, feed each frame's integrated position/velocity through `step`, and commit what comes back (via `beforeCommit` or `setPose`): positions beyond the rope clamp to its sphere and velocity loses its outward radial component, which is exactly a pendulum swing.
656
- - `GrappleSwingConfig` (interface): interface GrappleSwingConfig — ⚠ undocumented
657
- - `GrappleSwingState` (interface): interface GrappleSwingState — ⚠ undocumented
658
- - `GrappleSwingStep` (interface): interface GrappleSwingStep — ⚠ undocumented
763
+ - `GrappleSwingConfig` (interface): interface GrappleSwingConfig { reelSpeed?: number; minLength?: number; stiffness?: number; damping?: number } — ⚠ undocumented · used by `createGrappleSwing`: Grappling-hook rope swing physics with anchor, pendulum motion, and reel-in.
764
+ - `GrappleSwingState` (interface): interface GrappleSwingState { attached: boolean; anchor: SwingVec3 | null; ropeLength: number } — ⚠ undocumented
765
+ - `GrappleSwingStep` (interface): interface GrappleSwingStep { position: SwingVec3; velocity: SwingVec3 } — ⚠ undocumented
659
766
  - `SwingVec3` (type): type SwingVec3 = readonly [number, number, number] — ⚠ undocumented
660
767
  - `createGrappleSwing` (function): function createGrappleSwing(config: GrappleSwingConfig = {}): GrappleSwing — Grappling-hook rope swing physics with anchor, pendulum motion, and reel-in.
661
768
 
662
769
  ## @jgengine/core/movement/leaderTrail
663
770
 
664
771
  - `LeaderTrail` (interface): interface LeaderTrail — A live breadcrumb trail recorded from a moving leader, with followers placed by arc-length behind the head — conga lines, convoys, duckling chains, snake bodies. Call `record` with the leader's position every tick; read `followerAt(i)` for the pose `spacing * (i + 1)` behind.
665
- - `LeaderTrailConfig` (interface): interface LeaderTrailConfig — ⚠ undocumented
772
+ - `LeaderTrailConfig` (interface): interface LeaderTrailConfig { spacing: number; maxFollowers?: number; sampleDistance?: number } — ⚠ undocumented · used by `createLeaderTrail`: A trailing follower formation that chases a leader along its past path — snake/convoy trails.
666
773
  - `TrailPoint` (type): type TrailPoint = readonly [number, number, number] — ⚠ undocumented
667
- - `TrailPose` (interface): interface TrailPose — ⚠ undocumented
774
+ - `TrailPose` (interface): interface TrailPose { position: TrailPoint; heading: number } — ⚠ undocumented
668
775
  - `createLeaderTrail` (function): function createLeaderTrail(config: LeaderTrailConfig): LeaderTrail — A trailing follower formation that chases a leader along its past path — snake/convoy trails.
669
776
 
670
777
  ## @jgengine/core/movement/movementModel
@@ -677,17 +784,29 @@
677
784
  - `MovementFrameStep` (interface): interface MovementFrameStep — Horizontal step (world units) the avatar should commit this frame.
678
785
  - `MovementIntent` (interface): interface MovementIntent — Frame-independent description of what the player is asking the avatar to do. `forward`/`right` are in the range -1..1 in the avatar's local frame; the controller rotates them into world space against the camera each frame.
679
786
  - `MovementKey` (type): type MovementKey = "w" | "a" | "s" | "d" | "shift" | "control" | "c" | "space" — Movement core (pure model).
680
- - `MovementKeysState` (type): type MovementKeysState = Record<MovementKey, boolean> — ⚠ undocumented
787
+ - `MovementKeysState` (type): type MovementKeysState = Record<MovementKey, boolean> — ⚠ undocumented · used by `resolveFreeFlightIntent` (@jgengine/core/movement/freeFlight): Translate held keys + analog into a free-flight intent.
681
788
  - `MovementTuningOverrides` (interface): interface MovementTuningOverrides — Per-game overrides for the gravity/jump feel, sourced from `GameDefinition.physics`. Omitted fields fall back to {@link MOVEMENT_TUNING}.
682
789
  - `PlayerMotionState` (interface): interface PlayerMotionState — Mutable kinematic state carried between frames by the controller. Kept here so the velocity / jump / gravity integration is a pure function testable without a renderer — the controller just owns the ref.
683
790
 
791
+ ## @jgengine/core/movement/movementProbe
792
+
793
+ - `MovementProbeOptions` (interface): interface MovementProbeOptions — Scenario settings for {@link measureMovement}; every field has a default.
794
+ - `MovementProbeSubject` (interface): interface MovementProbeSubject — The walk character {@link measureMovement} drives: the same fields a game passes to `defineGame`.
795
+ - `MovementReport` (interface): interface MovementReport — Deterministic walk-feel metrics. `Infinity` means the target was never reached.
796
+ - `measureMovement` (function): function measureMovement(subject: MovementProbeSubject = {}, options: MovementProbeOptions = {}): MovementReport — Drives a walk character through fixed scenarios (standing start, release to stop, reversal, a standing jump, a tapped jump, a strafed jump) on the same integrator `stepPlayerMovement` uses, and reports the feel metrics a test can assert. Deterministic: the same subject and options always produce the same report, so a feel change shows up as a number moving. Collision, terrain and swimming are out of scope; it measures flat ground.
797
+
684
798
  ## @jgengine/core/movement/playerMovement
685
799
 
800
+ - `PlayerMovementSnapshot` (interface): interface PlayerMovementSnapshot — One player's serializable movement state: heading, facing, velocities, jump latch and controller capsule. The entity pose lives in the entity store.
801
+ - `PlayerMovementTelemetry` (interface): interface PlayerMovementTelemetry — Last completed shared movement step for one entity; a live read-only view, not a save snapshot.
686
802
  - `PlayerMovementTuning` (interface): interface PlayerMovementTuning — The resolved, per-world movement configuration {@link stepPlayerMovement} integrates against — the same inputs the shell FrameDriver used to read piecemeal, gathered into one struct so single-player and host movement run identical math.
687
803
  - `forgetPlayerMovement` (function): function forgetPlayerMovement(ctx: GameContext, userId: string): void — Drop a player's retained movement state (heading + kinematic body) — call on leave so a rejoin starts fresh instead of resuming stale velocity.
688
804
  - `playerMovementHeading` (function): function playerMovementHeading(ctx: GameContext, userId: string): number — One player's current heading (radians), integrated by {@link stepPlayerMovement} — the shell reads it back into its camera/aim yaw.
805
+ - `playerMovementTelemetry` (function): function playerMovementTelemetry(ctx: GameContext, entityId: string): Readonly<PlayerMovementTelemetry> | null — Read the entity's last shared movement result through indexed possession ownership. Returns `null` before a step, after movement restore/forget, when a commit policy replaces the motor proposal, or for an entity not currently driven by this motor. Reuses one live view; custom movers supply their own animation parameters. Flight never reports ground contact or a walking crouch.
689
806
  - `resolvePhysicsTuning` (function): function resolvePhysicsTuning(physics: PhysicsConfig | undefined): MovementTuningOverrides | undefined — Maps a game's declared `physics` onto the movement controllers' tuning. `PhysicsConfig.gravity` is a signed world acceleration (negative points down), but the controllers integrate `velocityY -= gravityAcceleration * dt` and expect a positive downward magnitude — so gravity is negated here to keep down-pointing gravity pulling down.
690
807
  - `resolvePlayerMovementTuning` (function): function resolvePlayerMovementTuning(opts: { collision?: VoxelCollisionConfig; movement?: PlayerMovementConfig; physics?: PhysicsConfig; world?: WorldFeature; }): PlayerMovementTuning — Gather a game's collision/movement/physics/world config into a {@link PlayerMovementTuning} — call once per world; both the shell and a host pass the result to {@link stepPlayerMovement}.
808
+ - `restorePlayerMovement` (function): function restorePlayerMovement(ctx: GameContext, userId: string, snapshot: PlayerMovementSnapshot): void — Put a player's movement state back to a {@link snapshotPlayerMovement} copy. Pending state survives until first spawn; a driven actor's despawn clears it.
809
+ - `snapshotPlayerMovement` (function): function snapshotPlayerMovement(ctx: GameContext, userId: string): PlayerMovementSnapshot | null — Copy one player's movement state for prediction, rollback or a save; `null` when the player has not moved yet.
691
810
  - `stepPlayerMovement` (function): function stepPlayerMovement(ctx: GameContext, userId: string, input: InputFrame, dt: number, tuning: PlayerMovementTuning, heading?: number, pitch?: number): void — Integrate one player's movement for a tick from their held-input frame and commit the pose — the single genre-agnostic controller both the shell (its local player) and a host (each connected player in `onTick`) call, so single-player and server-authoritative movement are identical. Reads the player's controlled entity, terrain, scene solids, and pending motion impulses; writes the entity pose via `setPose`. Retains heading + kinematic body per `userId` on the `ctx`. Pass `heading` to override the internally-integrated yaw (the shell owns yaw for its camera); omit it and the controller turns from the frame's `turnLeft`/`turnRight` actions. Pass `pitch` for 6DOF spectator flight when `alignWithLook` is set.
692
811
 
693
812
  ## @jgengine/core/movement/poseState
@@ -695,11 +814,11 @@
695
814
  - `AimMode` (type): type AimMode = "hip" | "ads" — ⚠ undocumented
696
815
  - `MovementPose` (type): type MovementPose = "standing" | "crouch" | "prone" | "running" — ⚠ undocumented
697
816
  - `POSE_HITBOX` (const): const POSE_HITBOX: Record<MovementPose, PoseHitbox> — ⚠ undocumented
698
- - `PoseAllowedStates` (interface): interface PoseAllowedStates — ⚠ undocumented
699
- - `PoseHitbox` (interface): interface PoseHitbox — ⚠ undocumented
700
- - `PoseRejection` (interface): interface PoseRejection — ⚠ undocumented
701
- - `PoseSnapshot` (interface): interface PoseSnapshot — ⚠ undocumented
702
- - `PoseState` (interface): interface PoseState — ⚠ undocumented
817
+ - `PoseAllowedStates` (interface): interface PoseAllowedStates { poses?: readonly MovementPose[]; aim?: readonly AimMode[] } — ⚠ undocumented · used by `createPoseState`: Stance/pose transitions — stand, crouch, prone — that change the hitbox and movement.
818
+ - `PoseHitbox` (interface): interface PoseHitbox { height: number; eyeHeight: number; speedMultiplier: number } — ⚠ undocumented
819
+ - `PoseRejection` (interface): interface PoseRejection { reason: string } — ⚠ undocumented
820
+ - `PoseSnapshot` (interface): interface PoseSnapshot { poses: Record<string, MovementPose>; aims: Record<string, AimMode> } — ⚠ undocumented
821
+ - `PoseState` (interface): interface PoseState { getPose(instanceId: string): MovementPose; setPose(instanceId: string, pose: MovementPose): PoseRejection | null; getAim(instanceId: string): AimMode; setAim(instanceId: string, mode: AimMode): PoseRejection | null; clear(instanceId: stri… — ⚠ undocumented · used by `createPoseState`: Stance/pose transitions — stand, crouch, prone — that change the hitbox and movement.
703
822
  - `createPoseState` (function): function createPoseState(resolveAllowed: (instanceId: string) => PoseAllowedStates | null | undefined): PoseState — Stance/pose transitions — stand, crouch, prone — that change the hitbox and movement.
704
823
 
705
824
  ## @jgengine/core/movement/serverStep
@@ -712,10 +831,11 @@
712
831
  - `ObstacleReachCache` (interface): interface ObstacleReachCache — Mutable reach cache; one per source, invalidated by object count.
713
832
  - `SolidObstacleSource` (interface): interface SolidObstacleSource — The slice of the scene's object store this query needs. Narrow on purpose: the scene context wires its own store here while it is still being constructed, `ctx.scene.object` satisfies it as-is, and a pure rules package with no `GameContext` to import can satisfy it from its own object list.
714
833
  - `createObstacleReachCache` (function): function createObstacleReachCache(): ObstacleReachCache — A fresh {@link ObstacleReachCache} for a caller that owns its own source.
834
+ - `obstacleFromSolid` (function): function obstacleFromSolid(solid: WorldSolid): CollisionObstacle — A {@link WorldSolid} as a {@link CollisionObstacle}. Axis-aligned solids are one box; a yawed solid is cut into strips along its long axis, each fitted with an AABB, so a turned building does not block the street beside it the way a single enclosing box would.
715
835
  - `resolveSourceWalkerStep` (function): function resolveSourceWalkerStep(source: SolidObstacleSource, cache: ObstacleReachCache, position: EntityPosition, stepX: number, stepZ: number, options: { radius?: number; stepUpHeight?: number } = {}): { stepX: number; stepZ: number } — {@link resolveWalkerStep} with no `GameContext`: gather plus slide against a bare {@link SolidObstacleSource} and a caller-owned {@link ObstacleReachCache}.
716
836
  - `resolveWalkerStep` (function): function resolveWalkerStep(ctx: GameContext, position: EntityPosition, stepX: number, stepZ: number, options: { radius?: number; stepUpHeight?: number } = {}): { stepX: number; stepZ: number } — Slide a walker's horizontal step against the same solid geometry that stops the player, returning the X/Z it may actually take this tick. A step into a wall is cut on the blocked axis and preserved on the other, exactly as {@link stepPlayerMovement} resolves the player's.
717
837
  - `slideStep` (function): function slideStep(position: EntityPosition, stepX: number, stepZ: number, obstacles: readonly CollisionObstacle[], radius = DEFAULT_OBSTACLE_PLAYER_RADIUS, stepUpHeight = 0): { stepX: number; stepZ: number } — {@link resolveWalkerStep} against an already-gathered obstacle set.
718
- - `solidObstaclesNear` (function): function solidObstaclesNear(ctx: GameContext, position: EntityPosition, reachX: number, reachZ: number, height = WALKER_HEIGHT): CollisionObstacle[] — Every blocking physical obstacle overlapping the box `position` ± `reachX`/`reachZ`, read from the scene's resolved colliders.
838
+ - `solidObstaclesNear` (function): function solidObstaclesNear(ctx: GameContext, position: EntityPosition, reachX: number, reachZ: number, height = WALKER_HEIGHT): CollisionObstacle[] — Every blocking physical obstacle overlapping the box `position` ± `reachX`/`reachZ`, read from the scene's resolved colliders and `ctx.world.solids`.
719
839
  - `sourceObstacleReach` (function): function sourceObstacleReach(source: SolidObstacleSource, cache: ObstacleReachCache): ObstacleReach — {@link solidObstacleReach} against a bare source and caller-owned cache.
720
840
  - `sourceObstaclesNear` (function): function sourceObstaclesNear(source: SolidObstacleSource, reach: ObstacleReach, position: EntityPosition, reachX: number, reachZ: number, height = WALKER_HEIGHT): CollisionObstacle[] — {@link solidObstaclesNear} against a bare source and an already-resolved reach.
721
841
 
@@ -727,67 +847,78 @@
727
847
  - `yawForward` (function): function yawForward(yaw: number): YawVectorXZ — XZ forward direction of a yaw (`rotationY`): `(sin yaw, cos yaw)`.
728
848
  - `yawRight` (function): function yawRight(yaw: number): YawVectorXZ — XZ screen-right of a yaw — `forward × up` with up = +Y: `(-cos yaw, sin yaw)`.
729
849
 
850
+ ## @jgengine/core/movement/terrainGrade
851
+
852
+ - `resolveTerrainGradeStep` (function): function resolveTerrainGradeStep(sampler: { sampleHeight(x: number, z: number): number } | ((x: number, z: number) => number), position: readonly [number, number, number], stepX: number, stepZ: number, maxClimbGrade: number): { stepX: number; stepZ: number } — Constrain an uphill heightfield step by its sampled rise/run grade. Try the full step, then X-only, then Z-only, preserving downhill travel and sliding along a traversable axis. The sampler can represent a game's terrain policy independently of the ground used for feet. This endpoint test also applies while jumping; it is not continuous terrain collision.
853
+
730
854
  ## @jgengine/core/movement/voxelController
731
855
 
732
856
  - `DEFAULT_VOXEL_DIMS` (const): const DEFAULT_VOXEL_DIMS: VoxelPlayerDims — ⚠ undocumented
733
857
  - `GroundHeightQuery` (type): type GroundHeightQuery = (x: number, z: number) => number — Samples the terrain floor height at a point; unset means flat ground plays no part in support.
734
- - `SolidQuery` (type): type SolidQuery = (x: number, y: number, z: number) => boolean — ⚠ undocumented
858
+ - `SolidQuery` (type): type SolidQuery = (x: number, y: number, z: number) => boolean — ⚠ undocumented · used by `advanceVoxelPlayer`: Advance one frame of voxel-collided player kinematics.
735
859
  - `VoxelPlayerBody` (interface): interface VoxelPlayerBody — Mutable kinematic state carried between frames by the voxel controller.
736
860
  - `VoxelPlayerDims` (interface): interface VoxelPlayerDims — Footprint + height of the player box, in world units.
737
- - `advanceVoxelPlayer` (function): function advanceVoxelPlayer(body: VoxelPlayerBody, intent: MovementIntent, forwardX: number, forwardZ: number, baseSpeed: number, rawDeltaSeconds: number, isSolid: SolidQuery, dims: VoxelPlayerDims = DEFAULT_VOXEL_DIMS, tuning?: MovementTuningOverrides, groundHeight?: GroundHeightQuery): void — Advance one frame of voxel-collided player kinematics. Mutates `body` (position, velocity, grounded) in place.
861
+ - `advanceVoxelPlayer` (function): function advanceVoxelPlayer(body: VoxelPlayerBody, intent: MovementIntent, forwardX: number, forwardZ: number, baseSpeed: number, rawDeltaSeconds: number, isSolid: SolidQuery, dims: VoxelPlayerDims = DEFAULT_VOXEL_DIMS, tuning?: MovementTuningOverrides, groundHeight?: GroundHeightQuery, options?: Pi… — Advance one frame of voxel-collided player kinematics. Mutates `body` (position, velocity, grounded) in place.
738
862
  - `createVoxelPlayerBody` (function): function createVoxelPlayerBody(x: number, y: number, z: number): VoxelPlayerBody — ⚠ undocumented
739
863
 
740
864
  ## @jgengine/core/nav/corridors
741
865
 
742
- - `CorridorEdge` (interface): interface CorridorEdge — ⚠ undocumented
866
+ - `CorridorEdge` (interface): interface CorridorEdge { points: readonly CorridorPoint[]; width: number } — ⚠ undocumented
743
867
  - `CorridorField` (interface): interface CorridorField — Organic corridor/path collision (#284.9): a distance-to-polyline width clamp over an edge graph — cave tunnels, river channels, winding roads. Grid/navmesh/voxel collision can't express a smooth ribbon; this can. Pure math over authored centerlines.
744
868
  - `CorridorPoint` (type): type CorridorPoint = readonly [number, number] — ⚠ undocumented
745
- - `CorridorSample` (interface): interface CorridorSample — ⚠ undocumented
869
+ - `CorridorSample` (interface): interface CorridorSample { distance: number; closest: CorridorPoint; edgeIndex: number } — ⚠ undocumented
746
870
  - `createCorridorField` (function): function createCorridorField(edges: readonly CorridorEdge[]): CorridorField — ⚠ undocumented
747
871
 
748
872
  ## @jgengine/core/nav/navConstrain
749
873
 
750
- - `NavConstrainEntity` (interface): interface NavConstrainEntity — ⚠ undocumented
751
- - `NavConstrainOptions` (interface): interface NavConstrainOptions — ⚠ undocumented
752
- - `NavConstrainProposed` (interface): interface NavConstrainProposed — ⚠ undocumented
874
+ - `NavConstrainEntity` (interface): interface NavConstrainEntity { position: EntityPosition } — ⚠ undocumented
875
+ - `NavConstrainOptions` (interface): interface NavConstrainOptions { y?: (point: NavPoint) => number } — ⚠ undocumented
876
+ - `NavConstrainProposed` (interface): interface NavConstrainProposed { position: EntityPosition; rotationY: number; grounded: boolean } — ⚠ undocumented
753
877
  - `constrainToNavGrid` (function): function constrainToNavGrid(grid: NavGrid, options?: NavConstrainOptions): (proposed: NavConstrainProposed, entity: NavConstrainEntity) => NavConstrainProposed | null — ⚠ undocumented
754
878
 
755
879
  ## @jgengine/core/nav/navFromEnvironment
756
880
 
757
- - `NavObstacleGrid` (interface): interface NavObstacleGrid — ⚠ undocumented
758
- - `populateNavGridFromEnvironment` (function): function populateNavGridFromEnvironment(grid: NavObstacleGrid, world: EnvironmentWorldFeature): number — Expands every structure descriptor on an environment world feature into its generated buildings and blocks their footprints on `grid`. Returns the number of buildings blocked.
881
+ - `NavObstacleGrid` (interface): interface NavObstacleGrid { blockAabb(aabb: Aabb): void } — ⚠ undocumented · used by `populateNavGridFromEnvironment`: Blocks the footprint of every solid building an environment feature's `structures` generate — the same set `ctx.world.solids` holds for it.
882
+ - `populateNavGridFromEnvironment` (function): function populateNavGridFromEnvironment(grid: NavObstacleGrid, world: EnvironmentWorldFeature): number — Blocks the footprint of every solid building an environment feature's `structures` generate — the same set `ctx.world.solids` holds for it. Returns the number of buildings blocked.
883
+ - `populateNavGridFromSolids` (function): function populateNavGridFromSolids(grid: NavObstacleGrid, solids: Pick<WorldSolids, "all">): number — Blocks the XZ footprint of every solid in `solids` (usually `ctx.world.solids`) on `grid`, so NPC paths avoid exactly what movement and physics collide with. Returns the number of solids blocked.
759
884
 
760
885
  ## @jgengine/core/nav/navGrid
761
886
 
762
- - `FindPathOptions` (interface): interface FindPathOptions — ⚠ undocumented
763
- - `NavCell` (interface): interface NavCell — ⚠ undocumented
764
- - `NavGrid` (interface): interface NavGrid — ⚠ undocumented
765
- - `NavGridConfig` (interface): interface NavGridConfig — ⚠ undocumented
766
- - `NavPoint` (type): type NavPoint = readonly [number, number] — ⚠ undocumented
887
+ - `FindPathOptions` (interface): interface FindPathOptions { smooth?: boolean; clearance?: number; stepCost?: (from: NavPoint, to: NavPoint) => number; canTraverse?: (from: NavPoint, to: NavPoint) => boolean; maxNodes?: number } — ⚠ undocumented · used by `findPath`: A* over the walkable grid.
888
+ - `FindPathResult` (type): type FindPathResult = | { status: "path"; points: NavPoint[]; visited: number } | { status: "no-path" | "budget"; visited: number } — Bounded grid search distinguishes exhausted work from a proven unreachable goal.
889
+ - `NavCell` (interface): interface NavCell { col: number; row: number } — ⚠ undocumented
890
+ - `NavGrid` (interface): interface NavGrid { readonly cols: number; readonly rows: number; readonly cellSize: number; readonly bounds: Aabb; readonly diagonal: boolean; isWalkable(col: number, row: number): boolean; setWalkable(col: number, row: number, walkable: boolean): void; blo… — ⚠ undocumented · used by `findPath`: A* over the walkable grid.
891
+ - `NavGridConfig` (interface): interface NavGridConfig { bounds: Aabb; cellSize: number; diagonal?: boolean } — ⚠ undocumented
892
+ - `NavPoint` (type): type NavPoint = readonly [number, number] — ⚠ undocumented · used by `findPath`: A* over the walkable grid.
767
893
  - `createNavGrid` (function): function createNavGrid(config: NavGridConfig): NavGrid — ⚠ undocumented
768
894
  - `findPath` (function): function findPath(grid: NavGrid, from: NavPoint, to: NavPoint, options: FindPathOptions = {}): NavPoint[] | null — A* over the walkable grid. Returns a polyline of world-space `[x, z]` waypoints from `from` to `to`, or `null` when no route exists. Blocked start/goal snap to the nearest walkable cell so a click on an obstacle still routes to its edge.
895
+ - `findPathResult` (function): function findPathResult(grid: NavGrid, from: NavPoint, to: NavPoint, options: FindPathOptions = {}): FindPathResult — Same endpoint snapping as {@link findPath}, with explicit search-budget exhaustion.
769
896
  - `slopeStepCost` (function): function slopeStepCost(field: { sampleHeight(x: number, z: number): number }, weight = DEFAULT_SLOPE_STEP_WEIGHT): (from: NavPoint, to: NavPoint) => number — `FindPathOptions.stepCost` factory that penalizes steep terrain: cost is `1 + weight * |Δheight| / horizontalDistance`, so with the default weight a 45° slope roughly doubles the step cost.
770
- - `smoothPath` (function): function smoothPath(grid: NavGrid, points: readonly NavPoint[]): NavPoint[] — Remove waypoints the mover can skip because it has clear line-of-sight past them.
897
+ - `smoothPath` (function): function smoothPath(grid: NavGrid, points: readonly NavPoint[], canTraverse?: (from: NavPoint, to: NavPoint) => boolean): NavPoint[] — Remove waypoints the mover can skip because it has clear line-of-sight past them.
771
898
 
772
899
  ## @jgengine/core/nav/navMesh
773
900
 
774
901
  - `NavMeshAdjacency` (interface): interface NavMeshAdjacency — Neighbor relationship for one navigation polygon.
775
- - `NavMeshData` (interface): interface NavMeshData — Serializable polygon navigation mesh data.
902
+ - `NavMeshData` (interface): interface NavMeshData — Serializable polygon navigation mesh data. Treat it as immutable once queried; queries cache derived geometry per object.
776
903
  - `NavMeshLink` (interface): interface NavMeshLink — Explicit traversable connection between two navigation polygons.
777
904
  - `NavMeshPath` (interface): interface NavMeshPath — Route points and polygons selected through a navigation mesh.
905
+ - `NavMeshQuery` (interface): interface NavMeshQuery — Cached, allocation-light queries over one {@link NavMeshData}.
906
+ - `NavMeshQueryOptions` (interface): interface NavMeshQueryOptions — Tunable pricing and bounds for a {@link NavMeshQuery}.
907
+ - `NavMeshQuerySnapshot` (interface): interface NavMeshQuerySnapshot — Serializable tuning of a {@link NavMeshQuery}.
778
908
  - `buildNavAdjacency` (function): function buildNavAdjacency(mesh: NavMeshData): NavMeshAdjacency[] — Build polygon adjacency from shared edges and explicit off-mesh links.
779
909
  - `closestPoint` (function): function closestPoint(mesh: NavMeshData, point: Vec3): Vec3 | null — Return the closest point on the mesh surface, or null for an empty mesh.
780
- - `findPath` (function): function findPath(mesh: NavMeshData, from: Vec3, to: Vec3): NavMeshPath | null — A* over polygon centers, followed by deterministic visibility string-pulling.
910
+ - `createNavMeshQuery` (function): function createNavMeshQuery(mesh: NavMeshData, options: NavMeshQueryOptions = {}): NavMeshQuery — Create a cached query over a nav mesh: height-aware polygon lookup through a uniform grid, binary-heap A* priced by area costs and bounded by `maxNodes`, portal-funnel path straightening, and edge-walking raycasts.
911
+ - `findPath` (function): function findPath(mesh: NavMeshData, from: Vec3, to: Vec3): NavMeshPath | null — A* over polygon portals with default area costs, straightened by a portal funnel. Use {@link createNavMeshQuery} to price areas.
781
912
  - `raycastNav` (function): function raycastNav(mesh: NavMeshData, from: Vec3, to: Vec3): boolean — True when the segment remains over walkable polygons.
782
913
 
783
914
  ## @jgengine/core/nav/pathFollow
784
915
 
785
- - `HeightSampler` (interface): interface HeightSampler — ⚠ undocumented
786
- - `PathFollowConfig` (interface): interface PathFollowConfig — ⚠ undocumented
916
+ - `HeightSampler` (interface): interface HeightSampler { sampleHeight(x: number, z: number): number } — ⚠ undocumented · used by `drapePolyline` (@jgengine/core/world): Drapes an XZ polyline onto the surface: subdivides each span to at most `spacing` and lifts every vertex to `sampleHeight + offset`, returni…
917
+ - `PathFollowConfig` (interface): interface PathFollowConfig { waypoints: readonly Waypoint[]; speed: number; loop?: boolean } — ⚠ undocumented · used by `advancePathFollow`: Advance a path-follower by `speed * dt` along its authored polyline.
787
918
  - `PathFollowProgress` (interface): interface PathFollowProgress — Read-only progress readout for inspection/debug tooling, produced by {@link pathFollowProgress}.
788
- - `PathFollowState` (interface): interface PathFollowState — ⚠ undocumented
919
+ - `PathFollowState` (interface): interface PathFollowState { position: Waypoint; target: number; heading: number; done: boolean; distanceTravelled: number } — ⚠ undocumented · used by `advancePathFollow`: Advance a path-follower by `speed * dt` along its authored polyline.
789
920
  - `PathProgress` (type): type PathProgress = | { readonly kind: "normalized"; readonly value: number } /** World-distance travelled from the first waypoint (looping paths wrap; clamped otherwise). */ | { readonly kind: "distance"; readonly value: number } /** Segment `index` (0-based) plus `fraction` `0..1` along that segme… — Semantic seek target for {@link pathFollowSeek} — the caller-facing progress vocabulary a stateful path behavior restores from, so a follower can start at a distributed phase or resume a serialized route without knowing waypoint internals. `direction` (forward heading) falls out of the resulting {@link PathFollowState.heading}, so it is an output rather than a seek input.
790
- - `Waypoint` (type): type Waypoint = readonly [number, number, number] — ⚠ undocumented
921
+ - `Waypoint` (type): type Waypoint = readonly [number, number, number] — ⚠ undocumented · used by `planSolidRoute` (@jgengine/core/nav/solidRoute): Plan an exact-endpoint local route over indexed movement boxes using the shared nav grid.
791
922
  - `advancePathFollow` (function): function advancePathFollow(config: PathFollowConfig, state: PathFollowState, dt: number): PathFollowState — Advance a path-follower by `speed * dt` along its authored polyline. Pure — returns the next state. Crosses multiple waypoints in one step, loops when configured, and reports `done` at the end of a non-looping path. No navmesh required (#52); feed it a navmesh route via `pathFromNav` for click-to-move (#51).
792
923
  - `createPathFollow` (function): function createPathFollow(config: PathFollowConfig): PathFollowState — ⚠ undocumented
793
924
  - `pathFollowProgress` (function): function pathFollowProgress(config: PathFollowConfig, state: PathFollowState): PathFollowProgress — Read a follower's current progress in every semantic form — the inverse of {@link pathFollowSeek}, for editor/debug inspection and progress HUDs. Pure and allocation-light.
@@ -797,23 +928,30 @@
797
928
 
798
929
  ## @jgengine/core/nav/railGraph
799
930
 
800
- - `RailEdge` (interface): interface RailEdge — ⚠ undocumented
931
+ - `RailEdge` (interface): interface RailEdge { id: string; from: string; to: string; via?: readonly Waypoint[] } — ⚠ undocumented
801
932
  - `RailGraph` (interface): interface RailGraph — A directed graph of track segments with junction switch-throw semantics — the topological seam `nav/navGrid`'s walkable cells can't express. Each node with several out-edges is a switch whose thrown edge decides where traffic exits; riders (`createRailRider`) run along edge polylines and take the thrown edge at every junction.
802
- - `RailGraphConfig` (interface): interface RailGraphConfig — ⚠ undocumented
803
- - `RailNode` (interface): interface RailNode — ⚠ undocumented
804
- - `RailRider` (interface): interface RailRider — ⚠ undocumented
805
- - `RailRiderConfig` (interface): interface RailRiderConfig — ⚠ undocumented
806
- - `RailRiderPose` (interface): interface RailRiderPose — ⚠ undocumented
807
- - `ResolvedRailEdge` (interface): interface ResolvedRailEdge — ⚠ undocumented
933
+ - `RailGraphConfig` (interface): interface RailGraphConfig { nodes: readonly RailNode[]; edges: readonly RailEdge[] } — ⚠ undocumented
934
+ - `RailNode` (interface): interface RailNode { id: string; at: Waypoint } — ⚠ undocumented
935
+ - `RailRider` (interface): interface RailRider { advance(dt: number): RailRiderPose; pose(): RailRiderPose; setSpeed(speed: number): void; place(edgeId: string, s?: number): void } — ⚠ undocumented
936
+ - `RailRiderConfig` (interface): interface RailRiderConfig { edgeId: string; s?: number; speed: number } — ⚠ undocumented
937
+ - `RailRiderPose` (interface): interface RailRiderPose { position: Waypoint; heading: number; edgeId: string; s: number; atDeadEnd: boolean } — ⚠ undocumented
938
+ - `ResolvedRailEdge` (interface): interface ResolvedRailEdge { id: string; from: string; to: string; points: readonly Waypoint[]; length: number } — ⚠ undocumented
808
939
  - `createRailGraph` (function): function createRailGraph(config: RailGraphConfig): RailGraph — ⚠ undocumented
809
940
  - `createRailRider` (function): function createRailRider(graph: RailGraph, config: RailRiderConfig): RailRider — ⚠ undocumented
810
941
 
942
+ ## @jgengine/core/nav/solidRoute
943
+
944
+ - `SolidRouteOptions` (interface): interface SolidRouteOptions — Caller-owned local collision query and terrain policy; no movement or perception state.
945
+ - `SolidRouteResult` (type): type SolidRouteResult = | { status: "path"; waypoints: Waypoint[]; work: SolidRouteWork } | { status: "no-path" | "budget"; work: SolidRouteWork } — Failures never carry a partial or snapped route.
946
+ - `SolidRouteWork` (interface): interface SolidRouteWork — Work counters exclude the caller's spatial-query implementation.
947
+ - `planSolidRoute` (function): function planSolidRoute(from: Waypoint, to: Waypoint, options: SolidRouteOptions): SolidRouteResult — Plan an exact-endpoint local route over indexed movement boxes using the shared nav grid. Every edge is swept at body width; bounded failure leaves retry/stop policy with the caller.
948
+
811
949
  ## @jgengine/core/nav/timetable
812
950
 
813
951
  - `RouteTimetable` (interface): interface RouteTimetable — A deterministic mover over an authored route: position is a pure function of absolute time, so a forecast preview (`poseAt(t + 30)`) and the live mover can never disagree — the seam `nav/pathFollow`'s dt-incremental state can't offer. Trains, ferries, patrol shuttles, crushers: anything on a timetable.
814
- - `RouteTimetableConfig` (interface): interface RouteTimetableConfig — ⚠ undocumented
815
- - `TimetablePose` (interface): interface TimetablePose — ⚠ undocumented
816
- - `TimetableStop` (interface): interface TimetableStop — ⚠ undocumented
952
+ - `RouteTimetableConfig` (interface): interface RouteTimetableConfig { waypoints: readonly (Waypoint | TimetableStop)[]; speed: number; loop?: boolean; dwellSeconds?: number; offsetSeconds?: number } — ⚠ undocumented
953
+ - `TimetablePose` (interface): interface TimetablePose { position: Waypoint; heading: number; segment: number; dwelling: boolean } — ⚠ undocumented
954
+ - `TimetableStop` (interface): interface TimetableStop { at: Waypoint; dwellSeconds?: number } — ⚠ undocumented
817
955
  - `createRouteTimetable` (function): function createRouteTimetable(config: RouteTimetableConfig): RouteTimetable — ⚠ undocumented
818
956
 
819
957
  ## @jgengine/core/orders/orderKinds
@@ -860,46 +998,85 @@
860
998
  - `createOrderQueue` (function): function createOrderQueue<TCtx, TPayload = unknown>(registry: OrderRegistry<TCtx>, options: OrderQueueOptions<TPayload> = {}): OrderQueue<TCtx, TPayload> — Create a per-entity order queue over a shared kind registry. The queue owns the deterministic lifecycle and preemption policy; the kinds own behavior. Nothing here is random or unbounded: id generation is injected, activation is bounded by the pending count, and a single `tick` advances at most the active order plus one activation.
861
999
  - `createOrderRegistry` (function): function createOrderRegistry<TCtx>(): OrderRegistry<TCtx> — Build an empty order-kind registry. Register the built-in compositions from `orders/orderKinds` or your own verbs, then hand it to `createOrderQueue`. One registry is shared by many per-entity queues.
862
1000
 
1001
+ ## @jgengine/core/physics/aircraftDynamics
1002
+
1003
+ - `AircraftAssistCommand` (interface): interface AircraftAssistCommand — Control commands, `-1..1`, that drive the actuators after the assists.
1004
+ - `AircraftAssistContext` (interface): interface AircraftAssistContext — What an assist sees each substep, derived from the integrator state so it replays deterministically.
1005
+ - `AircraftAssistTuning` (interface): interface AircraftAssistTuning — Flight assists. Each one moves the same actuators the pilot does, so a stronger assist still flies within the airframe's authority. Strengths are `0..1`; an axis the pilot is deflecting gets less help the further the stick is pushed, so full stick is always the pilot's.
1006
+ - `AircraftControlChannel` (interface): interface AircraftControlChannel — One control channel's actuator: how far the surfaces move and how fast.
1007
+ - `AircraftEngineTuning` (interface): interface AircraftEngineTuning — A thrust source fixed to the body.
1008
+ - `AircraftGearTuning` (interface): interface AircraftGearTuning — Wheels or skids: what the body rests on when it touches the ground.
1009
+ - `AircraftMotorTuning` (interface): interface AircraftMotorTuning — A rocket motor: thrust follows a curve over burn time, propellant leaves at a mass flow so the body gets lighter and easier to turn, and the nozzle gimbals on the pitch and yaw channels. Stage by `retune`-ing to the next stage's mass and motor.
1010
+ - `AircraftQuaternion` (type): type AircraftQuaternion = readonly [number, number, number, number] — Unit quaternion `[x, y, z, w]` taking body-frame vectors to world space.
1011
+ - `AircraftRotorTuning` (interface): interface AircraftRotorTuning — A main rotor. Collective sets blade pitch, throttle sets rotor speed, and cyclic (the pitch and roll channels) tilts the disc. Its drag torque yaws the body the other way unless a tail rotor, a second rotor or a pedal input cancels it.
1012
+ - `AircraftSurface` (interface): interface AircraftSurface — One lifting surface: a wing panel, a tailplane, a fin, a canard. Its lift acts at `at`, so the moments that pitch, roll and yaw the body come from where the surfaces sit, not from commanded rates.
1013
+ - `AircraftVector` (type): type AircraftVector = readonly [number, number, number] — Body-frame or world-frame vector, m or N. The body frame is `[left, up, forward]` about the centre of mass.
1014
+ - `RigidAircraft` (interface): interface RigidAircraft — A force-and-torque aircraft on the same tick/snapshot/retune contract as `VehicleDynamics`.
1015
+ - `RigidAircraftInput` (interface): interface RigidAircraftInput — Pilot input for one tick.
1016
+ - `RigidAircraftModifiers` (interface): interface RigidAircraftModifiers — Per-tick overrides layered over tuning: damage, icing, boost, gusts. Each scale defaults to `1`.
1017
+ - `RigidAircraftOptions` (interface): interface RigidAircraftOptions — World hooks for one aircraft instance.
1018
+ - `RigidAircraftState` (interface): interface RigidAircraftState — Serializable integrator state: everything `restore` needs to resume bit-for-bit.
1019
+ - `RigidAircraftStep` (interface): interface RigidAircraftStep — One aircraft tick: pose plus the telemetry HUDs, camera, sound and probes read.
1020
+ - `RigidAircraftTuning` (interface): interface RigidAircraftTuning — A rigid aircraft in physical units. There is no aircraft type: a jet, a glider and a paper plane differ only in these numbers. Rotation comes from surface forces acting on the inertia tensor, so loops, rolls, stalls and weathervaning are outcomes, not special cases.
1021
+ - `aircraftAttitudeQuaternion` (function): function aircraftAttitudeQuaternion(heading: number, pitch: number, bank: number): AircraftQuaternion — Quaternion from heading, nose-up pitch and right bank, rad — the inverse of a step's `heading`/`pitch`/`bank`. Matches three.js `Euler(-pitch, heading, bank, "YXZ")`.
1022
+ - `aircraftHeadingQuaternion` (function): function aircraftHeadingQuaternion(heading: number): AircraftQuaternion — Quaternion for a heading about world up, forward `[sin h, 0, cos h]`.
1023
+ - `createRigidAircraft` (function): function createRigidAircraft(initial: RigidAircraftTuning, options: RigidAircraftOptions = {}): RigidAircraft — Creates a {@link RigidAircraft}: a quaternion rigid body with a diagonal inertia tensor, pushed by lifting surfaces, fuselage drag, an engine and gravity. Controls deflect surfaces through rate-limited actuators; nothing commands a rotation rate, so a jet loops when its tail can push the nose around and a glider stalls when it runs out of speed. Fixed internal substeps make it deterministic for a given `dt` sequence.
1024
+
863
1025
  ## @jgengine/core/physics/ballisticSweep
864
1026
 
865
- - `BallisticSweep` (type): type BallisticSweep = ( origin: readonly [number, number, number], velocity: readonly [number, number, number], gravity: number, maxTime: number, ) => BallisticSweepHit | null — ⚠ undocumented
866
- - `BallisticSweepHit` (interface): interface BallisticSweepHit — ⚠ undocumented
867
- - `BallisticSweepOptions` (interface): interface BallisticSweepOptions — ⚠ undocumented
868
- - `createBallisticSweep` (function): function createBallisticSweep(world: PhysicsWorld, options: BallisticSweepOptions = {}): BallisticSweep — Marches the closed-form arc (constant gravity, straight lateral) through `world` and reports the first sample inside any live body's AABB — sleeping bodies included — refined by one bisection between the last clear sample and the hit sample. Returns `null` when the whole arc is clear.
1027
+ - `BallisticSweep` (type): type BallisticSweep = ( origin: readonly [number, number, number], velocity: readonly [number, number, number], gravity: number, maxTime: number, ) => BallisticSweepHit | null — ⚠ undocumented · used by `createBallisticSweep`: Sweeps each short chord of the closed-form arc through live body AABBs, including sleeping bodies.
1028
+ - `BallisticSweepHit` (interface): interface BallisticSweepHit { point: [number, number, number]; time: number } — ⚠ undocumented
1029
+ - `BallisticSweepOptions` (interface): interface BallisticSweepOptions { step?: number; radius?: number } — ⚠ undocumented · used by `createBallisticSweep`: Sweeps each short chord of the closed-form arc through live body AABBs, including sleeping bodies.
1030
+ - `createBallisticSweep` (function): function createBallisticSweep(world: PhysicsWorld, options: BallisticSweepOptions = {}): BallisticSweep — Sweeps each short chord of the closed-form arc through live body AABBs, including sleeping bodies. Thin cover between samples blocks; `step` controls the approximation of the curved path.
1031
+ - `sweepMovingBounds` (function): function sweepMovingBounds(from: readonly [number, number, number], to: readonly [number, number, number], targetFrom: readonly [number, number, number], targetTo: readonly [number, number, number], halfExtents: readonly [number, number, number]): number | null — Contact fraction for a point segment against a translating axis-aligned box; inflate extents for a projectile radius.
1032
+ - `sweepMovingSphere` (function): function sweepMovingSphere(from: readonly [number, number, number], to: readonly [number, number, number], targetFrom: readonly [number, number, number], targetTo: readonly [number, number, number], radius: number): number | null — Contact fraction for a point segment against a translating sphere; add the projectile radius to `radius`.
1033
+
1034
+ ## @jgengine/core/physics/boatDynamics
1035
+
1036
+ - `BoatDynamics` (interface): interface BoatDynamics — A force-based powered hull on the same tick/snapshot/retune contract as `VehicleDynamics`.
1037
+ - `BoatOptions` (interface): interface BoatOptions — World hooks for a boat instance.
1038
+ - `BoatState` (interface): interface BoatState — Serializable boat state.
1039
+ - `BoatSteering` (type): type BoatSteering = | { kind: "rudder"; /** Rudder area, m². */ area: number; /** Largest rudder angle, rad (default `0.6`). */ maxAngle?: number; /** Share of prop thrust that washes over the rudder as flow, `0..1` (default `0.5`) — steering at low speed. */ propWash?: number; } | { kind: "outboard… — How the boat turns: a rudder needs water flowing past it; an outboard swings its own thrust.
1040
+ - `BoatStep` (interface): interface BoatStep — One boat tick: pose plus the telemetry wake, spray and engine sound read.
1041
+ - `BoatTuning` (interface): interface BoatTuning — A powered hull in physical units. Drag rises toward hull speed (`√(g·L)·1.34`) and then falls away once the hull planes, the keel resists sliding sideways, and the prop's thrust fades as the boat approaches the prop's speed.
1042
+ - `createBoatDynamics` (function): function createBoatDynamics(initial: BoatTuning, options: BoatOptions = {}): BoatDynamics — Creates a {@link BoatDynamics}: throttle drives the prop, brake reverses it, steer swings the rudder or outboard. A rudder boat barely turns at rest unless the prop is washing over it; an outboard turns at rest because it swings the thrust itself. Deterministic for a given `dt` sequence.
869
1043
 
870
1044
  ## @jgengine/core/physics/buoyancy
871
1045
 
872
1046
  - `BuoyantBody` (class): class BuoyantBody — Floats a {@link PhysicsWorld} body on a CPU {@link WaterSurface}: each hull sample point pushes the body up by its submerged depth (Archimedes, coarse), with vertical and horizontal water drag so the hull settles at the waterline and rides the Gerstner waves. Passing an {@link AxisInput} drives it as a boat — throttle thrusts along the heading, steer yaws, a keel bleeds sideways slip. Call `update(dt, time, input?)` before the shared `world.step(dt)`.
873
- - `BuoyantBodyConfig` (interface): interface BuoyantBodyConfig — ⚠ undocumented
1047
+ - `BuoyantBodyConfig` (interface): interface BuoyantBodyConfig { body: number; water: WaterSurface; heading?: number; hullPoints?: readonly (readonly [number, number])[]; hullDepth?: number; buoyancy?: number; verticalDrag?: number; linearDrag?: number; engineAccel?: number; reverseAccel?: number; topS… — ⚠ undocumented
874
1048
  - `createBuoyantBody` (function): function createBuoyantBody(world: PhysicsWorld, config: BuoyantBodyConfig): BuoyantBody — ⚠ undocumented
875
1049
 
876
1050
  ## @jgengine/core/physics/carryable
877
1051
 
878
1052
  - `Carryable` (class): class Carryable — A grabbed physics object following a moving hold point through a spring constraint (the pick — a raycast — is the caller's/shell's job; core owns the constraint). Supports shared multi-owner carry (the follow point is the average of owners' hold points), an encumbrance read, and drop/throw. Reuses `PhysicsWorld.springJoint` to a world anchor moved each frame.
879
- - `CarryableConfig` (interface): interface CarryableConfig — ⚠ undocumented
1053
+ - `CarryableConfig` (interface): interface CarryableConfig { followStiffness?: number; followDamping?: number; maxForce?: number; carryCapacity?: number } — ⚠ undocumented
880
1054
  - `carrySpeedMultiplier` (function): function carrySpeedMultiplier(mass: number, carryCapacity: number, owners: number): number — Movement multiplier (1 = unhindered, →0 = crushed) for a body of `mass` carried by `owners`. Pure — the HUD/movement kit reads it to slow a laden hauler (Lethal Company) and to gate items that need 2+ people (R.E.P.O.).
881
1055
 
882
1056
  ## @jgengine/core/physics/damageZones
883
1057
 
884
1058
  - `DamageModel` (class): class DamageModel — Coarse crash-damage model (issue #86 — stages, not soft-body). Each zone accumulates contact impulse fed from G3's `onCollision` `CollisionEvent`; crossing a threshold bumps the zone to the next discrete stage (the caller swaps the visual/collider), an optional `detachStage` ejects a part as debris once, and a total-impulse `disableAt` flips the whole body to a disabled state. `absorb` returns a transition only when something actually changed, so it doubles as the event source for HUD/feed.
885
- - `DamageModelConfig` (interface): interface DamageModelConfig — ⚠ undocumented
886
- - `DamageTransition` (interface): interface DamageTransition — ⚠ undocumented
887
- - `DamageZoneDef` (interface): interface DamageZoneDef — ⚠ undocumented
888
- - `DamageZoneState` (interface): interface DamageZoneState — ⚠ undocumented
1059
+ - `DamageModelConfig` (interface): interface DamageModelConfig { zones: readonly DamageZoneDef[]; disableAt?: number; onStage?: (transition: DamageTransition) => void; onDetach?: (zoneId: string, accumulated: number) => void } — ⚠ undocumented
1060
+ - `DamageTransition` (interface): interface DamageTransition { zone: string; stage: number; previousStage: number; detached: boolean; disabled: boolean; accumulated: number; total: number } — ⚠ undocumented
1061
+ - `DamageZoneDef` (interface): interface DamageZoneDef { id: string; thresholds: readonly number[]; detachStage?: number } — ⚠ undocumented
1062
+ - `DamageZoneState` (interface): interface DamageZoneState { id: string; accumulated: number; stage: number; detached: boolean } — ⚠ undocumented
889
1063
  - `createDamageModel` (function): function createDamageModel(config: DamageModelConfig): DamageModel — ⚠ undocumented
890
1064
 
891
1065
  ## @jgengine/core/physics/drivableVehicle
892
1066
 
893
- - `DrivableVehicleOptions` (interface): interface DrivableVehicleOptions — Options for {@link tickDrivableVehicle} — ground snapping and per-tick tuning modifiers.
1067
+ - `DrivableSim` (interface): interface DrivableSim<TModifiers, TStep extends DrivableSimStep> — Any ground-vehicle sim {@link tickDrivableVehicle} can drive: `KinematicVehicle` or `VehicleDynamics`.
1068
+ - `DrivableSimStep` (interface): interface DrivableSimStep — The pose fields {@link tickDrivableVehicle} reads from a sim step; `KinematicVehicleStep` and `VehicleDynamicsStep` both carry them.
1069
+ - `DrivableVehicleOptions` (interface): interface DrivableVehicleOptions<TModifiers = KinematicVehicleModifiers> — Options for {@link tickDrivableVehicle} — ground snapping and per-tick tuning modifiers.
894
1070
  - `DrivableVehiclePose` (interface): interface DrivableVehiclePose — A `setPose`-ready patch — spread straight into `entities.setPose(vehicleId, drive.pose)`.
895
1071
  - `DrivableVehiclePosition` (type): type DrivableVehiclePosition = readonly [number, number, number] — World-space `[x, y, z]` for a drivable vehicle's resolved pose.
896
- - `DrivableVehicleStep` (interface): interface DrivableVehicleStep — {@link tickDrivableVehicle}'s result — the ready-to-apply pose patch plus the raw sim step for HUD/telemetry reads.
897
- - `tickDrivableVehicle` (function): function tickDrivableVehicle(vehicle: KinematicVehicle, dt: number, axis: AxisInput, options: DrivableVehicleOptions = {}): DrivableVehicleStep — Connects an `AxisInput` sample straight through a {@link KinematicVehicle} to a scene entity's pose for one tick (#533.1) — the throttle/steer/handbrake → sim → `setPose` loop every drivable-vehicle game hand-rolled. Ground-snaps the result when `groundHeight` is given (terrain-following cars, not just flat racetracks). Pair with `scene/vehicleSeat` for who is allowed to drive and where the camera points; this function only steps the sim and shapes the pose patch, nothing else.
1072
+ - `DrivableVehicleStep` (interface): interface DrivableVehicleStep<TStep extends DrivableSimStep = KinematicVehicleStep> — {@link tickDrivableVehicle}'s result — the ready-to-apply pose patch plus the raw sim step for HUD/telemetry reads.
1073
+ - `tickDrivableVehicle` (function): function tickDrivableVehicle<TStep extends DrivableSimStep = KinematicVehicleStep, TModifiers = KinematicVehicleModifiers>(vehicle: DrivableSim<TModifiers, TStep>, dt: number, axis: AxisInput, options: DrivableVehicleOptions<TModifiers> = {}): DrivableVehicleStep<TStep> — Connects an `AxisInput` sample straight through a ground-vehicle sim (`KinematicVehicle` or `VehicleDynamics`) to a scene entity's pose for one tick (#533.1) — the throttle/steer/handbrake → sim → `setPose` loop every drivable-vehicle game hand-rolled. Ground-snaps the result when `groundHeight` is given (terrain-following cars, not just flat racetracks). Pair with `scene/vehicleSeat` for who is allowed to drive and where the camera points; this function only steps the sim and shapes the pose patch, nothing else.
898
1074
 
899
1075
  ## @jgengine/core/physics/flightDynamics
900
1076
 
901
1077
  - `AircraftAxis` (type): type AircraftAxis = "pitch" | "roll" | "yaw" | "throttle" | "collective" | "airbrake" | "afterburner" | "vectoring" — Axes an aircraft can bind — which actions drive each flight control.
902
1078
  - `AircraftDynamics` (interface): interface AircraftDynamics — Stateful six-degree-of-freedom aircraft simulation.
1079
+ - `AircraftDynamicsState` (interface): interface AircraftDynamicsState — Serializable integrator state of an {@link AircraftDynamics}: what `restore` needs to resume bit-for-bit.
903
1080
  - `AircraftKind` (type): type AircraftKind = "fixedWing" | "rotorcraft" | "vtol" — Supported aerodynamic/propulsion families.
904
1081
  - `AircraftOptions` (interface): interface AircraftOptions — Spawn state and injectable world-field samplers for an aircraft instance.
905
1082
  - `AircraftStep` (interface): interface AircraftStep — Pose and aerodynamic telemetry returned after one flight tick.
@@ -907,28 +1084,32 @@
907
1084
  - `FlightControlInput` (interface): interface FlightControlInput — Normalized pilot inputs for one flight-simulation tick.
908
1085
  - `FlightControlRates` (interface): interface FlightControlRates — Angular authority, response, damping, and self-leveling configuration.
909
1086
  - `FlightVector` (type): type FlightVector = readonly [number, number, number] — Three-dimensional world-space vector used by the flight model.
910
- - `createAircraftDynamics` (function): function createAircraftDynamics(tuning: AircraftTuning, options: AircraftOptions = {}): AircraftDynamics — Six-degree-of-freedom arcade flight model for fixed-wing, helicopter, and VTOL aircraft.
1087
+ - `createAircraftDynamics` (function): function createAircraftDynamics(initialTuning: AircraftTuning, options: AircraftOptions = {}): AircraftDynamics — Six-degree-of-freedom arcade flight model for fixed-wing, helicopter, and VTOL aircraft.
911
1088
 
912
1089
  ## @jgengine/core/physics/flowTube
913
1090
 
914
1091
  - `FlowTube` (interface): interface FlowTube — An axial corridor of directional flow with radial core falloff and a spool scalar — fan tunnels, updraft shafts, river narrows, thruster wash. Pure math: sample `velocityAt(point, spool)` and add it to whatever integrator moves the body (walk controller drift, `BuoyantBody`, a custom sim).
915
- - `FlowTubeConfig` (interface): interface FlowTubeConfig — ⚠ undocumented
916
- - `FlowVec3` (type): type FlowVec3 = readonly [number, number, number] — ⚠ undocumented
1092
+ - `FlowTubeConfig` (interface): interface FlowTubeConfig { from: FlowVec3; to: FlowVec3; radius: number; strength: number; falloff?: number; capFalloff?: number } — ⚠ undocumented
1093
+ - `FlowVec3` (type): type FlowVec3 = readonly [number, number, number] — ⚠ undocumented · used by `combineFlowVelocity`: Sum several tubes' flow at one point — each entry carries its own spool.
917
1094
  - `combineFlowVelocity` (function): function combineFlowVelocity(tubes: readonly (FlowTube | { tube: FlowTube; spool: number })[], point: FlowVec3): FlowVec3 — Sum several tubes' flow at one point — each entry carries its own spool.
918
1095
  - `createFlowTube` (function): function createFlowTube(config: FlowTubeConfig): FlowTube — ⚠ undocumented
919
1096
 
920
1097
  ## @jgengine/core/physics/forceVolume
921
1098
 
922
- - `ForceMode` (type): type ForceMode = "impulse" | "velocity" | "accelerate" — ⚠ undocumented
1099
+ - `ForceFieldConfig` (interface): interface ForceFieldConfig — Renderer-free localized acceleration shared by physical actors and cosmetic particles.
1100
+ - `ForceMode` (type): type ForceMode = "impulse" | "velocity" | "accelerate" — ⚠ undocumented · used by `applyVolumeForce`: One tick's force math for a body outside `PhysicsWorld` — apply the returned velocity in any custom integrator.
1101
+ - `ForceVector` (type): type ForceVector = readonly [number, number, number] — World-space force position, axis or acceleration vector.
923
1102
  - `ForceVolume` (class): class ForceVolume — A trigger region that pushes bodies passing through it — boost pads (`impulse` + `once`), conveyors (`velocity`), fans/wind (`accelerate`). Call `apply` each tick; `once` mode fires only on entry by tracking membership between ticks.
924
- - `ForceVolumeConfig` (interface): interface ForceVolumeConfig — ⚠ undocumented
1103
+ - `ForceVolumeConfig` (interface): interface ForceVolumeConfig { bounds: PhysicsBounds; force: readonly [number, number, number]; mode?: ForceMode; once?: boolean } — ⚠ undocumented
925
1104
  - `PlatformCarry` (class): class PlatformCarry — Carries bodies standing on a moving platform by composing their transform with the platform's per-`step` delta — moving/rotating lifts and conveyor floors (Fall Guys, Gang Beasts). The platform is a body the game repositions each frame; riders are detected by overlap on its top face.
926
- - `PlatformCarryConfig` (interface): interface PlatformCarryConfig — ⚠ undocumented
1105
+ - `PlatformCarryConfig` (interface): interface PlatformCarryConfig { contactTolerance?: number } — ⚠ undocumented
927
1106
  - `VolumeTrigger` (interface): interface VolumeTrigger<TId> — The enter-once membership tracking from `ForceVolume`, freed from `PhysicsWorld`'s body indices (#286.8): feed any integrator's `{ id, position }` list each tick and act on the edges — apply `applyVolumeForce` on `entered` for a boost pad, on `inside` for a fan.
928
- - `VolumeTriggerConfig` (interface): interface VolumeTriggerConfig — ⚠ undocumented
929
- - `VolumeTriggerStep` (interface): interface VolumeTriggerStep<TId> — ⚠ undocumented
1107
+ - `VolumeTriggerConfig` (interface): interface VolumeTriggerConfig { bounds: PhysicsBounds } — ⚠ undocumented
1108
+ - `VolumeTriggerStep` (interface): interface VolumeTriggerStep<TId> { entered: readonly TId[]; inside: readonly TId[]; exited: readonly TId[] } — ⚠ undocumented
930
1109
  - `applyVolumeForce` (function): function applyVolumeForce(velocity: readonly [number, number, number], force: readonly [number, number, number], mode: ForceMode, dt: number): readonly [number, number, number] — One tick's force math for a body outside `PhysicsWorld` — apply the returned velocity in any custom integrator.
931
1110
  - `createVolumeTrigger` (function): function createVolumeTrigger<TId = string>(config: VolumeTriggerConfig): VolumeTrigger<TId> — ⚠ undocumented
1111
+ - `sampleForceField` (function): function sampleForceField(config: ForceFieldConfig, position: ForceVector, targetMask = 0xffffffff, out: [number, number, number] = [0, 0, 0]): [number, number, number] — Sample localized acceleration; `out` permits allocation-free integration.
1112
+ - `validateForceField` (function): function validateForceField(config: ForceFieldConfig): void — Validate force authoring once before sampling in a hot loop. Coordinates stay within the safe integer range; acceleration magnitudes stay within 1e12 units/s².
932
1113
 
933
1114
  ## @jgengine/core/physics/gravityField
934
1115
 
@@ -939,6 +1120,30 @@
939
1120
  - `pointGravity` (function): function pointGravity(options: PointGravityOptions): GravityField — Radial gravity toward one center, with optional inverse-square falloff for planetary worlds.
940
1121
  - `uniformGravity` (function): function uniformGravity(vector: GravityVector = [0, -9.81, 0]): GravityField — Constant vector gravity for ordinary worlds, space stations, and sideways-gravity levels.
941
1122
 
1123
+ ## @jgengine/core/physics/handlingProbe
1124
+
1125
+ - `AirReport` (interface): interface AirReport — Deterministic jump and air-control metrics.
1126
+ - `AirSubject` (interface): interface AirSubject — The slice of a jumping vehicle sim {@link measureAir} drives; a `VehicleDynamics` with `suspension`, `jump` and `air` fits.
1127
+ - `CourseProbeOptions` (interface): interface CourseProbeOptions — Settings for {@link measureCourse}; every field but `wheelbase` has a default.
1128
+ - `CourseReport` (interface): interface CourseReport — Deterministic driven-course metrics.
1129
+ - `CourseSubject` (interface): interface CourseSubject — A vehicle sim {@link measureCourse} can drive along a line: a {@link HandlingSubject} that also reports its pose and front-wheel angle.
1130
+ - `FlightProbeOptions` (interface): interface FlightProbeOptions — Scenario settings for {@link measureFlight}.
1131
+ - `FlightReport` (interface): interface FlightReport — Deterministic flight metrics. Fields that don't apply to the aircraft are `NaN`: turn and stall need wings, hover drift needs a rotor.
1132
+ - `FlightSpawn` (interface): interface FlightSpawn — Where {@link measureFlight} spawns each fresh aircraft: level, heading `0` (forward `+z`).
1133
+ - `HandlingProbeOptions` (interface): interface HandlingProbeOptions — Scenario settings for {@link measureHandling}; every field has a default.
1134
+ - `HandlingReport` (interface): interface HandlingReport — Deterministic feel metrics, in the units drivers and reviewers use. `Infinity` means the target was never reached.
1135
+ - `HandlingSubject` (interface): interface HandlingSubject — The slice of a vehicle sim {@link measureHandling} drives: `VehicleDynamics` and `KinematicVehicle` both fit.
1136
+ - `LeanReport` (interface): interface LeanReport — Deterministic lean metrics for a motorcycle or bicycle.
1137
+ - `LeanSubject` (interface): interface LeanSubject — The slice of a single-track sim {@link measureLean} drives; a `VehicleDynamics` with a `lean` block fits.
1138
+ - `RideReport` (interface): interface RideReport — Deterministic ride metrics for a sprung vehicle.
1139
+ - `RideSubject` (interface): interface RideSubject — The slice of a sprung vehicle sim {@link measureRide} drives; a `VehicleDynamics` with `suspension` fits.
1140
+ - `measureAir` (function): function measureAir(create: () => AirSubject, options: { dt?: number } = {}): AirReport — Jumps a fresh vehicle from rest and holds full air input on each axis, reporting jump height and timing, double-jump height, and how fast the body rotates in the air.
1141
+ - `measureCourse` (function): function measureCourse(create: () => CourseSubject, options: CourseProbeOptions): CourseReport — Drives a fresh vehicle from `create` through a steer ramp, a skidpad and a slalom with simple deterministic drivers, and reports understeer gradient, skidpad g and the fastest clean slalom.
1142
+ - `measureFlight` (function): function measureFlight(create: (spawn: FlightSpawn) => RigidAircraft, options: FlightProbeOptions = {}): FlightReport — Flies fresh aircraft from `create` through fixed scenarios with simple deterministic autopilots: a full-aileron roll, banked turns at full throttle, an idle deceleration holding altitude, a full-throttle climb at cruise speed, a throttle step from idle, and a hands-off hover. `create` gets a spawn to pass straight into `createRigidAircraft`. Deterministic, so a tuning change shows up as a number moving.
1143
+ - `measureHandling` (function): function measureHandling(create: () => HandlingSubject, options: HandlingProbeOptions = {}): HandlingReport — Drives fresh instances from `create` through fixed scenarios (launch, top speed, braking, a slow steer ramp, a step steer, a mid-corner lift-off, a handbrake pull, full throttle with full steer) and reports the feel metrics a test can assert. Deterministic: the same subject and options always produce the same report, so a tuning change shows up as a number moving, not an opinion.
1144
+ - `measureLean` (function): function measureLean(create: () => LeanSubject, options: { dt?: number; speed?: number } = {}): LeanReport — Brings a fresh bike to `speed` and holds full steer, reporting how far and how fast it leans and how much it tips the other way first.
1145
+ - `measureRide` (function): function measureRide(create: () => RideSubject, options: { dt?: number } = {}): RideReport — Drives fresh sprung vehicles from `create` through a vertical kick, a hard stop, a steady corner and a launch-and-land, and reports how the body moves: how fast it settles, how much it dives and rolls, and whether a landing bounces.
1146
+
942
1147
  ## @jgengine/core/physics/kinematicVehicle
943
1148
 
944
1149
  - `DEFAULT_REVERSE_FORCE_SCALE` (const): const DEFAULT_REVERSE_FORCE_SCALE: 0.48 — Default reverse drive force as a fraction of forward `engineForce` / `engineAccel`.
@@ -948,10 +1153,11 @@
948
1153
  - `KinematicSteeringTuning` (interface): interface KinematicSteeringTuning — Bicycle-model steering settings; all angles are radians.
949
1154
  - `KinematicVehicle` (interface): interface KinematicVehicle — The pure-kinematic arcade car every racing game hand-rolled (#282.1): steer-yaw scaled by speed, throttle/brake acceleration, and a grip-curve lateral-slip bleed — no `PhysicsWorld`, no wheels, just the drift-friendly integration the three shipped racers proved out. Games keep their flavor (drift meters, boost, off-track rules) via `surfaceFriction`/`dragAt` hooks and the returned slip.
950
1155
  - `KinematicVehicleModifiers` (interface): interface KinematicVehicleModifiers — Per-tick multipliers layered over the base tuning — the transient overrides games apply for one frame without rebuilding the vehicle: nitro/boost, a braced-plow bonus, or entering a speed zone or slow field. Each defaults to `1` (no change), so passing nothing leaves the base tuning untouched.
951
- - `KinematicVehicleOptions` (interface): interface KinematicVehicleOptions — ⚠ undocumented
952
- - `KinematicVehicleStep` (interface): interface KinematicVehicleStep — ⚠ undocumented
953
- - `KinematicVehicleTuning` (interface): interface KinematicVehicleTuning — ⚠ undocumented
954
- - `createKinematicVehicle` (function): function createKinematicVehicle(initialTuning: KinematicVehicleTuning, options: KinematicVehicleOptions = {}): KinematicVehicle — ⚠ undocumented
1156
+ - `KinematicVehicleOptions` (interface): interface KinematicVehicleOptions { position?: readonly [number, number, number]; heading?: number; surfaceFriction?: (x: number, z: number) => number; dragAt?: (x: number, z: number) => number; clampMove?: (from: readonly [number, number], to: readonly [number, number]) =>… — ⚠ undocumented · used by `createKinematicVehicle`: Arcade ground-vehicle sim (car, kart, bike) with throttle, brake, steer, drift and retunable handling.
1157
+ - `KinematicVehicleState` (interface): interface KinematicVehicleState — Serializable integrator state of a {@link KinematicVehicle}: what `restore` needs to resume bit-for-bit.
1158
+ - `KinematicVehicleStep` (interface): interface KinematicVehicleStep { position: readonly [number, number, number]; heading: number; forwardSpeed: number; lateralSpeed: number; slip: number; surface: number; gear: number; rpm: number; steerAngle: number; yawRate: number; longitudinalAcceleration: number; tra… — ⚠ undocumented · used by `tickDrivableVehicle` (@jgengine/core/physics/drivableVehicle): Connects an `AxisInput` sample straight through a ground-vehicle sim (`KinematicVehicle` or `VehicleDynamics`) to a scene entity's pose for …
1159
+ - `KinematicVehicleTuning` (interface): interface KinematicVehicleTuning { engineAccel: number; brakeAccel: number; topSpeed: number; reverseSpeed: number; turnRate: number; turnSpeedRef: number; grip?: GripCurve; gripStrength: number; handbrakeGrip: number; rollingResistance?: number; coastDeceleration?: number… — ⚠ undocumented · used by `createKinematicVehicle`: Arcade ground-vehicle sim (car, kart, bike) with throttle, brake, steer, drift and retunable handling.
1160
+ - `createKinematicVehicle` (function): function createKinematicVehicle(initialTuning: KinematicVehicleTuning, options: KinematicVehicleOptions = {}): KinematicVehicle — Arcade ground-vehicle sim (car, kart, bike) with throttle, brake, steer, drift and retunable handling.
955
1161
 
956
1162
  ## @jgengine/core/physics/physicsBackend
957
1163
 
@@ -986,21 +1192,21 @@
986
1192
  ## @jgengine/core/physics/physicsWorld
987
1193
 
988
1194
  - `AddBodyOptions` (type): type AddBodyOptions = BoxBodyOptions | SphereBodyOptions — ⚠ undocumented
989
- - `BodyCommonOptions` (interface): interface BodyCommonOptions — ⚠ undocumented
990
- - `BoxBodyOptions` (interface): interface BoxBodyOptions extends BodyCommonOptions — ⚠ undocumented
1195
+ - `BodyCommonOptions` (interface): interface BodyCommonOptions { position: readonly [number, number, number]; velocity?: readonly [number, number, number]; mass?: number; static?: boolean; asleep?: boolean } — ⚠ undocumented
1196
+ - `BoxBodyOptions` (interface): interface BoxBodyOptions extends BodyCommonOptions { shape?: "box"; halfExtents: readonly [number, number, number] } — ⚠ undocumented
991
1197
  - `CollisionEvent` (interface): interface CollisionEvent — A contact reported to `onCollision`. The object is reused each call — read/copy, never retain.
992
1198
  - `JOINT_DISTANCE` (const): const JOINT_DISTANCE: 2 — ⚠ undocumented
993
1199
  - `JOINT_FIXED` (const): const JOINT_FIXED: 0 — ⚠ undocumented
994
1200
  - `JOINT_HINGE` (const): const JOINT_HINGE: 1 — ⚠ undocumented
995
1201
  - `JOINT_SPRING` (const): const JOINT_SPRING: 3 — ⚠ undocumented
996
1202
  - `JointKind` (type): type JointKind = "hinge" | "distance" | "spring" | "fixed" — Hard bilateral: `fixed` pins the shared anchor (3-DOF), `hinge` pins only the plane perpendicular to `axis` (free along the axis — translational cylindrical), `distance` holds a fixed separation. `spring` is a soft PD drive toward `restLength`.
997
- - `JointOptions` (interface): interface JointOptions — ⚠ undocumented
1203
+ - `JointOptions` (interface): interface JointOptions { bodyA: number; bodyB?: number; anchorA?: readonly [number, number, number]; anchorB?: readonly [number, number, number]; restLength?: number; stiffness?: number; damping?: number; maxImpulse?: number; axis?: readonly [number, number, numb… — ⚠ undocumented
998
1204
  - `MAX_BROADPHASE_CELLS` (const): const MAX_BROADPHASE_CELLS: 1000000 — Cap on `nx*ny*nz` broadphase cells — guards a huge-bounds/tiny-cellSize config from hanging `step()`.
999
- - `PhysicsBounds` (interface): interface PhysicsBounds — ⚠ undocumented
1205
+ - `PhysicsBounds` (interface): interface PhysicsBounds { min: readonly [number, number, number]; max: readonly [number, number, number] } — ⚠ undocumented
1000
1206
  - `PhysicsPrecision` (type): type PhysicsPrecision = "low" | "standard" | "high" — Simulation fidelity intent: `low` (cheap — many bodies, loose stacks, forgiving sleep), `standard` (default), `high` (tight stacks, accurate joints, worth the extra solver work). Sets the solver knobs below to a matched preset; an individual knob left explicit always wins over the preset.
1001
- - `PhysicsStats` (interface): interface PhysicsStats — ⚠ undocumented
1207
+ - `PhysicsStats` (interface): interface PhysicsStats { count: number; awake: number; sleeping: number; contacts: number; pairs: number; substeps: number; stepMs: number } — ⚠ undocumented
1002
1208
  - `PhysicsWorld` (class): class PhysicsWorld — ⚠ undocumented
1003
- - `PhysicsWorldConfig` (interface): interface PhysicsWorldConfig — ⚠ undocumented
1209
+ - `PhysicsWorldConfig` (interface): interface PhysicsWorldConfig { capacity: number; bounds: PhysicsBounds; gravity?: number; precision?: PhysicsPrecision; fixedDt?: number; maxSubsteps?: number; restitution?: number; restitutionThreshold?: number; friction?: number; linearDamping?: number; sleepLinearVe… — ⚠ undocumented
1004
1210
  - `SHAPE_BOX` (const): const SHAPE_BOX: 0 — ⚠ undocumented
1005
1211
  - `SHAPE_SPHERE` (const): const SHAPE_SPHERE: 1 — ⚠ undocumented
1006
1212
  - `SphereBodyOptions` (interface): interface SphereBodyOptions extends BodyCommonOptions — The radius fills all three half-extent columns, so broadphase and bounds see the sphere's enclosing AABB.
@@ -1023,45 +1229,74 @@
1023
1229
  ## @jgengine/core/physics/ragdoll
1024
1230
 
1025
1231
  - `Ragdoll` (class): class Ragdoll — A jointed multi-body character — floppy by default, or active-ragdoll when a balance motor is configured. Bones are `PhysicsWorld` bodies; links are joints on the joint API. `balance(dt, mx, mz)` drives the root toward its target height (staying upright) and pushes it by the move vector.
1026
- - `RagdollBalanceConfig` (interface): interface RagdollBalanceConfig — ⚠ undocumented
1027
- - `RagdollBoneSpec` (interface): interface RagdollBoneSpec — ⚠ undocumented
1028
- - `RagdollConfig` (interface): interface RagdollConfig — ⚠ undocumented
1029
- - `RagdollLinkSpec` (interface): interface RagdollLinkSpec — ⚠ undocumented
1232
+ - `RagdollBalanceConfig` (interface): interface RagdollBalanceConfig { root: string; targetHeight: number; strength?: number; moveForce?: number } — ⚠ undocumented
1233
+ - `RagdollBoneSpec` (interface): interface RagdollBoneSpec { name: string; position: readonly [number, number, number]; halfExtents: readonly [number, number, number]; mass?: number } — ⚠ undocumented
1234
+ - `RagdollConfig` (interface): interface RagdollConfig { bones: RagdollBoneSpec[]; links: RagdollLinkSpec[]; balance?: RagdollBalanceConfig } — ⚠ undocumented
1235
+ - `RagdollLinkSpec` (interface): interface RagdollLinkSpec { a: string; b: string; kind?: "hinge" | "distance"; anchor?: readonly [number, number, number] } — ⚠ undocumented
1030
1236
  - `createRagdoll` (function): function createRagdoll(world: PhysicsWorld, config: RagdollConfig): Ragdoll — ⚠ undocumented
1031
1237
 
1032
1238
  ## @jgengine/core/physics/spatialGrid
1033
1239
 
1034
1240
  - `SpatialGrid` (class): class SpatialGrid — A uniform-grid broad-phase over the x/z plane, separate from the rigid-body sim, for cheap same-tick proximity across hundreds–thousands of simple movers (swarm enemies). Rebuild each tick from the caller's own position arrays, then `queryCircle` (enemies hitting the player / an AoE) or `forEachPair` (mutual separation). Both are precise: no false negatives, no false positives beyond the exact distance test.
1035
- - `SpatialGridConfig` (interface): interface SpatialGridConfig — ⚠ undocumented
1241
+ - `SpatialGridConfig` (interface): interface SpatialGridConfig { bounds: PhysicsBounds; cellSize: number; capacity: number } — ⚠ undocumented
1036
1242
 
1037
1243
  ## @jgengine/core/physics/structure
1038
1244
 
1039
- - `CollapseEvent` (interface): interface CollapseEvent — ⚠ undocumented
1040
- - `DebrisConfig` (interface): interface DebrisConfig — ⚠ undocumented
1041
- - `StructureEdgeSpec` (interface): interface StructureEdgeSpec — ⚠ undocumented
1245
+ - `CollapseEvent` (interface): interface CollapseEvent { fell: readonly string[]; collapsed: boolean } — ⚠ undocumented
1246
+ - `DebrisConfig` (interface): interface DebrisConfig { impulse?: number; origin?: readonly [number, number, number] } — ⚠ undocumented
1247
+ - `StructureEdgeSpec` (interface): interface StructureEdgeSpec { a: string; b: string; strength?: number } — ⚠ undocumented
1042
1248
  - `StructureGraph` (class): class StructureGraph — A structural-integrity graph over a building — nodes are pieces (walls, beams, floors), edges are load-bearing connections, some nodes are anchored foundations. `damage`/`damageEdge` wear pieces and connections down; when a piece shatters or an edge severs, the graph recomputes which pieces still reach an anchor and hands back every newly-disconnected piece as one `CollapseEvent`. Feed that to `toDebris` to sink the fallen pieces into a `PhysicsWorld` as rigid bodies ("The Finals" smooth destruction, Rainbow Six walls). Coarse by design: it replicates the collapse event, not per fragment.
1043
- - `StructureMaterial` (interface): interface StructureMaterial — ⚠ undocumented
1249
+ - `StructureMaterial` (interface): interface StructureMaterial { id: string; strength: number; mass?: number } — ⚠ undocumented
1044
1250
  - `StructureMaterialTable` (type): type StructureMaterialTable = Readonly<Record<string, StructureMaterial>> — ⚠ undocumented
1045
- - `StructureNodeSpec` (interface): interface StructureNodeSpec — ⚠ undocumented
1251
+ - `StructureNodeSpec` (interface): interface StructureNodeSpec { id: string; position: readonly [number, number, number]; halfExtents: readonly [number, number, number]; mass?: number; integrity?: number; material?: string; anchor?: boolean } — ⚠ undocumented
1046
1252
 
1047
1253
  ## @jgengine/core/physics/traversal
1048
1254
 
1049
1255
  - `Glide` (class): class Glide — A reduced-gravity, forward-thrust glide over a physics body — wingsuit / glider / paraglider (Enshrouded, Grounded). Call `apply(dt, steerX, steerZ)` each frame *before* `world.step`: it feeds back most of the gravity the sim is about to apply (leaving `gravityScale` of it), pushes the body along the steer vector by `thrust`, and clamps descent to `maxFallSpeed`. Stop calling it to fall normally again — no attach/detach state to leak.
1050
- - `GlideConfig` (interface): interface GlideConfig — ⚠ undocumented
1256
+ - `GlideConfig` (interface): interface GlideConfig { gravityScale?: number; thrust?: number; maxFallSpeed?: number } — ⚠ undocumented
1051
1257
  - `Grapple` (class): class Grapple — A fired-anchor rope on the joint API — grapple (reel toward a hit point), zipline (rigid cable to a far anchor you then slide/reel along), swing (rigid rope + gravity = a pendulum). `fire` attaches a `distance`/`spring` joint from the traveller body to a fixed world point; `reel` shrinks its rest length so the constraint drags the body in; `moveAnchor` re-points it (zipline glide, grapple-to- moving-target). The pick — a raycast to find the anchor — is the caller's; core owns the constraint.
1052
- - `GrappleConfig` (interface): interface GrappleConfig — ⚠ undocumented
1258
+ - `GrappleConfig` (interface): interface GrappleConfig { reelSpeed?: number; minLength?: number; maxLength?: number; elastic?: boolean; stiffness?: number; damping?: number } — ⚠ undocumented
1259
+
1260
+ ## @jgengine/core/physics/vehicleBackendLink
1261
+
1262
+ - `VehicleBackendLink` (interface): interface VehicleBackendLink — A vehicle sim's presence in a physics backend: a kinematic chassis that shoves props, and a wall-aware move clamp.
1263
+ - `VehicleBackendLinkOptions` (interface): interface VehicleBackendLinkOptions — Options for {@link createVehicleBackendLink}.
1264
+ - `VehicleLinkHit` (interface): interface VehicleLinkHit — A wall the last clamped move ran into.
1265
+ - `VehicleLinkPose` (interface): interface VehicleLinkPose — The pose fields {@link VehicleBackendLink.sync} reads; any vehicle step (`VehicleDynamicsStep`, `KinematicVehicleStep`) fits.
1266
+ - `createVehicleBackendLink` (function): function createVehicleBackendLink(backend: PhysicsBackend, options: VehicleBackendLinkOptions): VehicleBackendLink — Puts a vehicle sim (`createVehicleDynamics`, `createKinematicVehicle`) into a {@link PhysicsBackend} world without handing its handling to the rigid-body solver: the sim still owns grip and balance, while a kinematic chassis box follows it so Rapier or `PhysicsWorld` props get shoved, and `clampMove` sweeps that box so walls and parked cars stop it and it slides along them.
1053
1267
 
1054
1268
  ## @jgengine/core/physics/vehicleBody
1055
1269
 
1056
1270
  - `DEFAULT_GRIP_CURVE` (const): const DEFAULT_GRIP_CURVE: GripCurve — ⚠ undocumented
1057
- - `GripCurve` (interface): interface GripCurve — ⚠ undocumented
1271
+ - `GripCurve` (interface): interface GripCurve { points: readonly (readonly [number, number])[] } — ⚠ undocumented · used by `sampleGripCurve`: Piecewise-linear tire-grip curve: normalized lateral slip → available grip (0..1).
1058
1272
  - `VehicleBody` (class): class VehicleBody — Arcade vehicle over the {@link PhysicsWorld} rigid-body sim. The chassis is a single box body driven by an {@link AxisInput}: per-wheel suspension is a spring-damper held with G3's `springJoint` against the sampled ground, drive/brake push along the heading, and a tire-grip curve bleeds lateral velocity (cornering, and drift under handbrake). Because the chassis is a physics body it still collides — that contact feeds crash damage. Call `update(dt, input)` before the shared `world.step(dt)`.
1059
- - `VehicleBodyConfig` (interface): interface VehicleBodyConfig — ⚠ undocumented
1060
- - `WheelSpec` (interface): interface WheelSpec — ⚠ undocumented
1061
- - `WheelState` (interface): interface WheelState — ⚠ undocumented
1273
+ - `VehicleBodyConfig` (interface): interface VehicleBodyConfig { position: readonly [number, number, number]; heading?: number; chassisHalfExtents?: readonly [number, number, number]; mass?: number; wheels?: readonly WheelSpec[]; engineAccel?: number; brakeAccel?: number; topSpeed?: number; reverseSpee… — ⚠ undocumented
1274
+ - `WheelSpec` (interface): interface WheelSpec { offset: readonly [number, number, number]; radius?: number; restLength?: number; steered?: boolean; powered?: boolean; handbrakeLocks?: boolean } — ⚠ undocumented
1275
+ - `WheelState` (interface): interface WheelState { worldX: number; worldY: number; worldZ: number; grounded: boolean; compression: number; steerAngle: number } — ⚠ undocumented
1062
1276
  - `createVehicleBody` (function): function createVehicleBody(world: PhysicsWorld, config: VehicleBodyConfig): VehicleBody — ⚠ undocumented
1063
1277
  - `sampleGripCurve` (function): function sampleGripCurve(curve: GripCurve, slip: number): number — Piecewise-linear tire-grip curve: normalized lateral slip → available grip (0..1). Grip peaks near the breakaway slip then falls off as the tire slides — the shape that separates a planted corner from a drift. Points are read in ascending slip order; ends clamp.
1064
1278
 
1279
+ ## @jgengine/core/physics/vehicleDynamics
1280
+
1281
+ - `VehicleAeroTuning` (interface): interface VehicleAeroTuning — Aero and resistance. Uses air density 1.225 kg/m³.
1282
+ - `VehicleAirInput` (interface): interface VehicleAirInput — Air-control input, each `-1..1`: `pitch > 0` noses down, `yaw > 0` turns like `steer > 0`, `roll > 0` drops the left side.
1283
+ - `VehicleAirTuning` (interface): interface VehicleAirTuning — Control of the body while airborne (needs `suspension`, the only way to leave the ground).
1284
+ - `VehicleAssistTuning` (interface): interface VehicleAssistTuning — Electronic and arcade assists; each is a strength `0..1`, `0`/omitted = off.
1285
+ - `VehicleDirectDriveTuning` (interface): interface VehicleDirectDriveTuning — Single-speed drive (electric, kart, arcade ball-car): force capped by power, so top speed comes out of drag.
1286
+ - `VehicleDynamics` (interface): interface VehicleDynamics — Force-based planar car: a two-axle ("bicycle") model where yaw comes from tire forces rather than being commanded, so understeer, oversteer, lift-off rotation, trail-braking, power slides and handbrake turns emerge from the numbers instead of from special cases. Deterministic for a given `dt` sequence; ticks are split into fixed substeps. Pairs with `tickDrivableVehicle` for the entity pose and `measureHandling` for asserting feel.
1287
+ - `VehicleDynamicsModifiers` (interface): interface VehicleDynamicsModifiers — Per-tick overrides layered over tuning: boost, grip zones, damage. Each scale defaults to `1`.
1288
+ - `VehicleDynamicsOptions` (interface): interface VehicleDynamicsOptions — World hooks for one vehicle instance.
1289
+ - `VehicleDynamicsState` (interface): interface VehicleDynamicsState — Serializable integrator state: everything {@link VehicleDynamics.restore} needs to resume bit-for-bit.
1290
+ - `VehicleDynamicsStep` (interface): interface VehicleDynamicsStep — Result of one {@link VehicleDynamics.tick}: pose plus the telemetry camera, audio, haptics and HUD read.
1291
+ - `VehicleDynamicsTuning` (interface): interface VehicleDynamicsTuning — Tuning for {@link createVehicleDynamics}. Every number is a physical quantity in SI units (kg, m, N, N·m, rad, s), so a feel target maps to a knob a person can reason about: more rear grip or a lower centre of mass for stability, softer tires for forgiveness, more drive to the rear for power oversteer.
1292
+ - `VehicleGearboxTuning` (interface): interface VehicleGearboxTuning — Engine and gearbox. Drive force at the wheels = torque · curve(rpm) · gear · finalDrive · efficiency / wheelRadius.
1293
+ - `VehicleJumpTuning` (interface): interface VehicleJumpTuning — Jumps from the ground and in the air.
1294
+ - `VehicleLeanTuning` (interface): interface VehicleLeanTuning — Single-track lean for motorcycles, bicycles and scooters. Steer input asks for a lean angle; the front wheel then takes whatever angle a balanced turn at that lean needs (curvature = g·tan(lean) / v²), with a brief countersteer as the lean starts. Tire grip still limits the turn, so over-leaning on a low-grip surface slides.
1295
+ - `VehicleSteeringTuning` (interface): interface VehicleSteeringTuning — Steering rack: lock, speed-sensitive lock, rack speed, and caster self-alignment.
1296
+ - `VehicleSuspensionTuning` (interface): interface VehicleSuspensionTuning — Four sprung corners. With this block the chassis heaves, pitches and rolls on real springs, axle loads come from spring forces instead of a filtered estimate, slopes pull the car downhill, and the car can leave the ground and land. Omit it for the flat-ground model.
1297
+ - `VehicleTireTuning` (interface): interface VehicleTireTuning — One axle's tire, in physical terms. Lateral force follows a simplified Pacejka curve `μ·Fz·sin(C·atan(B·α))` whose `B`/`C` are solved from `peakSlipAngle` and `slideGrip`, so the three numbers read the way a driver feels them: how much grip, how early it peaks, how much is left sliding.
1298
+ - `createVehicleDynamics` (function): function createVehicleDynamics(initialTuning: VehicleDynamicsTuning, options: VehicleDynamicsOptions = {}): VehicleDynamics — Creates a {@link VehicleDynamics}.
1299
+
1065
1300
  ## @jgengine/core/physics/vehicleObstacles
1066
1301
 
1067
1302
  - `VehicleImpact` (interface): interface VehicleImpact — A single blocked-move event a car can react to (#1051): crash damage, screen shake, a metal-crunch cue. Reported once, when a move is first vetoed, then consumed by {@link VehicleObstacleClamp.takeImpact}.
@@ -1071,7 +1306,7 @@
1071
1306
  ## @jgengine/core/physics/worldColliders
1072
1307
 
1073
1308
  - `WorldColliderSync` (interface): interface WorldColliderSync — Handle for synchronizing authored static world collision with a physics backend.
1074
- - `syncWorldColliders` (function): function syncWorldColliders(backend: PhysicsBackend, ctx: GameContext): WorldColliderSync — Mirrors static scene-object physical colliders and the context's ground field into a physics backend.
1309
+ - `syncWorldColliders` (function): function syncWorldColliders(backend: PhysicsBackend, ctx: GameContext): WorldColliderSync — Mirrors static scene-object physical colliders, `ctx.world.solids` and the context's ground field into a physics backend.
1075
1310
 
1076
1311
  ## @jgengine/core/procedural
1077
1312
 
@@ -1086,10 +1321,10 @@
1086
1321
 
1087
1322
  ## @jgengine/core/scene/assetCatalog
1088
1323
 
1089
- - `AssetCatalog` (interface): interface AssetCatalog<TMeta extends ModelAssetRef = ModelAssetRef> — ⚠ undocumented
1324
+ - `AssetCatalog` (interface): interface AssetCatalog<TMeta extends ModelAssetRef = ModelAssetRef> { register(id: string, asset: TMeta): void; resolve(id: string): TMeta | null; has(id: string): boolean; ids(): readonly string[] } — ⚠ undocumented · used by `buildCatalog` (@jgengine/assets): Builds a game's asset catalog from the generated CC0 pack index plus singles, extras, and aliases.
1090
1325
  - `AssetRef` (type): type AssetRef = ModelAssetRef | GeneratorAssetRef — A catalog entry: a static GLB model, or a parametric generator.
1091
1326
  - `GeneratorAssetRef` (interface): interface GeneratorAssetRef — A catalog entry backed by a registered {@link registerAssetGenerator} instead of a GLB URL — a slider-driven parametric prop (bookcase, building). `defaults` seeds a fresh placement's params. Placed instances persist `{ assetId, params, seed }` in the scene and re-resolve at runtime.
1092
- - `ModelAssetRef` (interface): interface ModelAssetRef — ⚠ undocumented
1327
+ - `ModelAssetRef` (interface): interface ModelAssetRef { url: string; anchor?: "center" | "origin"; dims?: ModelDims; collisionMesh?: CollisionMeshData; space?: AssetSpace; clips?: readonly string[] } — ⚠ undocumented · used by `createHostedWorldSessionAsync` (@jgengine/core/runtime/hostedWorldSession): Build a hosted session from an asynchronous persistence backend.
1093
1328
  - `ModelDims` (interface): interface ModelDims — Measured horizontal footprint, footprint center, and lowest Y of a model in model space.
1094
1329
  - `createAssetCatalog` (function): function createAssetCatalog<TMeta extends ModelAssetRef = ModelAssetRef>(): AssetCatalog<TMeta> — ⚠ undocumented
1095
1330
 
@@ -1156,9 +1391,9 @@
1156
1391
 
1157
1392
  ## @jgengine/core/scene/autoTarget
1158
1393
 
1159
- - `AutoTargetDeps` (interface): interface AutoTargetDeps — ⚠ undocumented
1394
+ - `AutoTargetDeps` (interface): interface AutoTargetDeps { candidates: (fromId: string) => readonly string[]; distance: (fromId: string, toId: string) => number | null; strength?: (toId: string) => number; progress?: (toId: string) => number; rng?: () => number } — ⚠ undocumented
1160
1395
  - `AutoTargetPolicy` (type): type AutoTargetPolicy = | "nearest" | "farthest" | "random" | "strongest" | "weakest" | "first" | "last" — ⚠ undocumented
1161
- - `AutoTargeter` (interface): interface AutoTargeter — ⚠ undocumented
1396
+ - `AutoTargeter` (interface): interface AutoTargeter { policy(): AutoTargetPolicy; setPolicy(policy: AutoTargetPolicy): void; pick(fromId: string): string | null } — ⚠ undocumented
1162
1397
  - `createAutoTargeter` (function): function createAutoTargeter(policy: AutoTargetPolicy, deps: AutoTargetDeps): AutoTargeter — ⚠ undocumented
1163
1398
  - `selectAutoTarget` (function): function selectAutoTarget(policy: AutoTargetPolicy, fromId: string, deps: AutoTargetDeps): string | null — ⚠ undocumented
1164
1399
 
@@ -1166,26 +1401,30 @@
1166
1401
 
1167
1402
  - `BehaviorAction` (type): type BehaviorAction = DecisionAction<BehaviorActionContext> — Callback used by a registered decision graph behavior action.
1168
1403
  - `BehaviorActionContext` (interface): interface BehaviorActionContext — Context supplied to a registered decision graph action.
1404
+ - `BehaviorActionOptions` (interface): interface BehaviorActionOptions — Optional hooks for a registered behavior action set.
1169
1405
  - `BehaviorControl` (interface): interface BehaviorControl — Per-entity control surface for the behavior runtime — pause/resume/disable/enable an instance, seek it to semantic progress, serialize/restore its state, and inspect it, all keyed by stable entity id without a full-world scan. Obtain it with {@link behaviorControl}.
1170
1406
  - `BehaviorInspection` (interface): interface BehaviorInspection — Inspection readout for editor/debug tooling, from {@link BehaviorControl.inspect}/{@link BehaviorControl.list}.
1171
- - `BehaviorResumePolicy` (type): type BehaviorResumePolicy = "freeze" | "advance" — Catch-up policy applied when a paused instance resumes. `freeze` (default, deterministic) discards the time spent paused; `advance` silently fast-forwards the instance by the paused duration without emitting intermediate pose writes. Only patrol honors `advance`; wander treats it as `freeze`.
1172
- - `BehaviorSnapshot` (type): type BehaviorSnapshot = | { readonly kind: "patrol"; readonly state: PathFollowState } | { readonly kind: "wander"; readonly origin: Waypoint; readonly target: Waypoint | null } | { readonly kind: "decisionGraph"; readonly graph: ReturnType<DecisionGraphRuntime["snapshot"]>; readonly gate: InterestG… — Serializable snapshot of one behavior instance — round-trips exactly through {@link BehaviorControl.serialize}/{@link BehaviorControl.restore}.
1407
+ - `BehaviorResumePolicy` (type): type BehaviorResumePolicy = "freeze" | "advance" — Catch-up policy applied when a paused instance resumes. `freeze` (default, deterministic) discards the time spent paused; `advance` silently fast-forwards the instance by the paused duration without emitting intermediate pose writes. Only patrol honors `advance`; wander/pursue treat it as `freeze`.
1408
+ - `BehaviorSnapshot` (type): type BehaviorSnapshot = | { readonly kind: "patrol"; readonly state: PathFollowState } | { readonly kind: "wander"; readonly origin: Waypoint; readonly target: Waypoint | null } | PursueBehaviorSnapshot | { readonly kind: "decisionGraph"; readonly graph: ReturnType<DecisionGraphRuntime["snapshot"]>;… — Serializable snapshot of one behavior instance — round-trips exactly through {@link BehaviorControl.serialize}/{@link BehaviorControl.restore}.
1173
1409
  - `BehaviorStatus` (type): type BehaviorStatus = "active" | "paused" | "disabled" — Whether a behavior instance advances and writes pose (`active`), is temporarily suspended retaining state (`paused`), or is held off until re-enabled (`disabled`).
1174
- - `advanceBehaviors` (function): function advanceBehaviors(ctx: GameContext, dt: number): void — Advance every spawned entity carrying a `patrol` or `wander` {@link BehaviorDescriptor} one tick — the engine reads the descriptor, keeps the per-entity nav state itself, and poses the entity, so ambient traffic and idle NPC routes are register-once (attach the behavior at spawn) instead of a per-game per-frame `advancePathFollow` + `setPose` loop. Instances that are paused or disabled through {@link behaviorControl} retain their state and are skipped. The shell/host call this each frame; a game never does.
1410
+ - `PursueBehaviorSnapshot` (interface): interface PursueBehaviorSnapshot — Plain pursuit progress, including its original spawn home and scheduled elapsed time.
1411
+ - `advanceBehaviors` (function): function advanceBehaviors(ctx: GameContext, dt: number): void — Advance every spawned entity carrying a patrol, wander, pursuit or decision graph behavior one tick — the engine reads the descriptor, keeps the per-entity nav state itself, and poses the entity, so ambient traffic and idle NPC routes are register-once (attach the behavior at spawn) instead of a per-game per-frame `advancePathFollow` + `setPose` loop. Instances that are paused or disabled through {@link behaviorControl} retain their state and are skipped. The shell/host call this each frame; a game never does.
1175
1412
  - `behaviorControl` (function): function behaviorControl(ctx: GameContext): BehaviorControl — Obtain the per-context {@link BehaviorControl} surface for suspending, resuming, seeking, serializing, and inspecting behavior instances by entity id — the lifecycle contract games use to hand pose ownership to possession/streaming/staggering code instead of bypassing the behavior runtime.
1176
- - `registerBehaviorActions` (function): function registerBehaviorActions(id: string, actions: Record<string, BehaviorAction>): () => void — Register the named actions used by decision graph behavior descriptors.
1413
+ - `registerBehaviorActions` (function): function registerBehaviorActions(id: string, actions: Record<string, BehaviorAction>, options: BehaviorActionOptions = {}): () => void — Register the named actions used by decision graph behavior descriptors.
1177
1414
 
1178
1415
  ## @jgengine/core/scene/behaviors
1179
1416
 
1180
- - `BehaviorDescriptor` (type): type BehaviorDescriptor = | WanderBehavior | PatrolBehavior | PromptableBehavior | PlayerBehavior | DecisionGraphBehavior — ⚠ undocumented
1417
+ - `BehaviorDescriptor` (type): type BehaviorDescriptor = | WanderBehavior | PatrolBehavior | PromptableBehavior | PlayerBehavior | PursueBehavior | DecisionGraphBehavior — ⚠ undocumented
1181
1418
  - `DecisionGraphBehavior` (interface): interface DecisionGraphBehavior — Decision graph behavior descriptor using a named registered action set.
1182
- - `PatrolBehavior` (interface): interface PatrolBehavior — ⚠ undocumented
1183
- - `PlayerBehavior` (interface): interface PlayerBehavior — ⚠ undocumented
1184
- - `PromptableBehavior` (interface): interface PromptableBehavior — ⚠ undocumented
1185
- - `WanderBehavior` (interface): interface WanderBehavior — ⚠ undocumented
1419
+ - `PatrolBehavior` (interface): interface PatrolBehavior { kind: "patrol"; waypoints: readonly Waypoint[]; speed: number; loop: boolean; startProgress?: PathProgress; groundClamp?: boolean } — ⚠ undocumented
1420
+ - `PlayerBehavior` (interface): interface PlayerBehavior { kind: "player" } — ⚠ undocumented
1421
+ - `PromptableBehavior` (interface): interface PromptableBehavior { kind: "promptable"; prompt: ProximityPrompt } — ⚠ undocumented
1422
+ - `PursueBehavior` (interface): interface PursueBehavior — Scheduled chase/attack behavior; the default declares hostility toward player-role entities. GameDefinition.pursuit.eligible can further restrict every target source using the live world. Explicit targets and optional threat override nearest-player acquisition, not eligibility.
1423
+ - `WanderBehavior` (interface): interface WanderBehavior { kind: "wander"; radius: number } — ⚠ undocumented
1186
1424
  - `patrol` (function): function patrol({ waypoints, speed, loop = true, startProgress, groundClamp, }: { waypoints: readonly Waypoint[]; speed: number; loop?: boolean; startProgress?: PathProgress; groundClamp?: boolean; }): PatrolBehavior — ⚠ undocumented
1187
1425
  - `player` (function): function player(): PlayerBehavior — ⚠ undocumented
1188
1426
  - `promptable` (function): function promptable(prompt: ProximityPrompt): PromptableBehavior — ⚠ undocumented
1427
+ - `pursue` (function): function pursue(options: Omit<PursueBehavior, "kind">): PursueBehavior — Attach aggro, collision-aware chase, cooldown attack and latched return-to-spawn as data.
1189
1428
  - `talkable` (function): function talkable(dialogueId: string): PromptableBehavior — ⚠ undocumented
1190
1429
  - `wander` (function): function wander({ radius }: { radius: number }): WanderBehavior — ⚠ undocumented
1191
1430
 
@@ -1204,23 +1443,23 @@
1204
1443
 
1205
1444
  ## @jgengine/core/scene/captureCheck
1206
1445
 
1207
- - `CaptureCheckInput` (interface): interface CaptureCheckInput — ⚠ undocumented
1446
+ - `CaptureCheckInput` (interface): interface CaptureCheckInput { hpFraction: number; catchPower: number; difficulty?: number } — ⚠ undocumented
1208
1447
  - `captureChance` (function): function captureChance(input: CaptureCheckInput): number — ⚠ undocumented
1209
1448
  - `rollCapture` (function): function rollCapture(input: CaptureCheckInput, rng?: () => number): boolean — ⚠ undocumented
1210
1449
 
1211
1450
  ## @jgengine/core/scene/colliders
1212
1451
 
1213
- - `ColliderDef` (interface): interface ColliderDef — ⚠ undocumented
1452
+ - `ColliderDef` (interface): interface ColliderDef { name: string; purpose: ColliderPurpose; shape: ColliderShape; damageEligible?: boolean; blocks?: boolean } — ⚠ undocumented
1214
1453
  - `ColliderPurpose` (type): type ColliderPurpose = "physical" | "damage" — ⚠ undocumented
1215
1454
  - `ColliderShape` (type): type ColliderShape = | { kind: "sphere"; radius: number; offset?: EntityPosition } | { kind: "aabb"; halfExtents: EntityPosition; offset?: EntityPosition } | { kind: "mesh"; /** Prepared triangle mesh in model space — engine-derived from an opted-in catalog asset, never hand-authored. */ mesh: Prepa… — Collision geometry in entity-local space. `sphere` and `aabb` are analytic; `mesh` carries a prepared triangle mesh so opted-in concave models raycast their real surface while bounds and broadphase keep reading the conservative `halfExtents`.
1216
1455
  - `DEFAULT_ENTITY_BODY_HALF_EXTENTS` (const): const DEFAULT_ENTITY_BODY_HALF_EXTENTS: EntityPosition — Matches the shell's fallback actor capsule (~0.7m wide, feet at y=0 to head at ~1.8m).
1217
1456
  - `DEFAULT_ENTITY_BODY_OFFSET` (const): const DEFAULT_ENTITY_BODY_OFFSET: EntityPosition — Entity-local center of the default body hitbox (half its height above the feet).
1218
1457
  - `DEFAULT_ENTITY_HIT_RADIUS` (const): const DEFAULT_ENTITY_HIT_RADIUS: 0.5 — ⚠ undocumented
1219
1458
  - `DEFAULT_OBJECT_HALF_EXTENTS` (const): const DEFAULT_OBJECT_HALF_EXTENTS: EntityPosition — ⚠ undocumented
1220
- - `EntityColliderSet` (interface): interface EntityColliderSet — ⚠ undocumented
1459
+ - `EntityColliderSet` (interface): interface EntityColliderSet { body?: ColliderDef; hitboxes?: readonly ColliderDef[] } — ⚠ undocumented · used by `eyeHeightFromColliders` (@jgengine/core/combat): Eye height derived from a collider set: 90% of the tallest hitbox top, or the humanoid default when unknown.
1221
1460
  - `MeasuredBounds` (interface): interface MeasuredBounds — Entity-local AABB of what the renderer actually mounted for a kind — the runtime counterpart to index-measured `dims`. Reported through `ctx.scene.entity.reportBounds` / `ctx.scene.object.reportBounds` (the shell does this automatically for custom `renderEntity`/`renderObject` content and models without index dims) so collider resolution can wrap the real rendered shape instead of a fixed-size guess.
1222
1461
  - `ModelBodySource` (interface): interface ModelBodySource — The render-config subset collider fitting reads — structurally satisfied by a resolved `ModelConfig`, so the shell can hand its render config straight to the fitting math without a conversion step.
1223
- - `ResolvedCollider` (interface): interface ResolvedCollider — ⚠ undocumented
1462
+ - `ResolvedCollider` (interface): interface ResolvedCollider { name: string; purpose: ColliderPurpose; shape: ColliderShape; damageEligible: boolean; blocks: boolean } — ⚠ undocumented
1224
1463
 
1225
1464
  ## @jgengine/core/scene/collisionMesh
1226
1465
 
@@ -1259,19 +1498,20 @@
1259
1498
  ## @jgengine/core/scene/entityStore
1260
1499
 
1261
1500
  - `EntityBlackboard` (interface): interface EntityBlackboard — Per-entity scratch and cooldown timers, auto-cleared on despawn (#533.8) — the home for AI state (next-shot-at, alert level) that would otherwise be smuggled through the serializable `meta` or a hand-pruned module-level map. Keys share one namespace per entity: `arm`/`ready`/`remaining` store a deadline under a key, `get`/`set` store arbitrary scratch.
1262
- - `EntityMovement` (interface): interface EntityMovement — ⚠ undocumented
1263
- - `EntityPose` (interface): interface EntityPose — ⚠ undocumented
1264
- - `EntityPosition` (type): type EntityPosition = readonly [number, number, number] — ⚠ undocumented
1501
+ - `EntityMovement` (interface): interface EntityMovement { walkSpeed?: number; frozen?: boolean } — ⚠ undocumented
1502
+ - `EntityPose` (interface): interface EntityPose { position: SpawnPositionInput; rotationY?: number; rotationX?: number; rotationZ?: number; dt?: number } — ⚠ undocumented
1503
+ - `EntityPosition` (type): type EntityPosition = readonly [number, number, number] — ⚠ undocumented · used by `resolveSourceWalkerStep` (@jgengine/core/movement/solidObstacles): {@link resolveWalkerStep} with no `GameContext`: gather plus slide against a bare {@link SolidObstacleSource} and a caller-owned {@link Obst…
1265
1504
  - `EntityRole` (type): type EntityRole = "player" | "npc" | "prop" — ⚠ undocumented
1266
- - `EntityStore` (interface): interface EntityStore<TMeta = unknown> — ⚠ undocumented
1505
+ - `EntityStore` (interface): interface EntityStore<TMeta = unknown> — Entity state, pose writes, keyed behavior notifications and snapshot/hydration under stable instance IDs.
1506
+ - `EntityStoreOptions` (interface): interface EntityStoreOptions — Commit hook for a derived index; runs after the write and before existing subscribers. `undefined` denotes a whole-store replacement. Value overwrites keep membership false.
1267
1507
  - `EntityUpdatePatch` (type): type EntityUpdatePatch<TMeta = unknown> = Partial< Pick<SceneEntity<TMeta>, "name" | "rotationY" | "rotationX" | "rotationZ" | "role" | "movement" | "behaviors" | "hidden" | "meta"> > & { /** Accepts the same friendly shapes as `spawn`/`setPose` (#286.13). Raw patch semantics — velocity is not deriv… — ⚠ undocumented
1268
1508
  - `PoseConstraint` (type): type PoseConstraint = (frame: PoseConstraintFrame) => readonly [number, number, number] | undefined | void — Return a replacement position to constrain the step, or nothing to accept it.
1269
- - `PoseConstraintFrame` (interface): interface PoseConstraintFrame — ⚠ undocumented
1270
- - `SceneEntity` (interface): interface SceneEntity<TMeta = unknown> — ⚠ undocumented
1271
- - `SpawnOptions` (interface): interface SpawnOptions<TMeta = unknown> — ⚠ undocumented
1272
- - `SpawnPose` (interface): interface SpawnPose — ⚠ undocumented
1509
+ - `PoseConstraintFrame` (interface): interface PoseConstraintFrame { entityId: string; current: EntityPosition; next: EntityPosition; dt?: number } — ⚠ undocumented
1510
+ - `SceneEntity` (interface): interface SceneEntity<TMeta = unknown> { id: string; name: string; position: EntityPosition; rotationY: number; rotationX: number; rotationZ: number; velocity: EntityPosition; role: EntityRole; movement: EntityMovement; behaviors: readonly BehaviorDescriptor[]; hidden?: boolean;… — ⚠ undocumented · used by `entityMetaOf`: Narrow `entity.meta` with a type guard — prefer this over `entity.meta as T` so failed shapes return `null` instead of lying to the type che…
1511
+ - `SpawnOptions` (interface): interface SpawnOptions<TMeta = unknown> { id?: string; position?: SpawnPositionInput; rotationY?: number; rotationX?: number; rotationZ?: number; role?: EntityRole; movement?: EntityMovement; behaviors?: readonly BehaviorDescriptor[]; hidden?: boolean; meta?: TMeta; onExisting?: … — ⚠ undocumented
1512
+ - `SpawnPose` (interface): interface SpawnPose { position: EntityPosition; rotationY: number } — ⚠ undocumented
1273
1513
  - `SpawnPositionInput` (type): type SpawnPositionInput = EntityPosition | { x: number; y: number; z: number } — ⚠ undocumented
1274
- - `createEntityStore` (function): function createEntityStore<TMeta = unknown>(): EntityStore<TMeta> — ⚠ undocumented
1514
+ - `createEntityStore` (function): function createEntityStore<TMeta = unknown>(options: EntityStoreOptions = {}): EntityStore<TMeta> — ⚠ undocumented
1275
1515
  - `entityMetaOf` (function): function entityMetaOf<T>(entity: SceneEntity<unknown>, isMeta: (value: unknown) => value is T): T | null — Narrow `entity.meta` with a type guard — prefer this over `entity.meta as T` so failed shapes return `null` instead of lying to the type checker.
1276
1516
  - `groundSpeed` (function): function groundSpeed(entity: SceneEntity<unknown>): number — Ground speed (horizontal magnitude of velocity) in world units per second. Scale to km/h or mph in game code.
1277
1517
  - `movedWhileFrozen` (function): function movedWhileFrozen(entity: SceneEntity<unknown>, threshold = DEFAULT_FROZEN_MOVE_THRESHOLD): boolean — ⚠ undocumented
@@ -1282,13 +1522,13 @@
1282
1522
 
1283
1523
  ## @jgengine/core/scene/form
1284
1524
 
1285
- - `FormChangedEvent` (interface): interface FormChangedEvent — ⚠ undocumented
1286
- - `FormDef` (interface): interface FormDef — ⚠ undocumented
1287
- - `FormEntities` (interface): interface FormEntities — ⚠ undocumented
1288
- - `FormEvents` (interface): interface FormEvents — ⚠ undocumented
1289
- - `FormTime` (interface): interface FormTime — ⚠ undocumented
1290
- - `Forms` (interface): interface Forms — ⚠ undocumented
1291
- - `FormsDeps` (interface): interface FormsDeps — ⚠ undocumented
1525
+ - `FormChangedEvent` (interface): interface FormChangedEvent { instanceId: string; formId: string | null } — ⚠ undocumented
1526
+ - `FormDef` (interface): interface FormDef { id: string; movement?: EntityMovement; abilities?: readonly string[]; model?: string } — ⚠ undocumented
1527
+ - `FormEntities` (interface): interface FormEntities { get(id: string): SceneEntity | null; update(id: string, patch: {movement?: EntityMovement; name?: string}): boolean } — ⚠ undocumented
1528
+ - `FormEvents` (interface): interface FormEvents { emit(name: "form.changed", payload: FormChangedEvent): void } — ⚠ undocumented
1529
+ - `FormTime` (interface): interface FormTime { after(seconds: number, callback: () => void): () => void } — ⚠ undocumented
1530
+ - `Forms` (interface): interface Forms { register(defs: readonly FormDef[]): void; get(formId: string): FormDef | null; abilities(instanceId: string): readonly string[] | null; active(instanceId: string): string | null; shapeshift(instanceId: string, formId: string, durationSeco… — ⚠ undocumented
1531
+ - `FormsDeps` (interface): interface FormsDeps { entities: FormEntities; time: FormTime; events?: FormEvents } — ⚠ undocumented
1292
1532
  - `createForms` (function): function createForms(deps: FormsDeps): Forms — ⚠ undocumented
1293
1533
 
1294
1534
  ## @jgengine/core/scene/modelSockets
@@ -1302,63 +1542,64 @@
1302
1542
 
1303
1543
  - `MountController` (class): class MountController — Mount / rideable control-transfer (issue #83). Registers rideables (each with one or more seats — a control seat drives, the rest ride) and tracks who is on what. It owns no camera or physics: game code reads `cameraTarget(riderId)` to point the follow camera at the mount, and `driveTarget(riderId)` to route that rider's {@link import("../physics/vehicleBody").AxisInput}-driven input at the mount's movement kit — the same seam a horse, a truck, or a shared multi-seat ship all plug into.
1304
1544
  - `MountKind` (type): type MountKind = "ground" | "boat" | "flying" | "swimming" — ⚠ undocumented
1305
- - `MountKit` (interface): interface MountKit — ⚠ undocumented
1545
+ - `MountKit` (interface): interface MountKit { kind: MountKind; moveSpeed?: number; turnSpeed?: number } — ⚠ undocumented
1306
1546
  - `MountResult` (type): type MountResult = { ok: true; seat: MountSeat } | { ok: false; reason: string } — ⚠ undocumented
1307
- - `MountSeat` (interface): interface MountSeat — ⚠ undocumented
1308
- - `Occupant` (interface): interface Occupant — ⚠ undocumented
1309
- - `RideableConfig` (interface): interface RideableConfig — ⚠ undocumented
1310
- - `SeatRef` (interface): interface SeatRef — ⚠ undocumented
1547
+ - `MountSeat` (interface): interface MountSeat { id: string; offset: readonly [number, number, number]; control?: boolean } — ⚠ undocumented
1548
+ - `MountedRider` (interface): interface MountedRider extends SeatRef — Occupancy only; register the world's rideables before restoring.
1549
+ - `Occupant` (interface): interface Occupant { riderId: string; seatId: string } — ⚠ undocumented
1550
+ - `RideableConfig` (interface): interface RideableConfig { id: string; kit: MountKit; seats?: readonly MountSeat[] } — ⚠ undocumented
1551
+ - `SeatRef` (interface): interface SeatRef { mountId: string; seatId: string } — ⚠ undocumented
1311
1552
  - `createMountController` (function): function createMountController(): MountController — ⚠ undocumented
1312
1553
 
1313
1554
  ## @jgengine/core/scene/movementSpeed
1314
1555
 
1315
- - `MovementSpeedConfig` (interface): interface MovementSpeedConfig — ⚠ undocumented
1316
- - `MovementSpeedDeps` (interface): interface MovementSpeedDeps — ⚠ undocumented
1317
- - `MovementSpeedModifiers` (interface): interface MovementSpeedModifiers — ⚠ undocumented
1556
+ - `MovementSpeedConfig` (interface): interface MovementSpeedConfig { baseSpeed: number; multiplierStat?: string; flatBonusStat?: string } — ⚠ undocumented · used by `applyStatDrivenSpeed`: Recomputes `movement.walkSpeed` for an entity from its stats and writes it back via `deps.entities.update`.
1557
+ - `MovementSpeedDeps` (interface): interface MovementSpeedDeps { stats: {get(instanceId: string, statId: string): StatValue | null}; entities: {get(id: string): {movement?: {walkSpeed?: number}} | null; update(id: string, patch: {movement: {walkSpeed: number}}): boolean;} } — ⚠ undocumented · used by `applyStatDrivenSpeed`: Recomputes `movement.walkSpeed` for an entity from its stats and writes it back via `deps.entities.update`.
1558
+ - `MovementSpeedModifiers` (interface): interface MovementSpeedModifiers { multiplier?: number; flatBonus?: number } — ⚠ undocumented · used by `deriveWalkSpeed`: `base * (multiplier ?? 1) + (flatBonus ?? 0)`, clamped to never go negative.
1318
1559
  - `applyStatDrivenSpeed` (function): function applyStatDrivenSpeed(deps: MovementSpeedDeps, instanceId: string, config: MovementSpeedConfig): boolean — Recomputes `movement.walkSpeed` for an entity from its stats and writes it back via `deps.entities.update`. Either stat id is optional; an omitted or unresolved stat contributes its neutral value (multiplier 1, flat bonus 0) rather than failing. Returns false only when the entity itself doesn't exist.
1319
1560
  - `deriveWalkSpeed` (function): function deriveWalkSpeed(base: number, modifiers: MovementSpeedModifiers): number — `base * (multiplier ?? 1) + (flatBonus ?? 0)`, clamped to never go negative.
1320
1561
 
1321
1562
  ## @jgengine/core/scene/objectQuery
1322
1563
 
1323
1564
  - `ObjectQueryBroadphase` (interface): interface ObjectQueryBroadphase — Optional broadphase for large object sets — typically `ObjectStore.inBox`.
1324
- - `ObjectRaycastHit` (interface): interface ObjectRaycastHit — ⚠ undocumented
1325
- - `ObjectRaycastInput` (interface): interface ObjectRaycastInput — ⚠ undocumented
1565
+ - `ObjectRaycastHit` (interface): interface ObjectRaycastHit { instanceId: string; catalogId: string; distance: number; point: EntityPosition; normal: EntityPosition } — ⚠ undocumented
1566
+ - `ObjectRaycastInput` (interface): interface ObjectRaycastInput { origin: EntityPosition; direction: EntityPosition; maxDistance: number; halfExtents?: EntityPosition; filter?: (object: SceneObject) => boolean } — ⚠ undocumented
1326
1567
  - `ObjectRaycastSource` (type): type ObjectRaycastSource = readonly SceneObject[] | ObjectQueryBroadphase — ⚠ undocumented
1327
1568
  - `intersectAabb` (function): function intersectAabb(origin: EntityPosition, direction: EntityPosition, min: EntityPosition, max: EntityPosition, maxDistance: number): { distance: number; normal: EntityPosition } | null — ⚠ undocumented
1328
1569
  - `normalizeDirection` (function): function normalizeDirection(direction: EntityPosition): EntityPosition — ⚠ undocumented
1329
1570
 
1330
1571
  ## @jgengine/core/scene/objectStore
1331
1572
 
1332
- - `ObjectListFilter` (interface): interface ObjectListFilter — ⚠ undocumented
1333
- - `ObjectStore` (interface): interface ObjectStore — ⚠ undocumented
1334
- - `ObjectVisual` (interface): interface ObjectVisual — ⚠ undocumented
1335
- - `PlaceOptions` (interface): interface PlaceOptions — ⚠ undocumented
1336
- - `SceneObject` (interface): interface SceneObject — ⚠ undocumented
1573
+ - `ObjectListFilter` (interface): interface ObjectListFilter { parentSpace?: string } — ⚠ undocumented
1574
+ - `ObjectStore` (interface): interface ObjectStore { place(catalogId: string, x: number, y: number, z: number, options?: PlaceOptions): string; remove(instanceId: string): boolean; move(instanceId: string, x: number, y: number, z: number): boolean; rotate(instanceId: string, rotationY: numb… — ⚠ undocumented
1575
+ - `ObjectVisual` (interface): interface ObjectVisual { scale?: number | readonly [number, number, number]; color?: string; opacity?: number } — ⚠ undocumented
1576
+ - `PlaceOptions` (interface): interface PlaceOptions { instanceId?: string; parentSpace?: string; rotation?: number; visual?: ObjectVisual; state?: Record<string, unknown>; slots?: InventoryState; animation?: ModelAnimationConfig | "auto" | "none"; onExisting?: "throw" | "replace" | "keep" } — ⚠ undocumented
1577
+ - `SceneObject` (interface): interface SceneObject { instanceId: string; catalogId: string; position: EntityPosition; rotationY: number; parentSpace?: string; visual?: ObjectVisual; animation?: ModelAnimationConfig | "auto" | "none"; state?: Record<string, unknown>; slots?: InventoryState } — ⚠ undocumented · used by `fromRuntimeObjectRow` (@jgengine/core/runtime/objectRows): Inverse of {@link toRuntimeObjectRow}: rebuild the live placed object a host persisted.
1337
1578
  - `createObjectStore` (function): function createObjectStore(): ObjectStore — ⚠ undocumented
1338
1579
  - `objectVisualScale` (function): function objectVisualScale(visual: ObjectVisual | undefined): readonly [number, number, number] — ⚠ undocumented
1339
1580
 
1340
1581
  ## @jgengine/core/scene/paintLayer
1341
1582
 
1342
- - `PaintLayer` (interface): interface PaintLayer — ⚠ undocumented
1343
- - `PaintStroke` (interface): interface PaintStroke — ⚠ undocumented
1583
+ - `PaintLayer` (interface): interface PaintLayer { paint(instanceId: string, stroke: PaintStroke): void; strokes(instanceId: string): readonly PaintStroke[]; paintedIds(): readonly string[]; clear(instanceId?: string): void; version(instanceId: string): number; subscribe(listener: () => v… — ⚠ undocumented
1584
+ - `PaintStroke` (interface): interface PaintStroke { u: number; v: number; radius: number; color: string } — ⚠ undocumented
1344
1585
  - `createPaintLayer` (function): function createPaintLayer(): PaintLayer — ⚠ undocumented
1345
1586
 
1346
1587
  ## @jgengine/core/scene/possession
1347
1588
 
1348
- - `Possession` (interface): interface Possession — ⚠ undocumented
1349
- - `PossessionDeps` (interface): interface PossessionDeps — ⚠ undocumented
1350
- - `PossessionEntities` (interface): interface PossessionEntities — ⚠ undocumented
1351
- - `PossessionEvents` (interface): interface PossessionEvents — ⚠ undocumented
1352
- - `PossessionSnapshot` (interface): interface PossessionSnapshot — ⚠ undocumented
1353
- - `PossessionSwappedEvent` (interface): interface PossessionSwappedEvent — ⚠ undocumented
1589
+ - `Possession` (interface): interface Possession { own(userId: string, entityId: string): void; disown(userId: string, entityId: string): void; owns(userId: string, entityId: string): boolean; ownerOf(entityId: string): string | undefined; listOwned(userId: string): readonly string[]; act… — ⚠ undocumented
1590
+ - `PossessionDeps` (interface): interface PossessionDeps { entities: PossessionEntities; events?: PossessionEvents } — ⚠ undocumented
1591
+ - `PossessionEntities` (interface): interface PossessionEntities { get(id: string): SceneEntity | null; update(id: string, patch: {role?: SceneEntity["role"]}): boolean } — ⚠ undocumented
1592
+ - `PossessionEvents` (interface): interface PossessionEvents { emit(name: "possession.swapped", payload: PossessionSwappedEvent): void } — ⚠ undocumented
1593
+ - `PossessionSnapshot` (interface): interface PossessionSnapshot { owned: Record<string, readonly string[]>; active: Record<string, string> } — ⚠ undocumented
1594
+ - `PossessionSwappedEvent` (interface): interface PossessionSwappedEvent { userId: string; entityId: string; previousEntityId: string } — ⚠ undocumented
1354
1595
  - `createPossession` (function): function createPossession(deps: PossessionDeps): Possession — ⚠ undocumented
1355
1596
 
1356
1597
  ## @jgengine/core/scene/roster
1357
1598
 
1358
- - `Roster` (interface): interface Roster — ⚠ undocumented
1359
- - `RosterCaptureOptions` (interface): interface RosterCaptureOptions — ⚠ undocumented
1360
- - `RosterDeps` (interface): interface RosterDeps — ⚠ undocumented
1361
- - `RosterEntry` (interface): interface RosterEntry — ⚠ undocumented
1599
+ - `Roster` (interface): interface Roster { capture(userId: string, catalogId: string, options?: RosterCaptureOptions): RosterEntry; release(userId: string, entryId: string): boolean; has(userId: string, entryId: string): boolean; get(userId: string, entryId: string): RosterEntry |… — ⚠ undocumented
1600
+ - `RosterCaptureOptions` (interface): interface RosterCaptureOptions { id?: string; capturedAt?: number } — ⚠ undocumented
1601
+ - `RosterDeps` (interface): interface RosterDeps { now?: () => number } — ⚠ undocumented
1602
+ - `RosterEntry` (interface): interface RosterEntry { id: string; catalogId: string; capturedAt: number; equipped: boolean } — ⚠ undocumented
1362
1603
  - `createRoster` (function): function createRoster(deps: RosterDeps = {}): Roster — ⚠ undocumented
1363
1604
 
1364
1605
  ## @jgengine/core/scene/sceneKinds
@@ -1378,7 +1619,7 @@
1378
1619
  - `SceneKindDefinition` (interface): interface SceneKindDefinition<TResolved = unknown> — A registered scene kind — everything the editor/engine need to author and render a studio without bespoke code. `schema` drives the inspector + parse; `resolve` (optional) turns one document object into pure renderable data a matching `shell` renderer consumes; `add*` fields build the `+ Add` menu.
1379
1620
  - `SceneKindObject` (interface): interface SceneKindObject — The raw document object a resolver receives — shape shared by markers, volumes, and paths.
1380
1621
  - `SceneKindResolveContext` (interface): interface SceneKindResolveContext — Ground sampler + options a resolver may read (terrain height/normal snap).
1381
- - `SceneKindTarget` (type): type SceneKindTarget = "path" | "marker" | "volume" — The parametric-studio seam: a registry that maps an editor object `kind` to a typed parameter schema (drives the inspector + `meta` parse/validation), an optional pure-data `resolve` (turns a document object into renderable data), and `+ Add` menu metadata. Registering a kind lets a third party ship a new authorable "studio" (pole line, water, bookcase) — schema + resolver here, a matching renderer in `shell` — without editing editor or engine files. Scatter is the proof adopter.
1622
+ - `SceneKindTarget` (type): type SceneKindTarget = "path" | "marker" | "volume" — Which document collection a scene kind lives in. Drives placement + which `meta` bag holds params.
1382
1623
  - `ScenePathShape` (type): type ScenePathShape = "area" | "line" — A closed-polygon area vs an open polyline, for path-target kinds. Ignored for marker/volume kinds.
1383
1624
  - `SeedParamField` (interface): interface SeedParamField — A seed string with a reroll button — same seed reproduces the same generated result.
1384
1625
  - `SelectParamField` (interface): interface SelectParamField — A dropdown of fixed string options.
@@ -1392,25 +1633,25 @@
1392
1633
 
1393
1634
  ## @jgengine/core/scene/sceneRaycast
1394
1635
 
1395
- - `SceneEntityQuerySource` (interface): interface SceneEntityQuerySource — ⚠ undocumented
1396
- - `SceneObjectQuerySource` (interface): interface SceneObjectQuerySource — ⚠ undocumented
1397
- - `SceneRaycastApi` (interface): interface SceneRaycastApi — ⚠ undocumented
1398
- - `SceneRaycastDeps` (interface): interface SceneRaycastDeps — ⚠ undocumented
1399
- - `SceneRaycastFilter` (interface): interface SceneRaycastFilter — ⚠ undocumented
1400
- - `SceneRaycastHit` (interface): interface SceneRaycastHit — ⚠ undocumented
1401
- - `SceneRaycastInput` (interface): interface SceneRaycastInput — ⚠ undocumented
1636
+ - `SceneEntityQuerySource` (interface): interface SceneEntityQuerySource { list(): readonly {id: string; position: EntityPosition; rotationY: number; name?: string}[]; collidersOf?(instanceId: string): EntityColliderSet | null | undefined; inRadius?(center: EntityPosition, radius: number): readonly string[]; get… — ⚠ undocumented
1637
+ - `SceneObjectQuerySource` (interface): interface SceneObjectQuerySource { list(): readonly SceneObject[]; inBox?(min: EntityPosition, max: EntityPosition): readonly SceneObject[]; collidersOf?(instanceId: string): EntityColliderSet | null | undefined; halfExtentsOf?(catalogId: string): EntityPosition | null | u… — ⚠ undocumented
1638
+ - `SceneRaycastApi` (interface): interface SceneRaycastApi { raycast(input: SceneRaycastInput): SceneRaycastHit | null; raycastAll(input: SceneRaycastInput): SceneRaycastHit[] } — ⚠ undocumented
1639
+ - `SceneRaycastDeps` (interface): interface SceneRaycastDeps { entities?: SceneEntityQuerySource; objects?: SceneObjectQuerySource; terrain?: TerrainRaycastSource; walls?: readonly WallSegment[]; solids?: Pick<WorldSolids, "inBox" | "inRay"> } — ⚠ undocumented
1640
+ - `SceneRaycastFilter` (interface): interface SceneRaycastFilter { entities?: boolean; objects?: boolean; terrain?: boolean; walls?: boolean } — ⚠ undocumented
1641
+ - `SceneRaycastHit` (interface): interface SceneRaycastHit { targetKind: SceneRaycastTargetKind; instanceId: string; catalogId?: string; colliderName: string; purpose: ColliderPurpose; damageEligible: boolean; blocks: boolean; distance: number; point: EntityPosition; normal: EntityPosition } — ⚠ undocumented · used by `firstImpact`: First impact: nearest hit that blocks, or nearest hit if none block.
1642
+ - `SceneRaycastInput` (interface): interface SceneRaycastInput { origin: EntityPosition; direction: EntityPosition; maxDistance: number; excludeInstanceIds?: ReadonlySet<string> | readonly string[]; filter?: SceneRaycastFilter; accept?: (hit: SceneRaycastHit) => boolean } — ⚠ undocumented
1402
1643
  - `SceneRaycastTargetKind` (type): type SceneRaycastTargetKind = "entity" | "object" | "terrain" | "wall" — ⚠ undocumented
1403
- - `TerrainRaycastSource` (interface): interface TerrainRaycastSource — ⚠ undocumented
1404
- - `WallSegment` (interface): interface WallSegment — ⚠ undocumented
1644
+ - `TerrainRaycastSource` (interface): interface TerrainRaycastSource { sampleHeight(x: number, z: number): number } — ⚠ undocumented
1645
+ - `WallSegment` (interface): interface WallSegment { id: string; a: readonly [number, number]; b: readonly [number, number]; yCenter?: number; halfHeight?: number; thickness?: number } — ⚠ undocumented · used by `wallSolids` (@jgengine/core/world/worldSolids): One oriented solid per wall segment (see `wallSegments` in `world/walls`).
1405
1646
  - `createSceneRaycast` (function): function createSceneRaycast(deps: SceneRaycastDeps): SceneRaycastApi — ⚠ undocumented
1406
1647
  - `firstImpact` (function): function firstImpact(hits: readonly SceneRaycastHit[]): SceneRaycastHit | null — First impact: nearest hit that blocks, or nearest hit if none block.
1407
1648
  - `hitsUntilBlocked` (function): function hitsUntilBlocked(hits: readonly SceneRaycastHit[]): SceneRaycastHit[] — Hits up to and including the first blocking collider (damage hitboxes before a wall stay).
1408
1649
 
1409
1650
  ## @jgengine/core/scene/selection
1410
1651
 
1411
- - `ScreenPoint` (interface): interface ScreenPoint — ⚠ undocumented
1412
- - `ScreenRect` (interface): interface ScreenRect — ⚠ undocumented
1413
- - `SelectionSet` (interface): interface SelectionSet — ⚠ undocumented
1652
+ - `ScreenPoint` (interface): interface ScreenPoint { id: string; x: number; y: number } — ⚠ undocumented · used by `selectWithinRect`: Ids of the projected candidates whose screen point falls inside the marquee.
1653
+ - `ScreenRect` (interface): interface ScreenRect { minX: number; minY: number; maxX: number; maxY: number } — ⚠ undocumented · used by `isMarquee`: True when the drag is large enough to be a marquee rather than a click.
1654
+ - `SelectionSet` (interface): interface SelectionSet { add(id: string): void; remove(id: string): void; toggle(id: string): void; has(id: string): boolean; replace(ids: Iterable<string>): void; clear(): void; list(): string[]; size(): number } — ⚠ undocumented · used by `createSelectionSet`: An ordered, deduplicated set of selected instance ids for RTS unit-command routing.
1414
1655
  - `createSelectionSet` (function): function createSelectionSet(initial?: Iterable<string>): SelectionSet — An ordered, deduplicated set of selected instance ids for RTS unit-command routing.
1415
1656
  - `isMarquee` (function): function isMarquee(rect: ScreenRect, thresholdPx = 4): boolean — True when the drag is large enough to be a marquee rather than a click.
1416
1657
  - `rectContainsPoint` (function): function rectContainsPoint(rect: ScreenRect, x: number, y: number): boolean — ⚠ undocumented
@@ -1440,72 +1681,78 @@
1440
1681
 
1441
1682
  ## @jgengine/core/scene/spatial
1442
1683
 
1443
- - `Aim` (type): type Aim = | { origin: EntityPosition; direction: EntityPosition } | { yaw: number; pitch: number; spread?: number } — ⚠ undocumented
1444
- - `MoveTowardOptions` (interface): interface MoveTowardOptions — ⚠ undocumented
1445
- - `QueryArcOptions` (interface): interface QueryArcOptions — ⚠ undocumented
1446
- - `SpatialApi` (interface): interface SpatialApi — ⚠ undocumented
1447
- - `SpatialApiOptions` (interface): interface SpatialApiOptions — ⚠ undocumented
1448
- - `SpatialGridOptions` (interface): interface SpatialGridOptions — ⚠ undocumented
1449
- - `createSpatialApi` (function): function createSpatialApi(options: SpatialApiOptions): SpatialApi — ⚠ undocumented
1684
+ - `Aim` (type): type Aim = | { origin: EntityPosition; direction: EntityPosition } | { yaw: number; pitch: number; spread?: number } — ⚠ undocumented · used by `aimToPoint` (@jgengine/core/input/pointer): Build an `origin → point` aim for `item.use` / projectiles, firing toward the cursor.
1685
+ - `MoveTowardOptions` (interface): interface MoveTowardOptions { speed: number; stopDistance?: number; dt: number; avoidSolids?: boolean } — ⚠ undocumented
1686
+ - `QueryArcOptions` (interface): interface QueryArcOptions { from: string; aim: Aim; radius: number; halfAngleDeg?: number } — ⚠ undocumented
1687
+ - `SpatialApi` (interface): interface SpatialApi { distance(aInstanceId: string, bInstanceId: string): number | null; inRadius(center: EntityPosition | string, radius: number, filter?: (instanceId: string) => boolean): string[]; hasLineOfSight(fromInstanceId: string, toInstanceId: string)… — ⚠ undocumented · used by `createSpatialApi`: Spatial queries with an optional notified incremental broadphase.
1688
+ - `SpatialApiOptions` (interface): interface SpatialApiOptions { resolvePosition: (instanceId: string) => EntityPosition | undefined; candidates: () => readonly string[]; occluder?: (from: EntityPosition, to: EntityPosition) => boolean; grid?: SpatialGridOptions | false; getVersion?: () => number; incr… — ⚠ undocumented · used by `createSpatialApi`: Spatial queries with an optional notified incremental broadphase.
1689
+ - `SpatialGridOptions` (interface): interface SpatialGridOptions { cellSize: number } — ⚠ undocumented
1690
+ - `createSpatialApi` (function): function createSpatialApi(options: SpatialApiOptions): SpatialApi — Spatial queries with an optional notified incremental broadphase.
1450
1691
  - `distanceBetween` (function): function distanceBetween(a: EntityPosition, b: EntityPosition): number — ⚠ undocumented
1451
1692
 
1452
1693
  ## @jgengine/core/scene/stationClaim
1453
1694
 
1454
1695
  - `ClaimResult` (type): type ClaimResult = | { ok: true; station: Station } | { ok: false; reason: "unknown_vehicle" | "unknown_facet" | "station_taken" | "vehicle_full" } — ⚠ undocumented
1455
- - `SharedVehicleConfig` (interface): interface SharedVehicleConfig — ⚠ undocumented
1456
- - `Station` (interface): interface Station — ⚠ undocumented
1696
+ - `SharedVehicleConfig` (interface): interface SharedVehicleConfig { id: string; kit: MountKit; stations: readonly Station[] } — ⚠ undocumented
1697
+ - `Station` (interface): interface Station { id: string; facet: string; offset: readonly [number, number, number]; control?: boolean } — ⚠ undocumented
1457
1698
  - `StationClaim` (class): class StationClaim — ⚠ undocumented
1458
1699
  - `createStationClaim` (function): function createStationClaim(controller?: MountController): StationClaim — ⚠ undocumented
1459
1700
 
1460
1701
  ## @jgengine/core/scene/targeting
1461
1702
 
1462
- - `CycleTargetOptions` (interface): interface CycleTargetOptions — ⚠ undocumented
1703
+ - `CycleTargetOptions` (interface): interface CycleTargetOptions { filter?: TargetFilter; direction?: "next" | "prev"; maxDistance?: number } — ⚠ undocumented
1463
1704
  - `TargetFilter` (type): type TargetFilter = TargetRelation | "any" — ⚠ undocumented
1464
1705
  - `TargetRelation` (type): type TargetRelation = "hostile" | "friendly" — ⚠ undocumented
1465
- - `Targeting` (interface): interface Targeting — ⚠ undocumented
1466
- - `TargetingOptions` (interface): interface TargetingOptions — ⚠ undocumented
1706
+ - `Targeting` (interface): interface Targeting { setTarget(fromId: string, toId: string | null): void; getTarget(fromId: string): string | null; cycleTarget(fromId: string, options?: CycleTargetOptions): string | null; clearAll(instanceId: string): void } — ⚠ undocumented
1707
+ - `TargetingOptions` (interface): interface TargetingOptions { candidates: () => string[]; classify?: (fromId: string, toId: string) => TargetRelation; orderBy?: (a: string, b: string) => number; distance?: (fromId: string, toId: string) => number | null } — ⚠ undocumented
1467
1708
  - `createTargeting` (function): function createTargeting(options: TargetingOptions): Targeting — ⚠ undocumented
1468
1709
 
1469
1710
  ## @jgengine/core/scene/vehicleSeat
1470
1711
 
1712
+ - `BoardVehicleOptions` (interface): interface BoardVehicleOptions extends EnterVehicleOptions — Seat selection and optional local camera policy for an explicit rider transition.
1471
1713
  - `DismountOffset` (interface): interface DismountOffset — Where {@link VehicleSeats.exit} steps the rider out, relative to the vehicle's heading.
1472
1714
  - `EnterVehicleOptions` (interface): interface EnterVehicleOptions — Options for {@link VehicleSeats.enter}.
1473
1715
  - `EnterVehicleResult` (type): type EnterVehicleResult = | { ok: true; seat: MountSeat; /** Feed straight into `ctx.camera.follow(...)` — the vehicle while a control seat is taken, else the rider's own id. */ cameraTarget: string; /** Entity this rider's axis input should now drive (`scene/mount`'s `driveTarget`); `null` for a pa… — Result of {@link VehicleSeats.enter} — the resolved seat plus the camera/drive/movement patches to apply.
1474
- - `ExitVehicleResult` (type): type ExitVehicleResult = | { ok: true; vehicleId: string; /** Where to `setPose` the rider — alongside the vehicle's side door, facing its heading. */ placement: VehiclePose; cameraTarget: string; riderMovementPatch: RiderMovementPatch; } | { ok: false; reason: "not_seated" } — Result of {@link VehicleSeats.exit} — the side-door placement plus the camera/movement patches to apply.
1716
+ - `ExitVehicleResult` (type): type ExitVehicleResult = | { ok: true; vehicleId: string; /** Where to `setPose` the rider — alongside the vehicle's side door, facing its heading. */ placement: VehiclePose; cameraTarget: string; riderMovementPatch: RiderMovementPatch; } | { ok: false; reason: "not_seated" | "invalid_pose" } — Result of {@link VehicleSeats.exit} — the side-door placement plus the camera/movement patches to apply.
1717
+ - `LeaveVehicleOptions` (interface): interface LeaveVehicleOptions extends DismountOffset — Exit placement and optional local camera restoration for an explicit rider.
1475
1718
  - `RiderMovementPatch` (interface): interface RiderMovementPatch — Movement-lock patch for `entities.update(riderId, { movement: { ...current, ...patch } })` (#286.gameplay `movement.frozen`).
1476
1719
  - `VehiclePose` (interface): interface VehiclePose — A vehicle's world-space position and heading, used for {@link VehicleSeats.exit}'s dismount placement math.
1477
1720
  - `VehiclePosition` (type): type VehiclePosition = readonly [number, number, number] — World-space `[x, y, z]` for a vehicle's current position.
1478
1721
  - `VehicleSeats` (class): class VehicleSeats — Composes `scene/mount`'s control-transfer bookkeeping with the seat/camera/movement-mode transition every enter/exit-vehicle flow needs (#533.2): boarding resolves a free seat and reports the camera target, drive target, and rider movement-lock patch in one call; leaving computes a side-door placement next to the vehicle and reports the same triad in reverse. Pure — no entity/camera side effects — the caller applies `riderMovementPatch`/`placement`/`cameraTarget` via its own `ctx`.
1722
+ - `boardVehicle` (function): function boardVehicle(ctx: GameContext, seats: VehicleSeats, riderId: string, vehicleId: string, options: BoardVehicleOptions = {}): EnterVehicleResult — Apply an existing seat transition to the explicitly named rider. Motor/input policy stays caller-owned.
1479
1723
  - `createVehicleSeats` (function): function createVehicleSeats(controller?: MountController): VehicleSeats — Builds a {@link VehicleSeats}, optionally over an existing `MountController` to share its occupancy.
1724
+ - `leaveVehicle` (function): function leaveVehicle(ctx: GameContext, seats: VehicleSeats, riderId: string, options: LeaveVehicleOptions = {}): ExitVehicleResult | { ok: false; reason: "unknown_rider" | "unknown_vehicle" } — Apply dismount placement, restore rider movement/visibility and the configured camera rig.
1480
1725
 
1481
1726
  ## @jgengine/core/sensor/concealment
1482
1727
 
1483
1728
  - `ColorHex` (type): type ColorHex = string — ⚠ undocumented
1484
- - `ConcealmentSample` (interface): interface ConcealmentSample — ⚠ undocumented
1485
- - `ConcealmentSensor` (interface): interface ConcealmentSensor — ⚠ undocumented
1486
- - `ConcealmentTarget` (interface): interface ConcealmentTarget — ⚠ undocumented
1729
+ - `ConcealmentSample` (interface): interface ConcealmentSample { id: string; score: number; concealed: boolean; dwellSeconds: number } — ⚠ undocumented
1730
+ - `ConcealmentSensor` (interface): interface ConcealmentSensor { tick(targets: readonly ConcealmentTarget[], dt: number): ConcealmentSample[]; reset(id?: string): void; snapshot(): ConcealmentSensorState; restore(next: ConcealmentSensorState): void } — ⚠ undocumented
1731
+ - `ConcealmentSensorState` (interface): interface ConcealmentSensorState — Plain JSON state of a {@link ConcealmentSensor}: seconds each target has stayed concealed.
1732
+ - `ConcealmentTarget` (interface): interface ConcealmentTarget { id: string; entityColors: readonly ColorHex[]; backgroundColors: readonly ColorHex[] } — ⚠ undocumented
1487
1733
 
1488
1734
  ## @jgengine/core/sensor/freezeMonitor
1489
1735
 
1490
- - `FreezeMonitor` (interface): interface FreezeMonitor — ⚠ undocumented
1491
- - `FreezeSubject` (interface): interface FreezeSubject — ⚠ undocumented
1492
- - `FreezeViolation` (interface): interface FreezeViolation — ⚠ undocumented
1736
+ - `FreezeMonitor` (interface): interface FreezeMonitor { tick(subjects: readonly FreezeSubject[], frozenIds: ReadonlySet<string>, dt: number): FreezeViolation[]; reset(id?: string): void; snapshot(): FreezeMonitorState; restore(next: FreezeMonitorState): void } — ⚠ undocumented
1737
+ - `FreezeMonitorState` (interface): interface FreezeMonitorState — Plain JSON state of a {@link FreezeMonitor}: seconds each frozen subject has kept moving.
1738
+ - `FreezeSubject` (interface): interface FreezeSubject { id: string; groundSpeed: number } — ⚠ undocumented
1739
+ - `FreezeViolation` (interface): interface FreezeViolation { id: string; speed: number; movedSeconds: number } — ⚠ undocumented
1493
1740
 
1494
1741
  ## @jgengine/core/sensor/frustumSensor
1495
1742
 
1496
- - `FramingConfig` (interface): interface FramingConfig — ⚠ undocumented
1497
- - `FrustumCamera` (interface): interface FrustumCamera — ⚠ undocumented
1498
- - `FrustumProjection` (interface): interface FrustumProjection — ⚠ undocumented
1499
- - `FrustumSample` (interface): interface FrustumSample — ⚠ undocumented
1500
- - `FrustumSensor` (interface): interface FrustumSensor — ⚠ undocumented
1501
- - `FrustumTarget` (interface): interface FrustumTarget — ⚠ undocumented
1743
+ - `FramingConfig` (interface): interface FramingConfig { idealDistance?: number; centerWeight?: number; sizeWeight?: number } — ⚠ undocumented
1744
+ - `FrustumCamera` (interface): interface FrustumCamera { position: EntityPosition; lookAt: EntityPosition; fovDeg?: number; aspect?: number; near?: number; far?: number } — ⚠ undocumented
1745
+ - `FrustumProjection` (interface): interface FrustumProjection { inView: boolean; distance: number; screenX: number; screenY: number } — ⚠ undocumented
1746
+ - `FrustumSample` (interface): interface FrustumSample { id: string; inView: boolean; distance: number; screenX: number; screenY: number; framing: number; dwellSeconds: number } — ⚠ undocumented
1747
+ - `FrustumSensor` (interface): interface FrustumSensor { tick(camera: FrustumCamera, targets: readonly FrustumTarget[], dt: number): FrustumSample[]; reset(id?: string): void } — ⚠ undocumented
1748
+ - `FrustumTarget` (interface): interface FrustumTarget { id: string; position: EntityPosition; radius?: number } — ⚠ undocumented
1502
1749
 
1503
1750
  ## @jgengine/core/sensor/hiddenStateProbe
1504
1751
 
1505
- - `HiddenStateSource` (interface): interface HiddenStateSource — ⚠ undocumented
1752
+ - `HiddenStateSource` (interface): interface HiddenStateSource { id: string; position: EntityPosition; variables: Readonly<Record<string, HiddenStateValue>> } — ⚠ undocumented · used by `useHiddenStateProbe` (@jgengine/shell/vision/HiddenStateProbeHud): Reads a hidden zone/entity state variable in range (EMF / thermometer / geiger style sensor verb, #116).
1506
1753
  - `HiddenStateValue` (type): type HiddenStateValue = number | string | boolean — ⚠ undocumented
1507
- - `SensorProbeOptions` (interface): interface SensorProbeOptions — ⚠ undocumented
1508
- - `SensorReading` (interface): interface SensorReading — ⚠ undocumented
1754
+ - `SensorProbeOptions` (interface): interface SensorProbeOptions { range: number; variableId: string; falloff?: "linear" | "none" } — ⚠ undocumented · used by `useHiddenStateProbe` (@jgengine/shell/vision/HiddenStateProbeHud): Reads a hidden zone/entity state variable in range (EMF / thermometer / geiger style sensor verb, #116).
1755
+ - `SensorReading` (interface): interface SensorReading { sourceId: string; variableId: string; value: HiddenStateValue; strength: number; distance: number } — ⚠ undocumented · used by `useHiddenStateProbe` (@jgengine/shell/vision/HiddenStateProbeHud): Reads a hidden zone/entity state variable in range (EMF / thermometer / geiger style sensor verb, #116).
1509
1756
 
1510
1757
  ## @jgengine/core/sensor/perception
1511
1758
 
@@ -1522,30 +1769,30 @@
1522
1769
 
1523
1770
  ## @jgengine/core/sensor/recordingBuffer
1524
1771
 
1525
- - `RecordingBuffer` (interface): interface RecordingBuffer<T> — ⚠ undocumented
1526
- - `RecordingBufferOptions` (interface): interface RecordingBufferOptions — ⚠ undocumented
1527
- - `RecordingFrame` (interface): interface RecordingFrame<T> — ⚠ undocumented
1528
- - `RecordingPair` (interface): interface RecordingPair<T> — ⚠ undocumented
1772
+ - `RecordingBuffer` (interface): interface RecordingBuffer<T> { append(t: number, data: T): void; seek(t: number): RecordingFrame<T> | null; seekPair(t: number): RecordingPair<T>; range(fromT: number, toT: number): RecordingFrame<T>[]; clear(): void; duration(): number; frames(): readonly RecordingFra… — ⚠ undocumented · used by `useSessionRecorder` (@jgengine/shell/replay/useSessionRecorder): Session-recording buffer (#120) for replay / photo mode / kill-cam: records an entity's pose on game-time every frame into a `RecordingBuffe…
1773
+ - `RecordingBufferOptions` (interface): interface RecordingBufferOptions { maxDurationSeconds?: number; maxFrames?: number } — ⚠ undocumented · used by `useSessionRecorder` (@jgengine/shell/replay/useSessionRecorder): Session-recording buffer (#120) for replay / photo mode / kill-cam: records an entity's pose on game-time every frame into a `RecordingBuffe…
1774
+ - `RecordingFrame` (interface): interface RecordingFrame<T> { t: number; data: T } — ⚠ undocumented
1775
+ - `RecordingPair` (interface): interface RecordingPair<T> { before: RecordingFrame<T> | null; after: RecordingFrame<T> | null } — ⚠ undocumented
1529
1776
 
1530
1777
  ## @jgengine/core/sensor/replayLoop
1531
1778
 
1532
- - `RecordedPoseLike` (interface): interface RecordedPoseLike — ⚠ undocumented
1533
- - `ReplayEntityDeps` (interface): interface ReplayEntityDeps — ⚠ undocumented
1779
+ - `RecordedPoseLike` (interface): interface RecordedPoseLike { position: readonly [number, number, number]; rotationY?: number } — ⚠ undocumented
1780
+ - `ReplayEntityDeps` (interface): interface ReplayEntityDeps { has(id: string): boolean; spawn(id: string): void; setPose(id: string, pose: {position: readonly [number, number, number]; rotationY?: number; dt?: number}): unknown; despawn(id: string): unknown } — ⚠ undocumented
1534
1781
  - `ReplayLoop` (interface): interface ReplayLoop<T> — Replays a finished `RecordingBuffer` on a loop — modulo-time seek, frame interpolation, and spawn grace, the pieces every ghost-lap/echo feature hand-rolled (#286.4). `sample(t)` is a pure function of absolute time; `null` means "the ghost isn't on track right now".
1535
- - `ReplayLoopOptions` (interface): interface ReplayLoopOptions<T> — ⚠ undocumented
1782
+ - `ReplayLoopOptions` (interface): interface ReplayLoopOptions<T> { interpolate?: (before: T, after: T, alpha: number) => T; spawnGraceSeconds?: number } — ⚠ undocumented
1536
1783
 
1537
1784
  ## @jgengine/core/sensor/revealQuery
1538
1785
 
1539
- - `RevealHit` (interface): interface RevealHit — ⚠ undocumented
1540
- - `RevealQuery` (interface): interface RevealQuery — ⚠ undocumented
1541
- - `RevealQueryOptions` (interface): interface RevealQueryOptions — ⚠ undocumented
1786
+ - `RevealHit` (interface): interface RevealHit { instanceId: string; distance: number; tags: readonly string[] } — ⚠ undocumented · used by `useRevealHits` (@jgengine/shell/vision/RevealVision): Occlusion-ignoring tagged-entity radius query (#115), bound to the live scene.
1787
+ - `RevealQuery` (interface): interface RevealQuery { inRadius(center: EntityPosition | string, radius: number, tags: readonly string[]): RevealHit[] } — ⚠ undocumented
1788
+ - `RevealQueryOptions` (interface): interface RevealQueryOptions { resolvePosition: (instanceId: string) => EntityPosition | undefined; resolveTags: (instanceId: string) => readonly string[]; candidates: () => string[] } — ⚠ undocumented
1542
1789
 
1543
1790
  ## @jgengine/core/sensor/visionCone
1544
1791
 
1545
1792
  - `VisionCone` (interface): interface VisionCone — The guard-sight primitive every stealth game hand-rolled: an angle+range cone on the XZ plane whose sight lines are blocked by 2D wall segments (`world/walls` segments fit structurally). Pure math — feed entity positions and heading each tick.
1546
- - `VisionConeConfig` (interface): interface VisionConeConfig — ⚠ undocumented
1793
+ - `VisionConeConfig` (interface): interface VisionConeConfig { range: number; angle: number } — ⚠ undocumented
1547
1794
  - `VisionPoint` (type): type VisionPoint = readonly [number, number] — ⚠ undocumented
1548
- - `VisionTarget` (interface): interface VisionTarget<TId extends string = string> — ⚠ undocumented
1795
+ - `VisionTarget` (interface): interface VisionTarget<TId extends string = string> { id: TId; at: VisionPoint } — ⚠ undocumented
1549
1796
  - `VisionWall` (interface): interface VisionWall — Structurally matches `world/walls` `WallSegment` — pass those straight in as occluders.
1550
1797
 
1551
1798
  ## @jgengine/core/time/accrueSince
@@ -1555,18 +1802,18 @@
1555
1802
 
1556
1803
  ## @jgengine/core/time/beatClock
1557
1804
 
1558
- - `BeatAccuracyTier` (interface): interface BeatAccuracyTier — ⚠ undocumented
1559
- - `BeatClock` (interface): interface BeatClock — ⚠ undocumented
1560
- - `BeatClockConfig` (interface): interface BeatClockConfig — ⚠ undocumented
1561
- - `BeatInputBuffer` (interface): interface BeatInputBuffer<T> — ⚠ undocumented
1562
- - `BeatJudgement` (interface): interface BeatJudgement — ⚠ undocumented
1563
- - `BeatSnapshot` (interface): interface BeatSnapshot — ⚠ undocumented
1564
- - `BufferedAction` (interface): interface BufferedAction<T> — ⚠ undocumented
1805
+ - `BeatAccuracyTier` (interface): interface BeatAccuracyTier { id: string; windowSec: number } — ⚠ undocumented
1806
+ - `BeatClock` (interface): interface BeatClock { advance(gameDt: number): BeatSnapshot; snapshot(): BeatSnapshot; now(): number; beatDurationSec(): number; bpm(): number } — ⚠ undocumented
1807
+ - `BeatClockConfig` (interface): interface BeatClockConfig { bpm: number; beatsPerBar?: number } — ⚠ undocumented
1808
+ - `BeatInputBuffer` (interface): interface BeatInputBuffer<T> { buffer(action: T, nowSec: number): number; advance(nowSec: number): T[]; pendingCount(): number; clear(): void } — ⚠ undocumented
1809
+ - `BeatJudgement` (interface): interface BeatJudgement { tier: string; deltaSec: number } — ⚠ undocumented
1810
+ - `BeatSnapshot` (interface): interface BeatSnapshot { beat: number; beatIndex: number; bar: number; beatInBar: number; phase: number } — ⚠ undocumented
1811
+ - `BufferedAction` (interface): interface BufferedAction<T> { action: T; bufferedAtSec: number; fireAtSec: number } — ⚠ undocumented
1565
1812
  - `DEFAULT_BEAT_TIERS` (const): const DEFAULT_BEAT_TIERS: readonly BeatAccuracyTier[] — ⚠ undocumented
1566
1813
 
1567
1814
  ## @jgengine/core/time/calendarClock
1568
1815
 
1569
- - `GameTime` (interface): interface GameTime — ⚠ undocumented
1816
+ - `GameTime` (interface): interface GameTime { year: number; month: number; monthName: string; monthNameFull: string; dayOfMonth: number; hour: number; minute: number; second: number; formattedDate: string; formattedDateFull: string; formattedTime: string; formatted: string } — ⚠ undocumented
1570
1817
 
1571
1818
  ## @jgengine/core/time/dayNightCycle
1572
1819
 
@@ -1588,11 +1835,11 @@
1588
1835
 
1589
1836
  ## @jgengine/core/time/idleProgress
1590
1837
 
1591
- - `ExponentialCatchUpInput` (interface): interface ExponentialCatchUpInput — ⚠ undocumented
1592
- - `IdleWindow` (interface): interface IdleWindow — ⚠ undocumented
1593
- - `IdleWindowConfig` (interface): interface IdleWindowConfig — ⚠ undocumented
1594
- - `LinearCatchUpInput` (interface): interface LinearCatchUpInput — ⚠ undocumented
1595
- - `SteppedCatchUpResult` (interface): interface SteppedCatchUpResult — ⚠ undocumented
1838
+ - `ExponentialCatchUpInput` (interface): interface ExponentialCatchUpInput { current: number; factorPerSecond: number; min?: number; max?: number } — ⚠ undocumented
1839
+ - `IdleWindow` (interface): interface IdleWindow { elapsedSeconds: number; effectiveSeconds: number; capped: boolean } — ⚠ undocumented
1840
+ - `IdleWindowConfig` (interface): interface IdleWindowConfig { maxSeconds?: number; efficiency?: number } — ⚠ undocumented
1841
+ - `LinearCatchUpInput` (interface): interface LinearCatchUpInput { current: number; ratePerSecond: number; min?: number; max?: number } — ⚠ undocumented
1842
+ - `SteppedCatchUpResult` (interface): interface SteppedCatchUpResult { steps: number; remainderSeconds: number } — ⚠ undocumented
1596
1843
 
1597
1844
  ## @jgengine/core/time/rateWindow
1598
1845
 
@@ -1602,31 +1849,31 @@
1602
1849
 
1603
1850
  ## @jgengine/core/time/serverTick
1604
1851
 
1605
- - `PlanServerTickOptions` (type): type PlanServerTickOptions = { /** Cap catch-up runs per system per heartbeat to avoid spiral-of-death. Default 3. */ maxCatchUp?: number; } — ⚠ undocumented
1606
- - `ServerTickPlan` (interface): interface ServerTickPlan<TSystemId extends string = string> — ⚠ undocumented
1852
+ - `PlanServerTickOptions` (type): type PlanServerTickOptions = { /** Cap catch-up runs per system per heartbeat to avoid spiral-of-death. Default 3. */ maxCatchUp?: number; } — ⚠ undocumented · used by `planServerTick`: Decides which systems a heartbeat should run.
1853
+ - `ServerTickPlan` (interface): interface ServerTickPlan<TSystemId extends string = string> { due: TickSystemRun<TSystemId>[]; anchors: TickAnchors } — ⚠ undocumented · used by `planServerTick`: Decides which systems a heartbeat should run.
1607
1854
  - `TickAnchors` (type): type TickAnchors = Record<string, number> — Last-run timestamp per system id.
1608
- - `TickSystemDefinition` (interface): interface TickSystemDefinition<TSystemId extends string = string> — ⚠ undocumented
1855
+ - `TickSystemDefinition` (interface): interface TickSystemDefinition<TSystemId extends string = string> { id: TSystemId; intervalMs: number; scope?: "onlinePlayers" | "allServers"; batchSize?: number } — ⚠ undocumented · used by `planServerTick`: Decides which systems a heartbeat should run.
1609
1856
  - `TickSystemRun` (interface): interface TickSystemRun<TSystemId extends string = string> — One due system and how many times this heartbeat owes it.
1610
1857
  - `planServerTick` (function): function planServerTick<TSystemId extends string>(systems: readonly TickSystemDefinition<TSystemId>[], anchors: TickAnchors, now: number, options?: PlanServerTickOptions): ServerTickPlan<TSystemId> — Decides which systems a heartbeat should run. A system with no anchor runs immediately. When wall time stalls past multiple intervals the system's `runs` climbs (bounded by maxCatchUp), and the returned anchor moves by that many intervals — so a host that ignores `runs` loses the catch-up work. Excess lag past the bound resyncs the anchor to `now`. Returned anchors carry only systems present in the pipeline, so removed systems clean up.
1611
1858
  - `tickRunCount` (function): function tickRunCount<TSystemId extends string>(plan: ServerTickPlan<TSystemId>, id: TSystemId): number — Times this plan owes `id`, or 0 when it is not due. Saves a host a lookup over {@link ServerTickPlan.due}.
1612
1859
 
1613
1860
  ## @jgengine/core/time/simClock
1614
1861
 
1615
- - `CalendarTime` (interface): interface CalendarTime — ⚠ undocumented
1616
- - `ClockSnapshot` (interface): interface ClockSnapshot — ⚠ undocumented
1617
- - `SimClock` (interface): interface SimClock — ⚠ undocumented
1618
- - `SimClockOptions` (interface): interface SimClockOptions — ⚠ undocumented
1619
- - `TimeConfig` (interface): interface TimeConfig — ⚠ undocumented
1620
- - `createSimClock` (function): function createSimClock(options: SimClockOptions = {}): SimClock — ⚠ undocumented
1862
+ - `CalendarTime` (interface): interface CalendarTime { totalSeconds: number; day: number; hour: number; minute: number; second: number; dayFraction: number; year: number; dayOfYear: number; yearFraction: number; season?: string } — ⚠ undocumented
1863
+ - `ClockSnapshot` (interface): interface ClockSnapshot { now: number; paused: boolean; speed: number; playSpeed: number; timescale: number; scale: number; speeds: readonly number[]; calendar: CalendarTime } — ⚠ undocumented
1864
+ - `SimClock` (interface): interface SimClock { advance(realDt: number): number; now(): number; advancedSeconds?(): number; snapshot(): ClockSnapshot; hydrate(snapshot: ClockSnapshot): void; calendar(): CalendarTime; isPaused(): boolean; speed(): number; pause(): void; play(): void; to… — ⚠ undocumented · used by `createSimContext` (@jgengine/core/runtime/simContext): Build `ctx.sim` for a context; `createGameContext` calls this from `definition.simulation`.
1865
+ - `SimClockOptions` (interface): interface SimClockOptions { config?: TimeConfig; onChange?: () => void } — ⚠ undocumented
1866
+ - `TimeConfig` (interface): interface TimeConfig { scale?: number; speeds?: readonly number[]; dayLength?: number; start?: number; startPaused?: boolean; daysPerYear?: number; seasons?: readonly string[] } — ⚠ undocumented
1867
+ - `createSimClock` (function): function createSimClock(options: SimClockOptions = {}): SimClock & { advancedSeconds(): number } — ⚠ undocumented
1621
1868
 
1622
1869
  ## @jgengine/core/time/stateSchedule
1623
1870
 
1624
- - `ClearWindowScan` (interface): interface ClearWindowScan — ⚠ undocumented
1625
- - `SchedulePhase` (interface): interface SchedulePhase<T> — ⚠ undocumented
1626
- - `ScheduleSample` (interface): interface ScheduleSample<T> — ⚠ undocumented
1627
- - `ScheduleWindow` (interface): interface ScheduleWindow — ⚠ undocumented
1871
+ - `ClearWindowScan` (interface): interface ClearWindowScan { fromSeconds: number; horizonSeconds: number; stepSeconds: number; minDurationSeconds?: number } — ⚠ undocumented · used by `nextClearWindow`: Forward-scan any predicate-of-time — a timetable mover's "is the crossing clear at `t`" — for the next open window.
1872
+ - `SchedulePhase` (interface): interface SchedulePhase<T> { state: T; durationSeconds: number } — ⚠ undocumented
1873
+ - `ScheduleSample` (interface): interface ScheduleSample<T> { state: T; index: number; elapsedSeconds: number; remainingSeconds: number; fraction: number } — ⚠ undocumented
1874
+ - `ScheduleWindow` (interface): interface ScheduleWindow { start: number; end: number } — ⚠ undocumented · used by `nextClearWindow`: Forward-scan any predicate-of-time — a timetable mover's "is the crossing clear at `t`" — for the next open window.
1628
1875
  - `StateSchedule` (interface): interface StateSchedule<T> — A deterministic timeline of states — position-in-schedule is a pure function of absolute time, so a forecast preview and the live state can never disagree. One primitive behind zoned weather shifts, hazard cycles, and timetabled machinery: `stateAt`/`sampleAt` for the live state, `nextTransitionAt` for countdowns, `windowsOf`/`nextWindow` for "when is it safe" forecasts.
1629
- - `StateScheduleConfig` (interface): interface StateScheduleConfig<T> — ⚠ undocumented
1876
+ - `StateScheduleConfig` (interface): interface StateScheduleConfig<T> { phases: readonly SchedulePhase<T>[]; loop?: boolean; offsetSeconds?: number } — ⚠ undocumented
1630
1877
  - `createStateSchedule` (function): function createStateSchedule<T>(config: StateScheduleConfig<T>): StateSchedule<T> — ⚠ undocumented
1631
1878
  - `nextClearWindow` (function): function nextClearWindow(isClear: (t: number) => boolean, scan: ClearWindowScan): ScheduleWindow | null — Forward-scan any predicate-of-time — a timetable mover's "is the crossing clear at `t`" — for the next open window. Sampling-based: pick `stepSeconds` at or below half the shortest gap that matters.
1632
1879
 
@@ -1659,26 +1906,48 @@
1659
1906
  - `HitInput` (interface): interface HitInput — A single incoming hit to register on a {@link DamageDirectionTracker}. The angle is relative to the player's facing (renderer-agnostic): `0` points at the front/top of the reticle and increases clockwise, so a game passes the bearing from the player toward the attacker without knowing anything about the screen.
1660
1907
  - `createDamageDirectionTracker` (function): function createDamageDirectionTracker(options: DamageDirectionOptions = {}): DamageDirectionTracker — Create a damage-direction tracker: the classic "hit-from" feedback brain. A game calls `registerHit({ angle, intensity, kind })` with the bearing from the player toward the attacker (radians, `0` = front) and the tracker owns the fade timers and eased strength so the renderer just draws an arc per `active()` entry. It is renderer-free and genre-agnostic (the `kind` tag is never interpreted here), allocation-aware (a fixed pool, no per-frame garbage), and fully serializable via `snapshot`/`restore`. Optional angle merging collapses a burst from one direction into a single strong arc.
1661
1908
 
1909
+ ## @jgengine/core/vfx/feedbackMixer
1910
+
1911
+ - `FeedbackCurve` (type): type FeedbackCurve = readonly (readonly [number, number])[] — Piecewise-linear map from a signal value to an output value; points in ascending input order, ends clamp.
1912
+ - `FeedbackEvent` (interface): interface FeedbackEvent<TSignal extends string> — A one-shot fired when a signal crosses a threshold (landing thud, gear-change clunk, impact rumble).
1913
+ - `FeedbackMixer` (interface): interface FeedbackMixer<TSignal extends string, TTarget extends string> — Maps sim telemetry to presentation parameters through declared routes, curves and smoothing.
1914
+ - `FeedbackMixerConfig` (interface): interface FeedbackMixerConfig<TSignal extends string, TTarget extends string> — Config for {@link createFeedbackMixer}.
1915
+ - `FeedbackMixerState` (interface): interface FeedbackMixerState — Serializable mixer state: smoothed route values, last signal per event, and event cooldowns.
1916
+ - `FeedbackRoute` (interface): interface FeedbackRoute<TSignal extends string, TTarget extends string> — One wire from a sim signal to a named output.
1917
+ - `createFeedbackMixer` (function): function createFeedbackMixer<TSignal extends string, TTarget extends string>(initial: FeedbackMixerConfig<TSignal, TTarget>): FeedbackMixer<TSignal, TTarget> — Creates a {@link FeedbackMixer}: the one place a game declares how sim telemetry drives presentation — rpm to engine pitch, tire saturation to squeal and rumble, speed to FOV, landing speed to a thud — instead of hand-writing that glue per vehicle, aircraft or weapon. Deterministic, allocation-free per update.
1918
+ - `sampleFeedbackCurve` (function): function sampleFeedbackCurve(curve: FeedbackCurve | undefined, x: number): number — Sample a {@link FeedbackCurve}; an empty curve is the identity.
1919
+
1662
1920
  ## @jgengine/core/vfx/particleDirector
1663
1921
 
1664
1922
  - `ParticleAttachOptions` (interface): interface ParticleAttachOptions — Options for {@link ParticleDirector.attach}.
1665
1923
  - `ParticleBlendHint` (type): type ParticleBlendHint = "additive" | "normal" — How a particle effect composites on screen: `additive` for sparks/fire/glow, `normal` for smoke/dust.
1666
1924
  - `ParticleBurst` (interface): interface ParticleBurst — A one-shot burst request — consumed once by the renderer, never serialized.
1925
+ - `ParticleCollisionEffect` (interface): interface ParticleCollisionEffect — A bounded collision child effect; children cannot spawn further collision children.
1667
1926
  - `ParticleDirector` (interface): interface ParticleDirector — The game-side seam for particle effects. Game logic requests one-shot bursts and standing emitters as plain data; the shell renders them through `createParticleSystem`/`ParticleField`, applies the graphics-quality particle cap, and tracks `follow` entities. Nothing here touches a renderer, so commands and `onTick` systems can drive VFX headlessly and tests can assert the requested effects.
1927
+ - `ParticleDirectorEvent` (interface): interface ParticleDirectorEvent extends ParticleEvent — A cosmetic simulation event, labelled with its emitting effect.
1928
+ - `ParticleDirectorOptions` (interface): interface ParticleDirectorOptions — Bounded storage policy for transient effects and event exchange.
1668
1929
  - `ParticleDirectorState` (interface): interface ParticleDirectorState — Serializable director state: the standing emitters (bursts are transient by nature).
1669
- - `ParticleEmitterSpec` (interface): interface ParticleEmitterSpec — A keyed continuous emitter owned by the director until detached.
1670
- - `createParticleDirector` (function): function createParticleDirector(): ParticleDirector — Create the particle intent registry a `GameContext` exposes as `ctx.particles`. State is data-only and serializable: standing emitters survive `snapshot`/`restore`, queued bursts are transient. The renderer subscribes, drains bursts, and mirrors the emitter list — the director never allocates particle pools itself.
1930
+ - `ParticleEmitterSpec` (interface): interface ParticleEmitterSpec extends ParticleAttachOptions — A keyed continuous emitter owned by the director until detached.
1931
+ - `ParticleRenderHint` (interface): interface ParticleRenderHint — Render output selected independently of particle spawn and simulation.
1932
+ - `createParticleDirector` (function): function createParticleDirector(options: ParticleDirectorOptions = {}): ParticleDirector — Create the particle intent registry a `GameContext` exposes as `ctx.particles`. State is data-only and serializable: standing emitters survive `snapshot`/`restore`, queued bursts are transient. The renderer subscribes, drains bursts, and mirrors the emitter list — the director never allocates particle pools itself.
1933
+ - `validateParticleAttachOptions` (function): function validateParticleAttachOptions(value: unknown): string[] — Validate serializable binding, output, and bounded collision-child descriptors.
1671
1934
 
1672
1935
  ## @jgengine/core/vfx/particles
1673
1936
 
1674
1937
  - `Curve` (interface): interface Curve — A per-life start→end curve (linear interpolation from birth to death).
1675
1938
  - `EmitterConfig` (interface): interface EmitterConfig — A particle emitter: how particles spawn and how each one evolves over its life. Every field is data — no functions — so an emitter is fully serializable and an editor/tunable can drive it. Genre-agnostic: smoke, sparks, rain, magic, dust.
1676
1939
  - `ParticleBuffers` (interface): interface ParticleBuffers — Read-only packed buffers of the live particles, laid out for a renderer to upload directly (Structure-of-Arrays, no per-particle objects). Only the first `count` entries are live; the arrays themselves are reused every frame.
1940
+ - `ParticleCollisionConfig` (interface): interface ParticleCollisionConfig — Bounded particle contact policy; a query adapter can replace the fallback horizontal plane.
1941
+ - `ParticleCollisionHit` (interface): interface ParticleCollisionHit — One contact from an injected scene query, in simulation coordinates.
1942
+ - `ParticleEvent` (interface): interface ParticleEvent — Presentation events never authorize gameplay damage or physical impulses.
1677
1943
  - `ParticleSnapshot` (interface): interface ParticleSnapshot — Serializable simulation state for save/restore and deterministic replay.
1944
+ - `ParticleSpawnShape` (interface): interface ParticleSpawnShape — Serializable spawn volume in emitter coordinates; cone widens from its base along +Y.
1678
1945
  - `ParticleSystem` (interface): interface ParticleSystem — A live, dt-driven particle simulation.
1946
+ - `ParticleSystemOptions` (interface): interface ParticleSystemOptions — Optional geometry adapter; queries are capped independently of rendering.
1679
1947
  - `Range` (interface): interface Range — A `[min, max]` range a spawned particle draws uniformly from.
1680
1948
  - `Vec3` (type): type Vec3 = readonly [number, number, number] — A 3D vector `[x, y, z]`.
1681
- - `createParticleSystem` (function): function createParticleSystem(config: EmitterConfig = {}): ParticleSystem — A generic, allocation-aware particle system: one emitter, a fixed pool, and Structure-of-Arrays buffers a renderer uploads straight to the GPU. It is dt-driven (call `update(dt)` each frame) and deterministic — all randomness flows from an injected `seed`, so the same seed and dt sequence reproduce the same frames, and `snapshot`/`restore` round-trips the live pool. Nothing here is combat- or genre-specific: configure it for smoke, sparks, rain, dust, embers, magic, or confetti. Travel/gameplay stays elsewhere; this owns only the spawn-integrate-fade lifecycle.
1949
+ - `createParticleSystem` (function): function createParticleSystem(config: EmitterConfig = {}, options: ParticleSystemOptions = {}): ParticleSystem — A generic, allocation-aware particle system: one emitter, a fixed pool, and Structure-of-Arrays buffers a renderer uploads straight to the GPU. It is dt-driven (call `update(dt)` each frame) and deterministic — all randomness flows from an injected `seed`, so the same seed and dt sequence reproduce the same frames, and `snapshot`/`restore` round-trips the live pool. Nothing here is combat- or genre-specific: configure it for smoke, sparks, rain, dust, embers, magic, or confetti. Travel/gameplay stays elsewhere; this owns only the spawn-integrate-fade lifecycle.
1950
+ - `validateEmitterConfig` (function): function validateEmitterConfig(value: unknown): string[] — Validate the serializable spawn/update descriptor before authoring or loading it.
1682
1951
 
1683
1952
  ## @jgengine/core/vfx/screenEffects
1684
1953
 
@@ -1694,14 +1963,14 @@
1694
1963
 
1695
1964
  ## @jgengine/core/visibility/assetStreaming
1696
1965
 
1697
- - `AssetLoadResult` (interface): interface AssetLoadResult — ⚠ undocumented
1966
+ - `AssetLoadResult` (interface): interface AssetLoadResult { readonly bytes?: number; readonly value?: unknown } — ⚠ undocumented
1698
1967
  - `AssetLoadState` (type): type AssetLoadState = "queued" | "loading" | "loaded" | "unloaded" | "error" — ⚠ undocumented
1699
- - `AssetRecord` (interface): interface AssetRecord — ⚠ undocumented
1700
- - `AssetStreamingOptions` (interface): interface AssetStreamingOptions — ⚠ undocumented
1701
- - `AssetStreamingSystem` (interface): interface AssetStreamingSystem — ⚠ undocumented
1702
- - `CancelSignal` (interface): interface CancelSignal — ⚠ undocumented
1703
- - `StreamingStats` (interface): interface StreamingStats — ⚠ undocumented
1704
- - `createAssetStreamingSystem` (function): function createAssetStreamingSystem(options: AssetStreamingOptions): AssetStreamingSystem — ⚠ undocumented
1968
+ - `AssetRecord` (interface): interface AssetRecord { readonly id: string; state: AssetLoadState; bytes: number; priority: number; refCount: number; pinned: boolean; lastActiveMs: number; value: unknown } — ⚠ undocumented
1969
+ - `AssetStreamingOptions` (interface): interface AssetStreamingOptions { readonly load: (assetId: string, signal: CancelSignal) => Promise<AssetLoadResult>; readonly unload?: (assetId: string) => void; readonly settings?: Partial<StreamingSettings>; readonly now?: () => number } — ⚠ undocumented · used by `createAssetStreamingSystem`: Stream shared resources with bounded load concurrency, per-tick resident-byte eviction, and explicit inactive-history cleanup.
1970
+ - `AssetStreamingSystem` (interface): interface AssetStreamingSystem { request(assetId: string, priority?: number): void; retain(assetId: string): void; release(assetId: string): void; pin(assetId: string): void; unpin(assetId: string): void; markActive(assetId: string): void; tick(dt: number): void; cancel(… — ⚠ undocumented · used by `createAssetStreamingSystem`: Stream shared resources with bounded load concurrency, per-tick resident-byte eviction, and explicit inactive-history cleanup.
1971
+ - `CancelSignal` (interface): interface CancelSignal { readonly cancelled: boolean } — ⚠ undocumented
1972
+ - `StreamingStats` (interface): interface StreamingStats — Current demand/residency gauges and cumulative unload/cancellation counters; clear resets totals.
1973
+ - `createAssetStreamingSystem` (function): function createAssetStreamingSystem(options: AssetStreamingOptions): AssetStreamingSystem — Stream shared resources with bounded load concurrency, per-tick resident-byte eviction, and explicit inactive-history cleanup.
1705
1974
 
1706
1975
  ## @jgengine/core/visibility/bounds
1707
1976
 
@@ -1709,7 +1978,7 @@
1709
1978
  - `BoundsSpec` (type): type BoundsSpec = | { readonly kind: "sphere"; readonly radius: number; readonly offset?: Vec3 } | { readonly kind: "aabb"; readonly half: Vec3; readonly offset?: Vec3 } | { readonly kind: "rect"; readonly halfWidth: number; readonly halfDepth: number; readonly halfHeight?: number; readonly offset?:… — How a renderable declares its extent. AABB, bounding sphere, and 2D rectangle cover the common cases; `point` is the degenerate zero-size default for objects that never override. `offset` shifts the volume from the object origin (e.g. a tall model whose pivot is at its feet).
1710
1979
  - `DEFAULT_BOUNDS` (const): const DEFAULT_BOUNDS: BoundsSpec — Conservative default: a unit sphere. Objects without explicit bounds cull as a small volume, never a point.
1711
1980
  - `RenderBounds` (interface): interface RenderBounds — World-space bounds a culler tests against. Kept as flat scalars (not tuples) so the hot culling path reads them without allocating. Carries both a bounding sphere (cheap broad reject) and a tight AABB (precise reject) — the standard "engine bounds" the frustum, distance, and spatial-index tests all consume.
1712
- - `Vec3` (type): type Vec3 = EntityPosition — ⚠ undocumented
1981
+ - `Vec3` (type): type Vec3 = EntityPosition — ⚠ undocumented · used by `coverPoints` (@jgengine/core/ai/tacticalQueries): Returns boundary locations that are hidden from a threat.
1713
1982
 
1714
1983
  ## @jgengine/core/visibility/camera
1715
1984
 
@@ -1737,14 +2006,14 @@
1737
2006
  ## @jgengine/core/visibility/frustum
1738
2007
 
1739
2008
  - `CameraView` (type): type CameraView = PerspectiveView | OrthographicView — ⚠ undocumented
1740
- - `Frustum` (interface): interface Frustum — ⚠ undocumented
2009
+ - `Frustum` (interface): interface Frustum { readonly planes: Float64Array; readonly corners: Float64Array; minX: number; minY: number; minZ: number; maxX: number; maxY: number; maxZ: number } — ⚠ undocumented
1741
2010
  - `OrthographicView` (interface): interface OrthographicView — 2D / orthographic camera (top-down, side-scroller, minimap).
1742
2011
  - `PerspectiveView` (interface): interface PerspectiveView — 3D perspective camera (the default game camera).
1743
2012
 
1744
2013
  ## @jgengine/core/visibility/occlusion
1745
2014
 
1746
- - `AxisOccluderOptions` (interface): interface AxisOccluderOptions — ⚠ undocumented
1747
- - `OcclusionInput` (interface): interface OcclusionInput — ⚠ undocumented
2015
+ - `AxisOccluderOptions` (interface): interface AxisOccluderOptions { enabled?: boolean; minOccluderRadius?: number } — ⚠ undocumented · used by `createAxisAlignedOcclusionTester`: A deliberately conservative bounding-volume occlusion tester.
2016
+ - `OcclusionInput` (interface): interface OcclusionInput { readonly camera: CameraView; readonly bounds: RenderBounds; readonly occluders: Iterable<RenderBounds> } — ⚠ undocumented
1748
2017
  - `OcclusionTester` (interface): interface OcclusionTester — Optional occlusion stage. Kept behind a feature flag and disabled by default: a fragile occlusion test that wrongly hides a visible object is worse than no occlusion at all. Any tester here MUST be conservative — it may return false (visible) for a truly hidden object, but must never return true (hidden) for an object the player can actually see.
1749
2018
  - `createAxisAlignedOcclusionTester` (function): function createAxisAlignedOcclusionTester(options: AxisOccluderOptions = {}): OcclusionTester — A deliberately conservative bounding-volume occlusion tester. It hides an object only when a single large occluder's AABB fully contains the object's AABB on every axis AND sits strictly nearer the camera on the dominant view axis — the object is then provably in the occluder's shadow. Full containment (not just the two perpendicular axes) makes it stricter than it needs to be, which is the safe direction: it never hides a partially-visible object.
1750
2019
  - `noOcclusion` (const): const noOcclusion: OcclusionTester — The safe default: never occludes anything.
@@ -1755,44 +2024,47 @@
1755
2024
  - `DEFAULT_CULLING_SETTINGS` (const): const DEFAULT_CULLING_SETTINGS: CullingSettings — ⚠ undocumented
1756
2025
  - `DEFAULT_STREAMING_SETTINGS` (const): const DEFAULT_STREAMING_SETTINGS: StreamingSettings — ⚠ undocumented
1757
2026
  - `ObjectVisibilityOverrides` (interface): interface ObjectVisibilityOverrides — Per-object visibility/streaming overrides. Also the shape used at the layer, scene, and global-override levels — resolution merges object → layer → scene → global-override, so a game can, say, mark a whole "hud" layer alwaysVisible without touching each object.
1758
- - `ResolvedOverrides` (interface): interface ResolvedOverrides — ⚠ undocumented
1759
- - `StreamingSettings` (interface): interface StreamingSettings — ⚠ undocumented
2027
+ - `ResolvedOverrides` (interface): interface ResolvedOverrides { alwaysVisible: boolean; neverUnload: boolean; minRenderDistance: number | undefined; maxRenderDistance: number | undefined; preloadMargin: number | undefined; cullingDisabled: boolean; streamingDisabled: boolean; classification: "static" … — ⚠ undocumented · used by `resolveOverrides`: Merge override layers by precedence: earlier arguments win.
2028
+ - `StreamingSettings` (interface): interface StreamingSettings { readonly enabled: boolean; readonly preloadMargin: number; readonly unloadGraceSeconds: number; readonly maxLoadsPerFrame: number; readonly maxConcurrentLoads: number; readonly maxUnloadsPerFrame: number; readonly maxResidentBytes: number… — ⚠ undocumented
1760
2029
  - `mergeCullingSettings` (function): function mergeCullingSettings(base: CullingSettings, patch: Partial<CullingSettings>): CullingSettings — ⚠ undocumented
1761
2030
  - `mergeStreamingSettings` (function): function mergeStreamingSettings(base: StreamingSettings, patch: Partial<StreamingSettings>): StreamingSettings — ⚠ undocumented
1762
2031
  - `resolveOverrides` (function): function resolveOverrides(...layers: readonly (ObjectVisibilityOverrides | undefined)[]): ResolvedOverrides — Merge override layers by precedence: earlier arguments win. Pass object, then layer, then scene, then global.
1763
2032
 
1764
2033
  ## @jgengine/core/visibility/simulationCulling
1765
2034
 
1766
- - `SimulationCuller` (interface): interface SimulationCuller — ⚠ undocumented
2035
+ - `SimulationCuller` (interface): interface SimulationCuller { enabled(): boolean; setEnabled(value: boolean): void; step(id: string, distance: number, dt: number): SimulationDecision; forget(id: string): void; clear(): void; snapshot(): SimulationCullerState; restore(next: SimulationCullerState): vo… — ⚠ undocumented
2036
+ - `SimulationCullerState` (interface): interface SimulationCullerState — Plain JSON state of a {@link SimulationCuller}: the enabled flag and each id's throttle accumulator.
1767
2037
  - `SimulationCullingOptions` (interface): interface SimulationCullingOptions — Simulation culling is a SEPARATE, opt-in system from render culling. Render culling only decides what is drawn; this decides whether a low-priority off-screen entity updates this tick. It is disabled by default and never throttles a protected entity — physics-critical, networking-critical, audio-critical, scripted, or explicitly-active entities always update. Gameplay correctness must never depend on an entity being on-screen, so opt in only where skipping updates is provably safe.
1768
- - `SimulationDecision` (interface): interface SimulationDecision — ⚠ undocumented
2038
+ - `SimulationDecision` (interface): interface SimulationDecision { update: boolean; elapsed: number } — ⚠ undocumented
1769
2039
  - `createSimulationCuller` (function): function createSimulationCuller(options: SimulationCullingOptions = {}): SimulationCuller — ⚠ undocumented
1770
2040
 
1771
2041
  ## @jgengine/core/visibility/spatialIndex
1772
2042
 
2043
+ - `SpatialCellRange` (interface): interface SpatialCellRange — Integer cell bounds an object covers, inclusive on both ends.
1773
2044
  - `SpatialIndex` (interface): interface SpatialIndex — A uniform 3D spatial hash the renderer and streaming system query for potentially-visible objects instead of scanning the whole scene. Objects are keyed by their world AABB into every overlapping cell; a moving object only rewrites the cells that actually changed. Static objects are inserted once and never touched again. Oversized objects (huge terrain chunks, world bounds) are held separately so they are always considered.
1774
- - `SpatialIndexOptions` (interface): interface SpatialIndexOptions — ⚠ undocumented
2045
+ - `SpatialIndexOptions` (interface): interface SpatialIndexOptions { readonly cellSize?: number; readonly maxCellSpan?: number } — ⚠ undocumented
2046
+ - `SpatialIndexState` (interface): interface SpatialIndexState — Plain JSON state of a {@link SpatialIndex}. Cells and their ids keep insertion order, so a restored index returns query results in the same order.
1775
2047
  - `createSpatialIndex` (function): function createSpatialIndex(options: SpatialIndexOptions = {}): SpatialIndex — ⚠ undocumented
1776
2048
 
1777
2049
  ## @jgengine/core/visibility/visibilitySystem
1778
2050
 
1779
2051
  - `Renderable` (interface): interface Renderable — A scene object the visibility system considers. A normal game object already carries a position and a version counter, so it becomes cullable automatically — no separate "cullable" component. Everything else is optional override.
1780
- - `VisibilityDebugSnapshot` (interface): interface VisibilityDebugSnapshot — ⚠ undocumented
1781
- - `VisibilityResult` (interface): interface VisibilityResult — ⚠ undocumented
1782
- - `VisibilitySystem` (interface): interface VisibilitySystem — ⚠ undocumented
1783
- - `VisibilitySystemOptions` (interface): interface VisibilitySystemOptions — ⚠ undocumented
2052
+ - `VisibilityDebugSnapshot` (interface): interface VisibilityDebugSnapshot { cameras: {id: string; kind: CameraKind; corners: number[]; preloadMargin: number}[]; bounds: {id: string; minX: number; minY: number; minZ: number; maxX: number; maxY: number; maxZ: number; visible: boolean}[]; partitions: {key: string; c… — ⚠ undocumented
2053
+ - `VisibilityResult` (interface): interface VisibilityResult { readonly visible: ReadonlySet<string>; readonly preload: ReadonlySet<string>; readonly stats: VisibilityStats } — ⚠ undocumented
2054
+ - `VisibilitySystem` (interface): interface VisibilitySystem { update(): VisibilityResult; isVisible(id: string): boolean; isPreloaded(id: string): boolean; boundsOf(id: string): RenderBounds | undefined; settings(): CullingSettings; setSettings(patch: Partial<CullingSettings>): void; stats(): Visibi… — ⚠ undocumented
2055
+ - `VisibilitySystemOptions` (interface): interface VisibilitySystemOptions { readonly renderables: () => Iterable<Renderable>; readonly cameras: () => readonly CameraVisibilityContext[]; readonly settings?: Partial<CullingSettings>; readonly sceneOverrides?: ObjectVisibilityOverrides; readonly globalOverrides?: Ob… — ⚠ undocumented
1784
2056
  - `createVisibilitySystem` (function): function createVisibilitySystem(options: VisibilitySystemOptions): VisibilitySystem — ⚠ undocumented
1785
2057
 
1786
2058
  ## @jgengine/core/world
1787
2059
 
1788
2060
  - `ANNOTATION_FEED_ACTION` (const): const ANNOTATION_FEED_ACTION: "map.annotation" — Feed action shared drawings ride by default (pair with the party feed, like pings).
1789
- - `Aabb` (interface): interface Aabb — ⚠ undocumented
2061
+ - `Aabb` (interface): interface Aabb { minX: number; minZ: number; maxX: number; maxZ: number } — ⚠ undocumented · used by `polygonBounds`: Axis-aligned bounds of a polygon, or null if it has no points.
1790
2062
  - `AcquisitionEnvelope` (type): type AcquisitionEnvelope = number | ((selfId: string, candidateId: string) => number) — Composable target acquisition: the "which enemy do I lock onto?" decision split into the independent concerns every aggro system tangles together — a bounded candidate provider, an eligibility filter, a per-pair acquisition envelope (dynamic range), a perception/LOS gate, scoring, deterministic tie-break, and retention hysteresis. This owns *policy*; threat, brains, and movement stay separate. Feed `candidates` a spatial-index query, never a full-world scan.
1791
2063
  - `AcquisitionPolicy` (interface): interface AcquisitionPolicy — A fully composed acquisition policy. Every concern is an independent, injectable seam; the only required pieces are the bounded `candidates` provider and the `distance` metric. Omit the rest to fall back to the thin default — a static/unbounded range, everything eligible and perceptible, nearest-wins scoring, id tie-break, no hysteresis — which matches a plain proximity aggro radius.
1792
2064
  - `AcquisitionResult` (interface): interface AcquisitionResult — Outcome of one acquisition pass.
1793
2065
  - `AcquisitionRetention` (interface): interface AcquisitionRetention — Retention hysteresis that keeps an already-held target from flickering under churn.
1794
2066
  - `AddBodyOptions` (type): type AddBodyOptions = BoxBodyOptions | SphereBodyOptions — ⚠ undocumented
1795
- - `Aim` (type): type Aim = | { origin: EntityPosition; direction: EntityPosition } | { yaw: number; pitch: number; spread?: number } — ⚠ undocumented
2067
+ - `Aim` (type): type Aim = | { origin: EntityPosition; direction: EntityPosition } | { yaw: number; pitch: number; spread?: number } — ⚠ undocumented · used by `aimToPoint` (@jgengine/core/input/pointer): Build an `origin → point` aim for `item.use` / projectiles, firing toward the cursor.
1796
2068
  - `AircraftAxis` (type): type AircraftAxis = "pitch" | "roll" | "yaw" | "throttle" | "collective" | "airbrake" | "afterburner" | "vectoring" — Axes an aircraft can bind — which actions drive each flight control.
1797
2069
  - `AircraftDynamics` (interface): interface AircraftDynamics — Stateful six-degree-of-freedom aircraft simulation.
1798
2070
  - `AircraftKind` (type): type AircraftKind = "fixedWing" | "rotorcraft" | "vtol" — Supported aerodynamic/propulsion families.
@@ -1816,39 +2088,41 @@
1816
2088
  - `AreaStackPolicy` (type): type AreaStackPolicy<P> = (memberships: readonly AreaMembership<P>[]) => AreaMembership<P>[] — Reduce one receiver's overlapping memberships to the subset that applies under a stacking rule.
1817
2089
  - `AreaStepInput` (interface): interface AreaStepInput — Per-tick inputs the field needs to reconcile membership without scanning the world.
1818
2090
  - `AssetAnchor` (type): type AssetAnchor = "center" | "corner" | { readonly x: number; readonly z: number } — Where a model's pivot sits within its footprint: `center` (origin at the footprint center), `corner` (origin at the min-X/min-Z corner), or a normalized `{ x, z }` fraction in `[-0.5, 0.5]`.
1819
- - `AssetCatalog` (interface): interface AssetCatalog<TMeta extends ModelAssetRef = ModelAssetRef> — ⚠ undocumented
2091
+ - `AssetCatalog` (interface): interface AssetCatalog<TMeta extends ModelAssetRef = ModelAssetRef> { register(id: string, asset: TMeta): void; resolve(id: string): TMeta | null; has(id: string): boolean; ids(): readonly string[] } — ⚠ undocumented · used by `buildCatalog` (@jgengine/assets): Builds a game's asset catalog from the generated CC0 pack index plus singles, extras, and aliases.
1820
2092
  - `AssetSpace` (interface): interface AssetSpace — Catalog-level asset-space metadata: how a model is authored relative to the engine, owned by the catalog entry rather than re-derived per game. Every field is optional so an unmeasured asset stays valid; {@link resolveFacingRotationY}/{@link assetUnitScale}/{@link resolveAnchorOffset} supply the documented defaults.
1821
2093
  - `AssetSpaceIssue` (interface): interface AssetSpaceIssue — One problem found by {@link validateAssetSpace}: which metadata field and why it is invalid.
1822
2094
  - `AttackMoveOrderPayload` (interface): interface AttackMoveOrderPayload — Payload for an attack-move: advance toward `point`, engaging hostiles encountered en route.
1823
- - `AudioBusDef` (interface): interface AudioBusDef — ⚠ undocumented
1824
- - `AudioFalloffConfig` (interface): interface AudioFalloffConfig — ⚠ undocumented
2095
+ - `AudioBusDef` (interface): interface AudioBusDef { id: AudioBusId; gain?: number } — ⚠ undocumented
2096
+ - `AudioFalloffConfig` (interface): interface AudioFalloffConfig { minDistance?: number; maxDistance?: number; curve?: FalloffCurve } — ⚠ undocumented
1825
2097
  - `AuthoredTrigger` (interface): interface AuthoredTrigger — One resolved trigger binding from a document object.
1826
2098
  - `AuthoredTriggerRuntime` (interface): interface AuthoredTriggerRuntime — Runtime handle that watches authored triggers against moving actors each tick.
1827
2099
  - `AutoTargetPolicy` (type): type AutoTargetPolicy = | "nearest" | "farthest" | "random" | "strongest" | "weakest" | "first" | "last" — ⚠ undocumented
1828
2100
  - `AvoidZone` (interface): interface AvoidZone — A circular clearance around a gameplay spot (spawn, plot, path point, POI): scatter is repelled from it and terrain is flattened toward its center. `feather` (meters) is the soft outer band — full effect within `radius - feather`, ramping to zero at `radius`.
1829
2101
  - `AvoidanceAgent` (interface): interface AvoidanceAgent — A circular agent that avoidance may push on the XZ plane.
1830
- - `BallisticSweep` (type): type BallisticSweep = ( origin: readonly [number, number, number], velocity: readonly [number, number, number], gravity: number, maxTime: number, ) => BallisticSweepHit | null — ⚠ undocumented
1831
- - `BallisticSweepHit` (interface): interface BallisticSweepHit — ⚠ undocumented
2102
+ - `BallisticSweep` (type): type BallisticSweep = ( origin: readonly [number, number, number], velocity: readonly [number, number, number], gravity: number, maxTime: number, ) => BallisticSweepHit | null — ⚠ undocumented · used by `createBallisticSweep`: Sweeps each short chord of the closed-form arc through live body AABBs, including sleeping bodies.
2103
+ - `BallisticSweepHit` (interface): interface BallisticSweepHit { point: [number, number, number]; time: number } — ⚠ undocumented
1832
2104
  - `BandTrimOptions` (interface): interface BandTrimOptions extends JunctionGeometryOptions — Options for {@link trimBandAtJunctions} — junction geometry tunables plus a band-specific clearance.
1833
2105
  - `BehaviorAction` (type): type BehaviorAction = DecisionAction<BehaviorActionContext> — Callback used by a registered decision graph behavior action.
1834
2106
  - `BehaviorActionContext` (interface): interface BehaviorActionContext — Context supplied to a registered decision graph action.
2107
+ - `BehaviorActionOptions` (interface): interface BehaviorActionOptions — Optional hooks for a registered behavior action set.
1835
2108
  - `BehaviorControl` (interface): interface BehaviorControl — Per-entity control surface for the behavior runtime — pause/resume/disable/enable an instance, seek it to semantic progress, serialize/restore its state, and inspect it, all keyed by stable entity id without a full-world scan. Obtain it with {@link behaviorControl}.
1836
- - `BehaviorDescriptor` (type): type BehaviorDescriptor = | WanderBehavior | PatrolBehavior | PromptableBehavior | PlayerBehavior | DecisionGraphBehavior — ⚠ undocumented
2109
+ - `BehaviorDescriptor` (type): type BehaviorDescriptor = | WanderBehavior | PatrolBehavior | PromptableBehavior | PlayerBehavior | PursueBehavior | DecisionGraphBehavior — ⚠ undocumented
1837
2110
  - `BehaviorInspection` (interface): interface BehaviorInspection — Inspection readout for editor/debug tooling, from {@link BehaviorControl.inspect}/{@link BehaviorControl.list}.
1838
- - `BehaviorResumePolicy` (type): type BehaviorResumePolicy = "freeze" | "advance" — Catch-up policy applied when a paused instance resumes. `freeze` (default, deterministic) discards the time spent paused; `advance` silently fast-forwards the instance by the paused duration without emitting intermediate pose writes. Only patrol honors `advance`; wander treats it as `freeze`.
1839
- - `BehaviorSnapshot` (type): type BehaviorSnapshot = | { readonly kind: "patrol"; readonly state: PathFollowState } | { readonly kind: "wander"; readonly origin: Waypoint; readonly target: Waypoint | null } | { readonly kind: "decisionGraph"; readonly graph: ReturnType<DecisionGraphRuntime["snapshot"]>; readonly gate: InterestG… — Serializable snapshot of one behavior instance — round-trips exactly through {@link BehaviorControl.serialize}/{@link BehaviorControl.restore}.
2111
+ - `BehaviorResumePolicy` (type): type BehaviorResumePolicy = "freeze" | "advance" — Catch-up policy applied when a paused instance resumes. `freeze` (default, deterministic) discards the time spent paused; `advance` silently fast-forwards the instance by the paused duration without emitting intermediate pose writes. Only patrol honors `advance`; wander/pursue treat it as `freeze`.
2112
+ - `BehaviorSnapshot` (type): type BehaviorSnapshot = | { readonly kind: "patrol"; readonly state: PathFollowState } | { readonly kind: "wander"; readonly origin: Waypoint; readonly target: Waypoint | null } | PursueBehaviorSnapshot | { readonly kind: "decisionGraph"; readonly graph: ReturnType<DecisionGraphRuntime["snapshot"]>;… — Serializable snapshot of one behavior instance — round-trips exactly through {@link BehaviorControl.serialize}/{@link BehaviorControl.restore}.
1840
2113
  - `BehaviorStatus` (type): type BehaviorStatus = "active" | "paused" | "disabled" — Whether a behavior instance advances and writes pose (`active`), is temporarily suspended retaining state (`paused`), or is held off until re-enabled (`disabled`).
1841
2114
  - `BiomeBand` (interface): interface BiomeBand — A z-ordered ground palette zone — the linear-boundary counterpart to the radial `materialRegions`. Adjacent bands cross-fade into each other across a `fade`-wide window centered on the midpoint z between their centers, so a multi-biome world (vale → marsh → peaks along z) blends its ground color instead of hard-switching. Bands may also carry per-zone `fog`, `sky`, and `weather`. Order the list by ascending `z`.
1842
2115
  - `BoardGround` (interface): interface BoardGround — A 2D surface you look at — grid games, solitaire, tabletop. Physics is often zero-gravity or omitted; the game owns the face it draws on the board.
1843
2116
  - `BoardGroundSize` (interface): interface BoardGroundSize — Size of a `board` ground: a 2D surface you look at, in cells or layout units.
2117
+ - `BoardVehicleOptions` (interface): interface BoardVehicleOptions extends EnterVehicleOptions — Seat selection and optional local camera policy for an explicit rider transition.
1844
2118
  - `BookmarkRecallMode` (type): type BookmarkRecallMode = "replace" | "merge" — How a recalled bookmark folds into the active selection.
1845
2119
  - `BoundsSpec` (type): type BoundsSpec = | { readonly kind: "sphere"; readonly radius: number; readonly offset?: Vec3 } | { readonly kind: "aabb"; readonly half: Vec3; readonly offset?: Vec3 } | { readonly kind: "rect"; readonly halfWidth: number; readonly halfDepth: number; readonly halfHeight?: number; readonly offset?:… — How a renderable declares its extent. AABB, bounding sphere, and 2D rectangle cover the common cases; `point` is the degenerate zero-size default for objects that never override. `offset` shifts the volume from the object origin (e.g. a tall model whose pivot is at its feet).
1846
2120
  - `BoxFormationOptions` (interface): interface BoxFormationOptions — Options for {@link boxFormation}.
1847
2121
  - `BuildRole` (type): type BuildRole = "owner" | "editor" | "viewer" — ⚠ undocumented
1848
- - `BuildingEnvironmentDescriptor` (type): type BuildingEnvironmentDescriptor = { kind: "building" } & Required< Pick<BuildingEnvironmentConfig, "count" | "footprint" | "stories" | "storyHeight" | "spacing" | "style"> > & Pick<BuildingEnvironmentConfig, "seed" | "position" | "palette" | "along" | "kit"> — ⚠ undocumented
1849
- - `BuildingIndex` (interface): interface BuildingIndex — ⚠ undocumented
2122
+ - `BuildingEnvironmentDescriptor` (type): type BuildingEnvironmentDescriptor = { kind: "building" } & Required< Pick<BuildingEnvironmentConfig, "count" | "footprint" | "stories" | "storyHeight" | "spacing" | "style"> > & Pick<BuildingEnvironmentConfig, "seed" | "position" | "palette" | "along" | "kit" | "solid"> — ⚠ undocumented · used by `building`: Declares a cluster of procedurally-massed buildings for `environment()` — count, footprint, stories, style.
2123
+ - `BuildingIndex` (interface): interface BuildingIndex { readonly buildings: readonly GeneratedBuilding[]; at(point: Vec2): GeneratedBuilding | undefined; within(area: Aabb): GeneratedBuilding[]; nearest(point: Vec2): BuildingHit | undefined; isInside(point: Vec2): boolean; blockers(margin?: nu… — ⚠ undocumented
1850
2124
  - `BuildingPaletteOverrides` (type): type BuildingPaletteOverrides = Partial<BuildingPalette> — ⚠ undocumented
1851
- - `BuildingStyle` (type): type BuildingStyle = | "generic" | "capital" | "village" | "desert" | "industrial" | "coastal" | "neon" | "ruin" | "frontier" | "aerial" — ⚠ undocumented
2125
+ - `BuildingStyle` (type): type BuildingStyle = | "generic" | "capital" | "village" | "desert" | "industrial" | "coastal" | "neon" | "ruin" | "frontier" | "aerial" — ⚠ undocumented · used by `BUILDING_STYLE_WALL_TONES` (@jgengine/core/world/buildings): Per-style facade-tone family: the wall colours a district spreads across its buildings so neighbours differ in hue and value while the block…
1852
2126
  - `CITY_BUILDING_BUDGET` (const): const CITY_BUILDING_BUDGET: 2600 — Max buildings a single `city` district generates — the block/parcel pipeline's bounded lot cap.
1853
2127
  - `CITY_DEFAULTS` (const): const CITY_DEFAULTS: CityRules — City defaults: a zoned mixed metropolis — towers downtown, slabs mid-ring, houses at the edge.
1854
2128
  - `CITY_FILLER_CLASSES` (const): const CITY_FILLER_CLASSES: readonly CityFillerClass[] — All filler classes, for validation and interior-pass allow-lists.
@@ -1906,13 +2180,13 @@
1906
2180
  - `CityZoneBand` (type): type CityZoneBand = "core" | "mid" | "edge" — Zone band a lot falls in: dense core, middle ring, or the district edge.
1907
2181
  - `CityZoneMixes` (interface): interface CityZoneMixes — Weighted building-class mix per zone band; the radial profile decides which band a lot falls in.
1908
2182
  - `CityZoneProfile` (type): type CityZoneProfile = "core-out" | "inverted" | "uniform" — How the radial zone metric maps to bands.
1909
- - `ClockSnapshot` (interface): interface ClockSnapshot — ⚠ undocumented
1910
- - `CollapseEvent` (interface): interface CollapseEvent — ⚠ undocumented
2183
+ - `ClockSnapshot` (interface): interface ClockSnapshot { now: number; paused: boolean; speed: number; playSpeed: number; timescale: number; scale: number; speeds: readonly number[]; calendar: CalendarTime } — ⚠ undocumented
2184
+ - `CollapseEvent` (interface): interface CollapseEvent { fell: readonly string[]; collapsed: boolean } — ⚠ undocumented
1911
2185
  - `ColliderPurpose` (type): type ColliderPurpose = "physical" | "damage" — ⚠ undocumented
1912
2186
  - `CollisionEvent` (interface): interface CollisionEvent — A contact reported to `onCollision`. The object is reused each call — read/copy, never retain.
1913
2187
  - `CollisionObstacle` (interface): interface CollisionObstacle — A placed scene object the walking player collides against as a circle-vs-AABB obstacle.
1914
- - `ConcealmentSensor` (interface): interface ConcealmentSensor — ⚠ undocumented
1915
- - `ContextMenu` (interface): interface ContextMenu — ⚠ undocumented
2188
+ - `ConcealmentSensor` (interface): interface ConcealmentSensor { tick(targets: readonly ConcealmentTarget[], dt: number): ConcealmentSample[]; reset(id?: string): void; snapshot(): ConcealmentSensorState; restore(next: ConcealmentSensorState): void } — ⚠ undocumented
2189
+ - `ContextMenu` (interface): interface ContextMenu { kind: ContextTargetKind; targetId: string; point?: readonly [number, number, number]; verbs: readonly ContextVerb[] } — ⚠ undocumented · used by `buildContextMenu`: Assemble a menu from a target's catalog verbs; null when the target lists none.
1916
2190
  - `ContextVerb` (interface): interface ContextVerb — One right-click verb: a label plus the command it dispatches (walk-then-act supported by args).
1917
2191
  - `ContourLine` (interface): interface ContourLine — A single iso-elevation contour traced across a region as a flat list of XZ line segments.
1918
2192
  - `ContourOptions` (interface): interface ContourOptions — Shaping for {@link extractContours}: region, vertical spacing, emphasis cadence, and sample budget.
@@ -1941,35 +2215,37 @@
1941
2215
  - `DayNightSnapshot` (interface): interface DayNightSnapshot — Serializable day-night position — accumulated clock offset, pause state, and speed — for save/load.
1942
2216
  - `DecisionGraphBehavior` (interface): interface DecisionGraphBehavior — Decision graph behavior descriptor using a named registered action set.
1943
2217
  - `DrapeOptions` (interface): interface DrapeOptions — Shaping for surface draping: subdivision spacing and a lift to keep the line off the ground.
1944
- - `EditableTerrain` (interface): interface EditableTerrain extends TerrainField — ⚠ undocumented
2218
+ - `EditableTerrain` (interface): interface EditableTerrain extends TerrainField { readonly cols: number; readonly rows: number; readonly cellSize: number; apply(edit: TerraformEdit): number; applyRecording(edit: TerraformEdit, record: TerraformDeltaRecorder): number; editDelta(edit: TerraformEdit): TerraformDelta; appl… — ⚠ undocumented · used by `beginSurfaceStroke`: Opens a paint-stroke recorder over `terrain`; stamp paint edits into it, then read one net delta.
1945
2219
  - `ElevationReadout` (interface): interface ElevationReadout — Measurable elevation readout at a single world point — the cursor/hover feedback value.
1946
2220
  - `ElevationSummary` (interface): interface ElevationSummary — Aggregate elevation statistics over a region — the selection min/max/mean and legend range.
1947
2221
  - `EmittedCue` (interface): interface EmittedCue<Payload = unknown> — A cue plus the resolved firing context passed to {@link SequenceDirector.onCue} listeners.
1948
2222
  - `EmitterConfig` (interface): interface EmitterConfig — A particle emitter: how particles spawn and how each one evolves over its life. Every field is data — no functions — so an emitter is fully serializable and an editor/tunable can drive it. Genre-agnostic: smoke, sparks, rain, magic, dust.
1949
2223
  - `EmptyOrderPayload` (type): type EmptyOrderPayload = Record<string, never> — Payload for stop/hold orders — no data; the verb is the intent.
1950
- - `EnclosedFootprint` (interface): interface EnclosedFootprint — ⚠ undocumented
2224
+ - `EnclosedFootprint` (interface): interface EnclosedFootprint { polygon: readonly Vec2[]; aabb: Aabb; area: number; perimeter: number; closed: boolean } — ⚠ undocumented
1951
2225
  - `EngagementKindConfig` (interface): interface EngagementKindConfig extends OrderKindConfig — Move + engagement config carrying the default radii the payload may override.
1952
2226
  - `EngagementOrderState` (interface): interface EngagementOrderState — Live intent an attack-move/targeted order writes into `Order.state`; the game reads it to run the actual attack. Serializable.
1953
- - `EntityColliderSet` (interface): interface EntityColliderSet — ⚠ undocumented
1954
- - `EntityPosition` (type): type EntityPosition = readonly [number, number, number] — ⚠ undocumented
1955
- - `EnvironmentField` (interface): interface EnvironmentField — ⚠ undocumented
1956
- - `EnvironmentWorldFeature` (interface): interface EnvironmentWorldFeature — ⚠ undocumented
1957
- - `FactionDef` (interface): interface FactionDef — ⚠ undocumented
2227
+ - `EntityColliderSet` (interface): interface EntityColliderSet { body?: ColliderDef; hitboxes?: readonly ColliderDef[] } — ⚠ undocumented · used by `eyeHeightFromColliders` (@jgengine/core/combat): Eye height derived from a collider set: 90% of the tallest hitbox top, or the humanoid default when unknown.
2228
+ - `EntityPosition` (type): type EntityPosition = readonly [number, number, number] — ⚠ undocumented · used by `resolveSourceWalkerStep` (@jgengine/core/movement/solidObstacles): {@link resolveWalkerStep} with no `GameContext`: gather plus slide against a bare {@link SolidObstacleSource} and a caller-owned {@link Obst…
2229
+ - `EntityStore` (interface): interface EntityStore<TMeta = unknown> — Entity state, pose writes, keyed behavior notifications and snapshot/hydration under stable instance IDs.
2230
+ - `EnvironmentField` (interface): interface EnvironmentField { sample(x: number, z: number, time: number, y?: number): EnvironmentSample; temperature(x: number, z: number, time: number, y?: number): number; wetness(x: number, z: number, time: number): number; lightExposure(x: number, z: number, time:… — ⚠ undocumented · used by `createEnvironmentField`: A sampleable environment field: read temperature, wetness, sun/sky exposure, and ambient light at any world position and time.
2231
+ - `EnvironmentWorldFeature` (interface): interface EnvironmentWorldFeature { kind: "environment"; terrain?: TerrainEnvironmentDescriptor; islands?: readonly TerrainIslandDescriptor[]; sky?: SkyEnvironmentDescriptor; weather?: readonly WeatherEnvironmentDescriptor[]; vegetation?: readonly VegetationEnvironmentDescr… — ⚠ undocumented · used by `environment`: Composes an `environment()` feature from terrain, sky, weather, vegetation, water, structures, roads, and pads.
2232
+ - `FactionDef` (interface): interface FactionDef { id: string; relations?: Readonly<Record<string, FactionRelation>>; towardSelf?: FactionRelation; towardOthers?: FactionRelation } — ⚠ undocumented
1958
2233
  - `FallbackPolicy` (type): type FallbackPolicy<P extends SamplePoint = SamplePoint> = | "none" | "last-candidate" | { readonly point: P } — What to return when the attempt budget is exhausted. `"none"` yields no point (honest failure); `"last-candidate"` returns the final rejected draw (post-projection); `{ point }` returns a caller fixed fallback (a hand-placed safe spot). Explicit, so a caller never mistakes a fallback for a hit.
1959
2234
  - `FastTravelNetwork` (interface): interface FastTravelNetwork<TMeta = unknown> — A network of fast-travel points with per-player discovery + distance queries.
1960
2235
  - `FastTravelOptions` (interface): interface FastTravelOptions<TMeta = unknown> — Options for {@link createFastTravelNetwork}.
1961
2236
  - `FastTravelSnapshot` (interface): interface FastTravelSnapshot — Serializable discovery state.
1962
- - `FireGrid` (interface): interface FireGrid — ⚠ undocumented
2237
+ - `FireGrid` (interface): interface FireGrid { readonly cols: number; readonly rows: number; readonly cellSize: number; readonly origin: readonly [number, number]; step(dt: number, options?: FireStepOptions): void; igniteCell(col: number, row: number): void; extinguishCell(col: number… — ⚠ undocumented
1963
2238
  - `FlatGround` (interface): interface FlatGround — A 3D walkable plane/slab. `Infinity` axes are unbounded; no separate "infinite" mode exists.
1964
2239
  - `FlatGroundSize` (interface): interface FlatGroundSize — Size of a `flat` ground: extents in world units. `Infinity` on an axis means unbounded — an endless plain needs no bounds number invented for it. `y` optionally bounds vertical play space.
1965
2240
  - `FlightControlInput` (interface): interface FlightControlInput — Normalized pilot inputs for one flight-simulation tick.
1966
2241
  - `FlightControlRates` (interface): interface FlightControlRates — Angular authority, response, damping, and self-leveling configuration.
1967
2242
  - `FlightVector` (type): type FlightVector = readonly [number, number, number] — Three-dimensional world-space vector used by the flight model.
1968
- - `FogCells` (interface): interface FogCells — ⚠ undocumented
2243
+ - `FogCells` (interface): interface FogCells { cols: number; rows: number; minX: number; minZ: number; cellSize: number; revealed: readonly boolean[]; revealedCount: number } — ⚠ undocumented
1969
2244
  - `FogField` (interface): interface FogField — Reveal-on-event fog of war over a fixed grid. Walking (`revealAlong`) and digging/acting (`reveal`) clear cells; once revealed a cell stays revealed. Pure and renderer-free — the shell/react map draws `cells()`.
2245
+ - `FootprintRegion` (type): type FootprintRegion = RegionFootprint | MergedRegion — A standalone footprint or a pooled region carrying its original contributions.
1970
2246
  - `ForceVolume` (class): class ForceVolume — A trigger region that pushes bodies passing through it — boost pads (`impulse` + `once`), conveyors (`velocity`), fans/wind (`accelerate`). Call `apply` each tick; `once` mode fires only on entry by tracking membership between ticks.
1971
2247
  - `FormationSlotGenerator` (type): type FormationSlotGenerator = (count: number) => Vec2[] — Produces `count` slot offsets in the group's local frame — `[right, forward]` where `+forward` points where the group faces. Index order is the slot order; a generator must be pure (same `count` → same offsets) so placement stays deterministic. Sample generators below cover common shapes; games pass their own for anything else (crowds, convoys, sports positions) with no engine edit.
1972
- - `FramingConfig` (interface): interface FramingConfig — ⚠ undocumented
2248
+ - `FramingConfig` (interface): interface FramingConfig { idealDistance?: number; centerWeight?: number; sizeWeight?: number } — ⚠ undocumented
1973
2249
  - `FreeFlightBindings` (interface): interface FreeFlightBindings — Per-axis bindings for free-flight — which actions drive each flight axis. Positive is forward/right/up.
1974
2250
  - `FreeFlightController` (interface): interface FreeFlightController — Stateful handle for any free-flight actor with snapshot/restore/retune.
1975
2251
  - `FreeFlightIntent` (interface): interface FreeFlightIntent — Intent for one free-flight tick — forward/right from WASD/analog, vertical from jump/crouch.
@@ -1977,13 +2253,13 @@
1977
2253
  - `FreeFlightState` (interface): interface FreeFlightState — Velocity state for a free-flight actor — serializable and ownable by the caller.
1978
2254
  - `FreeFlightStep` (interface): interface FreeFlightStep — World displacement produced by one free-flight tick.
1979
2255
  - `FreeFlightTuning` (interface): interface FreeFlightTuning — Data-first tuning for one free-flight profile.
1980
- - `FreezeMonitor` (interface): interface FreezeMonitor — ⚠ undocumented
1981
- - `FreezeViolation` (interface): interface FreezeViolation — ⚠ undocumented
1982
- - `Frustum` (interface): interface Frustum — ⚠ undocumented
1983
- - `FrustumProjection` (interface): interface FrustumProjection — ⚠ undocumented
1984
- - `FrustumSample` (interface): interface FrustumSample — ⚠ undocumented
1985
- - `FrustumSensor` (interface): interface FrustumSensor — ⚠ undocumented
1986
- - `FrustumTarget` (interface): interface FrustumTarget — ⚠ undocumented
2256
+ - `FreezeMonitor` (interface): interface FreezeMonitor { tick(subjects: readonly FreezeSubject[], frozenIds: ReadonlySet<string>, dt: number): FreezeViolation[]; reset(id?: string): void; snapshot(): FreezeMonitorState; restore(next: FreezeMonitorState): void } — ⚠ undocumented
2257
+ - `FreezeViolation` (interface): interface FreezeViolation { id: string; speed: number; movedSeconds: number } — ⚠ undocumented
2258
+ - `Frustum` (interface): interface Frustum { readonly planes: Float64Array; readonly corners: Float64Array; minX: number; minY: number; minZ: number; maxX: number; maxY: number; maxZ: number } — ⚠ undocumented
2259
+ - `FrustumProjection` (interface): interface FrustumProjection { inView: boolean; distance: number; screenX: number; screenY: number } — ⚠ undocumented
2260
+ - `FrustumSample` (interface): interface FrustumSample { id: string; inView: boolean; distance: number; screenX: number; screenY: number; framing: number; dwellSeconds: number } — ⚠ undocumented
2261
+ - `FrustumSensor` (interface): interface FrustumSensor { tick(camera: FrustumCamera, targets: readonly FrustumTarget[], dt: number): FrustumSample[]; reset(id?: string): void } — ⚠ undocumented
2262
+ - `FrustumTarget` (interface): interface FrustumTarget { id: string; position: EntityPosition; radius?: number } — ⚠ undocumented
1987
2263
  - `GRASS_SCHEMA` (const): const GRASS_SCHEMA: ParamSchema — The grass parameter schema — drives the inspector and `meta` parse via the studio seam.
1988
2264
  - `GROUND_DECAL_LAYERS` (const): const GROUND_DECAL_LAYERS: { readonly road: 0.06; readonly junction: 0.06; readonly marking: 0.11; readonly glow: 0.14; } — The single owning table of ground-decal Y offsets, in world units above the sampled terrain.
1989
2265
  - `GeneratedAsset` (interface): interface GeneratedAsset — A resolved generator asset: its parts plus the overall local-space bounds (min/max corners).
@@ -1991,10 +2267,10 @@
1991
2267
  - `GeneratedPart` (interface): interface GeneratedPart — One generated primitive part — a box/panel placed in the asset's local space.
1992
2268
  - `Glide` (class): class Glide — A reduced-gravity, forward-thrust glide over a physics body — wingsuit / glider / paraglider (Enshrouded, Grounded). Call `apply(dt, steerX, steerZ)` each frame *before* `world.step`: it feeds back most of the gravity the sim is about to apply (leaving `gravityScale` of it), pushes the body along the steer vector by `thrust`, and clamps descent to `maxFallSpeed`. Stop calling it to fall normally again — no attach/detach state to leak.
1993
2269
  - `Grapple` (class): class Grapple — A fired-anchor rope on the joint API — grapple (reel toward a hit point), zipline (rigid cable to a far anchor you then slide/reel along), swing (rigid rope + gravity = a pendulum). `fire` attaches a `distance`/`spring` joint from the traveller body to a fixed world point; `reel` shrinks its rest length so the constraint drags the body in; `moveAnchor` re-points it (zipline glide, grapple-to- moving-target). The pick — a raycast to find the anchor — is the caller's; core owns the constraint.
1994
- - `GrassEnvironmentDescriptor` (type): type GrassEnvironmentDescriptor = { kind: "grass" } & Required< Pick<GrassEnvironmentConfig, "area" | "density" | "bladeHeight" | "bladeWidth" | "windStrength" | "colors"> > & Pick<GrassEnvironmentConfig, "seed" | "bladeBend" | "tuftRadius" | "colorVariation" | "normalLift"> — ⚠ undocumented
2270
+ - `GrassEnvironmentDescriptor` (type): type GrassEnvironmentDescriptor = { kind: "grass" } & Required< Pick<GrassEnvironmentConfig, "area" | "density" | "bladeHeight" | "bladeWidth" | "windStrength" | "colors"> > & Pick<GrassEnvironmentConfig, "seed" | "bladeBend" | "tuftRadius" | "colorVariation" | "normalLift"> — ⚠ undocumented · used by `grass`: Declares a grass vegetation patch for `environment()` — area, blade sizing, density, and colors.
1995
2271
  - `GravityField` (interface): interface GravityField — Position-dependent gravity source used by movement and vehicle simulations.
1996
2272
  - `GravityVector` (type): type GravityVector = readonly [number, number, number] — Three-dimensional acceleration vector sampled from a gravity field.
1997
- - `GripCurve` (interface): interface GripCurve — ⚠ undocumented
2273
+ - `GripCurve` (interface): interface GripCurve { points: readonly (readonly [number, number])[] } — ⚠ undocumented · used by `sampleGripCurve`: Piecewise-linear tire-grip curve: normalized lateral slip → available grip (0..1).
1998
2274
  - `GroundConfig` (type): type GroundConfig = FlatGround | RoundGround | VoxelGround | BoardGround — The substrate of a place, discriminated by `mode` — TS rejects a `radius` on `flat` and `x`/`z` on `round`.
1999
2275
  - `GroundGenerator` (interface): interface GroundGenerator — Serializable algorithm parameters for a procedural ground generator. Never a genre kit.
2000
2276
  - `GroundMode` (type): type GroundMode = "flat" | "round" | "voxel" | "board" — Canonical ground modes after normalization (`stage` → `board`).
@@ -2007,7 +2283,7 @@
2007
2283
  - `HeatSource` (interface): interface HeatSource — A localized warmth source — campfire, forge, geothermal vent.
2008
2284
  - `HeatState` (interface): interface HeatState — Serializable heat-system state — round-trips through `createHeatState`/`advanceHeat` each tick.
2009
2285
  - `HeightSampler` (type): type HeightSampler = (x: number, z: number) => number — A height sampler over the ground: world elevation at any `x`/`z`.
2010
- - `HiddenStateSource` (interface): interface HiddenStateSource — ⚠ undocumented
2286
+ - `HiddenStateSource` (interface): interface HiddenStateSource { id: string; position: EntityPosition; variables: Readonly<Record<string, HiddenStateValue>> } — ⚠ undocumented · used by `useHiddenStateProbe` (@jgengine/shell/vision/HiddenStateProbeHud): Reads a hidden zone/entity state variable in range (EMF / thermometer / geiger style sensor verb, #116).
2011
2287
  - `HitInput` (interface): interface HitInput — A single incoming hit to register on a {@link DamageDirectionTracker}. The angle is relative to the player's facing (renderer-agnostic): `0` points at the front/top of the reticle and increases clockwise, so a game passes the bearing from the player toward the attacker without knowing anything about the screen.
2012
2288
  - `InterestCensus` (interface): interface InterestCensus — Aggregate counts of active vs dormant gates — the metric the issue asks a scheduler to expose.
2013
2289
  - `InterestCensusAccumulator` (interface): interface InterestCensusAccumulator — A running census accumulator; call `record` inside the caller's existing tick loop (no extra pass).
@@ -2018,9 +2294,9 @@
2018
2294
  - `InterestState` (type): type InterestState = "active" | "dormant" — Interest scheduling: the scale primitive that lets far-away agents sleep instead of running acquisition and pathing every frame. A per-agent gate decides — from proximity to the nearest interest source plus explicit wake signals — whether this tick does expensive work, at what cadence, with hysteresis so it never thrashes at the boundary and deterministic staggering so a thousand siblings do not all wake on the same frame. State is a plain serializable object; the caller drives it from a bounded spatial query, never a full-world scan.
2019
2295
  - `InterestTier` (interface): interface InterestTier — A distance-keyed cadence tier: nearer agents tick faster, farther-but-awake agents tick slower.
2020
2296
  - `IntersectionStreet` (interface): interface IntersectionStreet — One street to trim + mesh through {@link buildTrimmedIntersections}.
2021
- - `Job` (interface): interface Job — ⚠ undocumented
2022
- - `JobDef` (interface): interface JobDef — ⚠ undocumented
2023
- - `JobReport` (interface): interface JobReport — ⚠ undocumented
2297
+ - `Job` (interface): interface Job — ⚠ undocumented · used by `activeJobs` (@jgengine/core/gameplay): Jobs currently progressing.
2298
+ - `JobDef` (interface): interface JobDef { id?: string; station: NavPoint; work: number; priority?: number; arriveRadius?: number; repeat?: boolean } — ⚠ undocumented
2299
+ - `JobReport` (interface): interface JobReport { jobId: string; workerId: string; cycle: number } — ⚠ undocumented
2024
2300
  - `JunctionApproach` (interface): interface JunctionApproach — One approach feeding {@link buildJunctionSurface}: the exact draped corner vertices a ribbon ends at.
2025
2301
  - `JunctionGeometryOptions` (interface): interface JunctionGeometryOptions — Tunables shared by {@link trimPathAtJunctions}, {@link buildJunctionSurface}, and {@link buildTrimmedIntersections}.
2026
2302
  - `KinematicChassisTuning` (interface): interface KinematicChassisTuning — Mass-and-force chassis layer (#1051); when present it supersedes `engineAccel`/`brakeAccel` with force/mass dynamics. Drive/brake become forces divided by `massKg`, a per-tick tire friction budget (`tireGrip` * grip curve * surface * downforce * m * g) is split lateral-first then longitudinal so hard slides and launches saturate, and `comHeight`/`trackWidth` set weight-transfer washout and body lean. Coasting decelerates from physical road load, not a per-vehicle constant: rolling resistance `μ_rr · m · g` always, plus engine braking `ENGINE_BRAKE_FRACTION · engineForce` routed through the current gear (so it strengthens as the car downshifts) whenever the throttle is lifted and a `powertrain` is configured. All fields are required; omit the whole block for the legacy model.
@@ -2028,10 +2304,11 @@
2028
2304
  - `KinematicPowertrainTuning` (interface): interface KinematicPowertrainTuning — Data-first gearbox and torque-curve tuning for a kinematic ground vehicle.
2029
2305
  - `KinematicSteeringTuning` (interface): interface KinematicSteeringTuning — Bicycle-model steering settings; all angles are radians.
2030
2306
  - `KinematicVehicle` (interface): interface KinematicVehicle — The pure-kinematic arcade car every racing game hand-rolled (#282.1): steer-yaw scaled by speed, throttle/brake acceleration, and a grip-curve lateral-slip bleed — no `PhysicsWorld`, no wheels, just the drift-friendly integration the three shipped racers proved out. Games keep their flavor (drift meters, boost, off-track rules) via `surfaceFriction`/`dragAt` hooks and the returned slip.
2031
- - `KinematicVehicleStep` (interface): interface KinematicVehicleStep — ⚠ undocumented
2032
- - `KinematicVehicleTuning` (interface): interface KinematicVehicleTuning — ⚠ undocumented
2307
+ - `KinematicVehicleStep` (interface): interface KinematicVehicleStep { position: readonly [number, number, number]; heading: number; forwardSpeed: number; lateralSpeed: number; slip: number; surface: number; gear: number; rpm: number; steerAngle: number; yawRate: number; longitudinalAcceleration: number; tra… — ⚠ undocumented · used by `tickDrivableVehicle`: Connects an `AxisInput` sample straight through a ground-vehicle sim (`KinematicVehicle` or `VehicleDynamics`) to a scene entity's pose for …
2308
+ - `KinematicVehicleTuning` (interface): interface KinematicVehicleTuning { engineAccel: number; brakeAccel: number; topSpeed: number; reverseSpeed: number; turnRate: number; turnSpeedRef: number; grip?: GripCurve; gripStrength: number; handbrakeGrip: number; rollingResistance?: number; coastDeceleration?: number… — ⚠ undocumented · used by `createKinematicVehicle`: Arcade ground-vehicle sim (car, kart, bike) with throttle, brake, steer, drift and retunable handling.
2033
2309
  - `LANDMARK_HARD_CAP` (const): const LANDMARK_HARD_CAP: 12 — Hard cap on grand plots emitted regardless of dial/city size.
2034
2310
  - `LOCK_ACTIONS` (const): const LOCK_ACTIONS: readonly LockAction[] — The five pick actions, in display order (shallow → deep).
2311
+ - `LeaveVehicleOptions` (interface): interface LeaveVehicleOptions extends DismountOffset — Exit placement and optional local camera restoration for an explicit rider.
2035
2312
  - `LineFormationOptions` (interface): interface LineFormationOptions — Options for {@link lineFormation}.
2036
2313
  - `LocalAvoidanceOptions` (interface): interface LocalAvoidanceOptions — Tuning for {@link resolveLocalAvoidance}.
2037
2314
  - `LockAction` (type): type LockAction = "hardSet" | "set" | "steady" | "ease" | "drop" — One discrete pick move: how far the pick drives into the lock this step.
@@ -2041,28 +2318,29 @@
2041
2318
  - `LockTierSpec` (interface): interface LockTierSpec — Difficulty dials for one lock: board size, forgiveness band, gates, fog window, traps.
2042
2319
  - `MOVEMENT_TUNING` (const): const MOVEMENT_TUNING: { readonly standEyeHeight: 1.7; readonly crouchEyeHeight: 1.15; readonly walkSpeedMultiplier: 1.75; readonly runSpeedMultiplier: 2.25; readonly crouchSpeedMultiplier: 0.45; readonly backpedalSpeedMultiplier: 0.65; readonly groundAcceleration: 26; readonly airAcceleration: 12; … — Kinematics + feel tuning for the first-person controller. Centralised here so movement feel lives in one place rather than scattered through the renderer.
2043
2320
  - `MagnitudeOf` (type): type MagnitudeOf<P> = (membership: AreaMembership<P>) => number — Read a comparable magnitude from a membership (e.g. buff strength, damage per tick).
2044
- - `MapCellStates` (interface): interface MapCellStates — ⚠ undocumented
2321
+ - `MapCellStates` (interface): interface MapCellStates { id: string; origin: MapXZ; cellSize: number; cells: readonly {col: number; row: number; tone: MapLayerTone; opacity?: number}[] } — ⚠ undocumented
2045
2322
  - `MapMarker` (interface): interface MapMarker<TMeta = unknown> extends MarkerView<TMeta> — A marker owned by {@link MarkerSet}, including its lifecycle and query fields.
2046
2323
  - `MapNote` (interface): interface MapNote — A text note pinned at a world point.
2047
- - `MapRoute` (interface): interface MapRoute — ⚠ undocumented
2324
+ - `MapRoute` (interface): interface MapRoute { id: string; points: readonly MapXZ[]; tone?: MapLayerTone; forecast?: boolean; width?: number; closed?: boolean } — ⚠ undocumented
2048
2325
  - `MapShapeAnnotation` (interface): interface MapShapeAnnotation — A drawn area annotation (circle/rect/polygon), reusing the map-zone shape vocabulary.
2049
2326
  - `MapStroke` (interface): interface MapStroke — A freehand drawn line on the map — a world-XZ polyline.
2050
- - `MapZone` (interface): interface MapZone — ⚠ undocumented
2327
+ - `MapZone` (interface): interface MapZone { id: string; shape: MapZoneShape; tone?: MapLayerTone; opacity?: number; forecast?: boolean; label?: string } — ⚠ undocumented
2051
2328
  - `MarkerCollection` (type): type MarkerCollection<TMarker extends MarkerView = MarkerView> = | readonly TMarker[] | MarkerSource<TMarker> | MarkerSet — Marker data accepted by portable consumers: static views, an external source, or a native set.
2052
2329
  - `MarkerKindStyle` (interface): interface MarkerKindStyle — Visual descriptor for a marker kind. Games supply their own palette; the engine ships `DEFAULT_MARKER_KINDS` as a content-agnostic starting set that the react minimap/compass read for colors and glyphs.
2053
- - `MarkerSet` (interface): interface MarkerSet<TMeta = unknown> — ⚠ undocumented
2330
+ - `MarkerSet` (interface): interface MarkerSet<TMeta = unknown> { add(marker: MarkerInput<TMeta>): string; remove(id: string): boolean; get(id: string): MapMarker<TMeta> | null; list(): readonly MapMarker<TMeta>[]; query(query: MarkerQuery): MapMarker<TMeta>[]; prune(now: number): number; clear(): void;… — ⚠ undocumented · used by `useLiveMarkers` (@jgengine/react): A self-ticking {@link MarkerSet} kept in sync with the live scene: on a heartbeat (default 100ms, and once immediately) it clears the set an…
2054
2331
  - `MarkerSource` (interface): interface MarkerSource<TMarker extends MarkerView = MarkerView> — Observable marker snapshots owned by an external project. `getSnapshot` must return the same array identity until the source changes and calls its subscribers, matching React's external-store contract.
2055
2332
  - `MarkerSourceOptions` (interface): interface MarkerSourceOptions<TEntity, TMarker extends MarkerView = MarkerView> — Configuration for projecting a caller-owned collection into display-only markers.
2056
2333
  - `MarkerView` (interface): interface MarkerView<TMeta = unknown> — Small, display-only marker shape consumed by map renderers. Existing games can project their own entities to this view without adopting marker lifecycle storage or duplicating them into a {@link MarkerSet}.
2057
2334
  - `MassingFootprint` (interface): interface MassingFootprint — Axis-aligned massing extents (lot-local): full width along x and depth along z.
2335
+ - `MergedRegion` (interface): interface MergedRegion extends RegionFootprint — Plain JSON pooled region. Contributions partition its cells and sum to its capacity.
2058
2336
  - `MinimapBake` (interface): interface MinimapBake — A baked minimap image: RGBA pixels plus the world bounds they span.
2059
2337
  - `MinimapBakeBounds` (interface): interface MinimapBakeBounds — Deterministic minimap terrain bake (#1036): rasterize an authored world's terrain (plus optional biome zones and water) into a top-down RGBA image and a matching world-bounds rectangle, then encode it as a PNG data URI. The editor runs this as a bake action and stores the result on the scene document; runtime feeds it straight into the existing `Minimap` / `WorldMap` `background` + `mapBounds` props — no new runtime render path, and the same authored world always bakes the same image (safe for verify/CI). Pure: no canvas, no DOM, `core` still imports nothing external.
2060
2338
  - `MinimapBakeOptions` (interface): interface MinimapBakeOptions — Options for {@link bakeMinimapImage}.
2061
2339
  - `MinimapBakePalette` (interface): interface MinimapBakePalette — The height→color ramp and water color used by the bake.
2062
2340
  - `MinimapBakeSource` (interface): interface MinimapBakeSource — The authored world a bake reads — a height sampler, its bounds, and optional zones/water.
2063
2341
  - `MinimapBakeZone` (interface): interface MinimapBakeZone — One biome/region tint painted over the terrain shade where the ground falls inside its polygon.
2064
- - `MinimapView` (interface): interface MinimapView — ⚠ undocumented
2065
- - `ModelAssetRef` (interface): interface ModelAssetRef — ⚠ undocumented
2342
+ - `MinimapView` (interface): interface MinimapView { center: WorldXZ; worldRadius: number; size: number; rotate?: number } — ⚠ undocumented · used by `projectToMinimap`: Project a world XZ (or XYZ) point into minimap pixel space.
2343
+ - `ModelAssetRef` (interface): interface ModelAssetRef { url: string; anchor?: "center" | "origin"; dims?: ModelDims; collisionMesh?: CollisionMeshData; space?: AssetSpace; clips?: readonly string[] } — ⚠ undocumented · used by `createHostedWorldSessionAsync` (@jgengine/core/runtime/hostedWorldSession): Build a hosted session from an asynchronous persistence backend.
2066
2344
  - `ModelDims` (interface): interface ModelDims — Measured horizontal footprint, footprint center, and lowest Y of a model in model space.
2067
2345
  - `ModelNode` (interface): interface ModelNode — Generic named-socket reader for loaded 3D models. Walks a node tree (any object with `.name`, `.position`, and `.children` — structurally satisfied by `THREE.Object3D`) and collects the local offsets of nodes whose name marks an attachment point. Genre-agnostic: wire anchors on a pylon, muzzle/hand mounts on a character, hardpoints on a ship, seat/decal slots on furniture — anything an artist tags with an empty in the GLB. Pure data (no three.js import), so it lives in core.
2068
2346
  - `MountController` (class): class MountController — Mount / rideable control-transfer (issue #83). Registers rideables (each with one or more seats — a control seat drives, the rest ride) and tracks who is on what. It owns no camera or physics: game code reads `cameraTarget(riderId)` to point the follow camera at the mount, and `driveTarget(riderId)` to route that rider's {@link import("../physics/vehicleBody").AxisInput}-driven input at the mount's movement kit — the same seam a horse, a truck, or a shared multi-seat ship all plug into.
@@ -2071,20 +2349,23 @@
2071
2349
  - `MusicInstrument` (type): type MusicInstrument = | "strings" | "flute" | "harp" | "horn" | "choir" | "bell" | "timpani" | "bass" | "stacc" | "pad" | "lute" | "dulcimer" | "frameDrum" | "warDrum" | "reed" | "pipe" | "squareLead" | "woodBlock" | "tinyBell" | "piano" | "shaker" | "brassStab" | "cymSwell" | "oboe" — Named synthesised instrument. Each maps to a voice in the shell's instrument library (`@jgengine/shell/audio/musicVoices`); an unknown name falls back to a plain sine voice so a theme is never silent.
2072
2350
  - `MusicTheme` (interface): interface MusicTheme — A through-composed, looping music track. `events` need not be sorted; the director schedules them ahead against a fixed anchor so loops are seamless.
2073
2351
  - `NOCLIP_FLIGHT_TUNING` (const): const NOCLIP_FLIGHT_TUNING: FreeFlightTuning — Preset for noclip — weightless, noclips, yaw-relative with independent vertical.
2074
- - `NavGrid` (interface): interface NavGrid — ⚠ undocumented
2352
+ - `NavGrid` (interface): interface NavGrid { readonly cols: number; readonly rows: number; readonly cellSize: number; readonly bounds: Aabb; readonly diagonal: boolean; isWalkable(col: number, row: number): boolean; setWalkable(col: number, row: number, walkable: boolean): void; blo… — ⚠ undocumented · used by `findPath`: A* over the walkable grid.
2075
2353
  - `NavMeshAdjacency` (interface): interface NavMeshAdjacency — Neighbor relationship for one navigation polygon.
2076
- - `NavMeshData` (interface): interface NavMeshData — Serializable polygon navigation mesh data.
2354
+ - `NavMeshData` (interface): interface NavMeshData — Serializable polygon navigation mesh data. Treat it as immutable once queried; queries cache derived geometry per object.
2077
2355
  - `NavMeshLink` (interface): interface NavMeshLink — Explicit traversable connection between two navigation polygons.
2078
2356
  - `NavMeshPath` (interface): interface NavMeshPath — Route points and polygons selected through a navigation mesh.
2079
- - `NavPoint` (type): type NavPoint = readonly [number, number] — ⚠ undocumented
2357
+ - `NavMeshQuery` (interface): interface NavMeshQuery — Cached, allocation-light queries over one {@link NavMeshData}.
2358
+ - `NavMeshQueryOptions` (interface): interface NavMeshQueryOptions — Tunable pricing and bounds for a {@link NavMeshQuery}.
2359
+ - `NavMeshQuerySnapshot` (interface): interface NavMeshQuerySnapshot — Serializable tuning of a {@link NavMeshQuery}.
2360
+ - `NavPoint` (type): type NavPoint = readonly [number, number] — ⚠ undocumented · used by `findPath`: A* over the walkable grid.
2080
2361
  - `NoiseFieldConfig` (interface): interface NoiseFieldConfig — Configuration for {@link noiseField}: seed, amplitude, and fractal noise shaping.
2081
2362
  - `NoiseVoice` (interface): interface NoiseVoice — A filtered white-noise burst — impacts, whooshes, breath, crackle. Realised from a shared 1s noise buffer at a randomised playback rate and start offset, decaying exponentially to silence at `duration * decay`.
2082
2363
  - `NoteEvent` (interface): interface NoteEvent — One scheduled note in a theme, positioned on the loop's quarter-note grid.
2083
2364
  - `ObjectSlotRejection` (type): type ObjectSlotRejection = | "unknown-object" | "no-slot-inventory" | "unknown-inventory" | "no-space" | "wrong-kind" | "slot-occupied" | "invalid-slot" | "insufficient" — Why an object slot mutation was refused — the instance, its catalog entry, the player inventory, or the declared `accepts`/capacity.
2084
2365
  - `ObjectSlotResult` (type): type ObjectSlotResult = { status: "ok" } | { status: "rejected"; reason: ObjectSlotRejection } — Outcome of an object slot mutation: applied, or refused with a reason and no state change on either side.
2085
2366
  - `ObjectSlotTransfer` (interface): interface ObjectSlotTransfer — Which player inventory a transfer moves between, and what it moves.
2086
- - `ObjectVisual` (interface): interface ObjectVisual — ⚠ undocumented
2087
- - `OceanEnvironmentDescriptor` (type): type OceanEnvironmentDescriptor = { kind: "ocean" } & Required< Pick<OceanEnvironmentConfig, "bounds" | "level" | "waveHeight" | "waveScale" | "waveSpeed" | "color"> > & Pick<OceanEnvironmentConfig, "position" | "levelAt"> — ⚠ undocumented
2367
+ - `ObjectVisual` (interface): interface ObjectVisual { scale?: number | readonly [number, number, number]; color?: string; opacity?: number } — ⚠ undocumented
2368
+ - `OceanEnvironmentDescriptor` (type): type OceanEnvironmentDescriptor = { kind: "ocean" } & Required< Pick<OceanEnvironmentConfig, "bounds" | "level" | "waveHeight" | "waveScale" | "waveSpeed" | "color"> > & Pick<OceanEnvironmentConfig, "position" | "levelAt"> — ⚠ undocumented · used by `ocean`: Declares an ocean water body for `environment()` — bounds, level, and wave tuning.
2088
2369
  - `Order` (interface): interface Order<TPayload = unknown> — One issued order. Fully serializable: `kind` is a registry key (not a closure), `payload` is the caller's plain data, and `state` is the kind's own plain progress scratch (e.g. a patrol waypoint index) written during `start`/`update`. Never holds functions, so the whole queue round-trips through `JSON`/`structuredClone`.
2089
2370
  - `OrderCancelReason` (type): type OrderCancelReason = "replaced" | "preempted" | "requested" | "invalid" | "failed" | "cleared" — Why an order left the queue without completing. `replaced`/`preempted` come from a newer order taking over; `requested` from explicit `cancelActive`; `invalid` when the kind's `start` rejects; `failed` when `update` reports failure; `cleared` from a full `clear`/`cancelAll`.
2090
2371
  - `OrderEvent` (interface): interface OrderEvent — A lifecycle event emitted synchronously as orders move through the queue.
@@ -2109,9 +2390,9 @@
2109
2390
  - `OrderVec3` (type): type OrderVec3 = readonly [number, number, number] — Composable entity orders: a serializable command queue with lifecycle, preemption, and pluggable order kinds (#912). Player- or AI-issued intent ("move here", "attack that", "patrol this route") becomes reusable data instead of a hard-coded RTS verb kit. The queue owns issue -> activate -> tick -> complete/cancel lifecycle and deterministic preemption policy; each order KIND owns what "running" means, composed over narrow world adapters. Engine code never branches on `move`, `attack`, or any game verb -- kinds are looked up by string in a registry, so a game adds a verb by registering data.
2110
2391
  - `POLE_LINE_SCHEMA` (const): const POLE_LINE_SCHEMA: ParamSchema — The pole-line parameter schema — drives the inspector and `meta` parse via the studio seam.
2111
2392
  - `POSE_HITBOX` (const): const POSE_HITBOX: Record<MovementPose, PoseHitbox> — ⚠ undocumented
2112
- - `PadEnvironmentDescriptor` (type): type PadEnvironmentDescriptor = { kind: "pad" } & Required< Pick<PadEnvironmentConfig, "center" | "size" | "height" | "color"> > & Pick<PadEnvironmentConfig, "rotationY" | "elevation"> — ⚠ undocumented
2393
+ - `PadEnvironmentDescriptor` (type): type PadEnvironmentDescriptor = { kind: "pad" } & Required< Pick<PadEnvironmentConfig, "center" | "size" | "height" | "color"> > & Pick<PadEnvironmentConfig, "rotationY" | "elevation"> — ⚠ undocumented · used by `padFlattenMasks` (@jgengine/core/world/features): Derives implicit `TerrainFlattenMask`s carving each pad's footprint into the terrain beneath it.
2113
2394
  - `PadSize` (type): type PadSize = readonly [number, number] | { radius: number } — ⚠ undocumented
2114
- - `PaintStroke` (interface): interface PaintStroke — ⚠ undocumented
2395
+ - `PaintStroke` (interface): interface PaintStroke { u: number; v: number; radius: number; color: string } — ⚠ undocumented
2115
2396
  - `ParamField` (type): type ParamField = | RangeParamField | NumberParamField | BoolParamField | SelectParamField | ColorParamField | TextParamField | SeedParamField | WeightedListParamField | ActionParamField — One row in a kind's parameter schema — the union the generic inspector knows how to render.
2116
2397
  - `ParamPreset` (interface): interface ParamPreset — A named slider/weight bundle for a kind — a saved set of field values the inspector (or the `apply_preset` verb) writes into an object's `meta` in one patch, ready to tweak afterwards. Presets are plain data on the schema, so kinds ship archetypes and games can override the registration with their own.
2117
2398
  - `ParamSchema` (interface): interface ParamSchema — A kind's full parameter surface: an ordered list of fields the inspector renders top-to-bottom.
@@ -2119,57 +2400,63 @@
2119
2400
  - `ParticleBuffers` (interface): interface ParticleBuffers — Read-only packed buffers of the live particles, laid out for a renderer to upload directly (Structure-of-Arrays, no per-particle objects). Only the first `count` entries are live; the arrays themselves are reused every frame.
2120
2401
  - `ParticleSnapshot` (interface): interface ParticleSnapshot — Serializable simulation state for save/restore and deterministic replay.
2121
2402
  - `ParticleSystem` (interface): interface ParticleSystem — A live, dt-driven particle simulation.
2122
- - `PathFollowConfig` (interface): interface PathFollowConfig — ⚠ undocumented
2403
+ - `PathFollowConfig` (interface): interface PathFollowConfig { waypoints: readonly Waypoint[]; speed: number; loop?: boolean } — ⚠ undocumented · used by `advancePathFollow`: Advance a path-follower by `speed * dt` along its authored polyline.
2123
2404
  - `PathFollowProgress` (interface): interface PathFollowProgress — Read-only progress readout for inspection/debug tooling, produced by {@link pathFollowProgress}.
2124
- - `PathFollowState` (interface): interface PathFollowState — ⚠ undocumented
2405
+ - `PathFollowState` (interface): interface PathFollowState { position: Waypoint; target: number; heading: number; done: boolean; distanceTravelled: number } — ⚠ undocumented · used by `advancePathFollow`: Advance a path-follower by `speed * dt` along its authored polyline.
2125
2406
  - `PathHeightPolicy` (type): type PathHeightPolicy = | { readonly kind: "sample" } | { readonly kind: "fixed"; readonly height: number } | { readonly kind: "grade"; readonly start: number; readonly end: number } — Where a path corridor drives its centerline height: - `sample` — follow the base terrain under the centerline, so the corridor drapes level across its width while still tracking the hills the path crosses (the natural policy for a road or trail). - `fixed` — hold one constant height for the whole corridor (a level causeway, runway, or dam crest). - `grade` — interpolate linearly from `start` to `end` height along the path's arc length, a constant-slope ramp between two anchors (a switchback, a graded rail bed, an accessibility ramp).
2126
2407
  - `PathProgress` (type): type PathProgress = | { readonly kind: "normalized"; readonly value: number } /** World-distance travelled from the first waypoint (looping paths wrap; clamped otherwise). */ | { readonly kind: "distance"; readonly value: number } /** Segment `index` (0-based) plus `fraction` `0..1` along that segme… — Semantic seek target for {@link pathFollowSeek} — the caller-facing progress vocabulary a stateful path behavior restores from, so a follower can start at a distributed phase or resume a serialized route without knowing waypoint internals. `direction` (forward heading) falls out of the resulting {@link PathFollowState.heading}, so it is an output rather than a seek input.
2127
2408
  - `PathRetaining` (interface): interface PathRetaining — Retaining behavior for a path corridor's shoulders. A wall rises only where the corridor already cuts or fills into the surrounding ground by at least `threshold` — gentle stretches stay open — so a road through a canyon gets held banks while the same profile on flat ground does not. The wall fades in smoothly across `taper` above the threshold so it never steps on or off between adjacent samples.
2128
2409
  - `PatrolOrderPayload` (interface): interface PatrolOrderPayload — Payload for a patrol: walk a waypoint route, optionally looping forever.
2129
2410
  - `PatrolOrderState` (interface): interface PatrolOrderState — Serializable patrol progress.
2130
- - `PhysicsStats` (interface): interface PhysicsStats — ⚠ undocumented
2411
+ - `PhysicsStats` (interface): interface PhysicsStats { count: number; awake: number; sleeping: number; contacts: number; pairs: number; substeps: number; stepMs: number } — ⚠ undocumented
2131
2412
  - `PhysicsWorld` (class): class PhysicsWorld — ⚠ undocumented
2132
2413
  - `PlaceAssetResult` (interface): interface PlaceAssetResult — Shared place-asset verb: one resolved payload for editor `place_asset` and in-game build-mode commits. Convert with {@link toStructureInput} / {@link toEditorMarker}.
2133
2414
  - `PlaceConfig` (interface): interface PlaceConfig — Input to {@link world}: place identity, substrate, and the laws of this place.
2134
2415
  - `PlaceWorldFeature` (interface): interface PlaceWorldFeature — A declared place — the `world()` result carried on `GameDefinition.world`.
2135
- - `PlacedStructure` (interface): interface PlacedStructure — ⚠ undocumented
2136
- - `PlacementCommit` (interface): interface PlacementCommit — ⚠ undocumented
2137
- - `PlacementController` (interface): interface PlacementController — ⚠ undocumented
2138
- - `PlacementPreview` (interface): interface PlacementPreview — ⚠ undocumented
2416
+ - `PlacedStructure` (interface): interface PlacedStructure { id: string; catalogId: string; position: StructureVec3; rotationY: number; plotId?: string; data?: Readonly<Record<string, unknown>> } — ⚠ undocumented
2417
+ - `PlacementCommit` (interface): interface PlacementCommit { center: Vec2; y: number; rotationY: number; quarterTurns: number; footprint: Footprint; aabb: Aabb; slotId?: string } — ⚠ undocumented · used by `placeAssetFromCommit`: Bridge a {@link PlacementCommit} into the shared place-asset verb.
2418
+ - `PlacementController` (interface): interface PlacementController { hover(hit: PlacementHit): PlacementPreview; current(): PlacementPreview | null; rotate(steps?: number): PlacementPreview | null; setSnapMode(mode: SnapMode): PlacementPreview | null; cycleSnapMode(): SnapMode; setFootprint(footprint: Foot… — ⚠ undocumented · used by `createPlacementController`: Headless placement ghost: hover → valid/invalid preview, rotate, grid/free/surface snap, commit.
2419
+ - `PlacementPreview` (interface): interface PlacementPreview { center: Vec2; y: number; quarterTurns: number; footprint: Footprint; aabb: Aabb; valid: boolean; reason?: "out-of-bounds" | "overlap" | "no-slot" | "territory.blocked"; snapMode: SnapMode; normal: PlacementVec3; slotId?: string } — ⚠ undocumented
2139
2420
  - `PlacementRotationPolicy` (type): type PlacementRotationPolicy = | { readonly mode: "locked"; readonly degrees?: number } | { readonly mode: "snap"; readonly snapDegrees: number } | { readonly mode: "free" } — A per-asset/per-tool rotation constraint, authored as data instead of hardcoded in build code: `locked` pins every placement to one heading, `snap` quantizes to an increment (15, 45, …), and `free` allows any heading. Serializable — this is the shape stored on a catalog entry or a tool.
2140
- - `PlacementRules` (interface): interface PlacementRules — ⚠ undocumented
2421
+ - `PlacementRules` (interface): interface PlacementRules { bounds?: Aabb; obstacles?: readonly PlacementObstacle[]; snap?: number; territory?: (aabb: Aabb) => {ok: boolean} } — ⚠ undocumented · used by `validatePlacement`: Footprint validity: bounds + obstacle overlap after optional grid snap.
2141
2422
  - `PlatformCarry` (class): class PlatformCarry — Carries bodies standing on a moving platform by composing their transform with the platform's per-`step` delta — moving/rotating lifts and conveyor floors (Fall Guys, Gang Beasts). The platform is a body the game repositions each frame; riders are detected by overlap on its top face.
2142
2423
  - `Point3` (type): type Point3 = readonly [number, number, number] — Deterministic spatial sampling — draw one or many positions from a geometric region under explicit constraints, with an injected RNG, bounded attempts, and a structured pass/fail result.
2143
2424
  - `PointGravityOptions` (interface): interface PointGravityOptions — Center, strength, and falloff for radial gravity.
2144
- - `PositionedPrompt` (interface): interface PositionedPrompt — ⚠ undocumented
2425
+ - `PositionedPrompt` (interface): interface PositionedPrompt { id: string; position: PromptPoint; priority?: number; prompt: ProximityPrompt } — ⚠ undocumented · used by `resolveActivePrompt`: Nearest prompt strictly within its radius wins; a higher-priority prompt in range beats any lower-priority one regardless of distance; equal…
2145
2426
  - `PositionedPromptPatch` (type): type PositionedPromptPatch = Partial<Omit<PositionedPrompt, "id">> — A partial edit applied to an already-registered {@link PositionedPrompt} by {@link PromptRegistry.update}. Any subset of the mutable fields — everything but the stable `id` — may be supplied; omitted fields keep their prior value.
2146
2427
  - `PromptRegistry` (interface): interface PromptRegistry — A live, observable set of interaction prompts that OWNS the registered {@link PositionedPrompt}s and tracks which one is active for a player. It is a thin stateful layer over {@link resolveActivePrompt}: registration/order lives here, the nearest-within-radius / higher-priority selection stays in the pure resolver. {@link resolve} is the per-frame hot path — call it as the player moves; subscribers fire only when the active prompt actually CHANGES.
2147
2428
  - `PromptRegistrySnapshot` (interface): interface PromptRegistrySnapshot — The serializable shape {@link PromptRegistry.snapshot} returns and {@link PromptRegistry.restore} consumes: the full set of positioned prompts (in registration order) plus the id of the prompt that was active when the snapshot was taken. Prompt display/command payloads are free strings and ids — keep them serializable and this round-trips through a save cleanly.
2148
- - `ProximityPrompt` (interface): interface ProximityPrompt — ⚠ undocumented
2149
- - `QteStep` (interface): interface QteStep — ⚠ undocumented
2429
+ - `ProximityPrompt` (interface): interface ProximityPrompt { radius: number; display: ProximityPromptDisplay; invoke: PromptCommand | null } — ⚠ undocumented
2430
+ - `PursueBehavior` (interface): interface PursueBehavior — Scheduled chase/attack behavior; the default declares hostility toward player-role entities. GameDefinition.pursuit.eligible can further restrict every target source using the live world. Explicit targets and optional threat override nearest-player acquisition, not eligibility.
2431
+ - `PursueBehaviorSnapshot` (interface): interface PursueBehaviorSnapshot — Plain pursuit progress, including its original spawn home and scheduled elapsed time.
2432
+ - `QteStep` (interface): interface QteStep { id: string; action: string; windowStart: number; windowEnd: number } — ⚠ undocumented · used by `evaluateQteSequence`: Evaluate a quick-time-event input sequence against timed hit windows.
2150
2433
  - `RadialDistribution` (type): type RadialDistribution = "area" | "radial" — Fill policy for a circle/ring: `"area"` = area-uniform (even density); `"radial"` = radius-uniform (clumps toward center).
2151
- - `RainEnvironmentDescriptor` (type): type RainEnvironmentDescriptor = { kind: "rain" } & Required< Pick<RainEnvironmentConfig, "area" | "density" | "speed" | "dropLength" | "wind" | "color" | "width" | "opacity"> > — ⚠ undocumented
2434
+ - `RainEnvironmentDescriptor` (type): type RainEnvironmentDescriptor = { kind: "rain" } & Required< Pick<RainEnvironmentConfig, "area" | "density" | "speed" | "dropLength" | "wind" | "color" | "width" | "opacity"> > — ⚠ undocumented · used by `rain`: Declares a rainfall weather effect for `environment()` — area, density, speed, wind, and drop width/opacity.
2152
2435
  - `Range` (interface): interface Range — A `[min, max]` range a spawned particle draws uniformly from.
2153
2436
  - `RecallBookmarkOptions` (interface): interface RecallBookmarkOptions — Caller hooks for {@link recallSelectionBookmark} — kept out of the store so focus and validity stay genre-owned.
2154
- - `RecordingBuffer` (interface): interface RecordingBuffer<T> — ⚠ undocumented
2155
- - `RecordingBufferOptions` (interface): interface RecordingBufferOptions — ⚠ undocumented
2156
- - `RegionField` (interface): interface RegionField<T = unknown> extends TerrainField — ⚠ undocumented
2437
+ - `RecordingBuffer` (interface): interface RecordingBuffer<T> { append(t: number, data: T): void; seek(t: number): RecordingFrame<T> | null; seekPair(t: number): RecordingPair<T>; range(fromT: number, toT: number): RecordingFrame<T>[]; clear(): void; duration(): number; frames(): readonly RecordingFra… — ⚠ undocumented · used by `useSessionRecorder` (@jgengine/shell/replay/useSessionRecorder): Session-recording buffer (#120) for replay / photo mode / kill-cam: records an entity's pose on game-time every frame into a `RecordingBuffe…
2438
+ - `RecordingBufferOptions` (interface): interface RecordingBufferOptions { maxDurationSeconds?: number; maxFrames?: number } — ⚠ undocumented · used by `useSessionRecorder` (@jgengine/shell/replay/useSessionRecorder): Session-recording buffer (#120) for replay / photo mode / kill-cam: records an entity's pose on game-time every frame into a `RecordingBuffe…
2439
+ - `RegionContribution` (interface): interface RegionContribution — Original footprint identity and capacity retained when regions pool.
2440
+ - `RegionField` (interface): interface RegionField<T = unknown> extends TerrainField { sampleRegion(x: number, z: number): RegionSample<T>; readonly regions: readonly RegionDef<T>[]; readonly seaLevel: number; readonly seed: number } — ⚠ undocumented · used by `scatterItems`: Deterministically place opaque items across `area`, grounded on a region field.
2441
+ - `RegionFootprint` (interface): interface RegionFootprint — Caller-owned logical footprint. Cells must be unique, safe integer addresses and cardinally connected.
2442
+ - `RegionSplitIds` (interface): interface RegionSplitIds — Identities for split sides containing multiple contributions; singletons retain their original id.
2443
+ - `RegionWorkBudget` (interface): interface RegionWorkBudget — Optional caller work limits, checked before cell traversal. Omitted limits follow input size.
2157
2444
  - `Renderable` (interface): interface Renderable — A scene object the visibility system considers. A normal game object already carries a position and a version counter, so it becomes cullable automatically — no separate "cullable" component. Everything else is optional override.
2158
2445
  - `ResolvedCity` (interface): interface ResolvedCity — A resolved city district: world-space network, zoned lots, parks, and furniture.
2159
2446
  - `ResolvedCityLot` (interface): interface ResolvedCityLot — One plot enriched with its zone/class/floors/massing — the renderer instances `pieces` at `center`.
2160
- - `ResolvedCollider` (interface): interface ResolvedCollider — ⚠ undocumented
2447
+ - `ResolvedCollider` (interface): interface ResolvedCollider { name: string; purpose: ColliderPurpose; shape: ColliderShape; damageEligible: boolean; blocks: boolean } — ⚠ undocumented
2161
2448
  - `ResolvedGround` (type): type ResolvedGround = FlatGround | RoundGround | VoxelGround | ResolvedBoardGround — A place's ground after `world()` normalization.
2162
2449
  - `ResolvedPoleLine` (interface): interface ResolvedPoleLine — The renderable payload the resolver returns and the shell renderer consumes.
2163
2450
  - `ResolvedTerrainDetail` (type): type ResolvedTerrainDetail = Required<Omit<TerrainDetailConfig, "waterLevel" | "material" | "sweeps">> & { waterLevel: number; material?: ResolvedTerrainDetailMaterial; sweeps: { dry: readonly [number, number, number]; wet: readonly [number, number, number] }; } — A {@link TerrainDetailConfig} with every field resolved to a concrete value — the shape the shell's detail material consumes.
2164
- - `ResolvedWeather` (interface): interface ResolvedWeather — ⚠ undocumented
2165
- - `RevealHit` (interface): interface RevealHit — ⚠ undocumented
2166
- - `RevealQuery` (interface): interface RevealQuery — ⚠ undocumented
2451
+ - `ResolvedWeather` (interface): interface ResolvedWeather { grip: number; visibility: number; structureDamage: number; chill: number; ignition: number; spread: number } — ⚠ undocumented
2452
+ - `RevealHit` (interface): interface RevealHit { instanceId: string; distance: number; tags: readonly string[] } — ⚠ undocumented · used by `useRevealHits` (@jgengine/shell/vision/RevealVision): Occlusion-ignoring tagged-entity radius query (#115), bound to the live scene.
2453
+ - `RevealQuery` (interface): interface RevealQuery { inRadius(center: EntityPosition | string, radius: number, tags: readonly string[]): RevealHit[] } — ⚠ undocumented
2167
2454
  - `RoadCut` (interface): interface RoadCut — One trim of a ribbon end back to a junction boundary — the seam the welded surface attaches to.
2168
2455
  - `RoadEnvironmentDescriptor` (type): type RoadEnvironmentDescriptor = { kind: "road" } & Required< Pick<RoadEnvironmentConfig, "path" | "width" | "color" | "markings" | "markingColor" | "elevation"> > & { /** Resolved sidewalk band, or `false` when the road has none. */ sidewalk: { width: number; color: string } | false; } — Resolved road descriptor produced by {@link road} and rendered by the shell environment scene.
2169
2456
  - `RoadJunctionInput` (interface): interface RoadJunctionInput — The minimal junction shape this geometry needs — a node position plus its outgoing arms. Matches the structural subset of `StreetJunction` (streetGenerator) so a network's `junctions` can be fed straight in. Arm `angle` follows the generator convention `atan2(dx, dz)`, i.e. the outward unit direction of an arm is `[sin(angle), cos(angle)]`.
2170
2457
  - `RoadSurfaceOptions` (interface): interface RoadSurfaceOptions — Grip levels and blend width for {@link roadSurfaceSampler}; each field is optional and defaulted.
2171
- - `RoofPlan` (interface): interface RoofPlan — ⚠ undocumented
2172
- - `RosterEntry` (interface): interface RosterEntry — ⚠ undocumented
2458
+ - `RoofPlan` (interface): interface RoofPlan { style: RoofStyle; ridge: readonly [Vec2, Vec2]; apexHeight: number; eaveHeight: number; faces: readonly (readonly WallVec3[])[] } — ⚠ undocumented
2459
+ - `RosterEntry` (interface): interface RosterEntry { id: string; catalogId: string; capturedAt: number; equipped: boolean } — ⚠ undocumented
2173
2460
  - `RoundGround` (interface): interface RoundGround — A planet/sphere you play on the outside of. Sized by `radius` only.
2174
2461
  - `RoundGroundSize` (interface): interface RoundGroundSize — Size of a `round` ground: the planet/sphere radius in world units. Nothing else.
2175
2462
  - `SCATTER_COVERAGE_SPECS` (const): const SCATTER_COVERAGE_SPECS: Record<ScatterCoverageKind, ScatterCoverageSpec> — The coverage spec per scatterable kind — the single source for its unit + budget.
@@ -2191,16 +2478,16 @@
2191
2478
  - `ScatterCoverage` (interface): interface ScatterCoverage — A resolved coverage readout: how many instances an authored patch WANTS (`requested`, when it can be computed cheaply from geometry × density), how many it will actually place after the budget clamp (`count`), the `budget` ceiling, and whether the request hit it (`capped`). `requested` is `null` for kinds whose pre-cap ask is not knowable without running the generator (city, where the count comes from the resolved street fabric).
2192
2479
  - `ScatterCoverageKind` (type): type ScatterCoverageKind = "grass_field" | "scatter" | "city" — The shared "how much stuff grows here" contract for scatterable scene kinds (`grass_field` / `scatter` / `city`). Every scatterable kind fills an authored AREA with instances at a DENSITY, and every kind caps the placed count at a bounded BUDGET so a huge or dense request can never generate unbounded work. Before this module each kind hand-rolled its own readout and clamp story (grass silently capped in the renderer, scatter never capped, city had no budget note); this is the one place that
2193
2480
  - `ScatterCoverageSpec` (interface): interface ScatterCoverageSpec — Per-kind coverage descriptor: the unit noun, whether density is per-m², and the placement budget.
2194
- - `ScatterInstance` (interface): interface ScatterInstance — ⚠ undocumented
2195
- - `ScatterPoint` (interface): interface ScatterPoint — ⚠ undocumented
2481
+ - `ScatterInstance` (interface): interface ScatterInstance — ⚠ undocumented · used by `resolveScatter`: Every scatter region's placements across a document, grounded on `terrain` when provided.
2482
+ - `ScatterPoint` (interface): interface ScatterPoint { x: number; z: number; index: number } — ⚠ undocumented
2196
2483
  - `ScatterTerrain` (interface): interface ScatterTerrain — Ground sampler a scatter resolve reads height/normal from (the sculpt terrain or the game's ground).
2197
- - `SceneEntity` (interface): interface SceneEntity<TMeta = unknown> — ⚠ undocumented
2484
+ - `SceneEntity` (interface): interface SceneEntity<TMeta = unknown> { id: string; name: string; position: EntityPosition; rotationY: number; rotationX: number; rotationZ: number; velocity: EntityPosition; role: EntityRole; movement: EntityMovement; behaviors: readonly BehaviorDescriptor[]; hidden?: boolean;… — ⚠ undocumented · used by `entityMetaOf`: Narrow `entity.meta` with a type guard — prefer this over `entity.meta as T` so failed shapes return `null` instead of lying to the type che…
2198
2485
  - `SceneKindObject` (interface): interface SceneKindObject — The raw document object a resolver receives — shape shared by markers, volumes, and paths.
2199
2486
  - `SceneKindResolveContext` (interface): interface SceneKindResolveContext — Ground sampler + options a resolver may read (terrain height/normal snap).
2200
- - `SceneObject` (interface): interface SceneObject — ⚠ undocumented
2487
+ - `SceneObject` (interface): interface SceneObject { instanceId: string; catalogId: string; position: EntityPosition; rotationY: number; parentSpace?: string; visual?: ObjectVisual; animation?: ModelAnimationConfig | "auto" | "none"; state?: Record<string, unknown>; slots?: InventoryState } — ⚠ undocumented · used by `fromRuntimeObjectRow` (@jgengine/core/runtime/objectRows): Inverse of {@link toRuntimeObjectRow}: rebuild the live placed object a host persisted.
2201
2488
  - `SceneObjectSlots` (interface): interface SceneObjectSlots — Per-instance container contents for placed objects whose catalog entry declares `slotInventory` — the engine side of "install a GPU into a rack", "put an item in a chest". Contents live on `SceneObject.slots`, so they replicate and save with the object rather than needing a game-owned table keyed by `instanceId`, and every mutation is validated against the declared `accepts` and stack limits.
2202
- - `SceneRaycastApi` (interface): interface SceneRaycastApi — ⚠ undocumented
2203
- - `SceneRaycastHit` (interface): interface SceneRaycastHit — ⚠ undocumented
2489
+ - `SceneRaycastApi` (interface): interface SceneRaycastApi { raycast(input: SceneRaycastInput): SceneRaycastHit | null; raycastAll(input: SceneRaycastInput): SceneRaycastHit[] } — ⚠ undocumented
2490
+ - `SceneRaycastHit` (interface): interface SceneRaycastHit { targetKind: SceneRaycastTargetKind; instanceId: string; catalogId?: string; colliderName: string; purpose: ColliderPurpose; damageEligible: boolean; blocks: boolean; distance: number; point: EntityPosition; normal: EntityPosition } — ⚠ undocumented · used by `firstImpact`: First impact: nearest hit that blocks, or nearest hit if none block.
2204
2491
  - `ScreenEffect` (interface): interface ScreenEffect — A live effect in the current composite: its label, color, region, and the eased opacity `0..1` to draw *right now*. These objects are pooled and reused across {@link ScreenEffectsController.composite} calls — read them, don't retain.
2205
2492
  - `ScreenEffectEasing` (type): type ScreenEffectEasing = "linear" | "easeIn" | "easeOut" | "easeInOut" — How a transient effect's intensity curves from peak to zero over its life.
2206
2493
  - `ScreenEffectShape` (type): type ScreenEffectShape = "full" | "vignette" — The screen region an effect tints. `"full"` grades the whole frame (a flash); `"vignette"` grades only the edges, leaving the center clear (a directional or ambient border tint). Purely a shape hint for the renderer — no genre meaning.
@@ -2208,13 +2495,13 @@
2208
2495
  - `ScreenEffectsController` (interface): interface ScreenEffectsController — A live, clock-driven screen-feedback controller.
2209
2496
  - `ScreenEffectsOptions` (interface): interface ScreenEffectsOptions — Options for {@link createScreenEffects}.
2210
2497
  - `ScreenEffectsSnapshot` (interface): interface ScreenEffectsSnapshot — Serializable state of every active effect, for save/restore and replay.
2211
- - `ScreenRect` (interface): interface ScreenRect — ⚠ undocumented
2498
+ - `ScreenRect` (interface): interface ScreenRect { minX: number; minY: number; maxX: number; maxY: number } — ⚠ undocumented · used by `isMarquee`: True when the drag is large enough to be a marquee rather than a click.
2212
2499
  - `SelectionBookmarkSnapshot` (interface): interface SelectionBookmarkSnapshot — The serializable shape of a {@link SelectionBookmarks} store: each key maps to its ordered, deduplicated id list. Plain data — safe to persist in a save file or replicate over the wire, and the exact input {@link createSelectionBookmarks} restores.
2213
2500
  - `SelectionBookmarks` (interface): interface SelectionBookmarks — A generic, keyed store of saved id sets ("bookmarks") over stable string ids — the reusable layer under RTS control groups, camera bookmarks, saved squads, editor selection presets, and accessibility recall. It owns storage only: binding, recall, enumeration, pruning, and serialization. It never touches the active {@link SelectionSet} or the camera — replacement/merge and focus stay caller hooks (see {@link recallSelectionBookmark}) so one store serves any genre, input scheme, or focus policy.
2214
2501
  - `SelectionPruneResult` (interface): interface SelectionPruneResult — Outcome of a {@link SelectionBookmarks.prune} pass — what the validity predicate removed.
2215
- - `SelectionSet` (interface): interface SelectionSet — ⚠ undocumented
2216
- - `SensorProbeOptions` (interface): interface SensorProbeOptions — ⚠ undocumented
2217
- - `SensorReading` (interface): interface SensorReading — ⚠ undocumented
2502
+ - `SelectionSet` (interface): interface SelectionSet { add(id: string): void; remove(id: string): void; toggle(id: string): void; has(id: string): boolean; replace(ids: Iterable<string>): void; clear(): void; list(): string[]; size(): number } — ⚠ undocumented · used by `createSelectionSet`: An ordered, deduplicated set of selected instance ids for RTS unit-command routing.
2503
+ - `SensorProbeOptions` (interface): interface SensorProbeOptions { range: number; variableId: string; falloff?: "linear" | "none" } — ⚠ undocumented · used by `useHiddenStateProbe` (@jgengine/shell/vision/HiddenStateProbeHud): Reads a hidden zone/entity state variable in range (EMF / thermometer / geiger style sensor verb, #116).
2504
+ - `SensorReading` (interface): interface SensorReading { sourceId: string; variableId: string; value: HiddenStateValue; strength: number; distance: number } — ⚠ undocumented · used by `useHiddenStateProbe` (@jgengine/shell/vision/HiddenStateProbeHud): Reads a hidden zone/entity state variable in range (EMF / thermometer / geiger style sensor verb, #116).
2218
2505
  - `SequenceCue` (interface): interface SequenceCue<Payload = unknown> — One scheduled beat of a cutscene: a typed cue that fires when the playhead reaches `atMs`.
2219
2506
  - `SequenceDirector` (interface): interface SequenceDirector<Payload = unknown> — A data-driven cutscene: an ordered timeline of typed cues that fire on a single injected clock. See {@link createSequenceDirector}.
2220
2507
  - `SequenceDirectorOptions` (interface): interface SequenceDirectorOptions<Payload = unknown> — Options for {@link createSequenceDirector}.
@@ -2222,28 +2509,28 @@
2222
2509
  - `SequenceState` (interface): interface SequenceState — A read-only view of the director's playback state, returned by {@link SequenceDirector.state}.
2223
2510
  - `SharedAnnotations` (interface): interface SharedAnnotations — Local annotation edits that also broadcast, plus `apply` for inbound edits. `add*`/`remove`/`clear` mirror {@link AnnotationLayer} but return globally-unique ids so two clients never collide.
2224
2511
  - `SharedAnnotationsDeps` (interface): interface SharedAnnotationsDeps — Construction options for {@link createSharedAnnotations}.
2225
- - `SimClock` (interface): interface SimClock — ⚠ undocumented
2226
- - `SkillCheckConfig` (interface): interface SkillCheckConfig — ⚠ undocumented
2227
- - `SkillCheckResult` (interface): interface SkillCheckResult — ⚠ undocumented
2512
+ - `SimClock` (interface): interface SimClock { advance(realDt: number): number; now(): number; advancedSeconds?(): number; snapshot(): ClockSnapshot; hydrate(snapshot: ClockSnapshot): void; calendar(): CalendarTime; isPaused(): boolean; speed(): number; pause(): void; play(): void; to… — ⚠ undocumented · used by `createSimContext` (@jgengine/core/runtime/simContext): Build `ctx.sim` for a context; `createGameContext` calls this from `definition.simulation`.
2513
+ - `SkillCheckConfig` (interface): interface SkillCheckConfig { trackWidth: number; zone: SkillCheckZone; markerPeriod: number; window: number; zoneDriftPerSecond?: number } — ⚠ undocumented · used by `skillCheckZoneAt`: A timing-bar skill check that succeeds when the moving marker is released inside the target zone.
2514
+ - `SkillCheckResult` (interface): interface SkillCheckResult { success: boolean; timedOut: boolean; markerPosition: number; zone: SkillCheckZone } — ⚠ undocumented
2228
2515
  - `SkyEnvironmentDescriptor` (type): type SkyEnvironmentDescriptor = { kind: "sky" } & Required< Pick<SkyEnvironmentConfig, "preset" | "timeOfDay"> > & Omit<SkyEnvironmentConfig, "preset" | "timeOfDay"> — ⚠ undocumented
2229
2516
  - `SlotAssignmentOptions` (interface): interface SlotAssignmentOptions — Tuning for {@link assignFormationSlots}' stable, low-churn matching.
2230
2517
  - `SnapMode` (type): type SnapMode = "grid" | "free" | "surface" — ⚠ undocumented
2231
- - `SnowEnvironmentDescriptor` (type): type SnowEnvironmentDescriptor = { kind: "snow" } & Required< Pick<SnowEnvironmentConfig, "area" | "density" | "speed" | "flakeSize" | "drift" | "wind" | "color" | "opacity"> > — ⚠ undocumented
2518
+ - `SnowEnvironmentDescriptor` (type): type SnowEnvironmentDescriptor = { kind: "snow" } & Required< Pick<SnowEnvironmentConfig, "area" | "density" | "speed" | "flakeSize" | "drift" | "wind" | "color" | "opacity"> > — ⚠ undocumented · used by `snow`: Declares a snowfall weather effect for `environment()` — area, density, drift, wind, and flake opacity.
2232
2519
  - `SoilRules` (interface): interface SoilRules — Fully-defaulted soil params parsed from a volume's `meta`.
2233
- - `SoundDef` (interface): interface SoundDef — ⚠ undocumented
2520
+ - `SoundDef` (interface): interface SoundDef { id: string; url?: string; synth?: SynthPatch; bus: AudioBusId; gain?: number; loop?: boolean; maxVoices?: number; priority?: number; positional?: boolean; falloff?: AudioFalloffConfig; doppler?: number; spatial?: {panning: "hrtf" | "equal… — ⚠ undocumented
2234
2521
  - `SpatialGrid` (class): class SpatialGrid — A uniform-grid broad-phase over the x/z plane, separate from the rigid-body sim, for cheap same-tick proximity across hundreds–thousands of simple movers (swarm enemies). Rebuild each tick from the caller's own position arrays, then `queryCircle` (enemies hitting the player / an AoE) or `forEachPair` (mutual separation). Both are precise: no false negatives, no false positives beyond the exact distance test.
2235
- - `SpawnDirectorConfig` (interface): interface SpawnDirectorConfig — ⚠ undocumented
2236
- - `SpawnDirectorState` (interface): interface SpawnDirectorState — ⚠ undocumented
2237
- - `SpawnEntry` (interface): interface SpawnEntry — ⚠ undocumented
2238
- - `SpawnRequest` (interface): interface SpawnRequest — ⚠ undocumented
2522
+ - `SpawnDirectorConfig` (interface): interface SpawnDirectorConfig { waves: readonly WaveManifest[]; maxAlive?: number; escalationPerSecond?: number; alertBudgetPerSecond?: number; alertDecayPerSecond?: number; playerBudgetPerSecond?: number; maxSpawnsPerTick?: number; loop?: boolean; seed?: number; spawnP… — ⚠ undocumented
2523
+ - `SpawnDirectorState` (interface): interface SpawnDirectorState { wave: number; elapsed: number; waveElapsed: number; budget: number; alert: number; spawnedThisWave: number; spawnedTotal: number; rng: RandomSeed; done: boolean } — ⚠ undocumented
2524
+ - `SpawnEntry` (interface): interface SpawnEntry { id: string; cost: number; weight?: number; minWave?: number } — ⚠ undocumented
2525
+ - `SpawnRequest` (interface): interface SpawnRequest { entryId: string; cost: number; wave: number; point?: NavPoint; laneId?: number } — ⚠ undocumented
2239
2526
  - `StatCatalog` (type): type StatCatalog = Record<string, { max: number; min?: number; current?: number }> — ⚠ undocumented
2240
2527
  - `StatValue` (interface): interface StatValue extends StatPool — Native entity-stat name retained as a compatibility bridge to the portable pool model.
2241
2528
  - `Station` (interface): interface Station — ⚠ undocumented
2242
2529
  - `StoredScreenEffect` (interface): interface StoredScreenEffect — A persisted effect record (all spec fields resolved plus its start time and id).
2243
2530
  - `StratifiedOptions` (interface): interface StratifiedOptions — Inputs for {@link sampleStratified}: a grid over `area` with one jittered point per cell.
2244
2531
  - `StructureGraph` (class): class StructureGraph — A structural-integrity graph over a building — nodes are pieces (walls, beams, floors), edges are load-bearing connections, some nodes are anchored foundations. `damage`/`damageEdge` wear pieces and connections down; when a piece shatters or an edge severs, the graph recomputes which pieces still reach an anchor and hands back every newly-disconnected piece as one `CollapseEvent`. Feed that to `toDebris` to sink the fallen pieces into a `PhysicsWorld` as rigid bodies ("The Finals" smooth destruction, Rainbow Six walls). Coarse by design: it replicates the collapse event, not per fragment.
2245
- - `StructureMaterial` (interface): interface StructureMaterial — ⚠ undocumented
2246
- - `SupportResult` (interface): interface SupportResult — ⚠ undocumented
2532
+ - `StructureMaterial` (interface): interface StructureMaterial { id: string; strength: number; mass?: number } — ⚠ undocumented
2533
+ - `SupportResult` (interface): interface SupportResult { supported: readonly string[]; unsupported: readonly string[]; distance: Readonly<Record<string, number>> } — ⚠ undocumented
2247
2534
  - `SurfaceDelta` (interface): interface SurfaceDelta — A compact record of the surface-material cells a paint stroke touched: parallel `indices`/`before`/`after` arrays into the per-cell surface grid. One per stroke keeps paint undo history small.
2248
2535
  - `SurfaceGridLine` (interface): interface SurfaceGridLine — One draped grid line from {@link surfaceGridLines}: its axis, emphasis, and draped vertices.
2249
2536
  - `SurfaceGridOptions` (interface): interface SurfaceGridOptions — Shaping for {@link surfaceGridLines}: region, spacing, emphasis cadence, and draping.
@@ -2258,23 +2545,23 @@
2258
2545
  - `TerraformFalloff` (type): type TerraformFalloff = "smooth" | "linear" | "none" — How a brush's strength fades from its center to its rim.
2259
2546
  - `TerraformMode` (type): type TerraformMode = "raise" | "lower" | "smooth" | "flatten" | "noise" | "ramp" | "paint" — A sculpt operation kind: heightfield brushes plus the surface-paint brush.
2260
2547
  - `TerraformShape` (type): type TerraformShape = "circle" | "square" — A brush footprint: a round disc or an axis-aligned square.
2261
- - `TerraformSnapshot` (interface): interface TerraformSnapshot — ⚠ undocumented
2548
+ - `TerraformSnapshot` (interface): interface TerraformSnapshot { bounds: Aabb; cellSize: number; cols: number; rows: number; offsets: readonly number[]; surfaces: readonly (string | null)[]; layers?: readonly TerrainMaterialLayer[]; weights?: readonly number[] } — ⚠ undocumented · used by `applyDeltaToSnapshot`: Returns a new snapshot with a delta's `after` offsets applied (copy-on-write — inputs untouched).
2262
2549
  - `TerraformStroke` (interface): interface TerraformStroke — Accumulates a whole drag — many brush stamps — into one compact {@link TerraformDelta}. Keeps each vertex's first `before` and latest `after`, so undo replays the stroke as a single step even though the pointer fired dozens of moves.
2263
2550
  - `TerrainCircleRegion` (interface): interface TerrainCircleRegion extends TerrainRegionStyle — A circular palette zone painted over the base terrain palette — snow caps, ash wastes, spawn circles.
2264
2551
  - `TerrainDetailConfig` (interface): interface TerrainDetailConfig — Procedural detail-surface layer for terrain: a noise-driven shader that keeps the biome-tinted base ground (from `colors`/`biomeBands`) and blends distinct rock, sand, and snow over it by slope, height, and waterline — turning a flat vertex-colour surface into varied, textured-reading ground with no image assets.
2265
2552
  - `TerrainDetailMaterialConfig` (interface): interface TerrainDetailMaterialConfig — Real PBR texture applied over the ground surface — the seam that lets a game put a `buildMaterialCatalog` material on terrain. Blends with, never replaces, the procedural detail shader: color/roughness/ao tile the maps by world position, `strength` fades them over the existing vertex-colour + noise look.
2266
- - `TerrainEnvironmentDescriptor` (type): type TerrainEnvironmentDescriptor = { kind: "terrain" } & Required< Pick<TerrainEnvironmentConfig, "bounds" | "height"> > & Omit<TerrainEnvironmentConfig, "bounds" | "height"> — ⚠ undocumented
2553
+ - `TerrainEnvironmentDescriptor` (type): type TerrainEnvironmentDescriptor = { kind: "terrain" } & Required< Pick<TerrainEnvironmentConfig, "bounds" | "height"> > & Omit<TerrainEnvironmentConfig, "bounds" | "height"> — ⚠ undocumented · used by `terrain`: Declares a heightfield terrain patch for `environment()` — bounds, noise, materials, and flatten masks.
2267
2554
  - `TerrainField` (interface): interface TerrainField — A sampleable ground surface: height and normal at any x/z, with optional bounds and water level.
2268
- - `TerrainFlattenMask` (interface): interface TerrainFlattenMask — ⚠ undocumented
2555
+ - `TerrainFlattenMask` (interface): interface TerrainFlattenMask { center: EnvironmentVec2; radius: number; height?: number; falloff?: number } — ⚠ undocumented · used by `padFlattenMasks` (@jgengine/core/world/features): Derives implicit `TerrainFlattenMask`s carving each pad's footprint into the terrain beneath it.
2269
2556
  - `TerrainMaterialLayer` (interface): interface TerrainMaterialLayer — One material layer in a terrain's reorderable stack: a palette `surface` id (drives the base color) plus its render parameters. Array order is the stack order — lower index paints under higher. `roughness`/`tiling`/`triplanar`/`tint`/`opacity` are carried as data so a runtime game reads them straight off the snapshot.
2270
2557
  - `TerrainMaterialRegion` (type): type TerrainMaterialRegion = TerrainCircleRegion | TerrainPolylineRegion | TerrainRectRegion — A palette zone painted over the base terrain palette. Circle (the default when no `shape` is given), `polyline` ribbons for roads/rivers, and rotatable `rect` districts all paint fully inside their core and blend back across `falloff`; later regions in the list win overlaps.
2271
- - `TerrainPalette` (interface): interface TerrainPalette — ⚠ undocumented
2558
+ - `TerrainPalette` (interface): interface TerrainPalette { low: string; high: string; waterline: string } — ⚠ undocumented
2272
2559
  - `TerrainPathProfile` (interface): interface TerrainPathProfile — A serializable path-driven terrain modifier. The `points` centerline reshapes the heightfield across a corridor of `width`, feathering back to the surrounding ground across `shoulder`. `height` sets the centerline target (sample / fixed / grade); `depth` carves a channel below it (deepest at the centerline, easing to zero at the core edge — rivers and trenches); `retaining` raises walls where the cut/fill exceeds a threshold; `maxCut`/`maxFill` cap how far the ground may move from its base height. All fields are plain data (numbers, string enums, point arrays) so a profile round-trips through the scene document and evaluates identically at author time and runtime.
2273
2560
  - `TerrainPolylineRegion` (interface): interface TerrainPolylineRegion extends TerrainRegionStyle — A ribbon palette zone following a centerline — roads and rivers, instead of chaining overlapping circles.
2274
2561
  - `TerrainRectRegion` (interface): interface TerrainRectRegion extends TerrainRegionStyle — A rectangular palette zone, optionally rotated about the world y axis — plazas, fields, districts.
2275
2562
  - `TerrainRegionStyle` (interface): interface TerrainRegionStyle — Palette and blend fields shared by every `TerrainMaterialRegion` shape.
2276
2563
  - `TerrainSurfaceRule` (interface): interface TerrainSurfaceRule — A height/slope predicate for auto-painting a surface layer (e.g. rock on steep slopes, snow up high).
2277
- - `ThreatTable` (interface): interface ThreatTable — ⚠ undocumented
2564
+ - `ThreatTable` (interface): interface ThreatTable { add(sourceId: string, amount: number): number; set(sourceId: string, amount: number): void; threatOf(sourceId: string): number; decay(dt: number): void; highest(options?: HighestThreatOptions): string | null; ranked(): ThreatEntry[]; taun… — ⚠ undocumented
2278
2565
  - `TimerDirection` (type): type TimerDirection = "down" | "up" — A serializable set of named countdown / countup timers evaluated against an injected clock. One primitive covers round timers, respawn clocks, and ability cooldown/charge — they are the same mechanic (elapsed vs. duration on a clock), so `id` and any labels are free strings the engine never interprets.
2279
2566
  - `TimerExpiryListener` (type): type TimerExpiryListener = (id: string) => void — Listener notified when a timer newly expires. Receives the timer id.
2280
2567
  - `TimerRead` (interface): interface TimerRead — A single timer's resolved state for one read. All fields are plain numbers so a HUD can render without touching the model. For a looping timer the values describe the current cycle; `expired` is only ever `true` for a finished non-looping timer (loops signal completion through {@link TimerSet.poll}).
@@ -2297,7 +2584,7 @@
2297
2584
  - `VISIBILITY_HIDDEN` (const): const VISIBILITY_HIDDEN: 0 — Numeric code for a cell no group member has seen, or has fully forgotten.
2298
2585
  - `VISIBILITY_OBSERVED` (const): const VISIBILITY_OBSERVED: 2 — Numeric code for a cell a group currently observes.
2299
2586
  - `VISIBILITY_REMEMBERED` (const): const VISIBILITY_REMEMBERED: 1 — Numeric code for a cell seen before but not currently in view (last-known).
2300
- - `Vec3` (type): type Vec3 = EntityPosition — ⚠ undocumented
2587
+ - `Vec3` (type): type Vec3 = EntityPosition — ⚠ undocumented · used by `catenaryCurve`: True hyperbolic catenary between two anchors — the shape a uniform cable actually takes under gravity.
2301
2588
  - `VehicleImpact` (interface): interface VehicleImpact — A single blocked-move event a car can react to (#1051): crash damage, screen shake, a metal-crunch cue. Reported once, when a move is first vetoed, then consumed by {@link VehicleObstacleClamp.takeImpact}.
2302
2589
  - `VehicleObstacleClamp` (interface): interface VehicleObstacleClamp — A planar move clamp for a kinematic car: feed {@link clampMove} to a vehicle's `clampMove` hook so an attempted XZ displacement slides along world solids instead of driving through them, and read {@link takeImpact} once per tick for the crash it produced.
2303
2590
  - `VehicleSeats` (class): class VehicleSeats — Composes `scene/mount`'s control-transfer bookkeeping with the seat/camera/movement-mode transition every enter/exit-vehicle flow needs (#533.2): boarding resolves a free seat and reports the camera target, drive target, and rider movement-lock patch in one call; leaving computes a side-door placement next to the vehicle and reports the same triad in reverse. Pure — no entity/camera side effects — the caller applies `riderMovementPatch`/`placement`/`cameraTarget` via its own `ctx`.
@@ -2310,24 +2597,24 @@
2310
2597
  - `VisibilityGroupState` (interface): interface VisibilityGroupState — Sparse serialized knowledge for one group — only non-hidden cells are stored.
2311
2598
  - `VisibilityMemory` (type): type VisibilityMemory = | { readonly kind: "permanent" } | { readonly kind: "none" } | { readonly kind: "decay"; readonly updates: number } — How a cell decays after a group stops observing it — the terrain-memory policy. `permanent` keeps last-known terrain forever (RTS explored map); `none` drops it straight back to hidden the moment it leaves view (pure line-of-sight stealth); `decay` remembers it for `updates` observe cycles, then hides it. Day/night, height, sensor, and stealth rules are NOT encoded here — the caller bakes those into which cells it reports as observed each update.
2312
2599
  - `VisibilityState` (type): type VisibilityState = "hidden" | "remembered" | "observed" — One viewer group's knowledge of a cell: never/forgotten, last-known, or live.
2313
- - `VisibilitySystem` (interface): interface VisibilitySystem — ⚠ undocumented
2600
+ - `VisibilitySystem` (interface): interface VisibilitySystem { update(): VisibilityResult; isVisible(id: string): boolean; isPreloaded(id: string): boolean; boundsOf(id: string): RenderBounds | undefined; settings(): CullingSettings; setSettings(patch: Partial<CullingSettings>): void; stats(): Visibi… — ⚠ undocumented
2314
2601
  - `VolumeDistribution` (type): type VolumeDistribution = "volume" | "radial" — Fill policy for a sphere/shell: `"volume"` = volume-uniform (even density); `"radial"` = radius-uniform.
2315
2602
  - `VolumetricCloudsConfig` (interface): interface VolumetricCloudsConfig — Volumetric cloud layer config for `sky()` — a raymarched cloud slab mounted from the environment `sky` seam. Pure config + defaulting here; the raymarch shader lives in the `shell` renderer (`environment/VolumetricClouds.tsx`), mounted alongside `SkyDome` whenever a sky descriptor carries this field. Off by default — omit `volumetricClouds` on `sky({...})` and no layer mounts.
2316
2603
  - `VolumetricCloudsRules` (interface): interface VolumetricCloudsRules — Fully-defaulted volumetric cloud params, resolved from a `VolumetricCloudsConfig`.
2317
2604
  - `VoxelFace` (type): type VoxelFace = "px" | "nx" | "py" | "ny" | "pz" | "nz" — ⚠ undocumented
2318
2605
  - `VoxelGround` (interface): interface VoxelGround — A procedural volume of blocks. `size` is the generator domain; `generator` is algorithm params only.
2319
2606
  - `VoxelGroundSize` (interface): interface VoxelGroundSize — Size of a `voxel` ground: the generator's domain per axis; axes may be `Infinity` for streaming volumes.
2320
- - `VoxelMaterial` (interface): interface VoxelMaterial — ⚠ undocumented
2607
+ - `VoxelMaterial` (interface): interface VoxelMaterial { id: number; name: string; strength: number; drop?: string } — ⚠ undocumented
2321
2608
  - `VoxelVolume` (class): class VoxelVolume — A runtime-editable dense voxel grid — the carve/deposit op behind destructible dig worlds (Deep Rock Galactic tunnels, Astroneer terrain). Cells hold a material id (0 = empty); `carve` clears a sphere of solid cells that a tool is strong enough to break and returns how many it removed (feed that to a loot roll), `deposit` fills a sphere with a material. World↔cell mapping is `origin`+`scale`.
2322
2609
  - `WATER_SCHEMA` (const): const WATER_SCHEMA: ParamSchema — The water parameter schema — drives the inspector and `meta` parse via the studio seam.
2323
2610
  - `WaterRules` (interface): interface WaterRules — Fully-defaulted water surface params parsed from a volume's `meta`.
2324
- - `WaterSurface` (interface): interface WaterSurface — ⚠ undocumented
2325
- - `WaveManifest` (interface): interface WaveManifest — ⚠ undocumented
2611
+ - `WaterSurface` (interface): interface WaterSurface { readonly level: number; readonly waves: readonly GerstnerWave[]; height(x: number, z: number, time: number): number; normal(x: number, z: number, time: number): WaterNormal; displace(x: number, z: number, time: number): readonly [number, … — ⚠ undocumented
2612
+ - `WaveManifest` (interface): interface WaveManifest { budget: number; entries: readonly SpawnEntry[]; duration?: number; budgetPerSecond?: number } — ⚠ undocumented
2326
2613
  - `WaveRunner` (interface): interface WaveRunner — A live, observable, serializable wave/spawn runner over a seeded spawn director.
2327
2614
  - `WaveRunnerConfig` (interface): interface WaveRunnerConfig extends SpawnDirectorConfig — Configuration for {@link createWaveRunner}: the full {@link SpawnDirectorConfig} (escalating waves, budgets, seed, spawn points) plus an optional {@link WaveSpawnSink}. The runner owns the director state — you never pass a `SpawnDirectorState` here.
2328
2615
  - `WaveSpawnSink` (type): type WaveSpawnSink = (request: SpawnRequest) => void — A callback the wave runner hands each {@link SpawnRequest} the underlying spawn director emits. The runner never instantiates entities itself — it forwards the request (a free-string `entryId` "kind", cost, wave, optional point/lane) to the game, which decides what to build. Keeps the runner genre-agnostic.
2329
2616
  - `WaveView` (interface): interface WaveView — A pooled, per-frame readout of the current wave — the "brain behind WAVE 3". Reused across {@link WaveRunner.view} calls (never per-frame allocated), so read it and render it, don't retain it.
2330
- - `Waypoint` (type): type Waypoint = readonly [number, number, number] — ⚠ undocumented
2617
+ - `Waypoint` (type): type Waypoint = readonly [number, number, number] — ⚠ undocumented · used by `planSolidRoute` (@jgengine/core/nav/solidRoute): Plan an exact-endpoint local route over indexed movement boxes using the shared nav grid.
2331
2618
  - `WaypointEntry` (interface): interface WaypointEntry — A single player-placed waypoint. World-XZ only — serializable and renderer-free.
2332
2619
  - `WaypointGuidance` (interface): interface WaypointGuidance — Direction/range to the tracked waypoint, for an on-screen guide arrow.
2333
2620
  - `WaypointSnapshot` (interface): interface WaypointSnapshot — Whole serializable state of a {@link WaypointStore} — drop into a save blob.
@@ -2335,18 +2622,18 @@
2335
2622
  - `WaypointStoreDeps` (interface): interface WaypointStoreDeps — Construction options for {@link createWaypointStore}.
2336
2623
  - `WeatherEnvironmentDescriptor` (type): type WeatherEnvironmentDescriptor = RainEnvironmentDescriptor | SnowEnvironmentDescriptor — ⚠ undocumented
2337
2624
  - `WeatherModifierTable` (type): type WeatherModifierTable<K extends string = string> = Record<K, WeatherModifier> — ⚠ undocumented
2338
- - `WeatherState` (interface): interface WeatherState — ⚠ undocumented
2625
+ - `WeatherState` (interface): interface WeatherState { kind: WeatherKind; intensity: number; wind?: WindVector } — ⚠ undocumented
2339
2626
  - `WedgeFormationOptions` (interface): interface WedgeFormationOptions — Options for {@link wedgeFormation}.
2340
2627
  - `WeightedParamEntry` (interface): interface WeightedParamEntry — One weighted entry in a `weightedList` param — an item id and its relative spawn weight.
2341
2628
  - `WeightedRegionEntry` (interface): interface WeightedRegionEntry<P extends SamplePoint = SamplePoint> — A weighted member of a {@link weightedRegion} composite.
2342
- - `WindField` (interface): interface WindField — ⚠ undocumented
2629
+ - `WindField` (interface): interface WindField { readonly direction: WindVector; readonly speed: number; at(time: number): WindVector; atPoint(x: number, z: number, time: number): WindVector; strengthAt(x: number, z: number, time: number): number } — ⚠ undocumented
2343
2630
  - `WorldFeature` (type): type WorldFeature = | PlaceWorldFeature | ({ kind: "biomes" } & BiomesWorldConfig) | ({ kind: "voxel" } & VoxelWorldConfig) | ({ kind: "plots" } & PlotsWorldConfig) | ({ kind: "tilemap" } & TilemapWorldConfig) | EnvironmentWorldFeature | { kind: "flat" } — A declared world shape passed to `defineGame`. The preferred model is the place feature from `world()` (`@jgengine/core/world/place`): substrate + laws, with all dressing authored in the editor. The remaining members — biomes, voxel grid, plots, tilemap, environment, flat — are the legacy code-declared shapes kept for existing games.
2344
- - `WorldGridCell` (interface): interface WorldGridCell — ⚠ undocumented
2631
+ - `WorldGridCell` (interface): interface WorldGridCell { x: number; z: number; height?: number; color?: string } — ⚠ undocumented
2345
2632
  - `WorldGridConfig` (interface): interface WorldGridConfig — Shared by `biomes()`/`voxel()`/`plots()`/`tilemap()` so the shell can render their declared content as instanced boxes without a hand-written renderer.
2346
- - `WorldXZ` (type): type WorldXZ = readonly [number, number] — ⚠ undocumented
2633
+ - `WorldXZ` (type): type WorldXZ = readonly [number, number] — ⚠ undocumented · used by `compassBearing`: Compass bearing (radians, 0 = map north = −Z, increasing clockwise toward +X = east) from one world XZ point to another.
2347
2634
  - `acquireTarget` (function): function acquireTarget(policy: AcquisitionPolicy, selfId: string, held: string | null = null): AcquisitionResult — Run one acquisition pass and pick the best target under `policy`. Pass the currently `held` target so retention hysteresis (`switchMargin`, `dropRangeScale`) can keep the lock stable; pass `null` for a cold acquire. Pure and allocation-light — the caller owns the held-target state.
2348
- - `advanceBehaviors` (function): function advanceBehaviors(ctx: GameContext, dt: number): void — Advance every spawned entity carrying a `patrol` or `wander` {@link BehaviorDescriptor} one tick — the engine reads the descriptor, keeps the per-entity nav state itself, and poses the entity, so ambient traffic and idle NPC routes are register-once (attach the behavior at spawn) instead of a per-game per-frame `advancePathFollow` + `setPose` loop. Instances that are paused or disabled through {@link behaviorControl} retain their state and are skipped. The shell/host call this each frame; a game never does.
2349
- - `advanceFreeFlight` (function): function advanceFreeFlight(state: FreeFlightState, intent: FreeFlightIntent, yaw: number, pitch: number | undefined, dt: number, tuning: FreeFlightTuning): FreeFlightStep — Advance one frame of free-flight kinematics. Horizontal uses yaw-relative strafe (A = left, D = right) — never roll/bank. Vertical is independent of pitch unless `alignWithLook` is true (spectator).
2635
+ - `advanceBehaviors` (function): function advanceBehaviors(ctx: GameContext, dt: number): void — Advance every spawned entity carrying a patrol, wander, pursuit or decision graph behavior one tick — the engine reads the descriptor, keeps the per-entity nav state itself, and poses the entity, so ambient traffic and idle NPC routes are register-once (attach the behavior at spawn) instead of a per-game per-frame `advancePathFollow` + `setPose` loop. Instances that are paused or disabled through {@link behaviorControl} retain their state and are skipped. The shell/host call this each frame; a game never does.
2636
+ - `advanceFreeFlight` (function): function advanceFreeFlight(state: FreeFlightState, intent: FreeFlightIntent, yaw: number, pitch: number | undefined, dt: number, tuning: FreeFlightTuning, options?: Pick<MotionFrameOptions, "authoritativeStep">): FreeFlightStep — Advance one frame of free-flight kinematics. Horizontal uses yaw-relative strafe (A = left, D = right) — never roll/bank. Vertical is independent of pitch unless `alignWithLook` is true (spectator).
2350
2637
  - `advanceInterestGate` (function): function advanceInterestGate(state: InterestGateState, config: InterestSchedulerConfig, dt: number, input: InterestGateInput = {}): InterestGateStep — Advance one gate by `dt` seconds against this tick's `input`, mutating `state` in place and returning what the caller should do. Sleeping skips the expensive work (`active: false`) while the gate's timers keep advancing, so state is preserved; a `wake` signal or crossing `wakeRadius` flips it back to active and fires an immediate tick.
2351
2638
  - `advancePathFollow` (function): function advancePathFollow(config: PathFollowConfig, state: PathFollowState, dt: number): PathFollowState — Advance a path-follower by `speed * dt` along its authored polyline. Pure — returns the next state. Crosses multiple waypoints in one step, loops when configured, and reports `done` at the end of a non-looping path. No navmesh required (#52); feed it a navmesh route via `pathFromNav` for click-to-move (#51).
2352
2639
  - `advanceSpawnDirector` (function): function advanceSpawnDirector(config: SpawnDirectorConfig, state: SpawnDirectorState, dt: number, ctx: DirectorContext): DirectorStep — ⚠ undocumented
@@ -2364,6 +2651,7 @@
2364
2651
  - `beginTerraformStroke` (function): function beginTerraformStroke(terrain: Pick<EditableTerrain, "applyRecording">): TerraformStroke — Opens a stroke recorder over `terrain`; stamp edits into it, then read one net delta.
2365
2652
  - `behaviorControl` (function): function behaviorControl(ctx: GameContext): BehaviorControl — Obtain the per-context {@link BehaviorControl} surface for suspending, resuming, seeking, serializing, and inspecting behavior instances by entity id — the lifecycle contract games use to hand pose ownership to possession/streaming/staggering code instead of bypassing the behavior runtime.
2366
2653
  - `biomes` (function): function biomes(config: BiomesWorldConfig): WorldFeature — Declares a biome-painted world — the whole-world alternative to a single `environment()` terrain.
2654
+ - `boardVehicle` (function): function boardVehicle(ctx: GameContext, seats: VehicleSeats, riderId: string, vehicleId: string, options: BoardVehicleOptions = {}): EnterVehicleResult — Apply an existing seat transition to the explicitly named rider. Motor/input policy stays caller-owned.
2367
2655
  - `boundaryNeighbors` (function): function boundaryNeighbors(grid: FootprintGrid, cells: readonly GridCell[]): AdjacentCell[] — Every occupied cell orthogonally touching `cells` but outside them — the connective-piece neighbor set.
2368
2656
  - `boxFormation` (function): function boxFormation(options: BoxFormationOptions): FormationSlotGenerator — A rectangular grid centered on the destination, front row toward `+forward` — a marching block or a phalanx. Rows fill front-to-back, left-to-right.
2369
2657
  - `boxRegion` (function): function boxRegion(min: Point3, max: Point3): SampleRegion<Point3> — An axis-aligned box `[min..max]` in 3D. Uniform density; draws x, y, z in order.
@@ -2375,6 +2663,7 @@
2375
2663
  - `buildTrimmedIntersections` (function): function buildTrimmedIntersections(streets: readonly IntersectionStreet[], junctions: readonly RoadJunctionInput[], sampleHeight: (x: number, z: number) => number, options: JunctionGeometryOptions = {}): TrimmedIntersections — Trim a set of streets against a set of junctions and weld the crossing surfaces in one call — the ergonomic entry the shell/playground consume for meshing.
2376
2664
  - `building` (function): function building(config: BuildingEnvironmentConfig = {}): BuildingEnvironmentDescriptor — Declares a cluster of procedurally-massed buildings for `environment()` — count, footprint, stories, style. Pass `along` to line road frontage instead of gridding around `position`.
2377
2665
  - `buildingIndex` (function): function buildingIndex(buildings: readonly GeneratedBuilding[]): BuildingIndex — ⚠ undocumented
2666
+ - `canMerge` (function): function canMerge(a: FootprintRegion, b: FootprintRegion, budget: RegionWorkBudget = {}): boolean — Matching kind/tier, disjoint valid source identities/cells, and cardinal contact. Linear in input size.
2378
2667
  - `cappedStacks` (function): function cappedStacks<P>(limit: number, magnitudeOf?: MagnitudeOf<P>): AreaStackPolicy<P> — Keep at most `limit` memberships per `stackKey` (the highest-magnitude ones when `magnitudeOf` is given, else the first-seen). Models capped stacks — e.g. a poison that stacks up to 5 times.
2379
2668
  - `carrySpeedMultiplier` (function): function carrySpeedMultiplier(mass: number, carryCapacity: number, owners: number): number — Movement multiplier (1 = unhindered, →0 = crushed) for a body of `mass` carried by `owners`. Pure — the HUD/movement kit reads it to slow a laden hauler (Lethal Company) and to gate items that need 2+ people (R.E.P.O.).
2380
2669
  - `carvableTerrain` (function): function carvableTerrain(base: TerrainField): CarvableField — ⚠ undocumented
@@ -2394,12 +2683,12 @@
2394
2683
  - `contextVerb` (function): function contextVerb(label: string, command: string, args?: Record<string, unknown>): ContextVerb — Builds a {@link ContextVerb} for a right-click menu entry.
2395
2684
  - `contextVerbInput` (function): function contextVerbInput(menu: ContextMenu, verb: ContextVerb): Record<string, unknown> — Command input a chosen verb dispatches: the verb's own args, plus the target id and the world point, so a single handler can walk the actor to the target then perform it.
2396
2685
  - `controlGroupKey` (function): function controlGroupKey(digit: number, options: ControlGroupOptions = {}): string — The stable bookmark key for a control-group `digit` under `options.keyPrefix` — the key a caller passes to `SelectionBookmarks.bind`/`recall` to store a group without going through {@link resolveControlGroupIntent}.
2397
- - `createAircraftDynamics` (function): function createAircraftDynamics(tuning: AircraftTuning, options: AircraftOptions = {}): AircraftDynamics — Six-degree-of-freedom arcade flight model for fixed-wing, helicopter, and VTOL aircraft.
2686
+ - `createAircraftDynamics` (function): function createAircraftDynamics(initialTuning: AircraftTuning, options: AircraftOptions = {}): AircraftDynamics — Six-degree-of-freedom arcade flight model for fixed-wing, helicopter, and VTOL aircraft.
2398
2687
  - `createAnnotationLayer` (function): function createAnnotationLayer(options: AnnotationLayerOptions = {}): AnnotationLayer — Player-drawn map annotation layer: freehand `strokes`, area `shapes`, and pinned `notes`, all world-XZ and serializable. `routes()`/`zones()` project strokes/shapes into the exact shapes `Minimap`/`WorldMap`/`FullscreenMap` already render via their `routes`/`zones` props, so drawing needs no new renderer. State is plain data; `snapshot`/`restore` round-trip through a save.
2399
2688
  - `createAreaEffectField` (function): function createAreaEffectField<P = unknown>(state?: AreaFieldState<P>): AreaEffectField<P> — Build a continuous area-effect field. Drive it with `setSource` (once per live source per tick, so shapes follow their emitters) and `step` (to reconcile membership and drain enter/refresh/leave edges). Optionally restore prior membership by passing a `serialize()` snapshot; re-`setSource` live shapes before the first `step` after restore, since shapes are transient.
2400
2689
  - `createAssetCatalog` (function): function createAssetCatalog<TMeta extends ModelAssetRef = ModelAssetRef>(): AssetCatalog<TMeta> — ⚠ undocumented
2401
2690
  - `createAuthoredTriggerRuntime` (function): function createAuthoredTriggerRuntime(options: { document: SceneDocumentLike; handlers?: TriggerHandlers; /** Invoked for every dispatch after the matching handler (if any). */ onDispatch?: (event: TriggerDispatchEvent) => void; /** Override the collected trigger list (tests / hot-reload). Default: … — Build a runtime that watches a document's authored triggers against moving actors and dispatches to per-action handlers (and optional catch-all). Pure membership math; the game supplies actors each tick from its own player/entity poses.
2402
- - `createBallisticSweep` (function): function createBallisticSweep(world: PhysicsWorld, options: BallisticSweepOptions = {}): BallisticSweep — Marches the closed-form arc (constant gravity, straight lateral) through `world` and reports the first sample inside any live body's AABB — sleeping bodies included — refined by one bisection between the last clear sample and the hit sample. Returns `null` when the whole arc is clear.
2691
+ - `createBallisticSweep` (function): function createBallisticSweep(world: PhysicsWorld, options: BallisticSweepOptions = {}): BallisticSweep — Sweeps each short chord of the closed-form arc through live body AABBs, including sleeping bodies. Thin cover between samples blocks; `step` controls the approximation of the curved path.
2403
2692
  - `createBodyBind` (function): function createBodyBind(deps: BodyBindDeps): BodyBind — Mirror a sim's body snapshots onto scene entities each tick — spawn on first sight, pose while bound, despawn on drop — replacing a per-body `setPose` loop plus its `despawn`/`spawn` respawn dance.
2404
2693
  - `createBuoyantBody` (function): function createBuoyantBody(world: PhysicsWorld, config: BuoyantBodyConfig): BuoyantBody — ⚠ undocumented
2405
2694
  - `createCameraShake` (function): function createCameraShake(config: CameraShakeConfig = {}): CameraShakeController — A seeded, serializable, trauma-based camera shake / impulse controller. A game calls `add(amount, kind?)` on impacts (a hit, an explosion, a landing) to raise trauma in `[0, 1]`; `update(dt)` bleeds it off over time; and `offset()` returns a pooled `{ x, y, z, pitch, yaw, roll }` kick equal to `trauma^exponent` times per-axis maxima times deterministic seeded value-noise sampled along an internal time cursor. A shell consumer applies that offset to the active camera each frame so the view visibly shakes. Nothing here is genre-specific: `kind` is a free label the game styles, all amplitudes/decay are parameters, and `snapshot`/ `restore` round-trips the shake through a save. Deterministic from `seed`: the same seed and dt sequence reproduce the same offsets.
@@ -2421,16 +2710,17 @@
2421
2710
  - `createGrappleSwing` (function): function createGrappleSwing(config: GrappleSwingConfig = {}): GrappleSwing — Grappling-hook rope swing physics with anchor, pendulum motion, and reel-in.
2422
2711
  - `createInterestCensus` (function): function createInterestCensus(): InterestCensusAccumulator — Create a census accumulator so the caller can tally active/dormant gates during the loop it already runs, avoiding any separate full-world scan just to report scheduler metrics.
2423
2712
  - `createInterestGateState` (function): function createInterestGateState(config: InterestSchedulerConfig, phase = 0): InterestGateState — Create a dormant gate state, staggered by `phase` (`[0,1)`, typically {@link interestPhase} of the agent id) so siblings created together do not all fire their first active tick on the same frame.
2424
- - `createKinematicVehicle` (function): function createKinematicVehicle(initialTuning: KinematicVehicleTuning, options: KinematicVehicleOptions = {}): KinematicVehicle — ⚠ undocumented
2713
+ - `createKinematicVehicle` (function): function createKinematicVehicle(initialTuning: KinematicVehicleTuning, options: KinematicVehicleOptions = {}): KinematicVehicle — Arcade ground-vehicle sim (car, kart, bike) with throttle, brake, steer, drift and retunable handling.
2425
2714
  - `createLeaderTrail` (function): function createLeaderTrail(config: LeaderTrailConfig): LeaderTrail — A trailing follower formation that chases a leader along its past path — snake/convoy trails.
2426
2715
  - `createLodScheduler` (function): function createLodScheduler(config: LodSchedulerConfig): LodScheduler — ⚠ undocumented
2427
2716
  - `createMarkerSet` (function): function createMarkerSet<TMeta = unknown>(now: () => number = Date.now): MarkerSet<TMeta> — ⚠ undocumented
2428
2717
  - `createMarkerSource` (function): function createMarkerSource<TEntity, TMarker extends MarkerView = MarkerView>(options: MarkerSourceOptions<TEntity, TMarker>): MarkerSource<TMarker> — Adapt a caller-owned array/store to an observable marker source. Projection is cached between source changes, so React reads do not copy the collection per frame. The caller retains ownership of entities, updates, persistence, and subscription scheduling.
2429
2718
  - `createMountController` (function): function createMountController(): MountController — ⚠ undocumented
2430
2719
  - `createNavGrid` (function): function createNavGrid(config: NavGridConfig): NavGrid — ⚠ undocumented
2720
+ - `createNavMeshQuery` (function): function createNavMeshQuery(mesh: NavMeshData, options: NavMeshQueryOptions = {}): NavMeshQuery — Create a cached query over a nav mesh: height-aware polygon lookup through a uniform grid, binary-heap A* priced by area costs and bounded by `maxNodes`, portal-funnel path straightening, and edge-walking raycasts.
2431
2721
  - `createOrderQueue` (function): function createOrderQueue<TCtx, TPayload = unknown>(registry: OrderRegistry<TCtx>, options: OrderQueueOptions<TPayload> = {}): OrderQueue<TCtx, TPayload> — Create a per-entity order queue over a shared kind registry. The queue owns the deterministic lifecycle and preemption policy; the kinds own behavior. Nothing here is random or unbounded: id generation is injected, activation is bounded by the pending count, and a single `tick` advances at most the active order plus one activation.
2432
2722
  - `createOrderRegistry` (function): function createOrderRegistry<TCtx>(): OrderRegistry<TCtx> — Build an empty order-kind registry. Register the built-in compositions from `orders/orderKinds` or your own verbs, then hand it to `createOrderQueue`. One registry is shared by many per-entity queues.
2433
- - `createParticleSystem` (function): function createParticleSystem(config: EmitterConfig = {}): ParticleSystem — A generic, allocation-aware particle system: one emitter, a fixed pool, and Structure-of-Arrays buffers a renderer uploads straight to the GPU. It is dt-driven (call `update(dt)` each frame) and deterministic — all randomness flows from an injected `seed`, so the same seed and dt sequence reproduce the same frames, and `snapshot`/`restore` round-trips the live pool. Nothing here is combat- or genre-specific: configure it for smoke, sparks, rain, dust, embers, magic, or confetti. Travel/gameplay stays elsewhere; this owns only the spawn-integrate-fade lifecycle.
2723
+ - `createParticleSystem` (function): function createParticleSystem(config: EmitterConfig = {}, options: ParticleSystemOptions = {}): ParticleSystem — A generic, allocation-aware particle system: one emitter, a fixed pool, and Structure-of-Arrays buffers a renderer uploads straight to the GPU. It is dt-driven (call `update(dt)` each frame) and deterministic — all randomness flows from an injected `seed`, so the same seed and dt sequence reproduce the same frames, and `snapshot`/`restore` round-trips the live pool. Nothing here is combat- or genre-specific: configure it for smoke, sparks, rain, dust, embers, magic, or confetti. Travel/gameplay stays elsewhere; this owns only the spawn-integrate-fade lifecycle.
2434
2724
  - `createPathFollow` (function): function createPathFollow(config: PathFollowConfig): PathFollowState — ⚠ undocumented
2435
2725
  - `createPlacedStructureStore` (function): function createPlacedStructureStore(): PlacedStructureStore — ⚠ undocumented
2436
2726
  - `createPlacementController` (function): function createPlacementController(config: PlacementControllerConfig): PlacementController — Headless placement ghost: hover → valid/invalid preview, rotate, grid/free/surface snap, commit. Pair with `@jgengine/shell/structures` `PlacementGhost` and {@link placeAssetFromCommit}.
@@ -2514,11 +2804,13 @@
2514
2804
  - `keybind` (function): function keybind(actionId: string, label?: string): KeybindPromptDisplay — ⚠ undocumented
2515
2805
  - `label` (function): function label(text: string): LabelPromptDisplay — ⚠ undocumented
2516
2806
  - `laneCenters` (function): function laneCenters(road: RoadEnvironmentDescriptor): readonly [StreetLane, StreetLane] — Two right-hand-traffic lane centerlines for a road — each offset a quarter of the drivable width from the center and ordered in its direction of travel. Feed a lane's `path` straight into `nav/pathFollow` for traffic AI, or use its endpoints as directed car spawn points.
2807
+ - `leaveVehicle` (function): function leaveVehicle(ctx: GameContext, seats: VehicleSeats, riderId: string, options: LeaveVehicleOptions = {}): ExitVehicleResult | { ok: false; reason: "unknown_rider" | "unknown_vehicle" } — Apply dismount placement, restore rider movement/visibility and the configured camera rig.
2517
2808
  - `lineFormation` (function): function lineFormation(options: LineFormationOptions): FormationSlotGenerator — A single rank abreast, centered on the destination and facing forward — a skirmish line or a chorus row. Slots run left→right along the group's right axis.
2518
2809
  - `listTriggerActions` (function): function listTriggerActions(target?: TriggerSourceKind): TriggerActionDefinition[] — Every registered action, optionally filtered by target collection.
2519
2810
  - `mapLayerColor` (function): function mapLayerColor(tone: MapLayerTone | undefined): string — ⚠ undocumented
2520
2811
  - `markerCatalogId` (function): function markerCatalogId(marker: AuthoredObjectMarkerLike): string | null — Catalog id for a marker: first-class `catalogId` field, else `meta.catalogId` migration alias. Returns null when the marker is not an authored catalog prop (spawn, mob, generator, …).
2521
2812
  - `markerKindStyle` (function): function markerKindStyle(kind: string | undefined, styles: Record<string, MarkerKindStyle> = DEFAULT_MARKER_KINDS): MarkerKindStyle — ⚠ undocumented
2813
+ - `mergeFootprints` (function): function mergeFootprints(a: FootprintRegion, b: FootprintRegion, id: string, budget: RegionWorkBudget = {}): MergedRegion | null — Pool compatible footprints under caller-owned `id`, retaining original capacities and identities. Returns null for invalid data, kind/tier mismatch, overlap, no cardinal contact or exhausted budget. Does not mutate inputs or grid reservations; costs, merge width and upgrade policy remain caller-owned. Capacity is the ordered JavaScript-number sum of original contributions, independent of cell area.
2522
2814
  - `migrateTerrainSnapshot` (function): function migrateTerrainSnapshot(snapshot: TerraformSnapshot): TerraformSnapshot — Upgrades a pre-2.0 snapshot in place-safe (copy-on-write) form: derives a {@link TerrainMaterialLayer} stack from the distinct painted surfaces (first-seen order, default params) when none exists. Leaves the lazy `weights` buffer absent — a single-layer terrain stays compact until blended. Idempotent: a snapshot that already carries `layers` is returned unchanged.
2523
2815
  - `minimapBakeToPngDataUri` (function): function minimapBakeToPngDataUri(bake: MinimapBake): string — Encodes a bake as a `data:image/png;base64,...` URI — the exact string the `Minimap` / `WorldMap` `background` prop takes. Deterministic for a fixed bake.
2524
2816
  - `mtof` (function): function mtof(midi: number): number — Standard equal-temperament MIDI-to-frequency (A4 = 440 Hz at MIDI 69).
@@ -2556,9 +2848,10 @@
2556
2848
  - `polygonArea` (function): function polygonArea(polygon: readonly Vec2[]): number — Shoelace area of a polygon (always non-negative), in square meters.
2557
2849
  - `polygonBounds` (function): function polygonBounds(polygon: readonly Vec2[]): Aabb | null — Axis-aligned bounds of a polygon, or null if it has no points.
2558
2850
  - `polygonRegion` (function): function polygonRegion(polygon: readonly Vec2[]): SampleRegion<Vec2> — An arbitrary closed polygon on the XZ plane. Candidates are drawn uniformly from the polygon's bounding box and gated by point-in-polygon `contains`, so a run's draw count stays fixed (the sampler's attempt budget bounds the rejection, not a hidden inner loop).
2559
- - `populateNavGridFromEnvironment` (function): function populateNavGridFromEnvironment(grid: NavObstacleGrid, world: EnvironmentWorldFeature): number — Expands every structure descriptor on an environment world feature into its generated buildings and blocks their footprints on `grid`. Returns the number of buildings blocked.
2851
+ - `populateNavGridFromEnvironment` (function): function populateNavGridFromEnvironment(grid: NavObstacleGrid, world: EnvironmentWorldFeature): number — Blocks the footprint of every solid building an environment feature's `structures` generate — the same set `ctx.world.solids` holds for it. Returns the number of buildings blocked.
2560
2852
  - `projectToMinimap` (function): function projectToMinimap(world: WorldXZ | readonly [number, number, number], view: MinimapView): MinimapPoint — Project a world XZ (or XYZ) point into minimap pixel space. Origin is the top-left of the `size×size` box; north (−Z) maps to −Y (up). Pass `view.rotate` to spin the map under a fixed north-up player arrow.
2561
2853
  - `proximityPrompt` (function): function proximityPrompt({ radius, display, invoke = null }: ProximityPromptConfig): ProximityPrompt — ⚠ undocumented
2854
+ - `pursue` (function): function pursue(options: Omit<PursueBehavior, "kind">): PursueBehavior — Attach aggro, collision-aware chase, cooldown attack and latched return-to-spawn as data.
2562
2855
  - `quarterTurnsToRotationY` (function): function quarterTurnsToRotationY(quarterTurns: number): number — Maps 0–3 quarter turns onto radians for ghost/commit rotation.
2563
2856
  - `rain` (function): function rain(config: RainEnvironmentConfig = {}): RainEnvironmentDescriptor — Declares a rainfall weather effect for `environment()` — area, density, speed, wind, and drop width/opacity.
2564
2857
  - `raiseAlert` (function): function raiseAlert(state: SpawnDirectorState, amount: number): SpawnDirectorState — ⚠ undocumented
@@ -2569,7 +2862,7 @@
2569
2862
  - `recallSelectionBookmark` (function): function recallSelectionBookmark(bookmarks: SelectionBookmarks, key: string, selection: SelectionSet, options: RecallBookmarkOptions = {}): string[] — Compose a bookmark recall onto an active {@link SelectionSet}: optionally prune stale ids (updating the stored bookmark), fold the survivors into the selection by `mode`, then fire the caller's focus hook. This is the one place the store, the selection, and the camera meet — kept as an explicit helper, not a store side effect, so games opt into the exact replacement/merge and focus policy they want. Returns the surviving ids that were applied.
2570
2863
  - `rectRegion` (function): function rectRegion(area: Aabb, options: { distribution?: AreaDistribution; edgeThickness?: number } = {}): SampleRegion<Vec2> — An axis-aligned rectangle on the XZ plane. `"uniform"` spreads evenly (draws x then z); `"edge"` biases onto a boundary band of `edgeThickness` meters (perimeter-weighted side, then inward depth) — the two are never conflated, the caller opts in.
2571
2864
  - `registerAssetGenerator` (function): function registerAssetGenerator(definition: AssetGeneratorDefinition): void — Register a parametric asset generator. Idempotent per id (last wins); call at module load.
2572
- - `registerBehaviorActions` (function): function registerBehaviorActions(id: string, actions: Record<string, BehaviorAction>): () => void — Register the named actions used by decision graph behavior descriptors.
2865
+ - `registerBehaviorActions` (function): function registerBehaviorActions(id: string, actions: Record<string, BehaviorAction>, options: BehaviorActionOptions = {}): () => void — Register the named actions used by decision graph behavior descriptors.
2573
2866
  - `registerSceneKind` (function): function registerSceneKind<TResolved>(definition: SceneKindDefinition<TResolved>): void — Register a scene kind — the plug-in point for a new parametric studio. Idempotent per `kind` (last registration wins), so a game's registration overrides a default. Call at module load; the editor inspector, `+ Add` menu, and `AuthoredScene` renderer lookup all read this registry.
2574
2867
  - `registerTriggerAction` (function): function registerTriggerAction(definition: TriggerActionDefinition): void — Declare a game action the editor can assign to volume/marker triggers. Idempotent per `id` (last registration wins). Call at module load next to catalogs.
2575
2868
  - `relativeBearing` (function): function relativeBearing(bearing: number, reference: number): number — Signed offset of `bearing` from `reference`, wrapped into (−π, π].
@@ -2632,6 +2925,7 @@
2632
2925
  - `solveLockPath` (function): function solveLockPath(spec: LockSpec): number[] | null — Return a concrete row-per-column solution, or null if the board is unsolvable.
2633
2926
  - `solveSupport` (function): function solveSupport(pieces: readonly SupportPiece[], links: readonly SupportLink[], config: SupportConfig = {}): SupportResult — ⚠ undocumented
2634
2927
  - `sphereRegion` (function): function sphereRegion(center: Point3, radius: number, options: { distribution?: VolumeDistribution } = {}): SampleRegion<Point3> — A filled ball. `"volume"` is volume-uniform (∛-corrected radius); `"radial"` is radius-uniform. Direction is drawn first (two draws), then radius.
2928
+ - `splitRegion` (function): function splitRegion(region: MergedRegion, leftIds: readonly string[], ids: RegionSplitIds = {}, budget: RegionWorkBudget = {}): [FootprintRegion, FootprintRegion] | null — Partition whole original contributions by `leftIds`; both sides must remain cardinally connected. Singletons restore their original id and capacity; larger sides require explicit output identities. Returns null for invalid provenance/budget, unknown or duplicate ids, empty/disconnected sides, or conflicting output identities (including identities from the opposite side's contributions). An output may reuse its own side's source identity. Cell cuts/capacity repricing require caller policy. Both sides inherit the current region kind/tier. Inputs and grid reservations remain unchanged.
2635
2929
  - `steerYaw` (function): function steerYaw(yaw: number, steerRight: number, turnRatePerSecond: number, dt: number): number — Integrate one steering step. `steerRight` is the signed steer input (+1 = turn right, matching `DRIVE_AXIS_BINDINGS`' KeyD/ArrowRight), `turnRatePerSecond` is radians per second at full lock. Steering right decreases yaw in the engine frame; this helper owns that sign so game code never re-derives it.
2636
2930
  - `stepLock` (function): function stepLock(spec: LockSpec, col: number, row: number, action: LockAction): { result: LockStepResult; col: number; row: number } — Authoritative single step. The caller owns the lives economy: a slip/bind/trap does not advance the pick and should cost a life; advanced/success move the pick.
2637
2931
  - `stepPlayerMovement` (function): function stepPlayerMovement(ctx: GameContext, userId: string, input: InputFrame, dt: number, tuning: PlayerMovementTuning, heading?: number, pitch?: number): void — Integrate one player's movement for a tick from their held-input frame and commit the pose — the single genre-agnostic controller both the shell (its local player) and a host (each connected player in `onTick`) call, so single-player and server-authoritative movement are identical. Reads the player's controlled entity, terrain, scene solids, and pending motion impulses; writes the entity pose via `setPose`. Retains heading + kinematic body per `userId` on the `ctx`. Pass `heading` to override the internally-integrated yaw (the shell owns yaw for its camera); omit it and the controller turns from the frame's `turnLeft`/`turnRight` actions. Pass `pitch` for 6DOF spectator flight when `alignWithLook` is set.
@@ -2644,7 +2938,7 @@
2644
2938
  - `terrain` (function): function terrain(config: TerrainEnvironmentConfig = {}): TerrainEnvironmentDescriptor — Declares a heightfield terrain patch for `environment()` — bounds, noise, materials, and flatten masks.
2645
2939
  - `terrainContourGuides` (function): function terrainContourGuides(field: Pick<TerrainField, "sampleHeight">, region: GuideRegion, targetLines = 12, resolution = 128): { interval: number; summary: ElevationSummary; contours: ContourLine[] } — Convenience over {@link extractContours} that auto-picks the interval from the field's own relief: summarises the region, chooses a readable interval for `targetLines` bands, and traces the contours — the one call the editor overlay makes to turn a `TerrainField` into ready-to-draw guides. Returns an empty list for flat ground.
2646
2940
  - `themeLoopSeconds` (function): function themeLoopSeconds(theme: MusicTheme): number — Loop length of a theme in seconds.
2647
- - `tickDrivableVehicle` (function): function tickDrivableVehicle(vehicle: KinematicVehicle, dt: number, axis: AxisInput, options: DrivableVehicleOptions = {}): DrivableVehicleStep — Connects an `AxisInput` sample straight through a {@link KinematicVehicle} to a scene entity's pose for one tick (#533.1) — the throttle/steer/handbrake → sim → `setPose` loop every drivable-vehicle game hand-rolled. Ground-snaps the result when `groundHeight` is given (terrain-following cars, not just flat racetracks). Pair with `scene/vehicleSeat` for who is allowed to drive and where the camera points; this function only steps the sim and shapes the pose patch, nothing else.
2941
+ - `tickDrivableVehicle` (function): function tickDrivableVehicle<TStep extends DrivableSimStep = KinematicVehicleStep, TModifiers = KinematicVehicleModifiers>(vehicle: DrivableSim<TModifiers, TStep>, dt: number, axis: AxisInput, options: DrivableVehicleOptions<TModifiers> = {}): DrivableVehicleStep<TStep> — Connects an `AxisInput` sample straight through a ground-vehicle sim (`KinematicVehicle` or `VehicleDynamics`) to a scene entity's pose for one tick (#533.1) — the throttle/steer/handbrake → sim → `setPose` loop every drivable-vehicle game hand-rolled. Ground-snaps the result when `groundHeight` is given (terrain-following cars, not just flat racetracks). Pair with `scene/vehicleSeat` for who is allowed to drive and where the camera points; this function only steps the sim and shapes the pose patch, nothing else.
2648
2942
  - `tierForStanding` (function): function tierForStanding(tiers: readonly ReputationTier[], standing: number): ReputationTier — Map a faction standing value to its named reputation tier.
2649
2943
  - `tilemap` (function): function tilemap(config: TilemapWorldConfig): WorldFeature — Declares a 2D tilemap world from a map string.
2650
2944
  - `toDebrisBodies` (function): function toDebrisBodies(pieces: readonly SupportPiece[], collapsedIds: readonly string[], options: DebrisOptions = {}): AddBodyOptions[] — ⚠ undocumented
@@ -2670,6 +2964,15 @@
2670
2964
  - `world` (function): function world(config: PlaceConfig): PlaceWorldFeature — Declares the place a game is played in: substrate (`ground`) + laws (`physics`, `ground.surface`). The thin default start — `flat` with `Infinity` axes and default physics, `board` for 2D — is a complete world; dressing it (sky, foliage, props, sculpt) is editor-authored scene content. Multiple worlds per game are first-class: declare one `world()` per place, each with its own id, ground, and physics. Games that are not a spatial place (pure UI/rules) omit `world` entirely.
2671
2965
  - `worldSockets` (function): function worldSockets(def: ConnectorPieceDef, piece: PlacedPiece): WorldSocket[] — ⚠ undocumented
2672
2966
 
2967
+ ## @jgengine/core/world/authoredAnimation
2968
+
2969
+ - `AuthoredAnimationDiagnostic` (interface): interface AuthoredAnimationDiagnostic — Rejected saved animation data with a document location and a repair.
2970
+ - `AuthoredAnimationDocumentLike` (interface): interface AuthoredAnimationDocumentLike — Structural marker input; immutable editor scene documents satisfy it.
2971
+ - `AuthoredAnimationResult` (interface): interface AuthoredAnimationResult — Resolved marker animation or the caller's unchanged default, with rejected-value diagnostics.
2972
+ - `createAuthoredAnimationReader` (function): function createAuthoredAnimationReader(getDocument: () => AuthoredAnimationDocumentLike, markerId: string, defaults?: AuthoredAnimation): () => AuthoredAnimationResult — Reads the latest immutable document each call, validating only when document identity changes. The caller owns the marker-to-character association and its asset-specific animation defaults.
2973
+ - `readAuthoredAnimation` (function): function readAuthoredAnimation(document: AuthoredAnimationDocumentLike, markerId: string, defaults?: AuthoredAnimation): AuthoredAnimationResult — Resolves a caller-selected marker's animation. Authored configuration replaces game defaults; omitted or malformed data retains the exact default. Partial roles remain available for editing. Asset clip and rig compatibility is diagnosed by the animation renderer.
2974
+ - `readAuthoredAnimationValue` (function): function readAuthoredAnimationValue(value: unknown, path: string, defaults?: AuthoredAnimation): AuthoredAnimationResult — Validates one saved animation value at the caller's document path without scanning markers. Omitted or malformed values retain the exact default; valid authored configuration replaces it whole.
2975
+
2673
2976
  ## @jgengine/core/world/authoredEntities
2674
2977
 
2675
2978
  - `AuthoredEntityDocumentLike` (interface): interface AuthoredEntityDocumentLike — The minimal document shape {@link authoredEntitySpawns} walks; any `EditorDocument` satisfies it.
@@ -2685,14 +2988,33 @@
2685
2988
  - `AuthoredObjectMarkerLike` (interface): interface AuthoredObjectMarkerLike extends SceneMarkerLike — Minimal marker shape {@link resolveAuthoredObjects} reads; any `EditorMarker` satisfies it.
2686
2989
  - `AuthoredObjectPlaceTarget` (interface): interface AuthoredObjectPlaceTarget — Structural place target — any `ObjectStore` satisfies it.
2687
2990
  - `AuthoredObjectsDocumentLike` (interface): interface AuthoredObjectsDocumentLike — Minimal document shape {@link resolveAuthoredObjects} walks; any `EditorDocument` satisfies it.
2991
+ - `AuthoredObjectsResult` (interface): interface AuthoredObjectsResult — Resolved catalog props and located repairs for rejected per-placement animation overrides.
2688
2992
  - `ENTITY_MARKER_KINDS` (const): const ENTITY_MARKER_KINDS: readonly string[] — Marker kinds that are spawnable entities (materialized by `authoredEntitySpawns`), not static catalog props. {@link resolveAuthoredObjects} skips them by default so a placed `mob`/`boss` is spawned once as an entity and never also placed as a static mesh.
2689
2993
  - `PlaceAuthoredObjectsOptions` (interface): interface PlaceAuthoredObjectsOptions — Options for {@link placeAuthoredObjects}.
2690
2994
  - `ResolveAuthoredObjectsOptions` (interface): interface ResolveAuthoredObjectsOptions — Options for {@link resolveAuthoredObjects}.
2691
- - `markerAnimation` (function): function markerAnimation(marker: AuthoredObjectMarkerLike): AuthoredAnimation | undefined — The authored per-placement rig animation override stored at `marker.meta.animation` (#1274), or `undefined` when absent/malformed. `"auto"` / `"none"` pass through as the string modes; any other object is a `ModelAnimationConfig` the shell applies verbatim (validated at author time in the editor). Runtime trusts the persisted document here exactly like it trusts position and rotation.
2995
+ - `markerAnimation` (function): function markerAnimation(marker: AuthoredObjectMarkerLike): AuthoredAnimation | undefined — Validated per-placement animation from `marker.meta.animation`. Missing or malformed overrides return undefined so the placed object retains its catalog animation; valid data replaces it whole.
2692
2996
  - `markerCatalogId` (function): function markerCatalogId(marker: AuthoredObjectMarkerLike): string | null — Catalog id for a marker: first-class `catalogId` field, else `meta.catalogId` migration alias. Returns null when the marker is not an authored catalog prop (spawn, mob, generator, …).
2693
2997
  - `placeAuthoredObjects` (function): function placeAuthoredObjects(store: AuthoredObjectPlaceTarget, objects: readonly AuthoredObject[], sampleHeight: (x: number, z: number) => number, options: PlaceAuthoredObjectsOptions = {}): string[] — Places resolved authored objects into an object store, grounding each on `sampleHeight(x,z)` plus per-object and options vertical offsets. Returns the instance ids that were placed (or kept).
2694
2998
  - `placeAuthoredObjectsFromDocument` (function): function placeAuthoredObjectsFromDocument(store: AuthoredObjectPlaceTarget, document: AuthoredObjectsDocumentLike, sampleHeight: (x: number, z: number) => number, options: PlaceAuthoredObjectsOptions = {}): string[] — Convenience: resolve a document then place every authored catalog prop.
2695
2999
  - `resolveAuthoredObjects` (function): function resolveAuthoredObjects(document: AuthoredObjectsDocumentLike, options: ResolveAuthoredObjectsOptions = {}): AuthoredObject[] — Every marker carrying a catalog id, as placeable props — pure, no terrain sample. Parallel to {@link resolveScatter}: games and headless tests read the same list `<AuthoredObjects>` places. Entity-spawn kinds (`mob`/`boss`) are skipped by default — they carry a `catalogId` for their entity definition, but are spawned via `authoredEntitySpawns`, not placed as static meshes.
3000
+ - `resolveAuthoredObjectsWithDiagnostics` (function): function resolveAuthoredObjectsWithDiagnostics(document: AuthoredObjectsDocumentLike, options: ResolveAuthoredObjectsOptions = {}): AuthoredObjectsResult — Resolves catalog props and validates their animation in one document pass. Rejected overrides are omitted so each model inherits its original catalog setting. Excluded and unplaced markers remain the responsibility of their own consumers.
3001
+ - `syncAuthoredObjects` (function): function syncAuthoredObjects(store: AuthoredObjectPlaceTarget & { remove(instanceId: string): boolean }, objects: readonly AuthoredObject[], previousObjects: readonly AuthoredObject[], sampleHeight: (x: number, z: number) => number, options: Pick<PlaceAuthoredObjectsOptions, "verticalOffset"> = {}):… — Update changed authored placements and remove previous authored ids absent from the next document. Initial placement keeps game onInit state; unrelated document edits preserve runtime prop state. Call on document changes, never on simulation ticks; runtime-created objects remain untouched.
3002
+
3003
+ ## @jgengine/core/world/authoredSimulation
3004
+
3005
+ - `AuthoredFlockState` (interface): interface AuthoredFlockState — A habitat's live poses; game species determine appearance and gameplay actor policy.
3006
+ - `AuthoredSimulationRuntime` (interface): interface AuthoredSimulationRuntime — Engine-owned authority uses the caller's simulation clock; render quality cannot change it.
3007
+ - `AuthoredSimulationSnapshot` (interface): interface AuthoredSimulationSnapshot — Runtime state excludes authored configuration and cosmetic particle pools.
3008
+ - `AuthoredSurface` (interface): interface AuthoredSurface extends WeatherExposureSurface — A watched surface is sampled in metres. Its accumulation survives runtime save/restore.
3009
+ - `EnvironmentAppearanceSignals` (interface): interface EnvironmentAppearanceSignals — Appearance adapters consume these units without owning weather state or its clock.
3010
+ - `createAuthoredSimulation` (function): function createAuthoredSimulation(options: { document?: EditorDocument; timeSeconds: () => number; particles?: ParticleDirector; maxSurfaces?: number }): AuthoredSimulationRuntime — Compose authored weather, shelter, accumulation, named effect intents and fire with one clock.
3011
+
3012
+ ## @jgengine/core/world/authoredSolids
3013
+
3014
+ - `AUTHORED_SOLID_LAYER_PREFIX` (const): const AUTHORED_SOLID_LAYER_PREFIX: "authored:" — Layer prefix `syncAuthoredSolids` owns in `ctx.world.solids`; one layer per document object.
3015
+ - `AuthoredSolidsDocumentLike` (interface): interface AuthoredSolidsDocumentLike — Minimal editor document shape {@link resolveAuthoredSolids} walks; any `EditorDocument` satisfies it.
3016
+ - `resolveAuthoredSolids` (function): function resolveAuthoredSolids(document: AuthoredSolidsDocumentLike, sampleHeight?: (x: number, z: number) => number): Map<string, readonly WorldSolid[]> — Solids for every document object whose scene kind declares `solids`, keyed by object id. Objects whose kind has no `solids` hook, or whose hook returns none, are left out.
3017
+ - `syncAuthoredSolids` (function): function syncAuthoredSolids(solids: WorldSolids, document: AuthoredSolidsDocumentLike, sampleHeight?: (x: number, z: number) => number): void — Write a document's studio solids into `solids` (usually `ctx.world.solids`), one `authored:<objectId>` layer per object, dropping authored layers the document no longer has. Call again whenever the document changes.
2696
3018
 
2697
3019
  ## @jgengine/core/world/authoredSpawn
2698
3020
 
@@ -2704,16 +3026,37 @@
2704
3026
  - `authoredSpawnRotation` (function): function authoredSpawnRotation(document: AuthoredSpawnDocumentLike, options: AuthoredSpawnOptions = {}): number — Facing (yaw radians) of the authored spawn marker, or 0 when the document has none — pair with {@link authoredSpawnPosition} to spawn players where and how the editor placed them.
2705
3027
  - `markersOfKind` (function): function markersOfKind<TMarker extends AuthoredSpawnMarkerLike>(document: { markers: readonly TMarker[] }, kind: string): TMarker[] — Every marker of `kind`, in document order — the generic query behind gameplay that references authored markers by kind instead of copying coordinates into code.
2706
3028
 
3029
+ ## @jgengine/core/world/authoredWeather
3030
+
3031
+ - `AuthoredWeather` (interface): interface AuthoredWeather — Pure spatial weather queries using the caller’s authoritative simulation clock.
3032
+ - `AuthoredWeatherConfig` (interface): interface AuthoredWeatherConfig — Serializable weather lists capped at 256 entries. Wind amplitudes and direction components are bounded to 1e6, gust frequency to 1000 Hz; coordinates and times to 1e12 metres/seconds.
3033
+ - `AuthoredWeatherMode` (type): type AuthoredWeatherMode = "clear" | "rain" | "snow" | "mixed" | "dust" — Wind is metres/second along world X,Z; world Y points up. Times are simulation seconds.
3034
+ - `AuthoredWeatherProfile` (interface): interface AuthoredWeatherProfile extends WeatherConditions — A named set of conditions referenced by the authored schedule.
3035
+ - `AuthoredWeatherSample` (interface): interface AuthoredWeatherSample extends WeatherConditions — Resolved conditions and wind at a world point and absolute simulation time.
3036
+ - `AuthoredWeatherTransition` (interface): interface AuthoredWeatherTransition — An absolute simulation-time profile change with an optional continuous blend.
3037
+ - `AuthoredWeatherZone` (interface): interface AuthoredWeatherZone — A local weather override and additive wind source centered in world X,Z metres.
3038
+ - `WeatherConditions` (interface): interface WeatherConditions — Precipitation intensity, temperature offset in degrees, and normalized lightning brightness.
3039
+ - `WeatherExposure` (interface): interface WeatherExposure — Bounded surface accumulation with restoration, live rate tuning and explicit release.
3040
+ - `WeatherExposureConfig` (interface): interface WeatherExposureConfig — Surface capacity and normalized wetting, drying, snowfall and melting rates per second.
3041
+ - `WeatherExposureSample` (interface): interface WeatherExposureSample — Retained normalized wetness and snow coverage, temperature, and sky exposure.
3042
+ - `WeatherExposureSnapshot` (interface): interface WeatherExposureSnapshot — Detached accumulation values keyed by retained surface id.
3043
+ - `WeatherExposureStorage` (interface): interface WeatherExposureStorage — Injected persistence for detached surface accumulation snapshots.
3044
+ - `WeatherExposureSurface` (interface): interface WeatherExposureSurface — A stable surface id and world X,Z position with explicit sky exposure and temperature.
3045
+ - `createAuthoredWeather` (function): function createAuthoredWeather(source: AuthoredWeatherConfig = {}): AuthoredWeather — Pure authored weather sampling; the caller's simulation clock owns pause, replay and restore.
3046
+ - `createWeatherEnvironmentField` (function): function createWeatherEnvironmentField(weather: AuthoredWeather, config: Omit<EnvironmentFieldConfig, "rain"> = {}): EnvironmentField — Environment temperature, sunlight and shelter read the same authored precipitation as rendering.
3047
+ - `createWeatherExposure` (function): function createWeatherExposure(config: WeatherExposureConfig = {}, storage?: WeatherExposureStorage): WeatherExposure — Bounded surface accumulation shared by gameplay and material adapters, with injected persistence.
3048
+ - `validateAuthoredWeather` (function): function validateAuthoredWeather(config: AuthoredWeatherConfig): void — Validate authoring references and work bounds before persisting a weather document.
3049
+
2707
3050
  ## @jgengine/core/world/buildPermissions
2708
3051
 
2709
- - `BuildActor` (interface): interface BuildActor — ⚠ undocumented
3052
+ - `BuildActor` (interface): interface BuildActor { userId: string; guildId?: string } — ⚠ undocumented
2710
3053
  - `BuildRole` (type): type BuildRole = "owner" | "editor" | "viewer" — ⚠ undocumented
2711
3054
  - `ContributionGoal` (type): type ContributionGoal = Readonly<Record<string, number>> — ⚠ undocumented
2712
- - `ContributionPool` (interface): interface ContributionPool — ⚠ undocumented
2713
- - `ContributionResult` (interface): interface ContributionResult — ⚠ undocumented
2714
- - `PlotPermissionConfig` (interface): interface PlotPermissionConfig — ⚠ undocumented
2715
- - `PlotPermissionSnapshot` (interface): interface PlotPermissionSnapshot — ⚠ undocumented
2716
- - `PlotPermissions` (interface): interface PlotPermissions — ⚠ undocumented
3055
+ - `ContributionPool` (interface): interface ContributionPool { contribute(userId: string, resource: string, amount: number): ContributionResult; totals(): Readonly<Record<string, number>>; remaining(): Readonly<Record<string, number>>; byContributor(userId: string): Readonly<Record<string, number>>; … — ⚠ undocumented
3056
+ - `ContributionResult` (interface): interface ContributionResult { accepted: number; overflow: number; complete: boolean } — ⚠ undocumented
3057
+ - `PlotPermissionConfig` (interface): interface PlotPermissionConfig { plotId: string; ownerId: string; guildId?: string; roles?: Readonly<Record<string, BuildRole>>; guildRole?: BuildRole } — ⚠ undocumented
3058
+ - `PlotPermissionSnapshot` (interface): interface PlotPermissionSnapshot { plotId: string; ownerId: string; guildId?: string; roles: Readonly<Record<string, BuildRole>>; guildRole?: BuildRole } — ⚠ undocumented
3059
+ - `PlotPermissions` (interface): interface PlotPermissions { roleOf(actor: BuildActor): BuildRole | null; canEdit(actor: BuildActor): boolean; canView(actor: BuildActor): boolean; grant(userId: string, role: BuildRole): void; revoke(userId: string): void; setGuild(guildId: string | null, role?: Bui… — ⚠ undocumented
2717
3060
  - `createContributionPool` (function): function createContributionPool(goal: ContributionGoal): ContributionPool — ⚠ undocumented
2718
3061
  - `createPlotPermissions` (function): function createPlotPermissions(config: PlotPermissionConfig): PlotPermissions — ⚠ undocumented
2719
3062
 
@@ -2744,8 +3087,8 @@
2744
3087
 
2745
3088
  ## @jgengine/core/world/buildingIndex
2746
3089
 
2747
- - `BuildingHit` (interface): interface BuildingHit — ⚠ undocumented
2748
- - `BuildingIndex` (interface): interface BuildingIndex — ⚠ undocumented
3090
+ - `BuildingHit` (interface): interface BuildingHit { building: GeneratedBuilding; distance: number } — ⚠ undocumented
3091
+ - `BuildingIndex` (interface): interface BuildingIndex { readonly buildings: readonly GeneratedBuilding[]; at(point: Vec2): GeneratedBuilding | undefined; within(area: Aabb): GeneratedBuilding[]; nearest(point: Vec2): BuildingHit | undefined; isInside(point: Vec2): boolean; blockers(margin?: nu… — ⚠ undocumented
2749
3092
  - `buildingIndex` (function): function buildingIndex(buildings: readonly GeneratedBuilding[]): BuildingIndex — ⚠ undocumented
2750
3093
 
2751
3094
  ## @jgengine/core/world/buildingKit
@@ -2757,7 +3100,7 @@
2757
3100
  - `BuildingKitPart` (interface): interface BuildingKitPart — One bindable variant for a building part slot. `model` is an opaque reference the host resolves (an asset id, a pack-relative path, or a URL) — core never loads anything.
2758
3101
  - `BuildingKitPartInput` (type): type BuildingKitPartInput = string | BuildingKitPart — A variant as authored: a bare model reference, or the full {@link BuildingKitPart} when it needs fit or transform tuning.
2759
3102
  - `buildingKitVariantCounts` (function): function buildingKitVariantCounts(kit: BuildingKit): Partial<BuildingVariantCounts> — The variant counts a kit actually supplies, shaped for `BuildingConfigInput.variants`. Feed this into `generateBuilding` so seeded picks spread across exactly the models the kit binds instead of indexing past them and wrapping unevenly.
2760
- - `defineBuildingKit` (function): function defineBuildingKit(input: BuildingKitInput): BuildingKit — Validates and normalizes a kit description, accepting bare model strings as single-field variants. Throws on empty variant lists so a typo fails at authoring time rather than silently falling back to untextured boxes.
3103
+ - `defineBuildingKit` (function): function defineBuildingKit(input: BuildingKitInput): BuildingKit — Validates and normalizes a kit description, accepting bare model strings as single-field variants. Throws on empty variants or invalid editor material references/selectors before loading models.
2761
3104
  - `resolveBuildingKitPart` (function): function resolveBuildingKitPart(kit: BuildingKit | undefined, kind: BuildingPartKind, slot?: BuildingKitSlot): BuildingKitBinding — Resolves one generated part against a kit. Pass the placement's `kind` and its `kit` slot; the slot's `variant` wraps around the bound list, so a kit may carry any number of variants independent of the generator's `variants` counts.
2762
3105
 
2763
3106
  ## @jgengine/core/world/buildingLots
@@ -2772,44 +3115,49 @@
2772
3115
 
2773
3116
  ## @jgengine/core/world/buildings
2774
3117
 
2775
- - `BUILDING_STYLE_PALETTES` (const): const BUILDING_STYLE_PALETTES: Record<BuildingStyle, BuildingPalette> — ⚠ undocumented
3118
+ - `BUILDING_STYLE_PALETTES` (const): const BUILDING_STYLE_PALETTES: Record<BuildingStyle, Record<BuildingPartKind, string>> — ⚠ undocumented
2776
3119
  - `BUILDING_STYLE_WALL_TONES` (const): const BUILDING_STYLE_WALL_TONES: Record<BuildingStyle, readonly string[]> — Per-style facade-tone family: the wall colours a district spreads across its buildings so neighbours differ in hue and value while the block stays one palette. Index 0 is the palette's own `wall`; the rest fan warmer/cooler and lighter/darker around it.
2777
- - `BuildingCellRef` (interface): interface BuildingCellRef — ⚠ undocumented
2778
- - `BuildingConfig` (interface): interface BuildingConfig — ⚠ undocumented
2779
- - `BuildingConfigInput` (type): type BuildingConfigInput = Partial< Omit<BuildingConfig, "probabilities" | "variants"> > & { probabilities?: Partial<BuildingProbabilities>; variants?: Partial<BuildingVariantCounts>; } — ⚠ undocumented
3120
+ - `BuildingCellRef` (interface): interface BuildingCellRef { facade: BuildingFacade; level: number; bay: number } — ⚠ undocumented
3121
+ - `BuildingConfig` (interface): interface BuildingConfig { id: string; seed: BuildingSeed; center: Vec2; floors: number; baysWide: number; baysDeep: number; bayWidth: number; floorHeight: number; facadeDepth: number; roofOverhang: number; probabilities: BuildingProbabilities; variants: BuildingVa… — ⚠ undocumented · used by `createBuildingConfig`: Fill a partial building description out to a complete {@link BuildingConfig}, clamping counts and probabilities.
3122
+ - `BuildingConfigInput` (type): type BuildingConfigInput = Partial< Omit<BuildingConfig, "probabilities" | "variants"> > & { probabilities?: Partial<BuildingProbabilities>; variants?: Partial<BuildingVariantCounts>; } — ⚠ undocumented · used by `createBuildingConfig`: Fill a partial building description out to a complete {@link BuildingConfig}, clamping counts and probabilities.
2780
3123
  - `BuildingFacade` (type): type BuildingFacade = "front" | "back" | "left" | "right" | "roof" — ⚠ undocumented
2781
- - `BuildingGridConfig` (interface): interface BuildingGridConfig — ⚠ undocumented
2782
- - `BuildingKitSlot` (interface): interface BuildingKitSlot — ⚠ undocumented
2783
- - `BuildingLot` (interface): interface BuildingLot — ⚠ undocumented
2784
- - `BuildingPalette` (type): type BuildingPalette = Record<BuildingPartKind, string> — ⚠ undocumented
3124
+ - `BuildingGridConfig` (interface): interface BuildingGridConfig { rows: number; columns: number; origin?: Vec2; lotSize?: Footprint; streetWidth?: number; seed?: BuildingSeed; idPrefix?: string; base?: BuildingConfigInput; floorRange?: readonly [number, number] } — ⚠ undocumented · used by `generateBuildingDistrict`: Generate a grid of buildings on lots sized by `lotSize` and separated by `streetWidth`, each with its floor count rolled from `floorRange`.
3125
+ - `BuildingKitSlot` (interface): interface BuildingKitSlot { key: string; variant: number } — ⚠ undocumented · used by `resolveBuildingKitPart` (@jgengine/core/world/buildingKit): Resolves one generated part against a kit.
3126
+ - `BuildingLot` (interface): interface BuildingLot { id: string; center: Vec2; footprint: Footprint; quarterTurns: number; config: BuildingConfig } — ⚠ undocumented
3127
+ - `BuildingPalette` (type): type BuildingPalette = Record<BuildingPartKind, BuildingSurface> — ⚠ undocumented
2785
3128
  - `BuildingPaletteOverrides` (type): type BuildingPaletteOverrides = Partial<BuildingPalette> — ⚠ undocumented
2786
- - `BuildingPartKind` (type): type BuildingPartKind = | "wall" | "window" | "awning" | "airConditioner" | "clothesline" | "storefront" | "shutter" | "storeSign" | "roof" | "roofProp" | "guardrail" | "corner" — ⚠ undocumented
2787
- - `BuildingPartPlacement` (interface): interface BuildingPartPlacement — ⚠ undocumented
3129
+ - `BuildingPartKind` (type): type BuildingPartKind = | "wall" | "window" | "awning" | "airConditioner" | "clothesline" | "storefront" | "shutter" | "storeSign" | "roof" | "roofProp" | "guardrail" | "corner" — ⚠ undocumented · used by `resolveBuildingKitPart` (@jgengine/core/world/buildingKit): Resolves one generated part against a kit.
3130
+ - `BuildingPartPlacement` (interface): interface BuildingPartPlacement { id: string; buildingId: string; kind: BuildingPartKind; facade: BuildingFacade; position: Vec3; rotationY: number; scale: Vec3; kit: BuildingKitSlot; cell?: BuildingCellRef; tags?: readonly BuildingPartTag[] } — ⚠ undocumented
2788
3131
  - `BuildingPartTag` (type): type BuildingPartTag = "ground" | "upper" | "open" | "closed" | "decor" | "structure" — ⚠ undocumented
2789
- - `BuildingProbabilities` (interface): interface BuildingProbabilities — ⚠ undocumented
3132
+ - `BuildingProbabilities` (interface): interface BuildingProbabilities { window: number; openStore: number; awning: number; airConditioner: number; clothesline: number; storeSign: number; roofProp: number } — ⚠ undocumented
2790
3133
  - `BuildingSeed` (type): type BuildingSeed = number | string — ⚠ undocumented
2791
- - `BuildingStyle` (type): type BuildingStyle = | "generic" | "capital" | "village" | "desert" | "industrial" | "coastal" | "neon" | "ruin" | "frontier" | "aerial" — ⚠ undocumented
2792
- - `BuildingVariantCounts` (interface): interface BuildingVariantCounts — ⚠ undocumented
3134
+ - `BuildingStyle` (type): type BuildingStyle = | "generic" | "capital" | "village" | "desert" | "industrial" | "coastal" | "neon" | "ruin" | "frontier" | "aerial" — ⚠ undocumented · used by `BUILDING_STYLE_WALL_TONES`: Per-style facade-tone family: the wall colours a district spreads across its buildings so neighbours differ in hue and value while the block…
3135
+ - `BuildingSurface` (type): type BuildingSurface = string | BuildingSurfaceMaterial — What a building part kind is painted with: a hex colour, or a colour plus tiled PBR maps.
3136
+ - `BuildingSurfaceMaps` (interface): interface BuildingSurfaceMaps — URLs of the PBR maps a building surface tiles; `buildMaterialCatalog(...).resolve(id)!.maps` fits directly.
3137
+ - `BuildingSurfaceMaterial` (interface): interface BuildingSurfaceMaterial — A textured building surface: a base colour plus optional tiled PBR maps. With `maps`, `color` multiplies the albedo (leave it light to show the map as authored); without, it is the flat colour the part has always had.
3138
+ - `BuildingVariantCounts` (interface): interface BuildingVariantCounts { wall: number; window: number; awning: number; airConditioner: number; clothesline: number; storefront: number; shutter: number; storeSign: number; roofProp: number } — ⚠ undocumented · used by `buildingKitVariantCounts` (@jgengine/core/world/buildingKit): The variant counts a kit actually supplies, shaped for `BuildingConfigInput.variants`.
2793
3139
  - `DEFAULT_BUILDING_CONFIG` (const): const DEFAULT_BUILDING_CONFIG: BuildingConfig — ⚠ undocumented
2794
3140
  - `DEFAULT_BUILDING_STYLE` (const): const DEFAULT_BUILDING_STYLE: BuildingStyle — ⚠ undocumented
2795
- - `GeneratedBuilding` (interface): interface GeneratedBuilding — ⚠ undocumented
2796
- - `Vec3` (type): type Vec3 = readonly [number, number, number] — ⚠ undocumented
3141
+ - `GeneratedBuilding` (interface): interface GeneratedBuilding { id: string; seed: BuildingSeed; center: Vec2; footprint: Footprint; floors: number; floorHeight: number; bounds: Aabb; parts: readonly BuildingPartPlacement[]; rotationY?: number } — ⚠ undocumented · used by `generateBuilding`: Generate one building as a flat list of placed facade parts: bays × floors per facade, a ground row, corners, and a roof, with every decorat…
3142
+ - `Vec3` (type): type Vec3 = readonly [number, number, number] — ⚠ undocumented · used by `coverPoints` (@jgengine/core/ai/tacticalQueries): Returns boundary locations that are hidden from a threat.
3143
+ - `buildingSurfaceColor` (function): function buildingSurfaceColor(surface: BuildingSurface | undefined, fallback = "#808080"): string — The flat colour of a surface: the hex itself, or a material's `color`, else `fallback`.
2797
3144
  - `createBuildingConfig` (function): function createBuildingConfig(input: BuildingConfigInput = {}): BuildingConfig — Fill a partial building description out to a complete {@link BuildingConfig}, clamping counts and probabilities. Use it to inspect or adjust the resolved shape before generating.
2798
3145
  - `generateBuilding` (function): function generateBuilding(input: BuildingConfigInput = {}): GeneratedBuilding — Generate one building as a flat list of placed facade parts: bays × floors per facade, a ground row, corners, and a roof, with every decorative part rolled from `probabilities` and every part tagged with a `kit` slot the renderer binds to a model (see `world/buildingKit`). Deterministic from `seed`.
2799
3146
  - `generateBuildingDistrict` (function): function generateBuildingDistrict(config: BuildingGridConfig): GeneratedBuilding[] — Generate a grid of buildings on lots sized by `lotSize` and separated by `streetWidth`, each with its floor count rolled from `floorRange`.
3147
+ - `resolveBuildingSurface` (function): function resolveBuildingSurface(surface: BuildingSurface): BuildingSurfaceMaterial — Normalises a surface to its material form so a renderer reads one shape.
2800
3148
 
2801
3149
  ## @jgengine/core/world/carve
2802
3150
 
2803
3151
  - `CarvableField` (class): class CarvableField implements TerrainField — A `TerrainField` you can write craters and mounds into at runtime — the height-field side of destructible terrain (Helldivers 2 explosion craters, engineer-deposited berms). Wraps a base field and layers smooth radial deformations on top, so `sampleHeight` (and therefore ground-snap, collision, and the shell's terrain mesh) all read the deformed surface. `carve` digs a bowl, `deposit` raises a mound.
2804
- - `CarveOp` (interface): interface CarveOp — ⚠ undocumented
2805
- - `CraterOp` (interface): interface CraterOp — ⚠ undocumented
2806
- - `DepositOp` (interface): interface DepositOp — ⚠ undocumented
3152
+ - `CarveOp` (interface): interface CarveOp { center: readonly [number, number, number]; radius: number; toolStrength?: number } — ⚠ undocumented
3153
+ - `CraterOp` (interface): interface CraterOp { x: number; z: number; radius: number; depth: number } — ⚠ undocumented
3154
+ - `DepositOp` (interface): interface DepositOp { center: readonly [number, number, number]; radius: number; material: number } — ⚠ undocumented
2807
3155
  - `EMPTY_VOXEL` (const): const EMPTY_VOXEL: 0 — ⚠ undocumented
2808
- - `MoundOp` (interface): interface MoundOp — ⚠ undocumented
2809
- - `VoxelMaterial` (interface): interface VoxelMaterial — ⚠ undocumented
3156
+ - `MoundOp` (interface): interface MoundOp { x: number; z: number; radius: number; height: number } — ⚠ undocumented
3157
+ - `VoxelMaterial` (interface): interface VoxelMaterial { id: number; name: string; strength: number; drop?: string } — ⚠ undocumented
2810
3158
  - `VoxelMaterialTable` (type): type VoxelMaterialTable = Readonly<Record<number, VoxelMaterial>> — ⚠ undocumented
2811
3159
  - `VoxelVolume` (class): class VoxelVolume — A runtime-editable dense voxel grid — the carve/deposit op behind destructible dig worlds (Deep Rock Galactic tunnels, Astroneer terrain). Cells hold a material id (0 = empty); `carve` clears a sphere of solid cells that a tool is strong enough to break and returns how many it removed (feed that to a loot roll), `deposit` fills a sphere with a material. World↔cell mapping is `origin`+`scale`.
2812
- - `VoxelVolumeConfig` (interface): interface VoxelVolumeConfig — ⚠ undocumented
3160
+ - `VoxelVolumeConfig` (interface): interface VoxelVolumeConfig { dims: readonly [number, number, number]; origin?: readonly [number, number, number]; scale?: number; fill?: number; materials?: VoxelMaterialTable } — ⚠ undocumented
2813
3161
  - `carvableTerrain` (function): function carvableTerrain(base: TerrainField): CarvableField — ⚠ undocumented
2814
3162
 
2815
3163
  ## @jgengine/core/world/catenary
@@ -2820,9 +3168,9 @@
2820
3168
 
2821
3169
  ## @jgengine/core/world/cellStates
2822
3170
 
2823
- - `CellRef` (interface): interface CellRef — ⚠ undocumented
3171
+ - `CellRef` (interface): interface CellRef { col: number; row: number } — ⚠ undocumented
2824
3172
  - `CellStateGrid` (interface): interface CellStateGrid<TState extends string> — A uniform world grid whose cells step through a discrete state ladder — pristine → cracked → burning → ruined — driven by whatever world events the game routes in (#284.8). Pure storage plus ladder mechanics; the game decides *when* to escalate, the grid guarantees ordered, clamped steps and cheap queries. `version()` bumps on every change for dirty-checked rendering.
2825
- - `CellStateGridConfig` (interface): interface CellStateGridConfig<TState extends string> — ⚠ undocumented
3173
+ - `CellStateGridConfig` (interface): interface CellStateGridConfig<TState extends string> { cols: number; rows: number; states: readonly TState[]; origin?: readonly [number, number]; cellSize?: number } — ⚠ undocumented
2826
3174
  - `createCellStateGrid` (function): function createCellStateGrid<TState extends string>(config: CellStateGridConfig<TState>): CellStateGrid<TState> — ⚠ undocumented
2827
3175
 
2828
3176
  ## @jgengine/core/world/cityBlocks
@@ -2954,18 +3302,19 @@
2954
3302
  - `CityVehicleKind` (type): type CityVehicleKind = "car" | "van" | "truck" — Silhouette class of a parked vehicle — sizes the body the renderer instances.
2955
3303
  - `ResolvedCity` (interface): interface ResolvedCity — A resolved city district: world-space network, zoned lots, parks, and furniture.
2956
3304
  - `ResolvedRace` (interface): interface ResolvedRace — An instanced street race lifted out of the district's OWN streets: a closed lap through the city, with every side street leaving it sealed off. World space, like the rest of {@link ResolvedCity}.
3305
+ - `citySolids` (function): function citySolids(city: ResolvedCity, sampleHeight: (x: number, z: number) => number = () => 0): WorldSolid[] — One solid per wall and roof piece of every lot, grounded the way the city renderer grounds it: grade at the highest lot corner, foundations down to half a metre under the lowest.
2957
3306
  - `resolveCityObject` (function): function resolveCityObject(object: SceneKindObject, context?: CityResolveContext): ResolvedCity | null — Synthesize the deterministic city plan for one `city` volume: streets → bridges → parks → zoned frontage lots with massing pieces → furniture, all in the volume's local frame and then rotated/translated into world space. Same volume (id, footprint, meta) over the same terrain always resolves to the identical plan. When `context` provides a ground sampler, lots respect the `maxSlope` cliff rule — hillside and canyon districts keep their steep faces open. When `context.zoneOverrides` carries sibling `cityzone` volumes, lots inside them adopt the override band/mix. Returns null without a usable footprint.
2958
3307
 
2959
3308
  ## @jgengine/core/world/connectors
2960
3309
 
2961
- - `ConnectorPieceDef` (interface): interface ConnectorPieceDef — ⚠ undocumented
3310
+ - `ConnectorPieceDef` (interface): interface ConnectorPieceDef { kind: string; sockets: readonly ConnectorSocket[] } — ⚠ undocumented
2962
3311
  - `ConnectorRegistry` (type): type ConnectorRegistry = (kind: string) => ConnectorPieceDef | null — ⚠ undocumented
2963
- - `ConnectorSocket` (interface): interface ConnectorSocket — ⚠ undocumented
3312
+ - `ConnectorSocket` (interface): interface ConnectorSocket { id: string; type: string; offset: ConnectorVec3; accepts?: readonly string[] } — ⚠ undocumented
2964
3313
  - `ConnectorVec3` (type): type ConnectorVec3 = readonly [number, number, number] — ⚠ undocumented
2965
- - `PlacedPiece` (interface): interface PlacedPiece — ⚠ undocumented
2966
- - `SnapOptions` (interface): interface SnapOptions — ⚠ undocumented
2967
- - `SnapResult` (interface): interface SnapResult — ⚠ undocumented
2968
- - `WorldSocket` (interface): interface WorldSocket — ⚠ undocumented
3314
+ - `PlacedPiece` (interface): interface PlacedPiece { id: string; kind: string; position: ConnectorVec3; rotationY: number } — ⚠ undocumented
3315
+ - `SnapOptions` (interface): interface SnapOptions { snapDistance?: number; rotationY?: number } — ⚠ undocumented
3316
+ - `SnapResult` (interface): interface SnapResult { socketId: string; targetPieceId: string; targetSocketId: string; position: ConnectorVec3; rotationY: number; distance: number } — ⚠ undocumented
3317
+ - `WorldSocket` (interface): interface WorldSocket { pieceId: string; socket: ConnectorSocket; position: ConnectorVec3 } — ⚠ undocumented
2969
3318
  - `collectWorldSockets` (function): function collectWorldSockets(registry: ConnectorRegistry, pieces: readonly PlacedPiece[]): WorldSocket[] — ⚠ undocumented
2970
3319
  - `snapToNearest` (function): function snapToNearest(registry: ConnectorRegistry, placed: readonly PlacedPiece[], movingDef: ConnectorPieceDef, cursor: ConnectorVec3, options: SnapOptions = {}): SnapResult | null — ⚠ undocumented
2971
3320
  - `socketWorldPosition` (function): function socketWorldPosition(socket: ConnectorSocket, origin: ConnectorVec3, rotationY: number): ConnectorVec3 — ⚠ undocumented
@@ -2974,27 +3323,32 @@
2974
3323
 
2975
3324
  ## @jgengine/core/world/envField
2976
3325
 
2977
- - `EnvironmentField` (interface): interface EnvironmentField — ⚠ undocumented
2978
- - `EnvironmentFieldConfig` (interface): interface EnvironmentFieldConfig — ⚠ undocumented
2979
- - `EnvironmentSample` (interface): interface EnvironmentSample — ⚠ undocumented
3326
+ - `EnvironmentField` (interface): interface EnvironmentField { sample(x: number, z: number, time: number, y?: number): EnvironmentSample; temperature(x: number, z: number, time: number, y?: number): number; wetness(x: number, z: number, time: number): number; lightExposure(x: number, z: number, time:… — ⚠ undocumented · used by `createEnvironmentField`: A sampleable environment field: read temperature, wetness, sun/sky exposure, and ambient light at any world position and time.
3327
+ - `EnvironmentFieldConfig` (interface): interface EnvironmentFieldConfig { dayLength?: number; baseTemperature?: number; nightDrop?: number; altitudeLapse?: number; seaLevel?: number; terrain?: TerrainField; rain?: ScalarField; occluders?: ShadeProvider; heatSources?: readonly HeatSource[]; ambientFloor?: number… — ⚠ undocumented · used by `createEnvironmentField`: A sampleable environment field: read temperature, wetness, sun/sky exposure, and ambient light at any world position and time.
3328
+ - `EnvironmentSample` (interface): interface EnvironmentSample { temperature: number; wetness: number; lightExposure: number; ambientLight: number; sheltered: boolean } — ⚠ undocumented · used by `resolveMaterialAppearance` (@jgengine/core/material/appearanceSignals): Resolve one surface from an immutable dry baseline, avoiding cumulative drift.
2980
3329
  - `HeatSource` (interface): interface HeatSource — A localized warmth source — campfire, forge, geothermal vent.
2981
3330
  - `OccluderRect` (interface): interface OccluderRect — A sky-occluding footprint — a roof, dense canopy, a cave mouth. `shade` 0..1 = fraction of sky blocked.
2982
3331
  - `ScalarField` (type): type ScalarField = number | ((x: number, z: number, time: number) => number) — ⚠ undocumented
2983
3332
  - `ShadeProvider` (type): type ShadeProvider = readonly OccluderRect[] | ((x: number, z: number) => number) — ⚠ undocumented
2984
3333
  - `createEnvironmentField` (function): function createEnvironmentField(config: EnvironmentFieldConfig = {}): EnvironmentField — A sampleable environment field: read temperature, wetness, sun/sky exposure, and ambient light at any world position and time. Built on the same renderer-free footing as terrain/wind/water so meters, spawn gating, and damage-in-sunlight read the world the shell renders — no three.js. Instantaneous and pure (no accumulation); stateful build-up belongs to a decay meter reading this field.
2985
3334
 
3335
+ ## @jgengine/core/world/environmentForces
3336
+
3337
+ - `EnvironmentMotionTarget` (interface): interface EnvironmentMotionTarget — The game selects affected players, coupling, target bits and its acceleration limit.
3338
+ - `installEnvironmentMotion` (function): function installEnvironmentMotion(ctx: GameContext, options: { targets: () => readonly EnvironmentMotionTarget[]; maxTargets?: number }): () => void — Inject authoritative spatial forces before existing movement consumes its motion intents.
3339
+
2986
3340
  ## @jgengine/core/world/environmentSummary
2987
3341
 
2988
- - `EnvironmentCounts` (interface): interface EnvironmentCounts — ⚠ undocumented
2989
- - `EnvironmentSummary` (interface): interface EnvironmentSummary — ⚠ undocumented
2990
- - `IslandSummary` (interface): interface IslandSummary extends TerrainSummary — ⚠ undocumented
3342
+ - `EnvironmentCounts` (interface): interface EnvironmentCounts { terrain: number; islands: number; structureGroups: number; buildings: number; buildingParts: number; vegetationFields: number; waterBodies: number; weatherSystems: number; roads: number } — ⚠ undocumented
3343
+ - `EnvironmentSummary` (interface): interface EnvironmentSummary { terrain?: TerrainSummary; islands: readonly IslandSummary[]; structures: readonly StructureSummary[]; vegetation: readonly VegetationSummary[]; water: readonly WaterSummary[]; weather: readonly WeatherSummary[]; roads: readonly RoadSummar… — ⚠ undocumented
3344
+ - `IslandSummary` (interface): interface IslandSummary extends TerrainSummary { origin: readonly [number, number] } — ⚠ undocumented
2991
3345
  - `RoadSummary` (interface): interface RoadSummary — One road descriptor's resolved footprint: vertex count, width, and total centerline length.
2992
- - `StructureSummary` (interface): interface StructureSummary — ⚠ undocumented
2993
- - `TerrainHeightStats` (interface): interface TerrainHeightStats — ⚠ undocumented
2994
- - `TerrainSummary` (interface): interface TerrainSummary — ⚠ undocumented
2995
- - `VegetationSummary` (interface): interface VegetationSummary — ⚠ undocumented
2996
- - `WaterSummary` (interface): interface WaterSummary — ⚠ undocumented
2997
- - `WeatherSummary` (interface): interface WeatherSummary — ⚠ undocumented
3346
+ - `StructureSummary` (interface): interface StructureSummary { style: BuildingStyle; palette: BuildingPalette; requested: number; buildings: number; parts: number; bounds: Aabb } — ⚠ undocumented
3347
+ - `TerrainHeightStats` (interface): interface TerrainHeightStats { min: number; max: number; mean: number; finite: boolean } — ⚠ undocumented
3348
+ - `TerrainSummary` (interface): interface TerrainSummary { bounds: WorldBounds; height: TerrainHeightStats; palette: TerrainPalette; waterLevel?: number } — ⚠ undocumented
3349
+ - `VegetationSummary` (interface): interface VegetationSummary { area: EnvironmentArea; density: number } — ⚠ undocumented
3350
+ - `WaterSummary` (interface): interface WaterSummary { level: number; bounds: WorldBounds } — ⚠ undocumented
3351
+ - `WeatherSummary` (interface): interface WeatherSummary { kind: "rain" | "snow"; area: EnvironmentArea; density: number } — ⚠ undocumented
2998
3352
  - `resolveStructureBuildings` (function): function resolveStructureBuildings(descriptor: BuildingEnvironmentDescriptor): GeneratedBuilding[] — ⚠ undocumented
2999
3353
  - `summarizeEnvironment` (function): function summarizeEnvironment(feature: EnvironmentWorldFeature): EnvironmentSummary — ⚠ undocumented
3000
3354
 
@@ -3012,56 +3366,57 @@
3012
3366
  - `BiomeBand` (interface): interface BiomeBand — A z-ordered ground palette zone — the linear-boundary counterpart to the radial `materialRegions`. Adjacent bands cross-fade into each other across a `fade`-wide window centered on the midpoint z between their centers, so a multi-biome world (vale → marsh → peaks along z) blends its ground color instead of hard-switching. Bands may also carry per-zone `fog`, `sky`, and `weather`. Order the list by ascending `z`.
3013
3367
  - `BiomeFog` (interface): interface BiomeFog — Per-band fog override cross-faded along z by `createBiomeFogSampler`; unset fields fall through to the base sky fog.
3014
3368
  - `BiomeSky` (interface): interface BiomeSky — Per-band sky/light override cross-faded along z by `createBiomeSkySampler`; unset fields fall through to the base sky.
3015
- - `BiomesWorldConfig` (interface): interface BiomesWorldConfig extends WorldGridConfig — ⚠ undocumented
3016
- - `BuildingEnvironmentConfig` (interface): interface BuildingEnvironmentConfig — ⚠ undocumented
3017
- - `BuildingEnvironmentDescriptor` (type): type BuildingEnvironmentDescriptor = { kind: "building" } & Required< Pick<BuildingEnvironmentConfig, "count" | "footprint" | "stories" | "storyHeight" | "spacing" | "style"> > & Pick<BuildingEnvironmentConfig, "seed" | "position" | "palette" | "along" | "kit"> — ⚠ undocumented
3369
+ - `BiomesWorldConfig` (interface): interface BiomesWorldConfig extends WorldGridConfig { map: string; zones: string; bounds?: WorldBounds } — ⚠ undocumented · used by `biomes`: Declares a biome-painted world — the whole-world alternative to a single `environment()` terrain.
3370
+ - `BuildingEnvironmentConfig` (interface): interface BuildingEnvironmentConfig { count?: number; position?: EnvironmentVec2; footprint?: WorldBounds; stories?: readonly [number, number]; storyHeight?: number; spacing?: number; style?: BuildingStyle; palette?: BuildingPaletteOverrides; seed?: string; along?: BuildingFr… — ⚠ undocumented · used by `building`: Declares a cluster of procedurally-massed buildings for `environment()` — count, footprint, stories, style.
3371
+ - `BuildingEnvironmentDescriptor` (type): type BuildingEnvironmentDescriptor = { kind: "building" } & Required< Pick<BuildingEnvironmentConfig, "count" | "footprint" | "stories" | "storyHeight" | "spacing" | "style"> > & Pick<BuildingEnvironmentConfig, "seed" | "position" | "palette" | "along" | "kit" | "solid"> — ⚠ undocumented · used by `building`: Declares a cluster of procedurally-massed buildings for `environment()` — count, footprint, stories, style.
3018
3372
  - `BuildingFrontageConfig` (interface): interface BuildingFrontageConfig — Street-aware placement for `building()`: instead of a grid scattered around `position`, lots are stepped along each road's frontage, offset by a consistent setback (a curb + sidewalk strip), and turned so every building FRONT faces its road. `footprint` (`w` = frontage width, `d` = depth) and `spacing` (along-road gap) are shared with the grid mode. Deterministic and bounded by `maxLots`.
3019
3373
  - `BuildingFrontageRoad` (interface): interface BuildingFrontageRoad — One road a `building({ along })` frontage lines with buildings: centerline + full width.
3020
- - `EnvironmentArea` (interface): interface EnvironmentArea extends WorldBounds — ⚠ undocumented
3374
+ - `EnvironmentArea` (interface): interface EnvironmentArea extends WorldBounds { h?: number; position?: EnvironmentVec2 } — ⚠ undocumented
3021
3375
  - `EnvironmentDescriptorList` (type): type EnvironmentDescriptorList<T> = T | readonly T[] — ⚠ undocumented
3022
3376
  - `EnvironmentVec2` (type): type EnvironmentVec2 = readonly [number, number] — ⚠ undocumented
3023
- - `EnvironmentWorldConfig` (interface): interface EnvironmentWorldConfig — ⚠ undocumented
3024
- - `EnvironmentWorldFeature` (interface): interface EnvironmentWorldFeature — ⚠ undocumented
3025
- - `GrassEnvironmentConfig` (interface): interface GrassEnvironmentConfig — ⚠ undocumented
3026
- - `GrassEnvironmentDescriptor` (type): type GrassEnvironmentDescriptor = { kind: "grass" } & Required< Pick<GrassEnvironmentConfig, "area" | "density" | "bladeHeight" | "bladeWidth" | "windStrength" | "colors"> > & Pick<GrassEnvironmentConfig, "seed" | "bladeBend" | "tuftRadius" | "colorVariation" | "normalLift"> — ⚠ undocumented
3027
- - `OceanEnvironmentConfig` (interface): interface OceanEnvironmentConfig — ⚠ undocumented
3028
- - `OceanEnvironmentDescriptor` (type): type OceanEnvironmentDescriptor = { kind: "ocean" } & Required< Pick<OceanEnvironmentConfig, "bounds" | "level" | "waveHeight" | "waveScale" | "waveSpeed" | "color"> > & Pick<OceanEnvironmentConfig, "position" | "levelAt"> — ⚠ undocumented
3029
- - `PadEnvironmentConfig` (interface): interface PadEnvironmentConfig — ⚠ undocumented
3030
- - `PadEnvironmentDescriptor` (type): type PadEnvironmentDescriptor = { kind: "pad" } & Required< Pick<PadEnvironmentConfig, "center" | "size" | "height" | "color"> > & Pick<PadEnvironmentConfig, "rotationY" | "elevation"> — ⚠ undocumented
3377
+ - `EnvironmentWorldConfig` (interface): interface EnvironmentWorldConfig { terrain?: TerrainEnvironmentDescriptor; islands?: readonly TerrainIslandDescriptor[]; sky?: SkyEnvironmentDescriptor; weather?: EnvironmentDescriptorList<WeatherEnvironmentDescriptor>; vegetation?: EnvironmentDescriptorList<VegetationEnvi… — ⚠ undocumented · used by `environment`: Composes an `environment()` feature from terrain, sky, weather, vegetation, water, structures, roads, and pads.
3378
+ - `EnvironmentWorldFeature` (interface): interface EnvironmentWorldFeature { kind: "environment"; terrain?: TerrainEnvironmentDescriptor; islands?: readonly TerrainIslandDescriptor[]; sky?: SkyEnvironmentDescriptor; weather?: readonly WeatherEnvironmentDescriptor[]; vegetation?: readonly VegetationEnvironmentDescr… — ⚠ undocumented · used by `environment`: Composes an `environment()` feature from terrain, sky, weather, vegetation, water, structures, roads, and pads.
3379
+ - `GrassEnvironmentConfig` (interface): interface GrassEnvironmentConfig { area?: EnvironmentArea; position?: EnvironmentVec2; density?: number; bladeHeight?: readonly [number, number]; bladeWidth?: number; windStrength?: number; colors?: readonly string[]; seed?: string; bladeBend?: readonly [number, number]; t… — ⚠ undocumented · used by `grass`: Declares a grass vegetation patch for `environment()` — area, blade sizing, density, and colors.
3380
+ - `GrassEnvironmentDescriptor` (type): type GrassEnvironmentDescriptor = { kind: "grass" } & Required< Pick<GrassEnvironmentConfig, "area" | "density" | "bladeHeight" | "bladeWidth" | "windStrength" | "colors"> > & Pick<GrassEnvironmentConfig, "seed" | "bladeBend" | "tuftRadius" | "colorVariation" | "normalLift"> — ⚠ undocumented · used by `grass`: Declares a grass vegetation patch for `environment()` — area, blade sizing, density, and colors.
3381
+ - `OceanEnvironmentConfig` (interface): interface OceanEnvironmentConfig { bounds?: WorldBounds; position?: EnvironmentVec2; level?: number; levelAt?: (time: number) => number; waveHeight?: number; waveScale?: number; waveSpeed?: number; color?: string } — ⚠ undocumented · used by `ocean`: Declares an ocean water body for `environment()` — bounds, level, and wave tuning.
3382
+ - `OceanEnvironmentDescriptor` (type): type OceanEnvironmentDescriptor = { kind: "ocean" } & Required< Pick<OceanEnvironmentConfig, "bounds" | "level" | "waveHeight" | "waveScale" | "waveSpeed" | "color"> > & Pick<OceanEnvironmentConfig, "position" | "levelAt"> — ⚠ undocumented · used by `ocean`: Declares an ocean water body for `environment()` — bounds, level, and wave tuning.
3383
+ - `PadEnvironmentConfig` (interface): interface PadEnvironmentConfig { center: EnvironmentVec2; size: PadSize; height?: number; elevation?: number; color?: string; rotationY?: number } — ⚠ undocumented
3384
+ - `PadEnvironmentDescriptor` (type): type PadEnvironmentDescriptor = { kind: "pad" } & Required< Pick<PadEnvironmentConfig, "center" | "size" | "height" | "color"> > & Pick<PadEnvironmentConfig, "rotationY" | "elevation"> — ⚠ undocumented · used by `padFlattenMasks`: Derives implicit `TerrainFlattenMask`s carving each pad's footprint into the terrain beneath it.
3031
3385
  - `PadSize` (type): type PadSize = readonly [number, number] | { radius: number } — ⚠ undocumented
3032
- - `PlotsWorldConfig` (interface): interface PlotsWorldConfig extends WorldGridConfig — ⚠ undocumented
3033
- - `RainEnvironmentConfig` (interface): interface RainEnvironmentConfig — ⚠ undocumented
3034
- - `RainEnvironmentDescriptor` (type): type RainEnvironmentDescriptor = { kind: "rain" } & Required< Pick<RainEnvironmentConfig, "area" | "density" | "speed" | "dropLength" | "wind" | "color" | "width" | "opacity"> > — ⚠ undocumented
3386
+ - `PlotsWorldConfig` (interface): interface PlotsWorldConfig extends WorldGridConfig { city?: string; interiors?: string } — ⚠ undocumented · used by `plots`: Declares a subdivided-plots world — farming, base-building, and other parcel-based layouts.
3387
+ - `RainEnvironmentConfig` (interface): interface RainEnvironmentConfig { area?: EnvironmentArea; position?: EnvironmentVec2; density?: number; speed?: number; dropLength?: number; wind?: EnvironmentVec2; color?: string; width?: number; opacity?: number } — ⚠ undocumented · used by `rain`: Declares a rainfall weather effect for `environment()` — area, density, speed, wind, and drop width/opacity.
3388
+ - `RainEnvironmentDescriptor` (type): type RainEnvironmentDescriptor = { kind: "rain" } & Required< Pick<RainEnvironmentConfig, "area" | "density" | "speed" | "dropLength" | "wind" | "color" | "width" | "opacity"> > — ⚠ undocumented · used by `rain`: Declares a rainfall weather effect for `environment()` — area, density, speed, wind, and drop width/opacity.
3035
3389
  - `RoadEnvironmentConfig` (interface): interface RoadEnvironmentConfig — Config for {@link road}: a flat asphalt ribbon draped over the terrain along a centerline.
3036
3390
  - `RoadEnvironmentDescriptor` (type): type RoadEnvironmentDescriptor = { kind: "road" } & Required< Pick<RoadEnvironmentConfig, "path" | "width" | "color" | "markings" | "markingColor" | "elevation"> > & { /** Resolved sidewalk band, or `false` when the road has none. */ sidewalk: { width: number; color: string } | false; } — Resolved road descriptor produced by {@link road} and rendered by the shell environment scene.
3037
- - `SkyEnvironmentConfig` (interface): interface SkyEnvironmentConfig — ⚠ undocumented
3391
+ - `SkyEnvironmentConfig` (interface): interface SkyEnvironmentConfig { preset?: "day" | "dusk" | "night"; timeOfDay?: boolean; horizonColor?: string; zenithColor?: string; sunIntensity?: number; ambientIntensity?: number; sun?: SkySunConfig; radius?: number; hazeStrength?: number; sunGlowStrength?: number; g… — ⚠ undocumented · used by `CINEMATIC_SKY` (@jgengine/core/render/lookPreset): A static day sky: a real dome plus the shell's view-following shadow-casting sun and hemisphere fill.
3038
3392
  - `SkyEnvironmentDescriptor` (type): type SkyEnvironmentDescriptor = { kind: "sky" } & Required< Pick<SkyEnvironmentConfig, "preset" | "timeOfDay"> > & Omit<SkyEnvironmentConfig, "preset" | "timeOfDay"> — ⚠ undocumented
3039
- - `SnowEnvironmentConfig` (interface): interface SnowEnvironmentConfig — ⚠ undocumented
3040
- - `SnowEnvironmentDescriptor` (type): type SnowEnvironmentDescriptor = { kind: "snow" } & Required< Pick<SnowEnvironmentConfig, "area" | "density" | "speed" | "flakeSize" | "drift" | "wind" | "color" | "opacity"> > — ⚠ undocumented
3393
+ - `SkySunConfig` (interface): interface SkySunConfig — Where the sun sits, as a compass bearing and a height above the horizon, both in degrees. `azimuth` 0 points toward -Z (north), 90 toward +X (east); `elevation` 90 is straight overhead.
3394
+ - `SnowEnvironmentConfig` (interface): interface SnowEnvironmentConfig { area?: EnvironmentArea; position?: EnvironmentVec2; density?: number; speed?: number; flakeSize?: number; drift?: number; wind?: EnvironmentVec2; color?: string; opacity?: number } — ⚠ undocumented · used by `snow`: Declares a snowfall weather effect for `environment()` — area, density, drift, wind, and flake opacity.
3395
+ - `SnowEnvironmentDescriptor` (type): type SnowEnvironmentDescriptor = { kind: "snow" } & Required< Pick<SnowEnvironmentConfig, "area" | "density" | "speed" | "flakeSize" | "drift" | "wind" | "color" | "opacity"> > — ⚠ undocumented · used by `snow`: Declares a snowfall weather effect for `environment()` — area, density, drift, wind, and flake opacity.
3041
3396
  - `StructureEnvironmentDescriptor` (type): type StructureEnvironmentDescriptor = BuildingEnvironmentDescriptor — ⚠ undocumented
3042
3397
  - `TerrainCircleRegion` (interface): interface TerrainCircleRegion extends TerrainRegionStyle — A circular palette zone painted over the base terrain palette — snow caps, ash wastes, spawn circles.
3043
- - `TerrainColors` (interface): interface TerrainColors — ⚠ undocumented
3398
+ - `TerrainColors` (interface): interface TerrainColors { low?: string; high?: string; waterline?: string } — ⚠ undocumented
3044
3399
  - `TerrainDetailConfig` (interface): interface TerrainDetailConfig — Procedural detail-surface layer for terrain: a noise-driven shader that keeps the biome-tinted base ground (from `colors`/`biomeBands`) and blends distinct rock, sand, and snow over it by slope, height, and waterline — turning a flat vertex-colour surface into varied, textured-reading ground with no image assets.
3045
3400
  - `TerrainDetailMaterialConfig` (interface): interface TerrainDetailMaterialConfig — Real PBR texture applied over the ground surface — the seam that lets a game put a `buildMaterialCatalog` material on terrain. Blends with, never replaces, the procedural detail shader: color/roughness/ao tile the maps by world position, `strength` fades them over the existing vertex-colour + noise look.
3046
- - `TerrainEnvironmentConfig` (interface): interface TerrainEnvironmentConfig — ⚠ undocumented
3047
- - `TerrainEnvironmentDescriptor` (type): type TerrainEnvironmentDescriptor = { kind: "terrain" } & Required< Pick<TerrainEnvironmentConfig, "bounds" | "height"> > & Omit<TerrainEnvironmentConfig, "bounds" | "height"> — ⚠ undocumented
3048
- - `TerrainFlattenMask` (interface): interface TerrainFlattenMask — ⚠ undocumented
3401
+ - `TerrainEnvironmentConfig` (interface): interface TerrainEnvironmentConfig { bounds?: WorldBounds; height?: number; heightMap?: string; heightField?: (x: number, z: number) => number; material?: TerrainMaterial; colors?: TerrainColors; materialRegions?: readonly TerrainMaterialRegion[]; biomeBands?: readonly Biome… — ⚠ undocumented · used by `terrain`: Declares a heightfield terrain patch for `environment()` — bounds, noise, materials, and flatten masks.
3402
+ - `TerrainEnvironmentDescriptor` (type): type TerrainEnvironmentDescriptor = { kind: "terrain" } & Required< Pick<TerrainEnvironmentConfig, "bounds" | "height"> > & Omit<TerrainEnvironmentConfig, "bounds" | "height"> — ⚠ undocumented · used by `terrain`: Declares a heightfield terrain patch for `environment()` — bounds, noise, materials, and flatten masks.
3403
+ - `TerrainFlattenMask` (interface): interface TerrainFlattenMask { center: EnvironmentVec2; radius: number; height?: number; falloff?: number } — ⚠ undocumented · used by `padFlattenMasks`: Derives implicit `TerrainFlattenMask`s carving each pad's footprint into the terrain beneath it.
3049
3404
  - `TerrainIslandConfig` (interface): interface TerrainIslandConfig extends TerrainEnvironmentConfig — A bounded terrain patch floating at its own altitude — sky islands, arena platforms, split landmasses with void between.
3050
3405
  - `TerrainIslandDescriptor` (type): type TerrainIslandDescriptor = Omit<TerrainEnvironmentDescriptor, "kind"> & { kind: "island"; origin: EnvironmentVec2; } — ⚠ undocumented
3051
- - `TerrainMaterial` (type): type TerrainMaterial = "grass" | "sand" | "snow" | "rock" | "ash" | "highland" | "slate" — ⚠ undocumented
3052
- - `TerrainMaterialMaps` (interface): interface TerrainMaterialMaps — PBR map URLs for a real ground texture — the same shape `buildMaterialCatalog({ basePath }).resolve(id)!.maps` from `@jgengine/assets` returns. Kept dependency-free here so `core` never imports the assets package; any URLs (pulled maps, a CDN, a data URI) satisfy it.
3406
+ - `TerrainMaterial` (type): type TerrainMaterial = "grass" | "sand" | "snow" | "rock" | "ash" | "highland" | "slate" — ⚠ undocumented · used by `TERRAIN_MATERIALS` (@jgengine/editor): The default terrain paint palette (surface id → color) shared by the panel and the mesh.
3407
+ - `TerrainMaterialMaps` (interface): interface TerrainMaterialMaps — PBR map URLs for a real ground texture — the same shape `buildMaterialCatalog({ basePath }).resolve(id)!.maps` from `@jgengine/assets` returns. Kept dependency-free here so `core` never imports the assets package; any URLs (pulled maps, a CDN, a data URI) satisfy it. Omitted roughness keeps the terrain scalar; omitted AO skips occlusion sampling.
3053
3408
  - `TerrainMaterialRegion` (type): type TerrainMaterialRegion = TerrainCircleRegion | TerrainPolylineRegion | TerrainRectRegion — A palette zone painted over the base terrain palette. Circle (the default when no `shape` is given), `polyline` ribbons for roads/rivers, and rotatable `rect` districts all paint fully inside their core and blend back across `falloff`; later regions in the list win overlaps.
3054
3409
  - `TerrainPolylineRegion` (interface): interface TerrainPolylineRegion extends TerrainRegionStyle — A ribbon palette zone following a centerline — roads and rivers, instead of chaining overlapping circles.
3055
3410
  - `TerrainRectRegion` (interface): interface TerrainRectRegion extends TerrainRegionStyle — A rectangular palette zone, optionally rotated about the world y axis — plazas, fields, districts.
3056
3411
  - `TerrainRegionStyle` (interface): interface TerrainRegionStyle — Palette and blend fields shared by every `TerrainMaterialRegion` shape.
3057
- - `TilemapWorldConfig` (interface): interface TilemapWorldConfig extends WorldGridConfig — ⚠ undocumented
3412
+ - `TilemapWorldConfig` (interface): interface TilemapWorldConfig extends WorldGridConfig { map: string; tileSet?: {atlas: SpriteAtlas; tiles: Record<string, string>}; sortingLayer?: string } — ⚠ undocumented · used by `tilemap`: Declares a 2D tilemap world from a map string.
3058
3413
  - `VegetationEnvironmentDescriptor` (type): type VegetationEnvironmentDescriptor = GrassEnvironmentDescriptor — ⚠ undocumented
3059
- - `VoxelWorldConfig` (interface): interface VoxelWorldConfig extends WorldGridConfig — ⚠ undocumented
3414
+ - `VoxelWorldConfig` (interface): interface VoxelWorldConfig extends WorldGridConfig { seed: string; generate?: string; streaming?: {radius: number} } — ⚠ undocumented · used by `voxel`: Declares a voxel-grid world for block-based games.
3060
3415
  - `WaterEnvironmentDescriptor` (type): type WaterEnvironmentDescriptor = OceanEnvironmentDescriptor — ⚠ undocumented
3061
3416
  - `WeatherEnvironmentDescriptor` (type): type WeatherEnvironmentDescriptor = RainEnvironmentDescriptor | SnowEnvironmentDescriptor — ⚠ undocumented
3062
- - `WorldBounds` (interface): interface WorldBounds — ⚠ undocumented
3417
+ - `WorldBounds` (interface): interface WorldBounds { w: number; d: number } — ⚠ undocumented · used by `terrainBoundsFromDocument` (@jgengine/core/editor): The origin-centered terrain footprint that covers every authored object in a document, padded and floored to `minBounds`.
3063
3418
  - `WorldFeature` (type): type WorldFeature = | PlaceWorldFeature | ({ kind: "biomes" } & BiomesWorldConfig) | ({ kind: "voxel" } & VoxelWorldConfig) | ({ kind: "plots" } & PlotsWorldConfig) | ({ kind: "tilemap" } & TilemapWorldConfig) | EnvironmentWorldFeature | { kind: "flat" } — A declared world shape passed to `defineGame`. The preferred model is the place feature from `world()` (`@jgengine/core/world/place`): substrate + laws, with all dressing authored in the editor. The remaining members — biomes, voxel grid, plots, tilemap, environment, flat — are the legacy code-declared shapes kept for existing games.
3064
- - `WorldGridCell` (interface): interface WorldGridCell — ⚠ undocumented
3419
+ - `WorldGridCell` (interface): interface WorldGridCell { x: number; z: number; height?: number; color?: string } — ⚠ undocumented
3065
3420
  - `WorldGridConfig` (interface): interface WorldGridConfig — Shared by `biomes()`/`voxel()`/`plots()`/`tilemap()` so the shell can render their declared content as instanced boxes without a hand-written renderer.
3066
3421
  - `biomes` (function): function biomes(config: BiomesWorldConfig): WorldFeature — Declares a biome-painted world — the whole-world alternative to a single `environment()` terrain.
3067
3422
  - `building` (function): function building(config: BuildingEnvironmentConfig = {}): BuildingEnvironmentDescriptor — Declares a cluster of procedurally-massed buildings for `environment()` — count, footprint, stories, style. Pass `along` to line road frontage instead of gridding around `position`.
@@ -3083,9 +3438,9 @@
3083
3438
 
3084
3439
  ## @jgengine/core/world/fog
3085
3440
 
3086
- - `FogBounds` (interface): interface FogBounds — ⚠ undocumented
3087
- - `FogCells` (interface): interface FogCells — ⚠ undocumented
3088
- - `FogConfig` (interface): interface FogConfig — ⚠ undocumented
3441
+ - `FogBounds` (interface): interface FogBounds { minX: number; minZ: number; maxX: number; maxZ: number } — ⚠ undocumented
3442
+ - `FogCells` (interface): interface FogCells { cols: number; rows: number; minX: number; minZ: number; cellSize: number; revealed: readonly boolean[]; revealedCount: number } — ⚠ undocumented
3443
+ - `FogConfig` (interface): interface FogConfig { bounds: FogBounds; cellSize: number; revealed?: readonly number[] } — ⚠ undocumented
3089
3444
  - `FogField` (interface): interface FogField — Reveal-on-event fog of war over a fixed grid. Walking (`revealAlong`) and digging/acting (`reveal`) clear cells; once revealed a cell stays revealed. Pure and renderer-free — the shell/react map draws `cells()`.
3090
3445
  - `createFogField` (function): function createFogField(config: FogConfig): FogField — ⚠ undocumented
3091
3446
 
@@ -3094,23 +3449,33 @@
3094
3449
  - `AdjacentCell` (interface): interface AdjacentCell — One occupied neighbor cell reported by {@link boundaryNeighbors}.
3095
3450
  - `FootprintGrid` (interface): interface FootprintGrid — Handle returned by {@link createFootprintGrid}.
3096
3451
  - `FootprintGridOptions` (interface): interface FootprintGridOptions — Config for {@link createFootprintGrid}.
3452
+ - `FootprintGridState` (interface): interface FootprintGridState — Plain JSON state of a {@link FootprintGrid}: live reservations in claim order.
3453
+ - `FootprintRegion` (type): type FootprintRegion = RegionFootprint | MergedRegion — A standalone footprint or a pooled region carrying its original contributions.
3097
3454
  - `FootprintReservation` (interface): interface FootprintReservation — A live claim on a {@link FootprintGrid}: which cells `id` (a `kind` tag for adjacency checks) holds.
3098
3455
  - `GridCell` (interface): interface GridCell — One integer cell address on a {@link FootprintGrid}.
3456
+ - `MergedRegion` (interface): interface MergedRegion extends RegionFootprint — Plain JSON pooled region. Contributions partition its cells and sum to its capacity.
3457
+ - `RegionContribution` (interface): interface RegionContribution — Original footprint identity and capacity retained when regions pool.
3458
+ - `RegionFootprint` (interface): interface RegionFootprint — Caller-owned logical footprint. Cells must be unique, safe integer addresses and cardinally connected.
3459
+ - `RegionSplitIds` (interface): interface RegionSplitIds — Identities for split sides containing multiple contributions; singletons retain their original id.
3460
+ - `RegionWorkBudget` (interface): interface RegionWorkBudget — Optional caller work limits, checked before cell traversal. Omitted limits follow input size.
3099
3461
  - `boundaryNeighbors` (function): function boundaryNeighbors(grid: FootprintGrid, cells: readonly GridCell[]): AdjacentCell[] — Every occupied cell orthogonally touching `cells` but outside them — the connective-piece neighbor set.
3462
+ - `canMerge` (function): function canMerge(a: FootprintRegion, b: FootprintRegion, budget: RegionWorkBudget = {}): boolean — Matching kind/tier, disjoint valid source identities/cells, and cardinal contact. Linear in input size.
3100
3463
  - `createFootprintGrid` (function): function createFootprintGrid(options: FootprintGridOptions = {}): FootprintGrid — Multi-cell footprint occupancy/reservation on a shared build grid — `world/placementController` only owns the ghost preview; this is the persistent claim a committed placement holds so the next hover's `isFree` check (or another player's, in a shared world) sees it. Bridge into `world/placement`'s `PlacementRules.obstacles` with {@link footprintObstacles} instead of hand-rolling an occupancy map per game.
3101
3464
  - `footprintObstacles` (function): function footprintObstacles(grid: FootprintGrid): PlacementObstacle[] — Bridges live reservations into `world/placement`'s `PlacementRules.obstacles` so `validatePlacement`/`createPlacementController` see the grid's committed footprints unchanged.
3102
3465
  - `hasValidAdjacency` (function): function hasValidAdjacency(grid: FootprintGrid, cells: readonly GridCell[], accepts: (neighborKind: string) => boolean, requireConnection = false): boolean — Connective-piece adjacency validity: every occupied neighbor of `cells` must satisfy `accepts` (no incompatible piece touching), and when `requireConnection` is true at least one neighbor must (a road/pipe/belt segment placed with nothing to connect to is invalid). An empty-bordered footprint (no occupied neighbors at all) passes unless `requireConnection` demands one.
3466
+ - `mergeFootprints` (function): function mergeFootprints(a: FootprintRegion, b: FootprintRegion, id: string, budget: RegionWorkBudget = {}): MergedRegion | null — Pool compatible footprints under caller-owned `id`, retaining original capacities and identities. Returns null for invalid data, kind/tier mismatch, overlap, no cardinal contact or exhausted budget. Does not mutate inputs or grid reservations; costs, merge width and upgrade policy remain caller-owned. Capacity is the ordered JavaScript-number sum of original contributions, independent of cell area.
3467
+ - `splitRegion` (function): function splitRegion(region: MergedRegion, leftIds: readonly string[], ids: RegionSplitIds = {}, budget: RegionWorkBudget = {}): [FootprintRegion, FootprintRegion] | null — Partition whole original contributions by `leftIds`; both sides must remain cardinally connected. Singletons restore their original id and capacity; larger sides require explicit output identities. Returns null for invalid provenance/budget, unknown or duplicate ids, empty/disconnected sides, or conflicting output identities (including identities from the opposite side's contributions). An output may reuse its own side's source identity. Cell cuts/capacity repricing require caller policy. Both sides inherit the current region kind/tier. Inputs and grid reservations remain unchanged.
3103
3468
 
3104
3469
  ## @jgengine/core/world/geometry
3105
3470
 
3106
- - `Aabb` (interface): interface Aabb — ⚠ undocumented
3471
+ - `Aabb` (interface): interface Aabb { minX: number; minZ: number; maxX: number; maxZ: number } — ⚠ undocumented · used by `polygonBounds` (@jgengine/core/world): Axis-aligned bounds of a polygon, or null if it has no points.
3107
3472
  - `AvoidCorridor` (interface): interface AvoidCorridor — A clearance *corridor* along a polyline (a path/road): scatter is repelled within `halfWidth` of the centerline, feathered over the outer band. Clean straight edges — unlike approximating a corridor with a scalloped chain of discs.
3108
3473
  - `AvoidMasks` (interface): interface AvoidMasks — A clearance region: point discs (spawns/plots) plus centerline corridors (paths).
3109
3474
  - `AvoidZone` (interface): interface AvoidZone — A circular clearance around a gameplay spot (spawn, plot, path point, POI): scatter is repelled from it and terrain is flattened toward its center. `feather` (meters) is the soft outer band — full effect within `radius - feather`, ramping to zero at `radius`.
3110
3475
  - `Ellipse` (interface): interface Ellipse — An axis-aligned ellipse: `center` with semi-axes `radiusX` and `radiusY`.
3111
- - `Footprint` (interface): interface Footprint — ⚠ undocumented
3112
- - `MoveOptions` (interface): interface MoveOptions — ⚠ undocumented
3113
- - `Vec2` (type): type Vec2 = readonly [number, number] — ⚠ undocumented
3476
+ - `Footprint` (interface): interface Footprint { w: number; d: number } — ⚠ undocumented · used by `rotatedFootprint` (@jgengine/core/scene/assetSpace): The axis-aligned footprint extent after rotating a rectangular {@link Footprint} by `headingDegrees` — footprint-aware placement: a 90-degre…
3477
+ - `MoveOptions` (interface): interface MoveOptions { bounds?: Aabb; radius?: number } — ⚠ undocumented
3478
+ - `Vec2` (type): type Vec2 = readonly [number, number] — ⚠ undocumented · used by `assignFormationSlots` (@jgengine/core/movement/formation): Match `members` to `slots` by a deterministic greedy nearest assignment: every (member, slot) pair is ranked by squared travel distance (min…
3114
3479
  - `Vec3` (type): type Vec3 = readonly [number, number, number] — A 3D point/vector as an `[x, y, z]` tuple — the engine's zero-allocation spatial primitive.
3115
3480
 
3116
3481
  ## @jgengine/core/world/grassKind
@@ -3123,7 +3488,7 @@
3123
3488
 
3124
3489
  ## @jgengine/core/world/gridInstances
3125
3490
 
3126
- - `GridInstanceTransform` (interface): interface GridInstanceTransform — ⚠ undocumented
3491
+ - `GridInstanceTransform` (interface): interface GridInstanceTransform { position: readonly [number, number, number]; scale: readonly [number, number, number]; color: string } — ⚠ undocumented
3127
3492
  - `resolveGridCells` (function): function resolveGridCells(config: WorldGridConfig | GridWorldFeature): readonly WorldGridCell[] — ⚠ undocumented
3128
3493
  - `resolveGridInstances` (function): function resolveGridInstances(config: WorldGridConfig | GridWorldFeature): readonly GridInstanceTransform[] — ⚠ undocumented
3129
3494
 
@@ -3133,19 +3498,20 @@
3133
3498
 
3134
3499
  ## @jgengine/core/world/interiors
3135
3500
 
3136
- - `EntityLocation` (interface): interface EntityLocation — ⚠ undocumented
3137
- - `Exterior` (interface): interface Exterior — ⚠ undocumented
3138
- - `Interior` (interface): interface Interior — ⚠ undocumented
3139
- - `Interiors` (interface): interface Interiors — ⚠ undocumented
3140
- - `InteriorsConfig` (interface): interface InteriorsConfig — ⚠ undocumented
3501
+ - `EntityLocation` (interface): interface EntityLocation { space: SpaceRef; position: Vec2 } — ⚠ undocumented
3502
+ - `Exterior` (interface): interface Exterior { bounds?: Aabb; obstacles?: readonly Aabb[] } — ⚠ undocumented
3503
+ - `Interior` (interface): interface Interior { id: string; origin: Vec2; rotation?: number; bounds: Aabb; obstacles?: readonly Aabb[] } — ⚠ undocumented
3504
+ - `Interiors` (interface): interface Interiors { move(location: EntityLocation, delta: Vec2): EntityLocation; enter(location: EntityLocation, id: string): EntityLocation | null; leave(location: EntityLocation): EntityLocation | null; toInterior(id: string, exterior: Vec2): Vec2 | null; … — ⚠ undocumented · used by `createInteriors`: Frame conversion and bounded movement across an exterior and its interiors; holds config only, no play state.
3505
+ - `InteriorsConfig` (interface): interface InteriorsConfig { exterior?: Exterior; interiors?: readonly Interior[]; radius?: number } — ⚠ undocumented · used by `createInteriors`: Frame conversion and bounded movement across an exterior and its interiors; holds config only, no play state.
3141
3506
  - `SpaceRef` (type): type SpaceRef = { kind: "exterior" } | { kind: "interior"; id: string } — ⚠ undocumented
3142
- - `createInteriors` (function): function createInteriors(config: InteriorsConfig = {}): Interiors — ⚠ undocumented
3507
+ - `createInteriors` (function): function createInteriors(config: InteriorsConfig = {}): Interiors — Frame conversion and bounded movement across an exterior and its interiors; holds config only, no play state.
3143
3508
 
3144
3509
  ## @jgengine/core/world/lod
3145
3510
 
3146
- - `LodBand` (interface): interface LodBand — ⚠ undocumented
3147
- - `LodScheduler` (interface): interface LodScheduler — ⚠ undocumented
3148
- - `LodSchedulerConfig` (interface): interface LodSchedulerConfig — ⚠ undocumented
3511
+ - `LodBand` (interface): interface LodBand { maxDistance: number; interval: number } — ⚠ undocumented
3512
+ - `LodScheduler` (interface): interface LodScheduler { bandIndex(distance: number): number; step(id: string, distance: number, dtSeconds: number): number; remove(id: string): void; clear(): void; size(): number; snapshot(): LodSchedulerState; restore(next: LodSchedulerState): void } — ⚠ undocumented
3513
+ - `LodSchedulerConfig` (interface): interface LodSchedulerConfig { bands: readonly LodBand[]; beyondInterval?: number | null; stagger?: boolean } — ⚠ undocumented
3514
+ - `LodSchedulerState` (interface): interface LodSchedulerState — Plain JSON state of a {@link LodScheduler}: each id's accumulated seconds.
3149
3515
  - `createLodScheduler` (function): function createLodScheduler(config: LodSchedulerConfig): LodScheduler — ⚠ undocumented
3150
3516
 
3151
3517
  ## @jgengine/core/world/mapAnnotations
@@ -3161,12 +3527,12 @@
3161
3527
  ## @jgengine/core/world/mapLayers
3162
3528
 
3163
3529
  - `MAP_LAYER_TONE_COLORS` (const): const MAP_LAYER_TONE_COLORS: Record<MapLayerTone, string> — ⚠ undocumented
3164
- - `MapCellStates` (interface): interface MapCellStates — ⚠ undocumented
3530
+ - `MapCellStates` (interface): interface MapCellStates { id: string; origin: MapXZ; cellSize: number; cells: readonly {col: number; row: number; tone: MapLayerTone; opacity?: number}[] } — ⚠ undocumented
3165
3531
  - `MapLayerTone` (type): type MapLayerTone = "danger" | "warning" | "info" | "safe" | "neutral" — ⚠ undocumented
3166
- - `MapRoute` (interface): interface MapRoute — ⚠ undocumented
3532
+ - `MapRoute` (interface): interface MapRoute { id: string; points: readonly MapXZ[]; tone?: MapLayerTone; forecast?: boolean; width?: number; closed?: boolean } — ⚠ undocumented
3167
3533
  - `MapXZ` (type): type MapXZ = readonly [number, number] — ⚠ undocumented
3168
- - `MapZone` (interface): interface MapZone — ⚠ undocumented
3169
- - `MapZoneShape` (type): type MapZoneShape = | { kind: "circle"; center: MapXZ; radius: number } | { kind: "rect"; center: MapXZ; w: number; d: number; rotate?: number } | { kind: "polygon"; points: readonly MapXZ[] } — ⚠ undocumented
3534
+ - `MapZone` (interface): interface MapZone { id: string; shape: MapZoneShape; tone?: MapLayerTone; opacity?: number; forecast?: boolean; label?: string } — ⚠ undocumented
3535
+ - `MapZoneShape` (type): type MapZoneShape = | { kind: "circle"; center: MapXZ; radius: number } | { kind: "rect"; center: MapXZ; w: number; d: number; rotate?: number } | { kind: "polygon"; points: readonly MapXZ[] } — ⚠ undocumented · used by `pointInMapZone`: Renderer-free overlay data for map surfaces (#285.1-2): zones with live + forecast states, route polylines, and per-cell status heatmaps.
3170
3536
  - `mapLayerColor` (function): function mapLayerColor(tone: MapLayerTone | undefined): string — ⚠ undocumented
3171
3537
  - `pointInMapZone` (function): function pointInMapZone(zone: MapZoneShape, x: number, z: number): boolean — Renderer-free overlay data for map surfaces (#285.1-2): zones with live + forecast states, route polylines, and per-cell status heatmaps. `@jgengine/react/map`'s `Minimap`/`WorldMap` take these straight in; `pointInMapZone` answers gameplay queries against the same data, so the HUD and the rules can never disagree about where the storm is.
3172
3538
 
@@ -3175,11 +3541,11 @@
3175
3541
  - `DEFAULT_MARKER_KINDS` (const): const DEFAULT_MARKER_KINDS: Record<string, MarkerKindStyle> — ⚠ undocumented
3176
3542
  - `MapMarker` (interface): interface MapMarker<TMeta = unknown> extends MarkerView<TMeta> — A marker owned by {@link MarkerSet}, including its lifecycle and query fields.
3177
3543
  - `MarkerCollection` (type): type MarkerCollection<TMarker extends MarkerView = MarkerView> = | readonly TMarker[] | MarkerSource<TMarker> | MarkerSet — Marker data accepted by portable consumers: static views, an external source, or a native set.
3178
- - `MarkerInput` (interface): interface MarkerInput<TMeta = unknown> — ⚠ undocumented
3544
+ - `MarkerInput` (interface): interface MarkerInput<TMeta = unknown> { id?: string; kind: string; position: MarkerPosition; label?: string; heading?: number; owner?: string; createdAt?: number; expiresAt?: number; meta?: TMeta } — ⚠ undocumented
3179
3545
  - `MarkerKindStyle` (interface): interface MarkerKindStyle — Visual descriptor for a marker kind. Games supply their own palette; the engine ships `DEFAULT_MARKER_KINDS` as a content-agnostic starting set that the react minimap/compass read for colors and glyphs.
3180
3546
  - `MarkerPosition` (type): type MarkerPosition = readonly [number, number, number] — ⚠ undocumented
3181
- - `MarkerQuery` (interface): interface MarkerQuery — ⚠ undocumented
3182
- - `MarkerSet` (interface): interface MarkerSet<TMeta = unknown> — ⚠ undocumented
3547
+ - `MarkerQuery` (interface): interface MarkerQuery { kind?: string; owner?: string; near?: MarkerPosition; radius?: number } — ⚠ undocumented
3548
+ - `MarkerSet` (interface): interface MarkerSet<TMeta = unknown> { add(marker: MarkerInput<TMeta>): string; remove(id: string): boolean; get(id: string): MapMarker<TMeta> | null; list(): readonly MapMarker<TMeta>[]; query(query: MarkerQuery): MapMarker<TMeta>[]; prune(now: number): number; clear(): void;… — ⚠ undocumented · used by `useLiveMarkers` (@jgengine/react): A self-ticking {@link MarkerSet} kept in sync with the live scene: on a heartbeat (default 100ms, and once immediately) it clears the set an…
3183
3549
  - `MarkerSource` (interface): interface MarkerSource<TMarker extends MarkerView = MarkerView> — Observable marker snapshots owned by an external project. `getSnapshot` must return the same array identity until the source changes and calls its subscribers, matching React's external-store contract.
3184
3550
  - `MarkerSourceOptions` (interface): interface MarkerSourceOptions<TEntity, TMarker extends MarkerView = MarkerView> — Configuration for projecting a caller-owned collection into display-only markers.
3185
3551
  - `MarkerView` (interface): interface MarkerView<TMeta = unknown> — Small, display-only marker shape consumed by map renderers. Existing games can project their own entities to this view without adopting marker lifecycle storage or duplicating them into a {@link MarkerSet}.
@@ -3205,9 +3571,9 @@
3205
3571
  ## @jgengine/core/world/minimap
3206
3572
 
3207
3573
  - `Cardinal` (type): type Cardinal = "N" | "NE" | "E" | "SE" | "S" | "SW" | "W" | "NW" — ⚠ undocumented
3208
- - `MinimapPoint` (interface): interface MinimapPoint — ⚠ undocumented
3209
- - `MinimapView` (interface): interface MinimapView — ⚠ undocumented
3210
- - `WorldXZ` (type): type WorldXZ = readonly [number, number] — ⚠ undocumented
3574
+ - `MinimapPoint` (interface): interface MinimapPoint { x: number; y: number; distance: number; inside: boolean } — ⚠ undocumented · used by `clampToMinimapEdge`: Clamp a projected point to the minimap edge, preserving direction (edge markers).
3575
+ - `MinimapView` (interface): interface MinimapView { center: WorldXZ; worldRadius: number; size: number; rotate?: number } — ⚠ undocumented · used by `projectToMinimap`: Project a world XZ (or XYZ) point into minimap pixel space.
3576
+ - `WorldXZ` (type): type WorldXZ = readonly [number, number] — ⚠ undocumented · used by `compassBearing`: Compass bearing (radians, 0 = map north = −Z, increasing clockwise toward +X = east) from one world XZ point to another.
3211
3577
  - `bearingToCardinal` (function): function bearingToCardinal(bearing: number): Cardinal — ⚠ undocumented
3212
3578
  - `clampToMinimapEdge` (function): function clampToMinimapEdge(point: MinimapPoint, size: number): { x: number; y: number } — Clamp a projected point to the minimap edge, preserving direction (edge markers).
3213
3579
  - `compassBearing` (function): function compassBearing(from: WorldXZ, to: WorldXZ): number — Compass bearing (radians, 0 = map north = −Z, increasing clockwise toward +X = east) from one world XZ point to another. Feeds both the minimap direction and the compass strip.
@@ -3284,30 +3650,30 @@
3284
3650
 
3285
3651
  ## @jgengine/core/world/placedStructureStore
3286
3652
 
3287
- - `AddStructureInput` (interface): interface AddStructureInput — ⚠ undocumented
3288
- - `PlacedStructure` (interface): interface PlacedStructure — ⚠ undocumented
3289
- - `PlacedStructureSnapshot` (interface): interface PlacedStructureSnapshot — ⚠ undocumented
3290
- - `PlacedStructureStore` (interface): interface PlacedStructureStore — ⚠ undocumented
3291
- - `StructureFilter` (interface): interface StructureFilter — ⚠ undocumented
3653
+ - `AddStructureInput` (interface): interface AddStructureInput { id?: string; catalogId: string; position: StructureVec3; rotationY?: number; plotId?: string; data?: Readonly<Record<string, unknown>> } — ⚠ undocumented · used by `toStructureInput` (@jgengine/core/world): Game-state form: feed {@link createPlacedStructureStore}.add.
3654
+ - `PlacedStructure` (interface): interface PlacedStructure { id: string; catalogId: string; position: StructureVec3; rotationY: number; plotId?: string; data?: Readonly<Record<string, unknown>> } — ⚠ undocumented
3655
+ - `PlacedStructureSnapshot` (interface): interface PlacedStructureSnapshot { structures: readonly PlacedStructure[] } — ⚠ undocumented
3656
+ - `PlacedStructureStore` (interface): interface PlacedStructureStore { add(input: AddStructureInput): PlacedStructure; move(id: string, position: StructureVec3): boolean; rotate(id: string, rotationY: number): boolean; remove(id: string): boolean; get(id: string): PlacedStructure | null; list(filter?: Struct… — ⚠ undocumented
3657
+ - `StructureFilter` (interface): interface StructureFilter { plotId?: string; catalogId?: string } — ⚠ undocumented
3292
3658
  - `StructureVec3` (type): type StructureVec3 = readonly [number, number, number] — ⚠ undocumented
3293
3659
  - `createPlacedStructureStore` (function): function createPlacedStructureStore(): PlacedStructureStore — ⚠ undocumented
3294
3660
 
3295
3661
  ## @jgengine/core/world/placement
3296
3662
 
3297
- - `PlacementObstacle` (interface): interface PlacementObstacle — ⚠ undocumented
3298
- - `PlacementRequest` (interface): interface PlacementRequest — ⚠ undocumented
3299
- - `PlacementResult` (type): type PlacementResult = | { status: "ok"; center: Vec2; aabb: Aabb } | { status: "rejected"; reason: "out-of-bounds" } | { status: "rejected"; reason: "territory.blocked" } | { status: "rejected"; reason: "overlap"; obstacle: PlacementObstacle; index: number } — ⚠ undocumented
3300
- - `PlacementRules` (interface): interface PlacementRules — ⚠ undocumented
3663
+ - `PlacementObstacle` (interface): interface PlacementObstacle { aabb: Aabb; id?: string } — ⚠ undocumented · used by `footprintObstacles` (@jgengine/core/world): Bridges live reservations into `world/placement`'s `PlacementRules.obstacles` so `validatePlacement`/`createPlacementController` see the gri…
3664
+ - `PlacementRequest` (interface): interface PlacementRequest { center: Vec2; footprint: Footprint; quarterTurns?: number } — ⚠ undocumented · used by `validatePlacement`: Footprint validity: bounds + obstacle overlap after optional grid snap.
3665
+ - `PlacementResult` (type): type PlacementResult = | { status: "ok"; center: Vec2; aabb: Aabb } | { status: "rejected"; reason: "out-of-bounds" } | { status: "rejected"; reason: "territory.blocked" } | { status: "rejected"; reason: "overlap"; obstacle: PlacementObstacle; index: number } — ⚠ undocumented · used by `validatePlacement`: Footprint validity: bounds + obstacle overlap after optional grid snap.
3666
+ - `PlacementRules` (interface): interface PlacementRules { bounds?: Aabb; obstacles?: readonly PlacementObstacle[]; snap?: number; territory?: (aabb: Aabb) => {ok: boolean} } — ⚠ undocumented · used by `validatePlacement`: Footprint validity: bounds + obstacle overlap after optional grid snap.
3301
3667
  - `footprintObstacle` (function): function footprintObstacle(request: PlacementRequest, id?: string): PlacementObstacle — ⚠ undocumented
3302
3668
  - `validatePlacement` (function): function validatePlacement(request: PlacementRequest, rules: PlacementRules = {}): PlacementResult — Footprint validity: bounds + obstacle overlap after optional grid snap.
3303
3669
 
3304
3670
  ## @jgengine/core/world/placementController
3305
3671
 
3306
- - `PlacementCommit` (interface): interface PlacementCommit — ⚠ undocumented
3307
- - `PlacementController` (interface): interface PlacementController — ⚠ undocumented
3308
- - `PlacementControllerConfig` (interface): interface PlacementControllerConfig — ⚠ undocumented
3309
- - `PlacementHit` (interface): interface PlacementHit — ⚠ undocumented
3310
- - `PlacementPreview` (interface): interface PlacementPreview — ⚠ undocumented
3672
+ - `PlacementCommit` (interface): interface PlacementCommit { center: Vec2; y: number; rotationY: number; quarterTurns: number; footprint: Footprint; aabb: Aabb; slotId?: string } — ⚠ undocumented · used by `placeAssetFromCommit` (@jgengine/core/world): Bridge a {@link PlacementCommit} into the shared place-asset verb.
3673
+ - `PlacementController` (interface): interface PlacementController { hover(hit: PlacementHit): PlacementPreview; current(): PlacementPreview | null; rotate(steps?: number): PlacementPreview | null; setSnapMode(mode: SnapMode): PlacementPreview | null; cycleSnapMode(): SnapMode; setFootprint(footprint: Foot… — ⚠ undocumented · used by `createPlacementController`: Headless placement ghost: hover → valid/invalid preview, rotate, grid/free/surface snap, commit.
3674
+ - `PlacementControllerConfig` (interface): interface PlacementControllerConfig { footprint: Footprint; rules?: PlacementRules; snapMode?: SnapMode; grid?: number; quarterTurns?: number; slots?: readonly PlacementSlot[]; slotRadius?: number } — ⚠ undocumented · used by `createPlacementController`: Headless placement ghost: hover → valid/invalid preview, rotate, grid/free/surface snap, commit.
3675
+ - `PlacementHit` (interface): interface PlacementHit { point: PlacementVec3; normal: PlacementVec3 } — ⚠ undocumented
3676
+ - `PlacementPreview` (interface): interface PlacementPreview { center: Vec2; y: number; quarterTurns: number; footprint: Footprint; aabb: Aabb; valid: boolean; reason?: "out-of-bounds" | "overlap" | "no-slot" | "territory.blocked"; snapMode: SnapMode; normal: PlacementVec3; slotId?: string } — ⚠ undocumented
3311
3677
  - `PlacementSlot` (interface): interface PlacementSlot — A fixed buildable slot the placement controller snaps to instead of grid/free positioning.
3312
3678
  - `PlacementVec3` (type): type PlacementVec3 = readonly [number, number, number] — ⚠ undocumented
3313
3679
  - `SnapMode` (type): type SnapMode = "grid" | "free" | "surface" — ⚠ undocumented
@@ -3346,10 +3712,10 @@
3346
3712
 
3347
3713
  ## @jgengine/core/world/realm
3348
3714
 
3349
- - `ComposedRealm` (interface): interface ComposedRealm — ⚠ undocumented
3715
+ - `ComposedRealm` (interface): interface ComposedRealm { params: RealmEnvironmentParams; weather: WeatherState; spawnTable: Record<string, number>; aesthetics: readonly string[]; cards: readonly RealmCard[]; environmentField(extra?: Omit<EnvironmentFieldConfig, "baseTemperature" | "nightDrop" |… — ⚠ undocumented · used by `composeRealm`: Assemble a played realm instance at runtime from a deck of modifier cards — the Nightingale "realm card" model.
3350
3716
  - `DEFAULT_REALM_ENVIRONMENT` (const): const DEFAULT_REALM_ENVIRONMENT: RealmEnvironmentParams — ⚠ undocumented
3351
- - `RealmBase` (interface): interface RealmBase — ⚠ undocumented
3352
- - `RealmCard` (interface): interface RealmCard — ⚠ undocumented
3717
+ - `RealmBase` (interface): interface RealmBase { environment?: Partial<RealmEnvironmentParams>; weather?: WeatherState; spawn?: Record<string, number> } — ⚠ undocumented · used by `composeRealm`: Assemble a played realm instance at runtime from a deck of modifier cards — the Nightingale "realm card" model.
3718
+ - `RealmCard` (interface): interface RealmCard { id: string; kind: "major" | "minor"; environment?: Partial<RealmEnvironmentParams>; weather?: Partial<WeatherState>; spawn?: SpawnTableOverride; aesthetic?: string } — ⚠ undocumented · used by `composeRealm`: Assemble a played realm instance at runtime from a deck of modifier cards — the Nightingale "realm card" model.
3353
3719
  - `RealmEnvironmentParams` (interface): interface RealmEnvironmentParams — The recomposable environment parameters a realm card can override.
3354
3720
  - `SpawnTableOverride` (interface): interface SpawnTableOverride — Spawn-table edits a card layers on: replace weights, add/scale, or remove ids.
3355
3721
  - `composeRealm` (function): function composeRealm(base: RealmBase, cards: readonly RealmCard[]): ComposedRealm — Assemble a played realm instance at runtime from a deck of modifier cards — the Nightingale "realm card" model. A major card is the biome base; minor cards layer weather, day length, and spawn edits. The result recomposes both the environment (into a sampleable field via `environmentField()`) and the spawn table, and it depends on the weather hooks in this group (#92) to turn its `weather` into gameplay modifiers. Cards apply in array order; sort your deck (majors first) before composing.
@@ -3358,10 +3724,10 @@
3358
3724
 
3359
3725
  - `ColorInput` (type): type ColorInput = string | Rgb — ⚠ undocumented
3360
3726
  - `RegionDef` (interface): interface RegionDef<T = unknown> — A content-agnostic region. The engine blends `height` and the visual/physical knobs (`tint`, `water`, `fog`, `speedMultiplier`, …) across region borders and selects the dominant region by nearest center in `selector` space. `data` is an opaque payload the engine never inspects — games hang their own content off it (spawn tables, prop palettes, structure ids, names, …).
3361
- - `RegionField` (interface): interface RegionField<T = unknown> extends TerrainField — ⚠ undocumented
3362
- - `RegionFieldConfig` (interface): interface RegionFieldConfig<T = unknown> — ⚠ undocumented
3363
- - `RegionHeight` (interface): interface RegionHeight — ⚠ undocumented
3364
- - `RegionSample` (interface): interface RegionSample<T = unknown> — ⚠ undocumented
3727
+ - `RegionField` (interface): interface RegionField<T = unknown> extends TerrainField { sampleRegion(x: number, z: number): RegionSample<T>; readonly regions: readonly RegionDef<T>[]; readonly seaLevel: number; readonly seed: number } — ⚠ undocumented · used by `scatterItems` (@jgengine/core/world): Deterministically place opaque items across `area`, grounded on a region field.
3728
+ - `RegionFieldConfig` (interface): interface RegionFieldConfig<T = unknown> { regions: readonly RegionDef<T>[]; seed?: number; bounds?: WorldBounds; seaLevel?: number; selectorFrequencies?: readonly number[]; axisWeights?: readonly number[]; sharpness?: number; warp?: number } — ⚠ undocumented
3729
+ - `RegionHeight` (interface): interface RegionHeight { baseHeight: number; amplitude: number; frequency: number; octaves?: number; lacunarity?: number; persistence?: number; ridged?: boolean } — ⚠ undocumented
3730
+ - `RegionSample` (interface): interface RegionSample<T = unknown> { region: RegionDef<T>; weight: number; selector: readonly number[]; tint: Rgb; steepTint: Rgb; water: Rgb | null; fog: Rgb | null; fogDensity: number; speedMultiplier: number; data: T | undefined } — ⚠ undocumented · used by `scatterItems` (@jgengine/core/world): Deterministically place opaque items across `area`, grounded on a region field.
3365
3731
  - `Rgb` (type): type Rgb = readonly [number, number, number] — ⚠ undocumented
3366
3732
  - `createRegionField` (function): function createRegionField<T = unknown>(config: RegionFieldConfig<T>): RegionField<T> — ⚠ undocumented
3367
3733
  - `isRegionField` (function): function isRegionField(field: TerrainField): field is RegionField — ⚠ undocumented
@@ -3415,9 +3781,9 @@
3415
3781
 
3416
3782
  ## @jgengine/core/world/scatter
3417
3783
 
3418
- - `ScatterArea` (interface): interface ScatterArea — ⚠ undocumented
3419
- - `ScatterConfig` (interface): interface ScatterConfig — ⚠ undocumented
3420
- - `ScatterPoint` (interface): interface ScatterPoint — ⚠ undocumented
3784
+ - `ScatterArea` (interface): interface ScatterArea { w: number; d: number; center?: Vec2 } — ⚠ undocumented
3785
+ - `ScatterConfig` (interface): interface ScatterConfig { area: ScatterArea | Aabb; count?: number; density?: number; seed?: string | number; minDistance?: number; avoid?: readonly Aabb[]; avoidMargin?: number; jitter?: number; maxAttempts?: number } — ⚠ undocumented
3786
+ - `ScatterPoint` (interface): interface ScatterPoint { x: number; z: number; index: number } — ⚠ undocumented
3421
3787
  - `scatter` (function): function scatter(config: ScatterConfig): ScatterPoint[] — ⚠ undocumented
3422
3788
  - `scatterAabb` (function): function scatterAabb(area: ScatterArea | Aabb): Aabb — ⚠ undocumented
3423
3789
 
@@ -3438,7 +3804,7 @@
3438
3804
 
3439
3805
  ## @jgengine/core/world/scatterItems
3440
3806
 
3441
- - `ScatterInstance` (interface): interface ScatterInstance — ⚠ undocumented
3807
+ - `ScatterInstance` (interface): interface ScatterInstance { id: string; item: string; x: number; z: number; y: number; scale: number; rotation: number; regionId: string } — ⚠ undocumented · used by `scatterItems`: Deterministically place opaque items across `area`, grounded on a region field.
3442
3808
  - `ScatterLayer` (interface): interface ScatterLayer — One placeable class. `item` is an opaque id the caller maps to a mesh/entity.
3443
3809
  - `pickWeighted` (function): function pickWeighted<T>(entries: readonly { value: T; weight: number }[], roll: number): T | null — Weighted pick from opaque entries; `roll` in [0, 1). Returns null when empty.
3444
3810
  - `scatterItems` (function): function scatterItems<T>(field: RegionField<T>, area: Aabb, layersFor: (sample: RegionSample<T>) => readonly ScatterLayer[], options: { cell?: number; max?: number; saltKey?: number } = {}): ScatterInstance[] — Deterministically place opaque items across `area`, grounded on a region field. For each grid cell it asks `layersFor` which items may appear in that region and rolls one against their densities. The engine never interprets `item` — a game maps it to a mesh or entity. Content scatter (region-driven density) as opposed to `scatter` in `./scatter`, which is renderer-free geometric point distribution.
@@ -3589,24 +3955,25 @@
3589
3955
 
3590
3956
  ## @jgengine/core/world/support
3591
3957
 
3592
- - `DebrisOptions` (interface): interface DebrisOptions — ⚠ undocumented
3593
- - `SupportConfig` (interface): interface SupportConfig — ⚠ undocumented
3594
- - `SupportLink` (interface): interface SupportLink — ⚠ undocumented
3595
- - `SupportPiece` (interface): interface SupportPiece — ⚠ undocumented
3596
- - `SupportResult` (interface): interface SupportResult — ⚠ undocumented
3958
+ - `DebrisOptions` (interface): interface DebrisOptions { scatter?: number; seed?: number } — ⚠ undocumented
3959
+ - `SupportConfig` (interface): interface SupportConfig { maxDistance?: number } — ⚠ undocumented
3960
+ - `SupportLink` (interface): interface SupportLink { a: string; b: string } — ⚠ undocumented
3961
+ - `SupportPiece` (interface): interface SupportPiece { id: string; grounded?: boolean; position?: ConnectorVec3; halfExtents?: ConnectorVec3; mass?: number } — ⚠ undocumented
3962
+ - `SupportResult` (interface): interface SupportResult { supported: readonly string[]; unsupported: readonly string[]; distance: Readonly<Record<string, number>> } — ⚠ undocumented
3597
3963
  - `solveSupport` (function): function solveSupport(pieces: readonly SupportPiece[], links: readonly SupportLink[], config: SupportConfig = {}): SupportResult — ⚠ undocumented
3598
3964
  - `toDebrisBodies` (function): function toDebrisBodies(pieces: readonly SupportPiece[], collapsedIds: readonly string[], options: DebrisOptions = {}): AddBodyOptions[] — ⚠ undocumented
3599
3965
 
3600
3966
  ## @jgengine/core/world/terraform
3601
3967
 
3602
3968
  - `BlendStroke` (interface): interface BlendStroke — Accumulates a whole blend-paint drag into one compact {@link WeightDelta}. Keeps each weight slot's first `before` and latest `after`, so undo replays the blend as a single step.
3603
- - `EditableTerrain` (interface): interface EditableTerrain extends TerrainField — ⚠ undocumented
3604
- - `EditableTerrainConfig` (interface): interface EditableTerrainConfig — ⚠ undocumented
3969
+ - `EditableTerrain` (interface): interface EditableTerrain extends TerrainField { readonly cols: number; readonly rows: number; readonly cellSize: number; apply(edit: TerraformEdit): number; applyRecording(edit: TerraformEdit, record: TerraformDeltaRecorder): number; editDelta(edit: TerraformEdit): TerraformDelta; appl… — ⚠ undocumented · used by `beginSurfaceStroke`: Opens a paint-stroke recorder over `terrain`; stamp paint edits into it, then read one net delta.
3970
+ - `EditableTerrainConfig` (interface): interface EditableTerrainConfig { bounds: Aabb; base?: TerrainField; cellSize?: number } — ⚠ undocumented · used by `createTerrainSnapshot`: A fresh, unedited terrain snapshot sized to `bounds`/`cellSize` — the seed for a new sculpt document.
3605
3971
  - `SurfaceDelta` (interface): interface SurfaceDelta — A compact record of the surface-material cells a paint stroke touched: parallel `indices`/`before`/`after` arrays into the per-cell surface grid. One per stroke keeps paint undo history small.
3606
3972
  - `SurfaceDeltaRecorder` (type): type SurfaceDeltaRecorder = (index: number, before: string | null, after: string | null) => void — Reports each changed cell during a recorded paint: grid index, prior surface id, new surface id.
3607
3973
  - `SurfaceStroke` (interface): interface SurfaceStroke — Accumulates a whole paint drag — many surface stamps — into one compact {@link SurfaceDelta}. Keeps each cell's first `before` and latest `after`, so undo replays the paint as a single step.
3608
- - `TerraformBrush` (interface): interface TerraformBrush — ⚠ undocumented
3609
- - `TerraformBrushConfig` (interface): interface TerraformBrushConfig — ⚠ undocumented
3974
+ - `TerraformBrush` (interface): interface TerraformBrush { raise(center: Vec2): number; lower(center: Vec2): number; flatten(center: Vec2, target?: number): number; paint(center: Vec2, surface?: string): number; setRadius(radius: number): void; setStrength(strength: number): void; config(): Requi… — ⚠ undocumented
3975
+ - `TerraformBrushConfig` (interface): interface TerraformBrushConfig { radius?: number; strength?: number; falloff?: TerraformFalloff; surface?: string } — ⚠ undocumented
3976
+ - `TerraformBrushState` (interface): interface TerraformBrushState — Plain JSON state of a {@link TerraformBrush}.
3610
3977
  - `TerraformDelta` (interface): interface TerraformDelta — A compact record of the vertices a sculpt stroke touched: parallel `indices`/`before`/`after` arrays into the offset grid. Storing one of these per stroke keeps undo history small — the whole terrain document is never copied.
3611
3978
  - `TerraformDeltaRecorder` (type): type TerraformDeltaRecorder = (index: number, before: number, after: number) => void — Reports each changed vertex during a recorded edit: grid index, prior offset, new offset.
3612
3979
  - `TerraformEdit` (interface): interface TerraformEdit — A single sculpt stamp: which brush, where, and its shaping parameters.
@@ -3614,7 +3981,7 @@
3614
3981
  - `TerraformHeightLimit` (interface): interface TerraformHeightLimit — Clamp bounds applied to the resulting terrain height after a heightfield edit.
3615
3982
  - `TerraformMode` (type): type TerraformMode = "raise" | "lower" | "smooth" | "flatten" | "noise" | "ramp" | "paint" — A sculpt operation kind: heightfield brushes plus the surface-paint brush.
3616
3983
  - `TerraformShape` (type): type TerraformShape = "circle" | "square" — A brush footprint: a round disc or an axis-aligned square.
3617
- - `TerraformSnapshot` (interface): interface TerraformSnapshot — ⚠ undocumented
3984
+ - `TerraformSnapshot` (interface): interface TerraformSnapshot { bounds: Aabb; cellSize: number; cols: number; rows: number; offsets: readonly number[]; surfaces: readonly (string | null)[]; layers?: readonly TerrainMaterialLayer[]; weights?: readonly number[] } — ⚠ undocumented · used by `applyDeltaToSnapshot`: Returns a new snapshot with a delta's `after` offsets applied (copy-on-write — inputs untouched).
3618
3985
  - `TerraformStroke` (interface): interface TerraformStroke — Accumulates a whole drag — many brush stamps — into one compact {@link TerraformDelta}. Keeps each vertex's first `before` and latest `after`, so undo replays the stroke as a single step even though the pointer fired dozens of moves.
3619
3986
  - `TerrainMaterialLayer` (interface): interface TerrainMaterialLayer — One material layer in a terrain's reorderable stack: a palette `surface` id (drives the base color) plus its render parameters. Array order is the stack order — lower index paints under higher. `roughness`/`tiling`/`triplanar`/`tint`/`opacity` are carried as data so a runtime game reads them straight off the snapshot.
3620
3987
  - `TerrainSurfaceRule` (interface): interface TerrainSurfaceRule — A height/slope predicate for auto-painting a surface layer (e.g. rock on steep slopes, snow up high).
@@ -3636,29 +4003,29 @@
3636
4003
  ## @jgengine/core/world/terrain
3637
4004
 
3638
4005
  - `ARENA_WATER_LEVEL` (const): const ARENA_WATER_LEVEL: -0.9 — ⚠ undocumented
3639
- - `ArenaFieldConfig` (interface): interface ArenaFieldConfig — ⚠ undocumented
4006
+ - `ArenaFieldConfig` (interface): interface ArenaFieldConfig { seed?: string | number; bounds?: WorldBounds } — ⚠ undocumented
3640
4007
  - `BiomeFogValue` (interface): interface BiomeFogValue — A concrete fog look — the shape `createBiomeFogSampler` resolves per z from a band's `fog` over the base fog.
3641
4008
  - `BiomeSkyValue` (interface): interface BiomeSkyValue — A concrete sky look — the shape `createBiomeSkySampler` resolves per z from a band's `sky` over the base sky.
3642
4009
  - `DEFAULT_MAX_WALK_SLOPE` (const): const DEFAULT_MAX_WALK_SLOPE: 0.6 — ⚠ undocumented
3643
4010
  - `DEFAULT_TERRAIN_MATERIAL` (const): const DEFAULT_TERRAIN_MATERIAL: TerrainMaterial — ⚠ undocumented
3644
4011
  - `FLAT_FIELD` (const): const FLAT_FIELD: TerrainField — ⚠ undocumented
3645
4012
  - `FractalNoiseConfig` (interface): interface FractalNoiseConfig — Octave settings for {@link fractalNoise}: frequency, layering, and optional ridged shaping.
3646
- - `GroundSnapEntityStore` (interface): interface GroundSnapEntityStore — ⚠ undocumented
3647
- - `GroundSnapTarget` (interface): interface GroundSnapTarget — ⚠ undocumented
4013
+ - `GroundSnapEntityStore` (interface): interface GroundSnapEntityStore { get(id: string): GroundSnapTarget | null; setPose(id: string, pose: {position?: readonly [number, number, number]}): boolean } — ⚠ undocumented
4014
+ - `GroundSnapTarget` (interface): interface GroundSnapTarget { position: readonly [number, number, number] } — ⚠ undocumented
3648
4015
  - `HeightFieldRayBounds` (interface): interface HeightFieldRayBounds — XZ region a {@link raycastHeightField} march is clipped to.
3649
4016
  - `HeightFieldRayHit` (interface): interface HeightFieldRayHit — Where a {@link raycastHeightField} march crossed the surface.
3650
4017
  - `HeightFieldRaycastOptions` (interface): interface HeightFieldRaycastOptions — Tuning for {@link raycastHeightField}: march resolution and refinement depth.
3651
- - `HeightMapFieldConfig` (interface): interface HeightMapFieldConfig — ⚠ undocumented
4018
+ - `HeightMapFieldConfig` (interface): interface HeightMapFieldConfig { columns: number; rows: number; samples: ArrayLike<number>; bounds?: WorldBounds; heightScale?: number; baseHeight?: number; waterLevel?: number } — ⚠ undocumented
3652
4019
  - `ISLAND_VOID_HEIGHT` (const): const ISLAND_VOID_HEIGHT: -256 — Ground height between islands when no base terrain exists — deep enough to read as a fall into the void, finite so physics stays sane.
3653
4020
  - `NoiseFieldConfig` (interface): interface NoiseFieldConfig — Configuration for {@link noiseField}: seed, amplitude, and fractal noise shaping.
3654
4021
  - `ResolvedTerrainDetail` (type): type ResolvedTerrainDetail = Required<Omit<TerrainDetailConfig, "waterLevel" | "material" | "sweeps">> & { waterLevel: number; material?: ResolvedTerrainDetailMaterial; sweeps: { dry: readonly [number, number, number]; wet: readonly [number, number, number] }; } — A {@link TerrainDetailConfig} with every field resolved to a concrete value — the shape the shell's detail material consumes.
3655
4022
  - `ResolvedTerrainDetailMaterial` (interface): interface ResolvedTerrainDetailMaterial — A resolved {@link TerrainDetailMaterialConfig} — `repeat`/`strength` filled with defaults, `maps` passed through.
3656
- - `RollingFieldConfig` (interface): interface RollingFieldConfig — ⚠ undocumented
4023
+ - `RollingFieldConfig` (interface): interface RollingFieldConfig { seed?: string | number; amplitude?: number; frequency?: number; bounds?: WorldBounds } — ⚠ undocumented
3657
4024
  - `TERRAIN_MATERIAL_PALETTES` (const): const TERRAIN_MATERIAL_PALETTES: Record<TerrainMaterial, TerrainPalette> — ⚠ undocumented
3658
4025
  - `TerrainField` (interface): interface TerrainField — A sampleable ground surface: height and normal at any x/z, with optional bounds and water level.
3659
4026
  - `TerrainNormal` (type): type TerrainNormal = readonly [number, number, number] — A surface normal vector at a terrain sample point.
3660
- - `TerrainPalette` (interface): interface TerrainPalette — ⚠ undocumented
3661
- - `TerrainSlopeSample` (interface): interface TerrainSlopeSample — ⚠ undocumented
4027
+ - `TerrainPalette` (interface): interface TerrainPalette { low: string; high: string; waterline: string } — ⚠ undocumented
4028
+ - `TerrainSlopeSample` (interface): interface TerrainSlopeSample { downhill: readonly [number, number]; steepness: number } — ⚠ undocumented
3662
4029
  - `applyPathProfiles` (function): function applyPathProfiles(field: TerrainField, profiles: readonly TerrainPathProfile[]): TerrainField — Composes authored path profiles onto a field — the shared seam that turns a scene road/river/ramp path into flattened, graded, carved, or retained ground. Applies after `flatten` masks in `resolveTerrainField` so a corridor grades over already-leveled pads; call it directly to layer profiles onto any field a game builds by hand. Returns the field unchanged when no profile is given.
3663
4030
  - `raycastHeightField` (function): function raycastHeightField(sampleHeight: (x: number, z: number) => number, origin: readonly [number, number, number], direction: readonly [number, number, number], options: HeightFieldRaycastOptions): HeightFieldRayHit | null — Intersects a ray with a `sampleHeight` field by fixed-step raymarching plus bisection — the O(steps) editor/gameplay picking seam that replaces brute-force triangle raycasts against a tessellated ground mesh (tens of thousands of triangle tests per pick on a sculpt-sized grid). The ray is clipped to `bounds` on XZ first, marched at `step`, and the first above→below crossing is bisected. An origin already under the surface hits immediately at entry. Returns null when the clipped ray never crosses the surface.
3664
4031
 
@@ -3732,34 +4099,35 @@
3732
4099
 
3733
4100
  - `VOXEL_FACES` (const): const VOXEL_FACES: readonly VoxelFace[] — ⚠ undocumented
3734
4101
  - `VOXEL_FACE_NORMALS` (const): const VOXEL_FACE_NORMALS: Record<VoxelFace, VoxelCoord> — ⚠ undocumented
3735
- - `VoxelBounds` (interface): interface VoxelBounds — ⚠ undocumented
3736
- - `VoxelCell` (interface): interface VoxelCell<T extends string = string> — ⚠ undocumented
4102
+ - `VoxelBounds` (interface): interface VoxelBounds { min: VoxelCoord; max: VoxelCoord } — ⚠ undocumented
4103
+ - `VoxelCell` (interface): interface VoxelCell<T extends string = string> { x: number; y: number; z: number; type: T } — ⚠ undocumented
3737
4104
  - `VoxelCoord` (type): type VoxelCoord = readonly [number, number, number] — ⚠ undocumented
3738
4105
  - `VoxelFace` (type): type VoxelFace = "px" | "nx" | "py" | "ny" | "pz" | "nz" — ⚠ undocumented
3739
- - `VoxelField` (interface): interface VoxelField<T extends string = string> — ⚠ undocumented
3740
- - `VoxelFieldConfig` (interface): interface VoxelFieldConfig — ⚠ undocumented
3741
- - `VoxelFieldSummary` (interface): interface VoxelFieldSummary<T extends string = string> — ⚠ undocumented
3742
- - `VoxelHit` (interface): interface VoxelHit<T extends string = string> — ⚠ undocumented
4106
+ - `VoxelField` (interface): interface VoxelField<T extends string = string> { set(x: number, y: number, z: number, type: T): boolean; remove(x: number, y: number, z: number): boolean; get(x: number, y: number, z: number): T | null; has(x: number, y: number, z: number): boolean; fill(min: VoxelCoord, max: VoxelCoord… — ⚠ undocumented
4107
+ - `VoxelFieldConfig` (interface): interface VoxelFieldConfig { chunkSize?: number } — ⚠ undocumented
4108
+ - `VoxelFieldSummary` (interface): interface VoxelFieldSummary<T extends string = string> { blocks: number; types: Record<T, number>; bounds: VoxelBounds | null } — ⚠ undocumented
4109
+ - `VoxelHit` (interface): interface VoxelHit<T extends string = string> { x: number; y: number; z: number; type: T; face: VoxelFace; adjacent: VoxelCoord; distance: number } — ⚠ undocumented
3743
4110
  - `createVoxelField` (function): function createVoxelField<T extends string = string>(config?: VoxelFieldConfig): VoxelField<T> — ⚠ undocumented
3744
4111
 
3745
4112
  ## @jgengine/core/world/walls
3746
4113
 
3747
- - `EnclosedFootprint` (interface): interface EnclosedFootprint — ⚠ undocumented
4114
+ - `EnclosedFootprint` (interface): interface EnclosedFootprint { polygon: readonly Vec2[]; aabb: Aabb; area: number; perimeter: number; closed: boolean } — ⚠ undocumented
3748
4115
  - `PaintTarget` (type): type PaintTarget = "floor" | "wall" — ⚠ undocumented
3749
- - `RoofConfig` (interface): interface RoofConfig — ⚠ undocumented
3750
- - `RoofPlan` (interface): interface RoofPlan — ⚠ undocumented
4116
+ - `RoofConfig` (interface): interface RoofConfig { style?: RoofStyle; eaveHeight?: number; pitch?: number; overhang?: number } — ⚠ undocumented
4117
+ - `RoofPlan` (interface): interface RoofPlan { style: RoofStyle; ridge: readonly [Vec2, Vec2]; apexHeight: number; eaveHeight: number; faces: readonly (readonly WallVec3[])[] } — ⚠ undocumented
3751
4118
  - `RoofStyle` (type): type RoofStyle = "hip" | "gable" | "flat" — ⚠ undocumented
3752
- - `SurfacePaintStore` (interface): interface SurfacePaintStore — ⚠ undocumented
3753
- - `WallDrawTool` (interface): interface WallDrawTool — ⚠ undocumented
3754
- - `WallSegment` (interface): interface WallSegment — ⚠ undocumented
4119
+ - `SurfacePaintStore` (interface): interface SurfacePaintStore { paint(target: PaintTarget, key: string, surface: string): void; clear(target: PaintTarget, key: string): void; get(target: PaintTarget, key: string): string | null; entries(target: PaintTarget): readonly (readonly [string, string])[]; sna… — ⚠ undocumented
4120
+ - `WallDrawTool` (interface): interface WallDrawTool { addPoint(point: Vec2, snap?: number): Vec2; undo(): void; close(): void; clear(): void; points(): readonly Vec2[]; segments(): WallSegment[]; isClosed(): boolean; footprint(): EnclosedFootprint | null; roof(config?: RoofConfig): RoofPlan … — ⚠ undocumented
4121
+ - `WallDrawToolState` (interface): interface WallDrawToolState — Plain JSON state of a {@link WallDrawTool}: the drawn points and whether the loop is closed.
4122
+ - `WallSegment` (interface): interface WallSegment { from: Vec2; to: Vec2; length: number; angle: number } — ⚠ undocumented · used by `wallSolids` (@jgengine/core/world/worldSolids): One oriented solid per wall segment (see `wallSegments` in `world/walls`).
3755
4123
  - `WallVec3` (type): type WallVec3 = readonly [number, number, number] — ⚠ undocumented
3756
4124
 
3757
4125
  ## @jgengine/core/world/water
3758
4126
 
3759
- - `GerstnerWave` (interface): interface GerstnerWave — ⚠ undocumented
4127
+ - `GerstnerWave` (interface): interface GerstnerWave { direction: readonly [number, number]; wavelength: number; amplitude: number; steepness: number; speed: number } — ⚠ undocumented
3760
4128
  - `WaterNormal` (type): type WaterNormal = readonly [number, number, number] — ⚠ undocumented
3761
- - `WaterSurface` (interface): interface WaterSurface — ⚠ undocumented
3762
- - `WaterSurfaceConfig` (interface): interface WaterSurfaceConfig — ⚠ undocumented
4129
+ - `WaterSurface` (interface): interface WaterSurface { readonly level: number; readonly waves: readonly GerstnerWave[]; height(x: number, z: number, time: number): number; normal(x: number, z: number, time: number): WaterNormal; displace(x: number, z: number, time: number): readonly [number, … — ⚠ undocumented
4130
+ - `WaterSurfaceConfig` (interface): interface WaterSurfaceConfig { level?: number; levelAt?: (time: number) => number; waveHeight?: number; waveScale?: number; waveSpeed?: number; waves?: number; choppiness?: number } — ⚠ undocumented
3763
4131
  - `synthesizeWaves` (function): function synthesizeWaves(config: WaterSurfaceConfig = {}): GerstnerWave[] — ⚠ undocumented
3764
4132
  - `waterSurface` (function): function waterSurface(config: WaterSurfaceConfig = {}): WaterSurface — ⚠ undocumented
3765
4133
  - `waterSurfaceFromDescriptor` (function): function waterSurfaceFromDescriptor(descriptor: OceanEnvironmentDescriptor, waves?: number): WaterSurface — ⚠ undocumented
@@ -3783,36 +4151,58 @@
3783
4151
 
3784
4152
  ## @jgengine/core/world/weather
3785
4153
 
3786
- - `FireCell` (interface): interface FireCell — ⚠ undocumented
4154
+ - `FireCell` (interface): interface FireCell { fuel: number; heat: number; state: FireCellState } — ⚠ undocumented
3787
4155
  - `FireCellState` (type): type FireCellState = "unburnt" | "burning" | "burnt" — ⚠ undocumented
3788
- - `FireGrid` (interface): interface FireGrid — ⚠ undocumented
3789
- - `FireGridConfig` (interface): interface FireGridConfig — ⚠ undocumented
3790
- - `FireStepOptions` (interface): interface FireStepOptions — ⚠ undocumented
3791
- - `ResolvedWeather` (interface): interface ResolvedWeather — ⚠ undocumented
4156
+ - `FireGrid` (interface): interface FireGrid { readonly cols: number; readonly rows: number; readonly cellSize: number; readonly origin: readonly [number, number]; step(dt: number, options?: FireStepOptions): void; igniteCell(col: number, row: number): void; extinguishCell(col: number… — ⚠ undocumented
4157
+ - `FireGridConfig` (interface): interface FireGridConfig { cols: number; rows: number; cellSize: number; origin?: readonly [number, number]; fuelAt?: (col: number, row: number) => number; ignitionThreshold?: number; spreadRate?: number; burnRate?: number; wind?: WindVector; windBias?: number; max… — ⚠ undocumented
4158
+ - `FireGridTuning` (type): type FireGridTuning = Pick<FireGridConfig, "ignitionThreshold" | "spreadRate" | "burnRate" | "wind" | "windBias"> — Live fire ignition, spread, consumption and wind policies; grid geometry stays fixed.
4159
+ - `FireStepOptions` (interface): interface FireStepOptions { spread?: number; wind?: WindVector; windAt?: (x: number, z: number) => WindVector; wetnessAt?: (col: number, row: number) => number; extinguishRate?: number } — ⚠ undocumented
4160
+ - `ResolvedWeather` (interface): interface ResolvedWeather { grip: number; visibility: number; structureDamage: number; chill: number; ignition: number; spread: number } — ⚠ undocumented
3792
4161
  - `WeatherKind` (type): type WeatherKind = string — ⚠ undocumented
3793
4162
  - `WeatherKindOf` (type): type WeatherKindOf<TTable extends WeatherModifierTable> = Extract<keyof TTable, string> — ⚠ undocumented
3794
- - `WeatherModifier` (interface): interface WeatherModifier — ⚠ undocumented
4163
+ - `WeatherModifier` (interface): interface WeatherModifier { grip?: number; visibility?: number; structureDamage?: number; chill?: number; ignition?: number; spread?: number } — ⚠ undocumented
3795
4164
  - `WeatherModifierTable` (type): type WeatherModifierTable<K extends string = string> = Record<K, WeatherModifier> — ⚠ undocumented
3796
- - `WeatherState` (interface): interface WeatherState — ⚠ undocumented
4165
+ - `WeatherState` (interface): interface WeatherState { kind: WeatherKind; intensity: number; wind?: WindVector } — ⚠ undocumented
3797
4166
  - `createFireGrid` (function): function createFireGrid(config: FireGridConfig): FireGrid — ⚠ undocumented
3798
4167
  - `resolveWeather` (function): function resolveWeather<TTable extends WeatherModifierTable>(state: WeatherState, table: TTable): ResolvedWeather — ⚠ undocumented
3799
4168
 
3800
4169
  ## @jgengine/core/world/wind
3801
4170
 
3802
- - `WindField` (interface): interface WindField — ⚠ undocumented
3803
- - `WindFieldConfig` (interface): interface WindFieldConfig — ⚠ undocumented
4171
+ - `WindField` (interface): interface WindField { readonly direction: WindVector; readonly speed: number; at(time: number): WindVector; atPoint(x: number, z: number, time: number): WindVector; strengthAt(x: number, z: number, time: number): number } — ⚠ undocumented
4172
+ - `WindFieldConfig` (interface): interface WindFieldConfig { direction?: WindVector; speed?: number; gust?: number; gustFrequency?: number; turbulence?: number; seed?: string | number } — ⚠ undocumented
3804
4173
  - `WindVector` (type): type WindVector = readonly [number, number] — ⚠ undocumented
3805
4174
  - `windField` (function): function windField(config: WindFieldConfig = {}): WindField — ⚠ undocumented
3806
4175
 
3807
4176
  ## @jgengine/core/world/windZones
3808
4177
 
3809
- - `WindShiftForecast` (interface): interface WindShiftForecast — ⚠ undocumented
3810
- - `WindZoneConfig` (interface): interface WindZoneConfig — ⚠ undocumented
3811
- - `WindZoneState` (interface): interface WindZoneState — ⚠ undocumented
4178
+ - `WindShiftForecast` (interface): interface WindShiftForecast { zoneId: string; at: number; inSeconds: number; next: WindZoneState } — ⚠ undocumented
4179
+ - `WindZoneConfig` (interface): interface WindZoneConfig { id: string; center: readonly [number, number]; radius: number; phases: readonly SchedulePhase<WindZoneState>[]; offsetSeconds?: number } — ⚠ undocumented
4180
+ - `WindZoneState` (interface): interface WindZoneState { direction: WindVector; speed: number; label?: string } — ⚠ undocumented
3812
4181
  - `WindZones` (interface): interface WindZones — Named, discrete wind zones over an ambient field — each zone runs a deterministic `StateSchedule` of wind states, so games get scheduled shifts with advance announcement (`forecastShift` drives the "gale in 12s" countdown) instead of one continuous field.
3813
- - `WindZonesConfig` (interface): interface WindZonesConfig — ⚠ undocumented
4182
+ - `WindZonesConfig` (interface): interface WindZonesConfig { zones: readonly WindZoneConfig[]; ambient?: WindField } — ⚠ undocumented
3814
4183
  - `createWindZones` (function): function createWindZones(config: WindZonesConfig): WindZones — ⚠ undocumented
3815
4184
 
4185
+ ## @jgengine/core/world/worldSolids
4186
+
4187
+ - `WallSolidOptions` (interface): interface WallSolidOptions — Wall height, thickness and base for {@link wallSolids}.
4188
+ - `WorldSolid` (interface): interface WorldSolid — One solid box in world space: the collision half of generated or authored world geometry.
4189
+ - `WorldSolidRayWork` (interface): interface WorldSolidRayWork — Caller-owned work counters, reset by each world-solid ray candidate query.
4190
+ - `WorldSolids` (interface): interface WorldSolids — Static collision for world geometry that is not a scene object: generated buildings, studio volumes like `city`, wall runs. Solids live in named layers so a feature can replace or drop its own set without touching the rest. `solidObstaclesNear` (player and NPC movement), `syncWorldColliders` (physics backends) and `populateNavGridFromSolids` all read this one store.
4191
+ - `WorldSolidsState` (interface): interface WorldSolidsState — Serializable contents of a {@link WorldSolids}: every layer's solids by layer id.
4192
+ - `buildingSolids` (function): function buildingSolids(buildings: readonly GeneratedBuilding[], groundHeight: (x: number, z: number) => number = () => 0): WorldSolid[] — One solid per generated building: its `bounds` footprint turned by `rotationY` about `center`, from the ground under its center up through its floors. `groundHeight` defaults to flat `0`.
4193
+ - `createWorldSolids` (function): function createWorldSolids(options: { cellSize?: number } = {}): WorldSolids — A store of static world solids in named layers.
4194
+ - `structureSolids` (function): function structureSolids(world: EnvironmentWorldFeature, groundHeight?: (x: number, z: number) => number): WorldSolid[] — Solids for every `structures` descriptor on an environment feature, skipping any marked `solid: false`.
4195
+ - `wallSolids` (function): function wallSolids(segments: readonly WallSegment[], options: WallSolidOptions): WorldSolid[] — One oriented solid per wall segment (see `wallSegments` in `world/walls`).
4196
+ - `worldSolidBounds` (function): function worldSolidBounds(solid: WorldSolid): { min: [number, number, number]; max: [number, number, number]; } — Yaw-expanded world AABB of an oriented solid box.
4197
+ - `worldSolidFootprint` (function): function worldSolidFootprint(solid: WorldSolid): Aabb — XZ footprint of a solid's world AABB, for 2D consumers such as nav grids.
4198
+
4199
+ ## @jgengine/navbake
4200
+
4201
+ - `BakeNavMeshOptions` (interface): interface BakeNavMeshOptions — Walkable geometry and agent dimensions for {@link bakeNavMesh}.
4202
+ - `bakeNavMesh` (function): function bakeNavMesh(options: BakeNavMeshOptions): NavMeshData — Voxelize indexed triangles with recast and return the walkable surface as convex polygons in the engine's serializable nav-mesh format: eroded by `agentRadius`, cut where clearance is under `agentHeight`, and split at slopes over `maxSlope` or steps over `maxClimb`. Requires {@link initNavBake}.
4203
+ - `initNavBake` (function): function initNavBake(): Promise<void> — Load the recast WebAssembly module once; await before the first {@link bakeNavMesh}.
4204
+ - `navBakeReady` (function): function navBakeReady(): boolean — Whether {@link initNavBake} has finished and {@link bakeNavMesh} can run synchronously.
4205
+
3816
4206
  ## @jgengine/rapier
3817
4207
 
3818
4208
  - `RapierBackendOptions` (interface): interface RapierBackendOptions extends PhysicsBackendConfig — Construction options for the Rapier-backed {@link PhysicsBackend}.