@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
@@ -11,8 +11,15 @@ export interface BallisticSweepOptions {
11
11
  radius?: number;
12
12
  }
13
13
  /**
14
- * Marches the closed-form arc (constant gravity, straight lateral) through `world` and reports the
15
- * first sample inside any live body's AABB — sleeping bodies included — refined by one bisection
16
- * between the last clear sample and the hit sample. Returns `null` when the whole arc is clear.
14
+ * Sweeps each short chord of the closed-form arc through live body AABBs, including sleeping bodies.
15
+ * Thin cover between samples blocks; `step` controls the approximation of the curved path.
17
16
  */
18
17
  export declare function createBallisticSweep(world: PhysicsWorld, options?: BallisticSweepOptions): BallisticSweep;
18
+ /** Contact fraction for a point segment against a translating axis-aligned box; inflate extents for a projectile radius.
19
+ * @capability moving-box-sweep detect contact with boxes crossing a projectile segment between simulation poses
20
+ */
21
+ export declare 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;
22
+ /** Contact fraction for a point segment against a translating sphere; add the projectile radius to `radius`.
23
+ * @capability moving-sphere-sweep detect contact with spheres crossing a projectile segment between simulation poses
24
+ */
25
+ export declare 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;
@@ -1,49 +1,73 @@
1
+ import { intersectAabb } from "../scene/objectQuery.js";
1
2
  const DEFAULT_SWEEP_STEP = 1 / 60;
2
3
  /**
3
- * Marches the closed-form arc (constant gravity, straight lateral) through `world` and reports the
4
- * first sample inside any live body's AABB — sleeping bodies included — refined by one bisection
5
- * between the last clear sample and the hit sample. Returns `null` when the whole arc is clear.
4
+ * Sweeps each short chord of the closed-form arc through live body AABBs, including sleeping bodies.
5
+ * Thin cover between samples blocks; `step` controls the approximation of the curved path.
6
6
  */
7
7
  export function createBallisticSweep(world, options = {}) {
8
8
  const step = options.step ?? DEFAULT_SWEEP_STEP;
9
9
  const radius = options.radius ?? 0;
10
- function overlapsAnyBody(x, y, z) {
11
- for (let i = 0; i < world.highWater; i += 1) {
12
- if (!world.isAlive(i))
13
- continue;
14
- if (Math.abs(x - world.posX[i]) > world.halfX[i] + radius)
15
- continue;
16
- if (Math.abs(y - world.posY[i]) > world.halfY[i] + radius)
17
- continue;
18
- if (Math.abs(z - world.posZ[i]) > world.halfZ[i] + radius)
19
- continue;
20
- return true;
21
- }
22
- return false;
23
- }
10
+ if (!Number.isFinite(step) || step <= 0 || !Number.isFinite(radius) || radius < 0)
11
+ throw new RangeError("Ballistic sweep requires a finite positive step and nonnegative radius");
24
12
  return (origin, velocity, gravity, maxTime) => {
13
+ if (!origin.every(Number.isFinite) || !velocity.every(Number.isFinite) || !Number.isFinite(gravity) || !Number.isFinite(maxTime) || maxTime < 0 || Math.ceil(maxTime / step) > 100_000)
14
+ throw new RangeError("Ballistic sweep requires finite inputs within its 100000 segment budget");
25
15
  const pointAt = (t) => [
26
16
  origin[0] + velocity[0] * t,
27
17
  origin[1] + velocity[1] * t - 0.5 * gravity * t * t,
28
18
  origin[2] + velocity[2] * t,
29
19
  ];
30
- let lastClear = 0;
31
20
  let t = 0;
32
21
  for (;;) {
33
- const sample = pointAt(t);
34
- if (overlapsAnyBody(sample[0], sample[1], sample[2])) {
35
- if (t === 0)
36
- return { point: sample, time: 0 };
37
- const mid = (lastClear + t) / 2;
38
- const midSample = pointAt(mid);
39
- return overlapsAnyBody(midSample[0], midSample[1], midSample[2])
40
- ? { point: midSample, time: mid }
41
- : { point: sample, time: t };
22
+ const nextTime = Math.min(t + step, maxTime);
23
+ const from = pointAt(t);
24
+ const to = pointAt(nextTime);
25
+ let fraction = null;
26
+ for (let i = 0; i < world.highWater; i += 1) {
27
+ if (!world.isAlive(i))
28
+ continue;
29
+ const contact = sweepMovingBounds(from, to, [world.posX[i], world.posY[i], world.posZ[i]], [world.posX[i], world.posY[i], world.posZ[i]], [world.halfX[i] + radius, world.halfY[i] + radius, world.halfZ[i] + radius]);
30
+ if (contact !== null && (fraction === null || contact < fraction))
31
+ fraction = contact;
32
+ }
33
+ if (fraction !== null) {
34
+ const time = t + (nextTime - t) * fraction;
35
+ return { point: pointAt(time), time };
42
36
  }
43
37
  if (t >= maxTime)
44
38
  return null;
45
- lastClear = t;
46
- t = Math.min(t + step, maxTime);
39
+ t = nextTime;
47
40
  }
48
41
  };
49
42
  }
43
+ /** Contact fraction for a point segment against a translating axis-aligned box; inflate extents for a projectile radius.
44
+ * @capability moving-box-sweep detect contact with boxes crossing a projectile segment between simulation poses
45
+ */
46
+ export function sweepMovingBounds(from, to, targetFrom, targetTo, halfExtents) {
47
+ const origin = [from[0] - targetFrom[0], from[1] - targetFrom[1], from[2] - targetFrom[2]];
48
+ const motion = [to[0] - targetTo[0] - origin[0], to[1] - targetTo[1] - origin[1], to[2] - targetTo[2] - origin[2]];
49
+ return intersectAabb(origin, motion, [-halfExtents[0], -halfExtents[1], -halfExtents[2]], halfExtents, 1)?.distance ?? null;
50
+ }
51
+ /** Contact fraction for a point segment against a translating sphere; add the projectile radius to `radius`.
52
+ * @capability moving-sphere-sweep detect contact with spheres crossing a projectile segment between simulation poses
53
+ */
54
+ export function sweepMovingSphere(from, to, targetFrom, targetTo, radius) {
55
+ const ox = from[0] - targetFrom[0];
56
+ const oy = from[1] - targetFrom[1];
57
+ const oz = from[2] - targetFrom[2];
58
+ const dx = to[0] - targetTo[0] - ox;
59
+ const dy = to[1] - targetTo[1] - oy;
60
+ const dz = to[2] - targetTo[2] - oz;
61
+ const c = ox * ox + oy * oy + oz * oz - radius * radius;
62
+ if (c <= 0)
63
+ return 0;
64
+ const a = dx * dx + dy * dy + dz * dz;
65
+ if (a <= 1e-18)
66
+ return null;
67
+ const b = ox * dx + oy * dy + oz * dz;
68
+ const discriminant = b * b - a * c;
69
+ if (discriminant < 0)
70
+ return null;
71
+ const fraction = (-b - Math.sqrt(discriminant)) / a;
72
+ return fraction >= 0 && fraction <= 1 ? fraction : null;
73
+ }
@@ -0,0 +1,116 @@
1
+ import type { AxisInput } from "../input/axisInput.js";
2
+ /** How the boat turns: a rudder needs water flowing past it; an outboard swings its own thrust. */
3
+ export type BoatSteering = {
4
+ kind: "rudder";
5
+ /** Rudder area, m². */
6
+ area: number;
7
+ /** Largest rudder angle, rad (default `0.6`). */
8
+ maxAngle?: number;
9
+ /** Share of prop thrust that washes over the rudder as flow, `0..1` (default `0.5`) — steering at low speed. */
10
+ propWash?: number;
11
+ } | {
12
+ kind: "outboard";
13
+ /** Largest thrust angle, rad (default `0.5`). */
14
+ maxAngle?: number;
15
+ };
16
+ /**
17
+ * A powered hull in physical units. Drag rises toward hull speed (`√(g·L)·1.34`) and then falls away once the hull
18
+ * planes, the keel resists sliding sideways, and the prop's thrust fades as the boat approaches the prop's speed.
19
+ */
20
+ export interface BoatTuning {
21
+ /** Displacement, kg. */
22
+ massKg: number;
23
+ /** Waterline length, m; sets hull speed and the rudder's lever arm. */
24
+ length: number;
25
+ /** Beam, m; sets the visual roll response. */
26
+ beam: number;
27
+ /** Peak prop thrust, N. */
28
+ maxThrust: number;
29
+ /** Speed at which prop thrust reaches zero, m/s. */
30
+ propSpeed: number;
31
+ /** Reverse thrust as a share of `maxThrust` (default `0.4`). */
32
+ reverseScale?: number;
33
+ /** Hull drag coefficient × wetted frontal area, m², before planing (default `0.3 · beam`). */
34
+ dragArea?: number;
35
+ /** Speed where the hull planes and drag drops, m/s; omit for a displacement hull that never planes. */
36
+ planingSpeed?: number;
37
+ /** Drag multiplier once fully planing, `0..1` (default `0.35`). */
38
+ planingDrag?: number;
39
+ /** Keel lateral resistance, N per (m/s)² of sideways speed (default `massKg · 6`). */
40
+ keel?: number;
41
+ /** Extra yaw damping, N·m per rad/s, on top of the keel resisting rotation along the hull (default `0`). */
42
+ yawDamping?: number;
43
+ steering: BoatSteering;
44
+ /** Rudder or outboard slew rate, rad/s (default `2`). */
45
+ steerRate?: number;
46
+ /** Largest bow-up pitch while climbing onto the plane, rad (default `0.12`). */
47
+ bowRise?: number;
48
+ /** Largest heel in a hard turn, rad (default `0.15`); planing hulls lean into the turn. */
49
+ maxHeel?: number;
50
+ /** Integration substep, s (default `1/240`). */
51
+ maxSubstep?: number;
52
+ }
53
+ /** World hooks for a boat instance. */
54
+ export interface BoatOptions {
55
+ position?: readonly [number, number, number];
56
+ heading?: number;
57
+ /** Water surface height, m, under a point at a time (waves); default flat at the spawn height. */
58
+ waterHeight?: (x: number, z: number, time: number) => number;
59
+ /** Surface current, m/s, `[vx, vz]`; the hull drags toward it. */
60
+ current?: (x: number, z: number) => readonly [number, number];
61
+ }
62
+ /** Serializable boat state. */
63
+ export interface BoatState {
64
+ x: number;
65
+ y: number;
66
+ z: number;
67
+ heading: number;
68
+ vx: number;
69
+ vz: number;
70
+ yawRate: number;
71
+ steerAngle: number;
72
+ time: number;
73
+ bodyPitch: number;
74
+ bodyRoll: number;
75
+ }
76
+ /** One boat tick: pose plus the telemetry wake, spray and engine sound read. */
77
+ export interface BoatStep {
78
+ position: readonly [number, number, number];
79
+ heading: number;
80
+ forwardSpeed: number;
81
+ lateralSpeed: number;
82
+ yawRate: number;
83
+ /** Rudder or outboard angle, rad, positive steers like `steer > 0`. */
84
+ steerAngle: number;
85
+ /** `0` displacement … `1` fully planing. */
86
+ planing: number;
87
+ /** Thrust in use as a share of `maxThrust`, `0..1` — engine load. */
88
+ engineLoad: number;
89
+ bodyPitch: number;
90
+ bodyRoll: number;
91
+ /** Always `0`: the hull rides the water surface. Present so `tickDrivableVehicle` can drive it. */
92
+ airOffset: number;
93
+ airborne: boolean;
94
+ }
95
+ /** A force-based powered hull on the same tick/snapshot/retune contract as `VehicleDynamics`. */
96
+ export interface BoatDynamics {
97
+ tick(dt: number, input: AxisInput): BoatStep;
98
+ pose(): {
99
+ position: readonly [number, number, number];
100
+ heading: number;
101
+ };
102
+ velocity(): readonly [number, number];
103
+ applyImpulse(dvx: number, dvz: number): void;
104
+ retune(next: BoatTuning): void;
105
+ tuning(): BoatTuning;
106
+ snapshot(): BoatState;
107
+ restore(state: BoatState): void;
108
+ resetTo(position: readonly [number, number, number], heading: number): void;
109
+ }
110
+ /**
111
+ * Creates a {@link BoatDynamics}: throttle drives the prop, brake reverses it, steer swings the rudder or outboard.
112
+ * A rudder boat barely turns at rest unless the prop is washing over it; an outboard turns at rest because it swings
113
+ * the thrust itself. Deterministic for a given `dt` sequence.
114
+ * @capability boat-dynamics force-based powered hull — hull-speed drag hump, planing, keel, rudder that needs flow or outboard thrust
115
+ */
116
+ export declare function createBoatDynamics(initial: BoatTuning, options?: BoatOptions): BoatDynamics;
@@ -0,0 +1,160 @@
1
+ const GRAVITY = 9.81;
2
+ const WATER_DENSITY = 1025;
3
+ const DEFAULT_SUBSTEP = 1 / 240;
4
+ function forwardOf(heading) {
5
+ return [Math.sin(heading), Math.cos(heading)];
6
+ }
7
+ /**
8
+ * Creates a {@link BoatDynamics}: throttle drives the prop, brake reverses it, steer swings the rudder or outboard.
9
+ * A rudder boat barely turns at rest unless the prop is washing over it; an outboard turns at rest because it swings
10
+ * the thrust itself. Deterministic for a given `dt` sequence.
11
+ * @capability boat-dynamics force-based powered hull — hull-speed drag hump, planing, keel, rudder that needs flow or outboard thrust
12
+ */
13
+ export function createBoatDynamics(initial, options = {}) {
14
+ let tuning = initial;
15
+ const spawnY = options.position?.[1] ?? 0;
16
+ const water = options.waterHeight ?? (() => spawnY);
17
+ const current = options.current;
18
+ const state = fresh(options.position ?? [0, 0, 0], options.heading ?? 0);
19
+ state.y = water(state.x, state.z, 0);
20
+ const telemetry = { planing: 0, engineLoad: 0 };
21
+ function fresh(position, heading) {
22
+ return { x: position[0], y: position[1], z: position[2], heading, vx: 0, vz: 0, yawRate: 0, steerAngle: 0, time: 0, bodyPitch: 0, bodyRoll: 0 };
23
+ }
24
+ function substep(h, input) {
25
+ const t = tuning;
26
+ const m = t.massKg;
27
+ const [fx, fz] = forwardOf(state.heading);
28
+ let relX = state.vx;
29
+ let relZ = state.vz;
30
+ if (current !== undefined) {
31
+ const [cx, cz] = current(state.x, state.z);
32
+ relX -= cx;
33
+ relZ -= cz;
34
+ }
35
+ const u = relX * fx + relZ * fz;
36
+ const v = -relX * fz + relZ * fx;
37
+ const r = state.yawRate;
38
+ const throttle = Math.max(0, Math.min(1, input.throttle));
39
+ const reverse = Math.max(0, Math.min(1, input.brake));
40
+ const steerInput = Math.max(-1, Math.min(1, input.steer));
41
+ const steering = t.steering;
42
+ const maxAngle = steering.maxAngle ?? (steering.kind === "rudder" ? 0.6 : 0.5);
43
+ const targetAngle = -steerInput * maxAngle;
44
+ const slew = (t.steerRate ?? 2) * h;
45
+ state.steerAngle += Math.max(-slew, Math.min(slew, targetAngle - state.steerAngle));
46
+ const propFade = Math.max(0, 1 - Math.max(0, u) / Math.max(0.1, t.propSpeed));
47
+ const thrust = throttle * t.maxThrust * propFade - reverse * t.maxThrust * (t.reverseScale ?? 0.4);
48
+ telemetry.engineLoad = Math.max(throttle * propFade, reverse);
49
+ let forceLong = 0;
50
+ let forceLat = 0;
51
+ let yawMoment = 0;
52
+ const lever = t.length * 0.45;
53
+ if (steering.kind === "outboard") {
54
+ // The motor swings its own thrust, so the stern is pushed sideways even at rest.
55
+ forceLong += thrust * Math.cos(state.steerAngle);
56
+ const side = thrust * Math.sin(state.steerAngle);
57
+ forceLat += side;
58
+ yawMoment += lever * side;
59
+ }
60
+ else {
61
+ forceLong += thrust;
62
+ const flow = Math.max(0, u) + (steering.propWash ?? 0.5) * Math.sqrt(Math.max(0, thrust) / (WATER_DENSITY * Math.max(0.05, steering.area)));
63
+ // Rudder lift ≈ ½ρ·A·flow²·π·α (thin foil), stalled past ~0.6 rad. It pushes the stern away from the turn.
64
+ const angle = Math.max(-0.6, Math.min(0.6, state.steerAngle));
65
+ const lift = 0.5 * WATER_DENSITY * steering.area * flow * flow * Math.PI * angle;
66
+ forceLat += lift;
67
+ yawMoment += lever * lift;
68
+ }
69
+ const hullSpeed = 1.34 * Math.sqrt(GRAVITY * t.length);
70
+ const planeSpeed = t.planingSpeed;
71
+ const planing = planeSpeed === undefined ? 0 : Math.max(0, Math.min(1, (Math.abs(u) - planeSpeed * 0.8) / (planeSpeed * 0.4)));
72
+ telemetry.planing = planing;
73
+ // Wave-making drag climbs toward hull speed; a planing hull rides over it and sheds most of the hull drag.
74
+ // A displacement hull that never planes keeps climbing its own bow wave, so past hull speed the drag is a wall.
75
+ const over = Math.abs(u) / hullSpeed - 1;
76
+ const hump = planeSpeed === undefined && over > 0
77
+ ? 3.5 + 60 * over * over
78
+ : 1 + 2.5 * Math.exp(-((over / 0.35) ** 2));
79
+ const dragArea = (t.dragArea ?? 0.3 * t.beam) * (1 - planing * (1 - (t.planingDrag ?? 0.35))) * (planeSpeed === undefined ? hump : 1 + (hump - 1) * (1 - planing));
80
+ forceLong -= 0.5 * WATER_DENSITY * dragArea * u * Math.abs(u) * 0.01;
81
+ // A planing hull runs on a fraction of its bottom, so it grips the water less sideways and turns more freely.
82
+ const keel = (t.keel ?? m * 6) * (1 - 0.7 * planing);
83
+ forceLat -= keel * v * Math.abs(v) + m * 0.8 * v;
84
+ // A turning hull drags every station of the keel sideways at r·x; integrated over the length that resists yaw.
85
+ const length4 = t.length ** 4;
86
+ yawMoment -= keel * r * Math.abs(r) * (length4 / 80) + m * 0.8 * r * (t.length * t.length) / 12 + (t.yawDamping ?? 0) * r;
87
+ const iz = Math.max(1, m * t.length * t.length / 12);
88
+ let du = forceLong / m - r * v;
89
+ const dv = forceLat / m + r * u;
90
+ if (h > 0 && thrust === 0 && Math.sign(u + du * h) !== Math.sign(u) && u !== 0)
91
+ du = -u / h;
92
+ const nu = u + du * h;
93
+ const nv = v + dv * h;
94
+ const nr = r + (yawMoment / iz) * h;
95
+ state.heading += nr * h;
96
+ const [nfx, nfz] = forwardOf(state.heading);
97
+ let nvx = nfx * nu - nfz * nv;
98
+ let nvz = nfz * nu + nfx * nv;
99
+ if (current !== undefined) {
100
+ const [cx, cz] = current(state.x, state.z);
101
+ nvx += cx;
102
+ nvz += cz;
103
+ }
104
+ state.vx = nvx;
105
+ state.vz = nvz;
106
+ state.yawRate = nr;
107
+ state.x += nvx * h;
108
+ state.z += nvz * h;
109
+ state.time += h;
110
+ state.y = water(state.x, state.z, state.time);
111
+ const response = 1 - Math.exp(-4 * h);
112
+ const climb = planeSpeed === undefined ? 0 : Math.max(0, Math.min(1, Math.abs(u) / planeSpeed)) * (1 - planing);
113
+ const pitchTarget = -(t.bowRise ?? 0.12) * climb;
114
+ const heelTarget = Math.max(-(t.maxHeel ?? 0.15), Math.min(t.maxHeel ?? 0.15, (r * u) / GRAVITY * (planing > 0.5 ? -0.4 : 0.25)));
115
+ state.bodyPitch += (pitchTarget - state.bodyPitch) * response;
116
+ state.bodyRoll += (heelTarget - state.bodyRoll) * response;
117
+ }
118
+ return {
119
+ tick(dt, input) {
120
+ const maxSub = Math.max(1 / 2000, tuning.maxSubstep ?? DEFAULT_SUBSTEP);
121
+ const steps = dt > 0 ? Math.max(1, Math.ceil(dt / maxSub - 1e-9)) : 0;
122
+ const h = steps > 0 ? dt / steps : 0;
123
+ for (let i = 0; i < steps; i += 1)
124
+ substep(h, input);
125
+ const [fx, fz] = forwardOf(state.heading);
126
+ return {
127
+ position: [state.x, state.y, state.z],
128
+ heading: state.heading,
129
+ forwardSpeed: state.vx * fx + state.vz * fz,
130
+ lateralSpeed: -state.vx * fz + state.vz * fx,
131
+ yawRate: state.yawRate,
132
+ steerAngle: -state.steerAngle,
133
+ planing: telemetry.planing,
134
+ engineLoad: telemetry.engineLoad,
135
+ bodyPitch: state.bodyPitch,
136
+ bodyRoll: state.bodyRoll,
137
+ airOffset: 0,
138
+ airborne: false,
139
+ };
140
+ },
141
+ pose: () => ({ position: [state.x, state.y, state.z], heading: state.heading }),
142
+ velocity: () => [state.vx, state.vz],
143
+ applyImpulse(dvx, dvz) {
144
+ state.vx += dvx;
145
+ state.vz += dvz;
146
+ },
147
+ retune(next) {
148
+ tuning = next;
149
+ },
150
+ tuning: () => tuning,
151
+ snapshot: () => ({ ...state }),
152
+ restore(next) {
153
+ Object.assign(state, next);
154
+ },
155
+ resetTo(position, heading) {
156
+ Object.assign(state, fresh(position, heading));
157
+ state.y = water(state.x, state.z, 0);
158
+ },
159
+ };
160
+ }
@@ -1,12 +1,24 @@
1
1
  import type { AxisInput } from "../input/axisInput.js";
2
- import type { KinematicVehicle, KinematicVehicleModifiers, KinematicVehicleStep } from "./kinematicVehicle.js";
2
+ import type { KinematicVehicleModifiers, KinematicVehicleStep } from "./kinematicVehicle.js";
3
3
  /** World-space `[x, y, z]` for a drivable vehicle's resolved pose. */
4
4
  export type DrivableVehiclePosition = readonly [number, number, number];
5
+ /** The pose fields {@link tickDrivableVehicle} reads from a sim step; `KinematicVehicleStep` and `VehicleDynamicsStep` both carry them. */
6
+ export interface DrivableSimStep {
7
+ position: readonly [number, number, number];
8
+ heading: number;
9
+ bodyPitch: number;
10
+ bodyRoll: number;
11
+ airOffset: number;
12
+ }
13
+ /** Any ground-vehicle sim {@link tickDrivableVehicle} can drive: `KinematicVehicle` or `VehicleDynamics`. */
14
+ export interface DrivableSim<TModifiers, TStep extends DrivableSimStep> {
15
+ tick(dt: number, axis: AxisInput, modifiers?: TModifiers): TStep;
16
+ }
5
17
  /** Options for {@link tickDrivableVehicle} — ground snapping and per-tick tuning modifiers. */
6
- export interface DrivableVehicleOptions {
18
+ export interface DrivableVehicleOptions<TModifiers = KinematicVehicleModifiers> {
7
19
  /** Resamples world-space Y each tick (terrain height, a ramp, a bridge deck); omit to keep the sim's own flat `y`. */
8
20
  groundHeight?: (x: number, z: number) => number;
9
- modifiers?: KinematicVehicleModifiers;
21
+ modifiers?: TModifiers;
10
22
  }
11
23
  /** A `setPose`-ready patch — spread straight into `entities.setPose(vehicleId, drive.pose)`. */
12
24
  export interface DrivableVehiclePose {
@@ -17,15 +29,17 @@ export interface DrivableVehiclePose {
17
29
  dt: number;
18
30
  }
19
31
  /** {@link tickDrivableVehicle}'s result — the ready-to-apply pose patch plus the raw sim step for HUD/telemetry reads. */
20
- export interface DrivableVehicleStep {
32
+ export interface DrivableVehicleStep<TStep extends DrivableSimStep = KinematicVehicleStep> {
21
33
  pose: DrivableVehiclePose;
22
- step: KinematicVehicleStep;
34
+ step: TStep;
23
35
  }
24
36
  /**
25
- * Connects an `AxisInput` sample straight through a {@link KinematicVehicle} to a scene entity's pose
26
- * for one tick (#533.1) — the throttle/steer/handbrake → sim → `setPose` loop every drivable-vehicle
37
+ * Connects an `AxisInput` sample straight through a ground-vehicle sim (`KinematicVehicle` or
38
+ * `VehicleDynamics`) to a scene entity's pose for one tick (#533.1) — the throttle/steer/handbrake → sim → `setPose` loop every drivable-vehicle
27
39
  * game hand-rolled. Ground-snaps the result when `groundHeight` is given (terrain-following cars, not
28
40
  * just flat racetracks). Pair with `scene/vehicleSeat` for who is allowed to drive and where the camera
29
41
  * points; this function only steps the sim and shapes the pose patch, nothing else.
42
+ *
43
+ * @capability drivable-vehicle drive a car from throttle/steer/handbrake input each tick: step the vehicle sim and pose its entity
30
44
  */
31
- export declare function tickDrivableVehicle(vehicle: KinematicVehicle, dt: number, axis: AxisInput, options?: DrivableVehicleOptions): DrivableVehicleStep;
45
+ export declare function tickDrivableVehicle<TStep extends DrivableSimStep = KinematicVehicleStep, TModifiers = KinematicVehicleModifiers>(vehicle: DrivableSim<TModifiers, TStep>, dt: number, axis: AxisInput, options?: DrivableVehicleOptions<TModifiers>): DrivableVehicleStep<TStep>;
@@ -1,9 +1,11 @@
1
1
  /**
2
- * Connects an `AxisInput` sample straight through a {@link KinematicVehicle} to a scene entity's pose
3
- * for one tick (#533.1) — the throttle/steer/handbrake → sim → `setPose` loop every drivable-vehicle
2
+ * Connects an `AxisInput` sample straight through a ground-vehicle sim (`KinematicVehicle` or
3
+ * `VehicleDynamics`) to a scene entity's pose for one tick (#533.1) — the throttle/steer/handbrake → sim → `setPose` loop every drivable-vehicle
4
4
  * game hand-rolled. Ground-snaps the result when `groundHeight` is given (terrain-following cars, not
5
5
  * just flat racetracks). Pair with `scene/vehicleSeat` for who is allowed to drive and where the camera
6
6
  * points; this function only steps the sim and shapes the pose patch, nothing else.
7
+ *
8
+ * @capability drivable-vehicle drive a car from throttle/steer/handbrake input each tick: step the vehicle sim and pose its entity
7
9
  */
8
10
  export function tickDrivableVehicle(vehicle, dt, axis, options = {}) {
9
11
  const step = vehicle.tick(dt, axis, options.modifiers);
@@ -27,7 +27,10 @@ export interface FlightControlRates {
27
27
  }
28
28
  /** Axes an aircraft can bind — which actions drive each flight control. */
29
29
  export type AircraftAxis = "pitch" | "roll" | "yaw" | "throttle" | "collective" | "airbrake" | "afterburner" | "vectoring";
30
- /** Data-first physical tuning shared by all aircraft instances of one catalog type. */
30
+ /**
31
+ * Data-first physical tuning shared by all aircraft instances of one catalog type.
32
+ * @deprecated Rotation is commanded from rates with pitch clamped to ±0.49π, so it can't loop, and mass and stall angle are fixed. Use `createRigidAircraft` (`physics/aircraftDynamics`), whose rotation comes from surface forces on an inertia tensor.
33
+ */
31
34
  export interface AircraftTuning {
32
35
  kind: AircraftKind;
33
36
  mass: number;
@@ -73,6 +76,15 @@ export interface AircraftStep {
73
76
  grounded: boolean;
74
77
  gForce: number;
75
78
  }
79
+ /** Serializable integrator state of an {@link AircraftDynamics}: what `restore` needs to resume bit-for-bit. */
80
+ export interface AircraftDynamicsState {
81
+ position: FlightVector;
82
+ velocity: FlightVector;
83
+ rotation: FlightVector;
84
+ angular: FlightVector;
85
+ rotor: number;
86
+ lastVerticalSpeed: number;
87
+ }
76
88
  /** Stateful six-degree-of-freedom aircraft simulation. */
77
89
  export interface AircraftDynamics {
78
90
  tick(dt: number, input: FlightControlInput): AircraftStep;
@@ -83,8 +95,13 @@ export interface AircraftDynamics {
83
95
  velocity(): FlightVector;
84
96
  resetTo(position: FlightVector, rotation?: FlightVector): void;
85
97
  setVelocity(velocity: FlightVector): void;
98
+ /** Swap tuning in place (damage, upgrades, a live tuning panel) without losing pose or momentum. */
99
+ retune(next: AircraftTuning): void;
100
+ snapshot(): AircraftDynamicsState;
101
+ restore(state: AircraftDynamicsState): void;
86
102
  }
87
- /** Six-degree-of-freedom arcade flight model for fixed-wing, helicopter, and VTOL aircraft.
88
- * @capability flight-dynamics simulate fixed-wing, helicopter, and VTOL aircraft
103
+ /**
104
+ * Six-degree-of-freedom arcade flight model for fixed-wing, helicopter, and VTOL aircraft.
105
+ * @deprecated Rotation is commanded from rates with pitch clamped to ±0.49π, so it can't loop, and mass and stall angle are fixed. Use `createRigidAircraft` (`physics/aircraftDynamics`), whose rotation comes from surface forces on an inertia tensor.
89
106
  */
90
- export declare function createAircraftDynamics(tuning: AircraftTuning, options?: AircraftOptions): AircraftDynamics;
107
+ export declare function createAircraftDynamics(initialTuning: AircraftTuning, options?: AircraftOptions): AircraftDynamics;
@@ -29,18 +29,20 @@ function wrapAngle(value) {
29
29
  angle += Math.PI * 2;
30
30
  return angle;
31
31
  }
32
- /** Six-degree-of-freedom arcade flight model for fixed-wing, helicopter, and VTOL aircraft.
33
- * @capability flight-dynamics simulate fixed-wing, helicopter, and VTOL aircraft
32
+ /**
33
+ * Six-degree-of-freedom arcade flight model for fixed-wing, helicopter, and VTOL aircraft.
34
+ * @deprecated Rotation is commanded from rates with pitch clamped to ±0.49π, so it can't loop, and mass and stall angle are fixed. Use `createRigidAircraft` (`physics/aircraftDynamics`), whose rotation comes from surface forces on an inertia tensor.
34
35
  */
35
- export function createAircraftDynamics(tuning, options = {}) {
36
+ export function createAircraftDynamics(initialTuning, options = {}) {
37
+ let tuning = initialTuning;
36
38
  let position = [...(options.position ?? [0, 0, 0])];
37
39
  let velocity = [...(options.velocity ?? [0, 0, 0])];
38
40
  let rotation = [...(options.rotation ?? [0, 0, 0])];
39
41
  let angular = [0, 0, 0];
40
42
  let rotor = 0;
41
43
  let lastVerticalSpeed = velocity[1];
42
- const gravity = Math.abs(tuning.gravity ?? 9.81);
43
- const gravityField = options.gravityField ?? uniformGravity([0, -gravity, 0]);
44
+ let gravity = Math.abs(tuning.gravity ?? 9.81);
45
+ let gravityField = options.gravityField ?? uniformGravity([0, -gravity, 0]);
44
46
  const windAt = options.wind ?? (() => [0, 0, 0]);
45
47
  const groundAt = options.groundHeight ?? (() => 0);
46
48
  return {
@@ -161,5 +163,26 @@ export function createAircraftDynamics(tuning, options = {}) {
161
163
  velocity = [...nextVelocity];
162
164
  lastVerticalSpeed = velocity[1];
163
165
  },
166
+ retune(next) {
167
+ tuning = next;
168
+ gravity = Math.abs(next.gravity ?? 9.81);
169
+ gravityField = options.gravityField ?? uniformGravity([0, -gravity, 0]);
170
+ },
171
+ snapshot: () => ({
172
+ position: [...position],
173
+ velocity: [...velocity],
174
+ rotation: [...rotation],
175
+ angular: [...angular],
176
+ rotor,
177
+ lastVerticalSpeed,
178
+ }),
179
+ restore(state) {
180
+ position = [...state.position];
181
+ velocity = [...state.velocity];
182
+ rotation = [...state.rotation];
183
+ angular = [...state.angular];
184
+ rotor = state.rotor;
185
+ lastVerticalSpeed = state.lastVerticalSpeed;
186
+ },
164
187
  };
165
188
  }
@@ -1,5 +1,46 @@
1
1
  import type { PhysicsBounds, PhysicsWorld } from "./physicsWorld.js";
2
2
  export type ForceMode = "impulse" | "velocity" | "accelerate";
3
+ /** World-space force position, axis or acceleration vector. */
4
+ export type ForceVector = readonly [number, number, number];
5
+ /** Renderer-free localized acceleration shared by physical actors and cosmetic particles. */
6
+ export interface ForceFieldConfig {
7
+ center: ForceVector;
8
+ shape: {
9
+ kind: "sphere";
10
+ radius: number;
11
+ } | {
12
+ kind: "box";
13
+ halfExtents: ForceVector;
14
+ };
15
+ /** Positive attracts toward the center; negative repels. Units/s². */
16
+ strength: number;
17
+ /** Edge-to-center falloff exponent; zero is uniform. Default 1. */
18
+ attenuation?: number;
19
+ /** Blend radial attraction with `direction`, from 0 to 1. */
20
+ directionality?: number;
21
+ /** World-space direction; default [0, 0, -1]. */
22
+ direction?: ForceVector;
23
+ /** Signed tangential acceleration around a world-space axis. */
24
+ vortex?: {
25
+ axis: ForceVector;
26
+ strength: number;
27
+ /** Independent signed acceleration along the normalized axis, in units/s². */
28
+ lift?: number;
29
+ };
30
+ /** Affected target bits; omission affects every target. */
31
+ mask?: number;
32
+ /** Cap the combined acceleration magnitude. */
33
+ maxAcceleration?: number;
34
+ }
35
+ /** Validate force authoring once before sampling in a hot loop.
36
+ * Coordinates stay within the safe integer range; acceleration magnitudes stay within 1e12 units/s².
37
+ * @capability force-field-validation reject invalid localized force authoring before simulation
38
+ */
39
+ export declare function validateForceField(config: ForceFieldConfig): void;
40
+ /** Sample localized acceleration; `out` permits allocation-free integration.
41
+ * @capability localized-forces sample signed attraction, directional force, vortex spin and axial lift with target masks and caps
42
+ */
43
+ export declare function sampleForceField(config: ForceFieldConfig, position: ForceVector, targetMask?: number, out?: [number, number, number]): [number, number, number];
3
44
  export interface ForceVolumeConfig {
4
45
  /** Region a body's center must be inside to be affected. */
5
46
  bounds: PhysicsBounds;