@jgengine/core 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 (404) hide show
  1. package/CHANGELOG.md +504 -7
  2. package/dist/ai/decisionGraph.d.ts +50 -4
  3. package/dist/ai/decisionGraph.js +159 -23
  4. package/dist/ai/flock.d.ts +1 -1
  5. package/dist/ai/flock.js +7 -4
  6. package/dist/ai/groupAssist.d.ts +10 -0
  7. package/dist/ai/groupAssist.js +11 -0
  8. package/dist/ai/pursuit.d.ts +1 -1
  9. package/dist/ai/pursuit.js +1 -1
  10. package/dist/ai/threat.d.ts +8 -0
  11. package/dist/ai/threat.js +10 -0
  12. package/dist/anim/animGraph.d.ts +43 -1
  13. package/dist/anim/animGraph.js +326 -10
  14. package/dist/anim/boneTexture.d.ts +55 -0
  15. package/dist/anim/boneTexture.js +43 -0
  16. package/dist/anim/footPlacement.d.ts +57 -0
  17. package/dist/anim/footPlacement.js +67 -0
  18. package/dist/anim/locomotionGraph.d.ts +18 -1
  19. package/dist/anim/locomotionGraph.js +42 -3
  20. package/dist/audio/audioFalloff.d.ts +6 -0
  21. package/dist/audio/doppler.d.ts +21 -0
  22. package/dist/audio/doppler.js +32 -0
  23. package/dist/audio/engineLayers.d.ts +83 -0
  24. package/dist/audio/engineLayers.js +115 -0
  25. package/dist/audio/voiceAllocator.d.ts +46 -0
  26. package/dist/audio/voiceAllocator.js +76 -0
  27. package/dist/combat/abilityKit.d.ts +20 -0
  28. package/dist/combat/abilityKit.js +41 -4
  29. package/dist/combat/death.d.ts +11 -3
  30. package/dist/combat/death.js +3 -3
  31. package/dist/combat/magazine.d.ts +9 -0
  32. package/dist/combat/magazine.js +24 -2
  33. package/dist/combat/projectiles.d.ts +94 -2
  34. package/dist/combat/projectiles.js +282 -61
  35. package/dist/combat/regenShield.d.ts +14 -0
  36. package/dist/combat/regenShield.js +23 -7
  37. package/dist/combat/shotOrigin.d.ts +6 -0
  38. package/dist/combat/shotOrigin.js +6 -0
  39. package/dist/combat/weaponFeedback.d.ts +36 -0
  40. package/dist/combat/weaponFeedback.js +42 -0
  41. package/dist/combat/weaponHandling.d.ts +161 -0
  42. package/dist/combat/weaponHandling.js +202 -0
  43. package/dist/combat/weaponPresentation.d.ts +115 -0
  44. package/dist/combat/weaponPresentation.js +96 -0
  45. package/dist/crafting/recipeCatalog.d.ts +16 -0
  46. package/dist/crafting/recipeCatalog.js +55 -0
  47. package/dist/devtools/warnOnce.d.ts +8 -0
  48. package/dist/devtools/warnOnce.js +18 -0
  49. package/dist/economy/auctionBook.d.ts +1 -1
  50. package/dist/economy/auctionBook.js +11 -1
  51. package/dist/economy/listingBook.d.ts +1 -1
  52. package/dist/economy/listingBook.js +11 -1
  53. package/dist/economy/resourceLedger.js +3 -3
  54. package/dist/economy/shopStock.js +7 -6
  55. package/dist/economy/techTree.js +7 -6
  56. package/dist/economy/unlockPoints.js +3 -6
  57. package/dist/editor/commandApply.js +89 -16
  58. package/dist/editor/commands.d.ts +38 -1
  59. package/dist/editor/commands.js +320 -3
  60. package/dist/editor/creatorStorage.d.ts +59 -0
  61. package/dist/editor/creatorStorage.js +164 -0
  62. package/dist/editor/document.d.ts +4 -0
  63. package/dist/editor/document.js +119 -18
  64. package/dist/editor/environment.js +8 -0
  65. package/dist/editor/index.d.ts +2 -0
  66. package/dist/editor/liveSync.d.ts +4 -1
  67. package/dist/editor/liveSync.js +26 -10
  68. package/dist/editor/materialAuthoring.d.ts +14 -0
  69. package/dist/editor/materialAuthoring.js +38 -0
  70. package/dist/editor/modelNodeVisibility.d.ts +11 -0
  71. package/dist/editor/modelNodeVisibility.js +29 -0
  72. package/dist/editor/movementCatalog.d.ts +90 -0
  73. package/dist/editor/movementCatalog.js +144 -0
  74. package/dist/editor/simulation.d.ts +60 -0
  75. package/dist/editor/simulation.js +202 -0
  76. package/dist/editor/staticPrefab.d.ts +17 -0
  77. package/dist/editor/staticPrefab.js +51 -0
  78. package/dist/editor/types.d.ts +15 -0
  79. package/dist/faction/reputation.d.ts +6 -0
  80. package/dist/faction/reputation.js +11 -0
  81. package/dist/game/cameraConfig.d.ts +54 -2
  82. package/dist/game/clipRoles.d.ts +2 -1
  83. package/dist/game/clipRoles.js +7 -3
  84. package/dist/game/contentValidation.d.ts +69 -0
  85. package/dist/game/contentValidation.js +118 -0
  86. package/dist/game/controlGate.d.ts +8 -0
  87. package/dist/game/controlGate.js +43 -6
  88. package/dist/game/defineGame.d.ts +32 -3
  89. package/dist/game/dialogueGraph.d.ts +20 -0
  90. package/dist/game/dialogueGraph.js +107 -0
  91. package/dist/game/events.d.ts +11 -0
  92. package/dist/game/keyValueStore.d.ts +17 -4
  93. package/dist/game/keyValueStore.js +35 -23
  94. package/dist/game/lootModifiers.d.ts +17 -0
  95. package/dist/game/lootModifiers.js +26 -0
  96. package/dist/game/lootTable.d.ts +4 -0
  97. package/dist/game/lootTable.js +16 -0
  98. package/dist/game/narrativeAuthoring.d.ts +67 -0
  99. package/dist/game/narrativeAuthoring.js +122 -0
  100. package/dist/game/playableGame.d.ts +134 -35
  101. package/dist/game/quest.d.ts +15 -0
  102. package/dist/game/quest.js +209 -63
  103. package/dist/game/questCatalog.d.ts +50 -0
  104. package/dist/game/questCatalog.js +205 -0
  105. package/dist/game/saveStore.d.ts +6 -5
  106. package/dist/game/saveStore.js +57 -57
  107. package/dist/game/systemRuntime.d.ts +2 -2
  108. package/dist/game/systemRuntime.js +84 -33
  109. package/dist/game/toasts.d.ts +8 -0
  110. package/dist/game/toasts.js +7 -0
  111. package/dist/game/vfxInstance.d.ts +9 -0
  112. package/dist/game/vfxInstance.js +12 -1
  113. package/dist/game/viewports.d.ts +43 -0
  114. package/dist/game/viewports.js +46 -0
  115. package/dist/game/worldItem.d.ts +5 -1
  116. package/dist/game/worldItem.js +0 -1
  117. package/dist/gameplay.d.ts +3 -0
  118. package/dist/gameplay.js +3 -0
  119. package/dist/input/actionBindings.d.ts +15 -1
  120. package/dist/input/actionBindings.js +14 -3
  121. package/dist/input/actionContexts.d.ts +25 -1
  122. package/dist/input/actionContexts.js +82 -25
  123. package/dist/input/axisInput.d.ts +4 -0
  124. package/dist/input/axisInput.js +4 -0
  125. package/dist/input/axisShaper.d.ts +73 -0
  126. package/dist/input/axisShaper.js +105 -0
  127. package/dist/input/gamepadModel.d.ts +36 -2
  128. package/dist/input/gamepadModel.js +69 -30
  129. package/dist/input/haptics.d.ts +50 -0
  130. package/dist/input/haptics.js +74 -0
  131. package/dist/input/inputBuffer.d.ts +40 -0
  132. package/dist/input/inputBuffer.js +59 -0
  133. package/dist/input/lookChannel.d.ts +10 -0
  134. package/dist/input/lookChannel.js +6 -0
  135. package/dist/input/pointer.d.ts +2 -0
  136. package/dist/input/pointer.js +4 -0
  137. package/dist/interaction/promptRegistry.d.ts +5 -4
  138. package/dist/interaction/promptRegistry.js +8 -2
  139. package/dist/inventory/inventoryModel.js +9 -6
  140. package/dist/inventory/shapedGrid.d.ts +48 -9
  141. package/dist/inventory/shapedGrid.js +69 -10
  142. package/dist/item/affix.d.ts +6 -0
  143. package/dist/item/affix.js +6 -0
  144. package/dist/item/durability.js +4 -0
  145. package/dist/item/itemInstanceRegistry.d.ts +32 -1
  146. package/dist/item/itemInstanceRegistry.js +111 -7
  147. package/dist/item/itemgen.d.ts +1 -1
  148. package/dist/item/itemgen.js +1 -1
  149. package/dist/item/modularItem.js +7 -1
  150. package/dist/material/appearanceSignals.d.ts +53 -0
  151. package/dist/material/appearanceSignals.js +47 -0
  152. package/dist/material/hairCards.d.ts +47 -0
  153. package/dist/material/hairCards.js +136 -0
  154. package/dist/material/materialAsset.d.ts +162 -0
  155. package/dist/material/materialAsset.js +382 -0
  156. package/dist/meta/changelog.d.ts +1 -1
  157. package/dist/meta/changelog.js +444 -1
  158. package/dist/movement/characterController.d.ts +5 -2
  159. package/dist/movement/characterController.js +50 -23
  160. package/dist/movement/freeFlight.d.ts +2 -2
  161. package/dist/movement/freeFlight.js +3 -3
  162. package/dist/movement/glideModel.d.ts +3 -0
  163. package/dist/movement/glideModel.js +6 -0
  164. package/dist/movement/grappleSwing.d.ts +2 -0
  165. package/dist/movement/grappleSwing.js +5 -1
  166. package/dist/movement/movementModel.d.ts +63 -1
  167. package/dist/movement/movementModel.js +136 -26
  168. package/dist/movement/movementProbe.d.ts +51 -0
  169. package/dist/movement/movementProbe.js +119 -0
  170. package/dist/movement/playerMovement.d.ts +48 -2
  171. package/dist/movement/playerMovement.js +493 -87
  172. package/dist/movement/poseState.d.ts +2 -0
  173. package/dist/movement/poseState.js +29 -1
  174. package/dist/movement/solidObstacles.d.ts +11 -1
  175. package/dist/movement/solidObstacles.js +48 -3
  176. package/dist/movement/terrainGrade.d.ts +13 -0
  177. package/dist/movement/terrainGrade.js +33 -0
  178. package/dist/movement/voxelController.d.ts +2 -2
  179. package/dist/movement/voxelController.js +10 -10
  180. package/dist/nav/navFromEnvironment.d.ts +9 -3
  181. package/dist/nav/navFromEnvironment.js +18 -12
  182. package/dist/nav/navGrid.d.ts +19 -1
  183. package/dist/nav/navGrid.js +26 -10
  184. package/dist/nav/navMesh.d.ts +43 -2
  185. package/dist/nav/navMesh.js +534 -128
  186. package/dist/nav/solidRoute.d.ts +50 -0
  187. package/dist/nav/solidRoute.js +144 -0
  188. package/dist/physics/aircraftDynamics.d.ts +390 -0
  189. package/dist/physics/aircraftDynamics.js +704 -0
  190. package/dist/physics/ballisticSweep.d.ts +10 -3
  191. package/dist/physics/ballisticSweep.js +53 -29
  192. package/dist/physics/boatDynamics.d.ts +116 -0
  193. package/dist/physics/boatDynamics.js +160 -0
  194. package/dist/physics/drivableVehicle.d.ts +22 -8
  195. package/dist/physics/drivableVehicle.js +4 -2
  196. package/dist/physics/flightDynamics.d.ts +21 -4
  197. package/dist/physics/flightDynamics.js +28 -5
  198. package/dist/physics/forceVolume.d.ts +41 -0
  199. package/dist/physics/forceVolume.js +88 -0
  200. package/dist/physics/handlingProbe.d.ts +249 -0
  201. package/dist/physics/handlingProbe.js +606 -0
  202. package/dist/physics/kinematicVehicle.d.ts +40 -5
  203. package/dist/physics/kinematicVehicle.js +17 -2
  204. package/dist/physics/physicsBackend.d.ts +12 -0
  205. package/dist/physics/physicsBackendConformance.js +34 -0
  206. package/dist/physics/physicsWorldBackend.d.ts +1 -0
  207. package/dist/physics/physicsWorldBackend.js +52 -3
  208. package/dist/physics/spatialGrid.d.ts +1 -1
  209. package/dist/physics/spatialGrid.js +8 -9
  210. package/dist/physics/vehicleBackendLink.d.ts +49 -0
  211. package/dist/physics/vehicleBackendLink.js +102 -0
  212. package/dist/physics/vehicleDynamics.d.ts +375 -0
  213. package/dist/physics/vehicleDynamics.js +737 -0
  214. package/dist/physics/vehicleObstacles.d.ts +3 -2
  215. package/dist/physics/vehicleObstacles.js +29 -5
  216. package/dist/physics/worldColliders.d.ts +1 -1
  217. package/dist/physics/worldColliders.js +35 -3
  218. package/dist/puzzle/cellGrid.d.ts +15 -9
  219. package/dist/puzzle/cellGrid.js +13 -9
  220. package/dist/render/lookPreset.d.ts +9 -2
  221. package/dist/render/lookPreset.js +29 -2
  222. package/dist/render/postProcessing.d.ts +30 -0
  223. package/dist/render/postProcessing.js +1 -1
  224. package/dist/runtime/cameraDirector.d.ts +54 -3
  225. package/dist/runtime/cameraDirector.js +187 -0
  226. package/dist/runtime/commandDispatch.d.ts +14 -0
  227. package/dist/runtime/commandDispatch.js +19 -0
  228. package/dist/runtime/commandInput.d.ts +37 -0
  229. package/dist/runtime/commandInput.js +58 -0
  230. package/dist/runtime/commandRunner.d.ts +27 -0
  231. package/dist/runtime/commandRunner.js +31 -3
  232. package/dist/runtime/context/combat.d.ts +12 -4
  233. package/dist/runtime/context/combat.js +179 -11
  234. package/dist/runtime/context/deathLoot.d.ts +11 -8
  235. package/dist/runtime/context/deathLoot.js +17 -16
  236. package/dist/runtime/context/scene.d.ts +2 -0
  237. package/dist/runtime/context/scene.js +13 -21
  238. package/dist/runtime/descriptors/baseline.js +34 -3
  239. package/dist/runtime/descriptors/features.d.ts +2 -1
  240. package/dist/runtime/descriptors/features.js +18 -0
  241. package/dist/runtime/gameContext.js +86 -13
  242. package/dist/runtime/gameContextTypes.d.ts +33 -3
  243. package/dist/runtime/gameRuntime.d.ts +3 -1
  244. package/dist/runtime/gameRuntime.js +4 -0
  245. package/dist/runtime/headlessRunner.d.ts +3 -1
  246. package/dist/runtime/headlessRunner.js +11 -11
  247. package/dist/runtime/hostedGameRunner.d.ts +24 -1
  248. package/dist/runtime/hostedGameRunner.js +156 -10
  249. package/dist/runtime/hostedWorldSession.d.ts +17 -5
  250. package/dist/runtime/hostedWorldSession.js +154 -16
  251. package/dist/runtime/hostedWorldStore.d.ts +2 -2
  252. package/dist/runtime/inputRecorder.d.ts +1 -1
  253. package/dist/runtime/inputRecorder.js +21 -1
  254. package/dist/runtime/inputSnapshot.d.ts +22 -4
  255. package/dist/runtime/inputSnapshot.js +41 -3
  256. package/dist/runtime/localPlayers.d.ts +73 -0
  257. package/dist/runtime/localPlayers.js +119 -0
  258. package/dist/runtime/runtimeSave.d.ts +10 -6
  259. package/dist/runtime/runtimeSave.js +11 -5
  260. package/dist/runtime/simContext.d.ts +8 -3
  261. package/dist/runtime/simContext.js +7 -3
  262. package/dist/runtime/simSnapshot.js +2 -0
  263. package/dist/runtime/transport.d.ts +9 -0
  264. package/dist/runtime/transport.js +7 -1
  265. package/dist/runtime/worldReplication.d.ts +2 -0
  266. package/dist/runtime/worldReplication.js +1 -1
  267. package/dist/scene/assetCatalog.d.ts +2 -0
  268. package/dist/scene/behaviorRuntime.d.ts +38 -7
  269. package/dist/scene/behaviorRuntime.js +293 -30
  270. package/dist/scene/behaviors.d.ts +35 -2
  271. package/dist/scene/behaviors.js +29 -0
  272. package/dist/scene/entityStore.d.ts +14 -3
  273. package/dist/scene/entityStore.js +45 -9
  274. package/dist/scene/mount.d.ts +9 -0
  275. package/dist/scene/mount.js +35 -0
  276. package/dist/scene/possession.d.ts +2 -0
  277. package/dist/scene/possession.js +21 -1
  278. package/dist/scene/sceneKinds.d.ts +9 -0
  279. package/dist/scene/sceneRaycast.d.ts +7 -0
  280. package/dist/scene/sceneRaycast.js +55 -0
  281. package/dist/scene/spatial.d.ts +20 -1
  282. package/dist/scene/spatial.js +94 -2
  283. package/dist/scene/vehicleSeat.d.ts +41 -3
  284. package/dist/scene/vehicleSeat.js +94 -3
  285. package/dist/sensor/concealment.d.ts +6 -0
  286. package/dist/sensor/concealment.js +8 -0
  287. package/dist/sensor/freezeMonitor.d.ts +6 -0
  288. package/dist/sensor/freezeMonitor.js +8 -0
  289. package/dist/sensor/perception.d.ts +11 -1
  290. package/dist/sensor/perception.js +57 -10
  291. package/dist/sensor/recordingBuffer.d.ts +3 -0
  292. package/dist/sensor/recordingBuffer.js +8 -0
  293. package/dist/session/roundState.d.ts +4 -0
  294. package/dist/session/roundState.js +11 -0
  295. package/dist/settings/frameRateLimit.d.ts +16 -0
  296. package/dist/settings/frameRateLimit.js +37 -0
  297. package/dist/settings/graphicsProfile.d.ts +2 -0
  298. package/dist/settings/graphicsProfile.js +12 -0
  299. package/dist/settings/graphicsSettings.d.ts +40 -0
  300. package/dist/settings/graphicsSettings.js +81 -0
  301. package/dist/settings/settingsModel.d.ts +9 -1
  302. package/dist/settings/settingsModel.js +10 -0
  303. package/dist/stats/accumulatorMeter.d.ts +10 -0
  304. package/dist/stats/accumulatorMeter.js +6 -0
  305. package/dist/stats/eventMeter.d.ts +10 -3
  306. package/dist/stats/eventMeter.js +6 -3
  307. package/dist/store/defineStore.d.ts +3 -1
  308. package/dist/store/defineStore.js +3 -1
  309. package/dist/store/observableKeyedStore.d.ts +1 -1
  310. package/dist/store/observableKeyedStore.js +8 -6
  311. package/dist/time/simClock.d.ts +6 -2
  312. package/dist/time/simClock.js +5 -0
  313. package/dist/vfx/feedbackMixer.d.ts +61 -0
  314. package/dist/vfx/feedbackMixer.js +106 -0
  315. package/dist/vfx/particleDirector.d.ts +58 -6
  316. package/dist/vfx/particleDirector.js +113 -22
  317. package/dist/vfx/particles.d.ts +68 -5
  318. package/dist/vfx/particles.js +284 -32
  319. package/dist/visibility/assetStreaming.d.ts +21 -3
  320. package/dist/visibility/assetStreaming.js +108 -24
  321. package/dist/visibility/settings.d.ts +8 -2
  322. package/dist/visibility/settings.js +3 -0
  323. package/dist/visibility/simulationCulling.d.ts +8 -1
  324. package/dist/visibility/simulationCulling.js +7 -0
  325. package/dist/visibility/spatialIndex.d.ts +21 -0
  326. package/dist/visibility/spatialIndex.js +16 -0
  327. package/dist/visibility/visibilitySystem.js +1 -0
  328. package/dist/work/expedition.d.ts +110 -0
  329. package/dist/work/expedition.js +195 -0
  330. package/dist/work/staffedStation.d.ts +29 -0
  331. package/dist/work/staffedStation.js +27 -0
  332. package/dist/world/authoredAnimation.d.ts +38 -0
  333. package/dist/world/authoredAnimation.js +99 -0
  334. package/dist/world/authoredObjects.d.ts +24 -4
  335. package/dist/world/authoredObjects.js +43 -16
  336. package/dist/world/authoredSimulation.d.ts +74 -0
  337. package/dist/world/authoredSimulation.js +302 -0
  338. package/dist/world/authoredSolids.d.ts +46 -0
  339. package/dist/world/authoredSolids.js +75 -0
  340. package/dist/world/authoredWeather.d.ts +120 -0
  341. package/dist/world/authoredWeather.js +314 -0
  342. package/dist/world/buildingGenerator.js +2 -2
  343. package/dist/world/buildingKit.d.ts +11 -4
  344. package/dist/world/buildingKit.js +21 -8
  345. package/dist/world/buildings.d.ts +34 -2
  346. package/dist/world/buildings.js +11 -1
  347. package/dist/world/cityKind.d.ts +7 -0
  348. package/dist/world/cityKind.js +52 -1
  349. package/dist/world/environmentForces.d.ts +21 -0
  350. package/dist/world/environmentForces.js +42 -0
  351. package/dist/world/features.d.ts +32 -5
  352. package/dist/world/features.js +6 -0
  353. package/dist/world/footprintGrid.d.ts +62 -0
  354. package/dist/world/footprintGrid.js +217 -0
  355. package/dist/world/interiors.d.ts +1 -0
  356. package/dist/world/interiors.js +5 -3
  357. package/dist/world/lod.d.ts +6 -0
  358. package/dist/world/lod.js +8 -0
  359. package/dist/world/pathTerrain.d.ts +1 -1
  360. package/dist/world/terraform.d.ts +9 -1
  361. package/dist/world/terraform.js +53 -18
  362. package/dist/world/walls.d.ts +7 -0
  363. package/dist/world/walls.js +7 -0
  364. package/dist/world/weather.d.ts +17 -0
  365. package/dist/world/weather.js +116 -20
  366. package/dist/world/worldSolids.d.ts +95 -0
  367. package/dist/world/worldSolids.js +351 -0
  368. package/dist/world.d.ts +6 -6
  369. package/dist/world.js +5 -5
  370. package/package.json +1 -1
  371. package/skills/ce-handoff/SKILL.md +2 -2
  372. package/skills/game-design/SKILL.md +2 -2
  373. package/skills/jgengine/SKILL.md +28 -6
  374. package/skills/jgengine/api.md +139 -99
  375. package/skills/jgengine/capabilities.md +28 -0
  376. package/skills/jgengine/recipes/minimal-game.md +10 -2
  377. package/skills/jgengine-combat/SKILL.md +13 -2
  378. package/skills/jgengine-combat/api.md +150 -103
  379. package/skills/jgengine-combat/capabilities.md +29 -1
  380. package/skills/jgengine-combat/recipes/portable-runtime-state.md +60 -6
  381. package/skills/jgengine-combat/recipes/portable-weapon-plumbing.md +37 -3
  382. package/skills/jgengine-combat/recipes/weapon-feel.md +50 -0
  383. package/skills/jgengine-combat/reference.md +24 -2
  384. package/skills/jgengine-gameplay/SKILL.md +63 -0
  385. package/skills/jgengine-gameplay/api.md +583 -463
  386. package/skills/jgengine-gameplay/capabilities.md +92 -0
  387. package/skills/jgengine-gameplay/recipes/device-save-status.md +46 -0
  388. package/skills/jgengine-gameplay/recipes/expeditions.md +108 -0
  389. package/skills/jgengine-gameplay/recipes/narrative-authoring.md +76 -0
  390. package/skills/jgengine-gameplay/recipes/paged-item-storage.md +101 -0
  391. package/skills/jgengine-gameplay/recipes/quest-authoring.md +63 -0
  392. package/skills/jgengine-gameplay/recipes/shaped-inventory.md +50 -0
  393. package/skills/jgengine-gameplay/reference-content-validation.md +74 -0
  394. package/skills/jgengine-gameplay/reference-systems.md +123 -0
  395. package/skills/jgengine-world/SKILL.md +25 -4
  396. package/skills/jgengine-world/api.md +1045 -655
  397. package/skills/jgengine-world/capabilities.md +246 -9
  398. package/skills/jgengine-world/recipes/character-animation.md +62 -0
  399. package/skills/jgengine-world/recipes/controls.md +48 -0
  400. package/skills/jgengine-world/recipes/flight-feel.md +76 -0
  401. package/skills/jgengine-world/recipes/npc-senses.md +55 -0
  402. package/skills/jgengine-world/recipes/vehicle-feel.md +87 -0
  403. package/skills/jgengine-world/reference.md +180 -4
  404. package/skills/jgengine-world/visibility.md +139 -0
@@ -0,0 +1,144 @@
1
+ const FIELDS = [
2
+ { key: "walkSpeed", label: "Walk speed", group: "movement", min: 0, step: 0.1 },
3
+ { key: "gravity", label: "Gravity", group: "jump", step: 0.5 },
4
+ { key: "jumpVelocity", label: "Jump velocity", group: "jump", min: 0, step: 0.1 },
5
+ { key: "stepHeight", label: "Step height", group: "movement", min: 0, step: 0.05 },
6
+ { key: "collisionHeight", label: "Collision height", group: "movement", min: Number.MIN_VALUE, step: 0.1 },
7
+ { key: "maxClimbGrade", label: "Max climb grade", group: "movement", min: 0, step: 0.05 },
8
+ { key: "groundAcceleration", label: "Ground acceleration", group: "response", min: 0, step: 1 },
9
+ { key: "airAcceleration", label: "Air control", group: "response", min: 0, step: 1 },
10
+ { key: "groundFriction", label: "Braking", group: "response", min: 0, step: 1 },
11
+ { key: "runMultiplier", label: "Sprint multiplier", group: "movement", min: 0, step: 0.1 },
12
+ { key: "crouchMultiplier", label: "Crouch multiplier", group: "movement", min: 0, step: 0.05 },
13
+ { key: "jumpBufferMs", label: "Jump buffer (ms)", group: "jump-response", min: 0, step: 10 },
14
+ { key: "coyoteMs", label: "Coyote time (ms)", group: "jump-response", min: 0, step: 10 },
15
+ { key: "jumpCutFactor", label: "Jump release factor", group: "jump-response", min: 0, max: 1, step: 0.05 },
16
+ { key: "apexGravityScale", label: "Apex gravity multiplier", group: "jump-response", min: 0, step: 0.05 },
17
+ { key: "apexSpeed", label: "Apex speed threshold (m/s)", group: "jump-response", min: 0, step: 0.1 },
18
+ { key: "fallGravityScale", label: "Fall gravity multiplier", group: "jump-response", min: 0, step: 0.05 },
19
+ { key: "landingRecoveryMs", label: "Landing recovery (ms)", group: "jump-response", min: 0, step: 10 },
20
+ { key: "landingSpeedScale", label: "Landing speed multiplier", group: "jump-response", min: 0, step: 0.05 },
21
+ ];
22
+ function validValue(field, value) {
23
+ return typeof value === "number" && Number.isFinite(value) && (!("min" in field) || value >= field.min) && (!("max" in field) || value <= field.max);
24
+ }
25
+ function numberRequirement(field) {
26
+ if (field.key === "jumpCutFactor")
27
+ return "finite number between 0 and 1";
28
+ return `finite${field.key === "collisionHeight" ? " positive" : "min" in field ? " nonnegative" : ""} number`;
29
+ }
30
+ /**
31
+ * Creates editor/RPC controls only for the values a game supplies, with those game's defaults.
32
+ * Gravity retains the game's sign convention; collision height is positive, other controls nonnegative.
33
+ * Jump release is bounded to 0..1; supplied deeper jump controls share a collapsed response group.
34
+ * @capability editor-movement author game-chosen character movement controls through ordinary catalog rows
35
+ */
36
+ export function createMovementSchema(defaults) {
37
+ const fields = [];
38
+ for (const field of FIELDS) {
39
+ const value = defaults[field.key];
40
+ if (value === undefined)
41
+ continue;
42
+ if (!validValue(field, value))
43
+ throw new Error(`Invalid movement default ${field.key}: use a ${numberRequirement(field)}`);
44
+ fields.push({ ...field, type: "number", default: value });
45
+ }
46
+ return {
47
+ fields,
48
+ groups: [
49
+ { id: "movement", label: "Movement" },
50
+ { id: "jump", label: "Jump" },
51
+ { id: "response", label: "Response", collapsed: true },
52
+ { id: "jump-response", label: "Jump response", collapsed: true },
53
+ ].filter((group) => fields.some((field) => field.group === group.id)),
54
+ };
55
+ }
56
+ /**
57
+ * Reads one game-named row as partial runtime tuning, retaining game defaults for omitted or invalid
58
+ * values. Rejected values include their document path and repair; no policy callbacks are replaced.
59
+ * @capability editor-movement resolve saved movement controls without replacing a game's character rules
60
+ */
61
+ export function readAuthoredMovement(document, catalogId, entryId) {
62
+ const catalogIndex = document.catalogs.findIndex((catalog) => catalog.id === catalogId);
63
+ const catalog = document.catalogs[catalogIndex];
64
+ const entryIndex = catalog?.entries.findIndex((entry) => entry.id === entryId) ?? -1;
65
+ const meta = catalog?.entries[entryIndex]?.meta;
66
+ const config = {};
67
+ const diagnostics = [];
68
+ if (meta === undefined)
69
+ return { config, diagnostics };
70
+ for (const field of FIELDS) {
71
+ const value = meta[field.key];
72
+ if (value === undefined)
73
+ continue;
74
+ if (!validValue(field, value)) {
75
+ diagnostics.push({
76
+ path: `catalogs[${catalogIndex}].entries[${entryIndex}].meta.${field.key}`,
77
+ message: `${field.key} must be a ${numberRequirement(field)}`,
78
+ repair: `Set ${field.key} to a valid number or remove it to inherit the game setting.`,
79
+ });
80
+ continue;
81
+ }
82
+ if (field.key === "walkSpeed")
83
+ config.walkSpeed = value;
84
+ else if (field.key === "gravity" || field.key === "jumpVelocity")
85
+ (config.physics ??= {})[field.key] = value;
86
+ else if (field.key === "stepHeight" || field.key === "collisionHeight" || field.key === "maxClimbGrade")
87
+ (config.movement ??= {})[field.key] = value;
88
+ else
89
+ ((config.movement ??= {}).feel ??= {})[field.key] = value;
90
+ }
91
+ return { config, diagnostics };
92
+ }
93
+ /**
94
+ * Memoizes movement resolution by immutable document identity; reads the latest document on every
95
+ * call while catalog lookup and diagnostics run only after authoring changes.
96
+ * @capability editor-movement consume live saved movement tuning with bounded per-frame work
97
+ */
98
+ export function createAuthoredMovementReader(getDocument, catalogId, entryId) {
99
+ let previous;
100
+ let result;
101
+ return () => {
102
+ const document = getDocument();
103
+ if (document !== previous) {
104
+ result = readAuthoredMovement(document, catalogId, entryId);
105
+ previous = document;
106
+ }
107
+ return result;
108
+ };
109
+ }
110
+ /**
111
+ * Binds live authored numeric controls onto stable runtime objects, preserving the game's callbacks,
112
+ * collision, backend and other policy. The movement controller reads these getters each step.
113
+ * @capability editor-movement bind live character tuning while preserving distinctive game policy
114
+ */
115
+ export function bindAuthoredMovement(read, defaults) {
116
+ let previous;
117
+ let previousBase;
118
+ let feel;
119
+ return {
120
+ movement: {
121
+ ...defaults.movement,
122
+ get stepHeight() { return read().config.movement?.stepHeight ?? defaults.movement?.stepHeight; },
123
+ get collisionHeight() { return read().config.movement?.collisionHeight ?? defaults.movement?.collisionHeight; },
124
+ get maxClimbGrade() { return read().config.movement?.maxClimbGrade ?? defaults.movement?.maxClimbGrade; },
125
+ get feel() {
126
+ const current = read();
127
+ const base = defaults.movement?.feel;
128
+ if (current !== previous || base !== previousBase) {
129
+ const authored = current.config.movement?.feel;
130
+ feel = authored === undefined ? base : { ...base, ...authored };
131
+ previous = current;
132
+ previousBase = base;
133
+ }
134
+ return feel;
135
+ },
136
+ },
137
+ physics: {
138
+ ...defaults.physics,
139
+ get gravity() { return read().config.physics?.gravity ?? defaults.physics?.gravity; },
140
+ get jumpVelocity() { return read().config.physics?.jumpVelocity ?? defaults.physics?.jumpVelocity; },
141
+ },
142
+ get diagnostics() { return read().diagnostics; },
143
+ };
144
+ }
@@ -0,0 +1,60 @@
1
+ import { type ForceFieldConfig } from "../physics/forceVolume.js";
2
+ import type { AuthoredWeatherConfig } from "../world/authoredWeather.js";
3
+ import { type EmitterConfig } from "../vfx/particles.js";
4
+ import { type ParticleAttachOptions } from "../vfx/particleDirector.js";
5
+ import type { FireGridConfig } from "../world/weather.js";
6
+ import type { FlockConfig } from "../ai/flock.js";
7
+ import type { EditorDocument, EditorVec3 } from "./types.js";
8
+ /** A named emitter authored in metres, with game-owned appearance and attachment ids. */
9
+ export interface AuthoredEmitter {
10
+ id: string;
11
+ /** Spawn only while this authored fire area burns; live particles finish their lifetime. */
12
+ fireAreaId?: string;
13
+ position: EditorVec3;
14
+ config: EmitterConfig;
15
+ options?: ParticleAttachOptions;
16
+ }
17
+ /** A bounded fire grid; fuel and damage policy remain game content. */
18
+ export interface AuthoredFireArea {
19
+ id: string;
20
+ /** Rain cooling in heat units/second at full exposure; zero disables automatic extinguishing. */
21
+ extinguishRate?: number;
22
+ position: EditorVec3;
23
+ config: Omit<FireGridConfig, "fuelAt" | "origin" | "wind">;
24
+ fuel?: readonly number[];
25
+ ignitions?: readonly {
26
+ col: number;
27
+ row: number;
28
+ }[];
29
+ }
30
+ /** An authored habitat selects a game species and optionally follows a document path. */
31
+ export interface AuthoredFlockHabitat {
32
+ id: string;
33
+ species: string;
34
+ position: EditorVec3;
35
+ radius: number;
36
+ count: number;
37
+ seed: string | number;
38
+ role: "cosmetic" | "gameplay";
39
+ routeId?: string;
40
+ steering: FlockConfig;
41
+ /** Opt into authored wind/force acceleration; maxAcceleration is the game policy cap. */
42
+ /** Coupling in 1/second, bounded to 0..1e12. */
43
+ windResponse?: number;
44
+ /** Maximum acceleration in metres/second², bounded to 0..1e12. */
45
+ maxAcceleration?: number;
46
+ forceMask?: number;
47
+ }
48
+ /** Authored simulation inputs. Runtime particles, heat, accumulation and actors save separately. */
49
+ export interface EditorSimulation {
50
+ weather?: AuthoredWeatherConfig;
51
+ /** Authoritative acceleration volumes; games select target masks and caps. */
52
+ forces?: readonly ForceFieldConfig[];
53
+ emitters?: readonly AuthoredEmitter[];
54
+ fires?: readonly AuthoredFireArea[];
55
+ habitats?: readonly AuthoredFlockHabitat[];
56
+ }
57
+ /** Decode authoring inputs with located diagnostics and bounded work before any document mutation.
58
+ * @capability validate-simulation diagnose authored weather, effect, fire, force and habitat documents before publication
59
+ */
60
+ export declare function decodeEditorSimulation(value: unknown, document?: Pick<EditorDocument, "paths">): EditorSimulation;
@@ -0,0 +1,202 @@
1
+ import { validateForceField } from "../physics/forceVolume.js";
2
+ import { validateAuthoredWeather } from "../world/authoredWeather.js";
3
+ import { validateEmitterConfig } from "../vfx/particles.js";
4
+ import { validateParticleAttachOptions } from "../vfx/particleDirector.js";
5
+ function object(value, path) {
6
+ if (value === null || typeof value !== "object" || Array.isArray(value))
7
+ throw new Error(`${path}: expected an object`);
8
+ return value;
9
+ }
10
+ function known(value, allowed, path) {
11
+ for (const key of Object.keys(value))
12
+ if (!allowed.includes(key))
13
+ throw new Error(`${path}.${key}: unknown field`);
14
+ }
15
+ function finite(value, path, min = -Infinity, max = Infinity) {
16
+ if (typeof value !== "number" || !Number.isFinite(value) || value < min || value > max)
17
+ throw new Error(`${path}: expected a finite number in ${min}..${max}`);
18
+ return value;
19
+ }
20
+ function text(value, path) {
21
+ if (typeof value !== "string" || value.trim().length === 0 || value.length > 128)
22
+ throw new Error(`${path}: expected a nonempty string of at most 128 characters`);
23
+ }
24
+ function vector(value, path) {
25
+ const point = object(value, path);
26
+ for (const axis of ["x", "y", "z"])
27
+ finite(point[axis], `${path}.${axis}`);
28
+ }
29
+ function tuple(value, path) {
30
+ if (!Array.isArray(value) || value.length !== 3)
31
+ throw new Error(`${path}: expected [x,y,z]`);
32
+ value.forEach((component, index) => finite(component, `${path}[${index}]`));
33
+ }
34
+ function serializable(value, path, depth = 0) {
35
+ if (depth > 24)
36
+ throw new Error(`${path}: nesting exceeds 24 levels`);
37
+ if (value === null || typeof value === "string" || typeof value === "boolean" || value === undefined)
38
+ return;
39
+ if (typeof value === "number") {
40
+ finite(value, path);
41
+ return;
42
+ }
43
+ if (typeof value !== "object")
44
+ throw new Error(`${path}: expected serializable data`);
45
+ for (const [key, item] of Object.entries(value))
46
+ serializable(item, `${path}.${key}`, depth + 1);
47
+ }
48
+ function rows(value, path, limit, check) {
49
+ if (value === undefined)
50
+ return;
51
+ if (!Array.isArray(value) || value.length > limit)
52
+ throw new Error(`${path}: expected at most ${limit} entries`);
53
+ const ids = new Set();
54
+ value.forEach((entry, index) => {
55
+ const location = `${path}[${index}]`;
56
+ const row = object(entry, location);
57
+ text(row.id, `${location}.id`);
58
+ if (ids.has(row.id))
59
+ throw new Error(`${location}.id: duplicate id ${row.id}`);
60
+ ids.add(row.id);
61
+ vector(row.position, `${location}.position`);
62
+ check(row, location);
63
+ });
64
+ }
65
+ /** Decode authoring inputs with located diagnostics and bounded work before any document mutation.
66
+ * @capability validate-simulation diagnose authored weather, effect, fire, force and habitat documents before publication
67
+ */
68
+ export function decodeEditorSimulation(value, document) {
69
+ const source = object(value, "$.simulation");
70
+ known(source, ["weather", "forces", "emitters", "fires", "habitats"], "$.simulation");
71
+ serializable(source, "$.simulation");
72
+ if (source.weather !== undefined) {
73
+ object(source.weather, "$.simulation.weather");
74
+ try {
75
+ validateAuthoredWeather(source.weather);
76
+ }
77
+ catch (error) {
78
+ throw new Error(`$.simulation.weather: ${error instanceof Error ? error.message : String(error)}`);
79
+ }
80
+ }
81
+ if (source.forces !== undefined) {
82
+ if (!Array.isArray(source.forces) || source.forces.length > 128)
83
+ throw new Error("$.simulation.forces: expected at most 128 fields");
84
+ source.forces.forEach((value, index) => {
85
+ try {
86
+ validateForceField(value);
87
+ }
88
+ catch (error) {
89
+ throw new Error(`$.simulation.forces[${index}]: ${error instanceof Error ? error.message : String(error)}`);
90
+ }
91
+ });
92
+ }
93
+ rows(source.emitters, "$.simulation.emitters", 128, (row, path) => {
94
+ known(row, ["id", "position", "config", "options", "fireAreaId"], path);
95
+ if (row.fireAreaId !== undefined) {
96
+ text(row.fireAreaId, `${path}.fireAreaId`);
97
+ if (!Array.isArray(source.fires) || !source.fires.some((area) => area !== null && typeof area === "object" && area.id === row.fireAreaId))
98
+ throw new Error(`${path}.fireAreaId: missing fire area ${row.fireAreaId}`);
99
+ }
100
+ const diagnostics = validateEmitterConfig(row.config);
101
+ if (diagnostics.length)
102
+ throw new Error(`${path}.config: ${diagnostics.join("; ")}`);
103
+ if (row.options !== undefined) {
104
+ const errors = validateParticleAttachOptions(row.options);
105
+ if (errors.length)
106
+ throw new Error(`${path}.options: ${errors.join("; ")}`);
107
+ }
108
+ const config = object(row.config, `${path}.config`);
109
+ for (const key of ["max", "rate", "spread", "drag"])
110
+ if (config[key] !== undefined)
111
+ finite(config[key], `${path}.config.${key}`, 0, key === "max" ? 100_000 : key === "drag" ? 1 : Infinity);
112
+ if (config.max !== undefined && !Number.isInteger(config.max))
113
+ throw new Error(`${path}.config.max: expected an integer`);
114
+ for (const key of ["position", "spawnJitter", "direction", "gravity"])
115
+ if (config[key] !== undefined)
116
+ tuple(config[key], `${path}.config.${key}`);
117
+ for (const key of ["lifetime", "speed"])
118
+ if (config[key] !== undefined) {
119
+ const range = object(config[key], `${path}.config.${key}`);
120
+ finite(range.min, `${path}.config.${key}.min`, 0);
121
+ finite(range.max, `${path}.config.${key}.max`, range.min);
122
+ }
123
+ if (row.options !== undefined) {
124
+ const options = object(row.options, `${path}.options`);
125
+ if (options.space !== undefined && options.space !== "local" && options.space !== "world")
126
+ throw new Error(`${path}.options.space: expected local or world`);
127
+ if (options.active !== undefined && typeof options.active !== "boolean")
128
+ throw new Error(`${path}.options.active: expected boolean`);
129
+ }
130
+ });
131
+ rows(source.fires, "$.simulation.fires", 32, (row, path) => {
132
+ known(row, ["id", "position", "config", "fuel", "ignitions", "extinguishRate"], path);
133
+ if (row.extinguishRate !== undefined)
134
+ finite(row.extinguishRate, `${path}.extinguishRate`, 0);
135
+ const config = object(row.config, `${path}.config`);
136
+ known(config, ["cols", "rows", "cellSize", "ignitionThreshold", "spreadRate", "burnRate", "windBias", "maxCells"], `${path}.config`);
137
+ const cols = finite(config.cols, `${path}.config.cols`, 1, 256);
138
+ const count = cols * finite(config.rows, `${path}.config.rows`, 1, 256);
139
+ if (!Number.isInteger(cols) || !Number.isInteger(config.rows) || count > 16_384)
140
+ throw new Error(`${path}.config: expected integral dimensions with at most 16384 cells`);
141
+ finite(config.cellSize, `${path}.config.cellSize`, Number.EPSILON);
142
+ if (config.maxCells !== undefined) {
143
+ const capacity = finite(config.maxCells, `${path}.config.maxCells`, count, 1_048_576);
144
+ if (!Number.isInteger(capacity))
145
+ throw new Error(`${path}.config.maxCells: expected an integer`);
146
+ }
147
+ for (const key of ["ignitionThreshold", "spreadRate", "burnRate", "windBias"])
148
+ if (config[key] !== undefined)
149
+ finite(config[key], `${path}.config.${key}`, 0, key === "windBias" ? 1 : Infinity);
150
+ if (row.fuel !== undefined) {
151
+ if (!Array.isArray(row.fuel) || row.fuel.length !== count)
152
+ throw new Error(`${path}.fuel: expected ${count} cell values`);
153
+ row.fuel.forEach((fuel, index) => finite(fuel, `${path}.fuel[${index}]`, 0, 1));
154
+ }
155
+ if (row.ignitions !== undefined) {
156
+ if (!Array.isArray(row.ignitions) || row.ignitions.length > count)
157
+ throw new Error(`${path}.ignitions: expected at most ${count} cells`);
158
+ for (const cell of row.ignitions) {
159
+ const point = object(cell, `${path}.ignitions`);
160
+ known(point, ["col", "row"], `${path}.ignitions`);
161
+ finite(point.col, `${path}.ignitions.col`, 0, cols - 1);
162
+ finite(point.row, `${path}.ignitions.row`, 0, config.rows - 1);
163
+ if (!Number.isInteger(point.col) || !Number.isInteger(point.row))
164
+ throw new Error(`${path}.ignitions: expected integral cell coordinates`);
165
+ }
166
+ }
167
+ });
168
+ rows(source.habitats, "$.simulation.habitats", 32, (row, path) => {
169
+ known(row, ["id", "species", "position", "radius", "count", "seed", "role", "routeId", "steering", "windResponse", "maxAcceleration", "forceMask"], path);
170
+ text(row.species, `${path}.species`);
171
+ finite(row.radius, `${path}.radius`, Number.EPSILON);
172
+ const count = finite(row.count, `${path}.count`, 0, 256);
173
+ if (!Number.isInteger(count))
174
+ throw new Error(`${path}.count: expected an integer`);
175
+ if (row.role !== "cosmetic" && row.role !== "gameplay")
176
+ throw new Error(`${path}.role: expected cosmetic or gameplay`);
177
+ if (typeof row.seed !== "string" && typeof row.seed !== "number")
178
+ throw new Error(`${path}.seed: expected string or number`);
179
+ if (typeof row.seed === "number")
180
+ finite(row.seed, `${path}.seed`);
181
+ for (const key of ["windResponse", "maxAcceleration"])
182
+ if (row[key] !== undefined)
183
+ finite(row[key], `${path}.${key}`, 0, 1e12);
184
+ if (row.forceMask !== undefined) {
185
+ finite(row.forceMask, `${path}.forceMask`, 0, 0xffffffff);
186
+ if (!Number.isInteger(row.forceMask))
187
+ throw new Error(`${path}.forceMask: expected a uint32`);
188
+ }
189
+ const steering = object(row.steering, `${path}.steering`);
190
+ known(steering, ["maxSpeed", "accel", "separationRadius", "separationWeight", "neighborRadius", "cohesionWeight", "alignmentWeight", "seekWeight", "stragglerRadius", "stragglerBoost"], `${path}.steering`);
191
+ for (const key of ["maxSpeed", "separationRadius", "neighborRadius"])
192
+ finite(steering[key], `${path}.steering.${key}`, Number.EPSILON);
193
+ for (const [key, item] of Object.entries(steering))
194
+ finite(item, `${path}.steering.${key}`, 0);
195
+ if (row.routeId !== undefined) {
196
+ text(row.routeId, `${path}.routeId`);
197
+ if (document !== undefined && !document.paths.some((route) => route.id === row.routeId && route.points.length > 1))
198
+ throw new Error(`${path}.routeId: missing route ${row.routeId} with at least two points`);
199
+ }
200
+ });
201
+ return structuredClone(source);
202
+ }
@@ -0,0 +1,17 @@
1
+ /** A solid or clearance volume in the prefab's unnormalized local asset space. */
2
+ export interface StaticPrefabBox {
3
+ min: readonly [number, number, number];
4
+ max: readonly [number, number, number];
5
+ }
6
+ /** Persisted static export settings; source parts remain editable in the prefab fragment. */
7
+ export interface StaticPrefabBake {
8
+ assetId: string;
9
+ /** Omit for a conservative closed exterior. Explicit boxes preserve authored openings. */
10
+ collisionBoxes?: readonly StaticPrefabBox[];
11
+ /** Required empty spaces, checked against movement solids before export. */
12
+ clearances?: readonly (StaticPrefabBox & {
13
+ id: string;
14
+ })[];
15
+ }
16
+ /** Validate and copy static prefab export settings from document or RPC input. */
17
+ export declare function parseStaticPrefabBake(value: unknown): StaticPrefabBake;
@@ -0,0 +1,51 @@
1
+ /** Validate and copy static prefab export settings from document or RPC input. */
2
+ export function parseStaticPrefabBake(value) {
3
+ const record = (input) => {
4
+ if (input === null || typeof input !== "object" || Array.isArray(input))
5
+ throw new Error("expected an object");
6
+ return input;
7
+ };
8
+ const input = record(value);
9
+ for (const key of Object.keys(input)) {
10
+ if (!["assetId", "collisionBoxes", "clearances"].includes(key))
11
+ throw new Error(`unknown static bake field: ${key}`);
12
+ }
13
+ if (typeof input.assetId !== "string" || input.assetId.trim().length === 0)
14
+ throw new Error("assetId: expected a nonempty string");
15
+ const boxes = (items, clearance) => {
16
+ if (!Array.isArray(items) || items.length === 0 || items.length > 4096)
17
+ throw new Error("expected 1..4096 boxes");
18
+ const ids = new Set();
19
+ return items.map((item, index) => {
20
+ const box = record(item);
21
+ for (const key of Object.keys(box)) {
22
+ if (!["min", "max", ...(clearance ? ["id"] : [])].includes(key))
23
+ throw new Error(`box ${index}: unknown field ${key}`);
24
+ }
25
+ for (const key of ["min", "max"]) {
26
+ const vector = box[key];
27
+ if (!Array.isArray(vector) || vector.length !== 3 || !vector.every((axis) => typeof axis === "number" && Number.isFinite(axis)))
28
+ throw new Error(`box ${index}.${key}: expected three finite numbers`);
29
+ }
30
+ const min = box.min;
31
+ const max = box.max;
32
+ if (!min.every((axis, i) => axis < max[i]))
33
+ throw new Error(`box ${index}: min must be less than max on every axis`);
34
+ if (clearance) {
35
+ if (typeof box.id !== "string" || box.id.trim().length === 0 || ids.has(box.id))
36
+ throw new Error(`clearance ${index}: expected a unique nonempty id`);
37
+ ids.add(box.id);
38
+ }
39
+ return { min: [...min], max: [...max], ...(clearance ? { id: box.id } : {}) };
40
+ });
41
+ };
42
+ const collisionBoxes = input.collisionBoxes === undefined ? undefined : boxes(input.collisionBoxes, false);
43
+ const clearances = input.clearances === undefined ? undefined : boxes(input.clearances, true);
44
+ if (clearances !== undefined && collisionBoxes === undefined)
45
+ throw new Error("clearances require explicit collisionBoxes");
46
+ for (const clearance of clearances ?? []) {
47
+ if (collisionBoxes.some((solid) => solid.min.every((axis, i) => axis < clearance.max[i] && solid.max[i] > clearance.min[i])))
48
+ throw new Error(`clearance ${clearance.id} intersects a collision box`);
49
+ }
50
+ return { assetId: input.assetId, ...(collisionBoxes === undefined ? {} : { collisionBoxes }), ...(clearances === undefined ? {} : { clearances }) };
51
+ }
@@ -1,3 +1,6 @@
1
+ import type { EditorSimulation } from "./simulation.js";
2
+ import type { MaterialAsset } from "../material/materialAsset.js";
3
+ import type { StaticPrefabBake } from "./staticPrefab.js";
1
4
  import type { ParamSchema } from "../scene/sceneKinds.js";
2
5
  import type { EditorUiDocument } from "../ui/hudDocument.js";
3
6
  import type { MinimapBakeBounds } from "../world/minimapBake.js";
@@ -100,6 +103,8 @@ export interface EditorNote {
100
103
  export type EditorTerrain = TerraformSnapshot;
101
104
  /** The four placeable-object collections a prefab fragment or clipboard fragment carries. */
102
105
  export interface EditorFragmentContent {
106
+ /** Referenced reusable materials travel with prefab and clipboard content. */
107
+ materialAssets?: MaterialAsset[];
103
108
  markers: readonly EditorMarker[];
104
109
  volumes: readonly EditorVolume[];
105
110
  paths: readonly EditorPath[];
@@ -115,6 +120,8 @@ export interface EditorPrefab {
115
120
  id: string;
116
121
  name: string;
117
122
  fragment: EditorFragmentContent;
123
+ /** Offline export settings; world instances remain authored markers. */
124
+ staticBake?: StaticPrefabBake;
118
125
  }
119
126
  /**
120
127
  * A named, persisted list of object ids — a selection bookmark (restore, add-to) that can also
@@ -219,11 +226,19 @@ export interface EditorEnvironment {
219
226
  zenithColor?: string;
220
227
  sunIntensity?: number;
221
228
  ambientIntensity?: number;
229
+ /** Sun compass bearing in degrees (0 = -Z, 90 = +X); runtime `SkySunConfig.azimuth`. */
230
+ sunAzimuth?: number;
231
+ /** Sun height above the horizon in degrees; runtime `SkySunConfig.elevation`. */
232
+ sunElevation?: number;
222
233
  fog?: EditorFogConfig;
223
234
  }
224
235
  /** The full authored scene: every marker, volume, path, note, and sculpted terrain for a game. */
225
236
  export interface EditorDocument {
237
+ /** Reusable material library; per-marker named assignments live in meta.materialAssignments. */
238
+ materialAssets?: MaterialAsset[];
226
239
  version: 1;
240
+ /** Authored weather, emitter, fire and habitat inputs; runtime state saves separately. */
241
+ simulation?: EditorSimulation;
227
242
  markers: EditorMarker[];
228
243
  volumes: EditorVolume[];
229
244
  paths: EditorPath[];
@@ -17,6 +17,10 @@ export interface ReputationLedgerConfig {
17
17
  min?: number;
18
18
  max?: number;
19
19
  }
20
+ /** Plain JSON standings of a {@link ReputationLedger}: stored values only, `initial` stays config. */
21
+ export interface ReputationLedgerState {
22
+ records: Record<string, Record<string, number>>;
23
+ }
20
24
  export interface ReputationLedger {
21
25
  standing(actorId: string, factionId: string): number;
22
26
  hasStanding(actorId: string, factionId: string): boolean;
@@ -26,6 +30,8 @@ export interface ReputationLedger {
26
30
  relation(actorId: string, factionId: string): FactionRelation;
27
31
  standings(actorId: string): Record<string, number>;
28
32
  reset(actorId: string, factionId?: string): void;
33
+ snapshot(): ReputationLedgerState;
34
+ restore(next: ReputationLedgerState): void;
29
35
  }
30
36
  export declare function createReputationLedger(config?: ReputationLedgerConfig): ReputationLedger;
31
37
  export interface EffectiveRelationInput {
@@ -77,6 +77,17 @@ export function createReputationLedger(config = {}) {
77
77
  else
78
78
  records.get(actorId)?.delete(factionId);
79
79
  },
80
+ snapshot() {
81
+ const out = {};
82
+ for (const [actorId, row] of records)
83
+ out[actorId] = Object.fromEntries(row);
84
+ return { records: out };
85
+ },
86
+ restore(next) {
87
+ records.clear();
88
+ for (const [actorId, row] of Object.entries(next.records))
89
+ records.set(actorId, new Map(Object.entries(row)));
90
+ },
80
91
  };
81
92
  }
82
93
  export function effectiveRelation(input) {
@@ -1,3 +1,5 @@
1
+ import type { WeaponHandlingFrame } from "../combat/weaponHandling.js";
2
+ import type { WeaponPresentationTuning } from "../combat/weaponPresentation.js";
1
3
  export interface CameraFollowState {
2
4
  entityId: string;
3
5
  target: {
@@ -12,6 +14,11 @@ export interface CameraFollowState {
12
14
  };
13
15
  distance: number;
14
16
  }
17
+ /** What a camera rig reads about the held weapon each frame; see `GameCameraConfig.weapon`. */
18
+ export interface CameraWeaponView {
19
+ handling: WeaponHandlingFrame;
20
+ presentation?: WeaponPresentationTuning;
21
+ }
15
22
  export interface FirstPersonCameraConfig {
16
23
  /** Camera height above the followed entity's origin. Default 1.6. */
17
24
  eyeHeight?: number;
@@ -162,6 +169,8 @@ export interface LockOnCameraConfig {
162
169
  export type ChaseView = "chase" | "cockpit" | "hood" | "rear";
163
170
  /** Speed-reactive vehicle chase rig (#27) — speed→FOV, spring arm, procedural shake, interior views. */
164
171
  export interface ChaseCameraConfig {
172
+ /** Heading the chase boom follows: target body (default), or independent input/aim yaw for camera-relative walking. Seat views always follow the body. */
173
+ headingSource?: "body" | "input";
165
174
  /** Boom distance behind the vehicle. Default 6. */
166
175
  distance?: number;
167
176
  /** Camera height above the follow point. Default 2.6. */
@@ -170,11 +179,47 @@ export interface ChaseCameraConfig {
170
179
  lookHeight?: number;
171
180
  /** Spring-arm damping (higher = stiffer, less lag). Default 6. */
172
181
  springDamping?: number;
173
- /** Speed→FOV curve: FOV lerps from `base` to `max` as speed climbs to `speedForMax`. */
182
+ /**
183
+ * Speed→FOV curve: FOV lerps from `base` to `max` as speed climbs to `speedForMax`. Speed is the
184
+ * followed entity's sim `velocity` when it publishes one. `response` (default 6) eases the FOV
185
+ * toward the curve with `1-exp(-response*dt)`; `Infinity` applies it unsmoothed.
186
+ */
174
187
  fov?: {
175
188
  base?: number;
176
189
  max?: number;
177
190
  speedForMax?: number;
191
+ response?: number;
192
+ };
193
+ /** Pull the boom back by up to `extra` world units as speed climbs to `speedForMax` (default the FOV curve's `speedForMax`). */
194
+ distanceBySpeed?: {
195
+ extra: number;
196
+ speedForMax?: number;
197
+ };
198
+ /**
199
+ * Tilt the boom with the target's body pitch (`rotationX`) so the camera stays behind it on
200
+ * slopes and ramps. `blend` (default 1) is the fraction of the pitch followed, clamped to `max`
201
+ * radians (default 0.6) and eased by `response` (default 5). Omit the block to keep a level boom.
202
+ */
203
+ pitchFollow?: {
204
+ blend?: number;
205
+ max?: number;
206
+ response?: number;
207
+ };
208
+ /** How `ctx.camera.kickFov(degrees)` impulses feel: the kick decays at `decay` per second (default 7) and the summed kick is clamped to `max` degrees (default 20). */
209
+ fovKick?: {
210
+ decay?: number;
211
+ max?: number;
212
+ };
213
+ /** Input action that, while held, swings the chase view to look back at what is behind the target. */
214
+ lookBackAction?: string;
215
+ /**
216
+ * Pull the boom in past scene colliders, objects, terrain and walls via `ctx.scene.raycast`
217
+ * (entities never block). `radius` (default 0.3) keeps the lens off the surface; `minDistance`
218
+ * (default 0.8) is the closest the camera gets to the look pivot. Default on; `false` disables.
219
+ */
220
+ collision?: boolean | {
221
+ radius?: number;
222
+ minDistance?: number;
178
223
  };
179
224
  /** Procedural shake amplitude per unit of speed (adds to the trauma channel). Default 0.0. */
180
225
  shakePerSpeed?: number;
@@ -434,7 +479,7 @@ export interface GameCameraConfig {
434
479
  };
435
480
  initialDistance?: number;
436
481
  initialHeight?: number;
437
- /** Initial horizontal boom facing (radians): 0 = camera behind on -Z, PI = camera on +Z. Seeds the orbit rig; unset keeps the legacy -Z placement. */
482
+ /** Initial horizontal boom facing (radians): 0 = camera behind on -Z, PI = camera on +Z. Seeds orbit and input-owned chase once; unset keeps orbit's -Z placement or seeds chase from its first acquired target's heading. */
438
483
  initialYaw?: number;
439
484
  /** Initial boom elevation (radians): 0 = level, positive = camera above the target looking down. Seeds the orbit rig; unset derives elevation from `initialHeight`/`initialDistance`. */
440
485
  initialPitch?: number;
@@ -446,6 +491,13 @@ export interface GameCameraConfig {
446
491
  followEntityId?: string | null;
447
492
  /** Fired each frame after cameraFollow lock is applied. */
448
493
  onCameraFollow?: (state: CameraFollowState) => void;
494
+ /**
495
+ * The followed entity's held weapon, read every frame by the `first` and `shoulder` rigs. Return the
496
+ * weapon's `createWeaponHandling` frame and its presentation tuning, or `null` for none. First person
497
+ * poses the viewmodel from it (offset, ADS pose, viewmodel FOV, sway, bob, kick) and adds the recoil to
498
+ * the look; over-the-shoulder takes its ADS blend from `adsProgress` and adds the same recoil.
499
+ */
500
+ weapon?: (entityId: string) => CameraWeaponView | null;
449
501
  rotateSpeed?: number;
450
502
  zoomSpeed?: number;
451
503
  dampingFactor?: number;