@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
@@ -1,5 +1,10 @@
1
1
  import { steerYaw } from "../movement/steering.js";
2
2
  import { DEFAULT_GRIP_CURVE, sampleGripCurve } from "./vehicleBody.js";
3
+ /**
4
+ * Arcade ground-vehicle sim (car, kart, bike) with throttle, brake, steer, drift and retunable handling.
5
+ *
6
+ * @capability kinematic-vehicle arcade car/kart driving physics with throttle, brake, steer and retunable handling
7
+ */
3
8
  export function createKinematicVehicle(initialTuning, options = {}) {
4
9
  // Mutable so `retune` can swap the whole block without disturbing the integrator's state; every
5
10
  // read below is per-tick rather than captured at construction for the same reason.
@@ -231,8 +236,8 @@ export function createKinematicVehicle(initialTuning, options = {}) {
231
236
  const toZ = z + vz * dt;
232
237
  if (clampMove !== undefined && dt > 0) {
233
238
  const allowed = clampMove([x, z], [toX, toZ]);
234
- vx = (allowed[0] - x) / dt;
235
- vz = (allowed[1] - z) / dt;
239
+ vx = (allowed.motion?.[0] ?? allowed[0] - x) / dt;
240
+ vz = (allowed.motion?.[1] ?? allowed[1] - z) / dt;
236
241
  x = allowed[0];
237
242
  z = allowed[1];
238
243
  }
@@ -309,6 +314,16 @@ export function createKinematicVehicle(initialTuning, options = {}) {
309
314
  retune(next) {
310
315
  tuning = next;
311
316
  },
317
+ snapshot: () => ({
318
+ x, y, z, heading, vx, vz, gearIndex, rpm, shiftRemaining, steerAngle, yawRate,
319
+ previousForwardSpeed, bodyPitch, bodyRoll, airOffset, verticalVelocity,
320
+ }),
321
+ restore(state) {
322
+ ({
323
+ x, y, z, heading, vx, vz, gearIndex, rpm, shiftRemaining, steerAngle, yawRate,
324
+ previousForwardSpeed, bodyPitch, bodyRoll, airOffset, verticalVelocity,
325
+ } = state);
326
+ },
312
327
  resetTo(position, nextHeading) {
313
328
  x = position[0];
314
329
  y = position[1];
@@ -52,6 +52,10 @@ export interface BodyDesc {
52
52
  mask?: number;
53
53
  /** Continuous collision detection for fast small bodies (bullets, thrown props). */
54
54
  ccd?: boolean;
55
+ /** Per-second linear velocity damping on this body (air drag, water); default `0`. */
56
+ linearDamping?: number;
57
+ /** Per-second angular velocity damping on this body; default `0`. Ignored by rotation-free backends. */
58
+ angularDamping?: number;
55
59
  asleep?: boolean;
56
60
  userData?: unknown;
57
61
  }
@@ -170,6 +174,14 @@ export interface PhysicsBackend {
170
174
  setVelocity(handle: BodyHandle, velocity: PhysicsVec3): void;
171
175
  setAngularVelocity(handle: BodyHandle, angularVelocity: PhysicsVec3): void;
172
176
  applyImpulse(handle: BodyHandle, impulse: PhysicsVec3, point?: PhysicsVec3): void;
177
+ /**
178
+ * Apply a world-space force, N, over the next {@link step} only (thrust, wind, buoyancy, a tow rope). Call it
179
+ * every tick the force should act. A `point` off the centre of mass also applies torque on rotation backends.
180
+ * Pending forces are not part of a snapshot.
181
+ */
182
+ applyForce(handle: BodyHandle, force: PhysicsVec3, point?: PhysicsVec3): void;
183
+ /** Apply a world-space torque, N·m, over the next {@link step} only; a no-op on rotation-free backends. */
184
+ applyTorque(handle: BodyHandle, torque: PhysicsVec3): void;
173
185
  /** Move a kinematic body so the solver sees its velocity over the next step (platforms, doors, hands). */
174
186
  setKinematicTarget(handle: BodyHandle, position: PhysicsVec3, rotation?: PhysicsQuat): void;
175
187
  wake(handle: BodyHandle): void;
@@ -109,6 +109,40 @@ export function runPhysicsBackendConformance(create, harness) {
109
109
  const later = backend.addBody({ shape: { kind: "sphere", radius: 0.3 }, position: [2, 2, 2] });
110
110
  expect(later).not.toBe(crate);
111
111
  });
112
+ test("applyForce accelerates at F/m for one step and then stops acting", () => {
113
+ const backend = create();
114
+ const held = backend.addBody({ shape: { kind: "sphere", radius: 0.5 }, position: [0, 20, 0], mass: 2 });
115
+ const once = backend.addBody({ shape: { kind: "sphere", radius: 0.5 }, position: [10, 20, 0], mass: 2 });
116
+ backend.applyForce(once, [4, 0, 0]);
117
+ for (let i = 0; i < 60; i += 1) {
118
+ backend.applyForce(held, [4, 0, 0]);
119
+ backend.step(1 / 60);
120
+ }
121
+ expect(backend.body(held).velocity[0]).toBeGreaterThan(1.6);
122
+ expect(backend.body(held).velocity[0]).toBeLessThan(2.2);
123
+ expect(backend.body(once).velocity[0]).toBeGreaterThan(0.02);
124
+ expect(backend.body(once).velocity[0]).toBeLessThan(0.05);
125
+ });
126
+ test("linear damping bleeds a body's speed faster than an undamped twin", () => {
127
+ const backend = create();
128
+ const damped = backend.addBody({ shape: { kind: "sphere", radius: 0.5 }, position: [0, 30, 0], velocity: [10, 0, 0], linearDamping: 1.5 });
129
+ const free = backend.addBody({ shape: { kind: "sphere", radius: 0.5 }, position: [0, 30, 20], velocity: [10, 0, 0] });
130
+ settle(backend, 1);
131
+ expect(backend.body(damped).velocity[0]).toBeLessThan(backend.body(free).velocity[0] * 0.5);
132
+ });
133
+ test("applyTorque spins a body on rotation backends and is harmless elsewhere", () => {
134
+ const backend = create();
135
+ const wheel = backend.addBody({ shape: { kind: "box", halfExtents: [0.5, 0.5, 0.5] }, position: [0, 30, 0] });
136
+ for (let i = 0; i < 30; i += 1) {
137
+ backend.applyTorque(wheel, [0, 2, 0]);
138
+ backend.step(1 / 60);
139
+ }
140
+ const spin = backend.body(wheel).angularVelocity[1];
141
+ if (backend.capabilities.rotation)
142
+ expect(spin).toBeGreaterThan(0.1);
143
+ else
144
+ expect(spin).toBe(0);
145
+ });
112
146
  test("capabilities describe the backend", () => {
113
147
  const backend = create();
114
148
  expect(backend.capabilities.shapes.length).toBeGreaterThan(1);
@@ -21,6 +21,7 @@ interface SavedBody {
21
21
  position: [number, number, number];
22
22
  velocity: [number, number, number];
23
23
  sleeping: boolean;
24
+ linearDamping?: number;
24
25
  }
25
26
  /** Serializable backend state: every live body and joint, with handles preserved. */
26
27
  export interface PhysicsWorldBackendState {
@@ -1,5 +1,10 @@
1
1
  import { IDENTITY_QUAT, isIdentityQuat, shapeHalfExtents, } from "./physicsBackend.js";
2
2
  import { PhysicsWorld } from "./physicsWorld.js";
3
+ /** Exact touching is not overlap; account for the same few-ulp face rounding as shapecast. */
4
+ function axisOverlaps(center, bodyCenter, combinedHalf) {
5
+ const tolerance = Number.EPSILON * 8 * Math.max(1, Math.abs(center), Math.abs(bodyCenter - combinedHalf), Math.abs(bodyCenter + combinedHalf));
6
+ return combinedHalf - Math.abs(bodyCenter - center) > tolerance;
7
+ }
3
8
  const ALL_LAYERS = 0xffffffff;
4
9
  const CAPABILITIES = {
5
10
  rotation: false,
@@ -132,6 +137,8 @@ export function createPhysicsWorldBackend(options) {
132
137
  half,
133
138
  kinematicVelocity: [0, 0, 0],
134
139
  kinematicTarget: null,
140
+ linearDamping: desc.linearDamping ?? 0,
141
+ force: [0, 0, 0],
135
142
  });
136
143
  handleOfIndex.set(index, handle);
137
144
  masses.set(handle, desc.mass ?? 1);
@@ -248,6 +255,17 @@ export function createPhysicsWorldBackend(options) {
248
255
  const invMass = world.invMass[i];
249
256
  world.setVelocity(i, world.velX[i] + impulse[0] * invMass, world.velY[i] + impulse[1] * invMass, world.velZ[i] + impulse[2] * invMass);
250
257
  },
258
+ applyForce(handle, force) {
259
+ const rec = record(handle);
260
+ if (rec.kind !== "dynamic")
261
+ return;
262
+ rec.force[0] += force[0];
263
+ rec.force[1] += force[1];
264
+ rec.force[2] += force[2];
265
+ },
266
+ applyTorque(handle) {
267
+ record(handle);
268
+ },
251
269
  setKinematicTarget(handle, position) {
252
270
  const rec = record(handle);
253
271
  rec.kinematicTarget = [position[0], position[1], position[2]];
@@ -288,6 +306,24 @@ export function createPhysicsWorldBackend(options) {
288
306
  world.setPosition(i, target[0], target[1], target[2]);
289
307
  rec.kinematicTarget = null;
290
308
  }
309
+ for (const rec of records.values()) {
310
+ if (rec.kind !== "dynamic")
311
+ continue;
312
+ const f = rec.force;
313
+ const i = rec.index;
314
+ if (f[0] !== 0 || f[1] !== 0 || f[2] !== 0) {
315
+ const scale = world.invMass[i] * dt;
316
+ world.setVelocity(i, world.velX[i] + f[0] * scale, world.velY[i] + f[1] * scale, world.velZ[i] + f[2] * scale);
317
+ world.wake(i);
318
+ f[0] = 0;
319
+ f[1] = 0;
320
+ f[2] = 0;
321
+ }
322
+ if (rec.linearDamping > 0) {
323
+ const keep = Math.max(0, 1 - rec.linearDamping * dt);
324
+ world.setVelocity(i, world.velX[i] * keep, world.velY[i] * keep, world.velZ[i] * keep);
325
+ }
326
+ }
291
327
  world.step(dt);
292
328
  },
293
329
  raycast(desc) {
@@ -377,6 +413,17 @@ export function createPhysicsWorldBackend(options) {
377
413
  least === 2 ? -1 : least === 3 ? 1 : 0,
378
414
  least === 4 ? -1 : least === 5 ? 1 : 0,
379
415
  ];
416
+ // Expanding an AABB by a capsule's half-height can round exact floor contact inside
417
+ // by a few ulps. Keep entering contacts and real penetration, but allow departing or
418
+ // tangent motion through this numerical touch so it cannot mask a later wall/ceiling.
419
+ const faceScale = least < 2
420
+ ? Math.max(1, Math.abs(ox), Math.abs(minX), Math.abs(maxX))
421
+ : least < 4
422
+ ? Math.max(1, Math.abs(oy), Math.abs(minY), Math.abs(maxY))
423
+ : Math.max(1, Math.abs(oz), Math.abs(minZ), Math.abs(maxZ));
424
+ const touchTolerance = Number.EPSILON * 8 * faceScale;
425
+ if (motionLen > 0 && pens[least] <= touchTolerance && mx * normal[0] + my * normal[1] + mz * normal[2] >= 0)
426
+ continue;
380
427
  }
381
428
  else {
382
429
  if (!(motionLen > 0))
@@ -406,11 +453,11 @@ export function createPhysicsWorldBackend(options) {
406
453
  if (!bodyMatches(rec, desc.mask, desc.exclude, handle))
407
454
  continue;
408
455
  const i = rec.index;
409
- if (Math.abs(world.posX[i] - desc.position[0]) >= rec.half[0] + half[0])
456
+ if (!axisOverlaps(desc.position[0], world.posX[i], rec.half[0] + half[0]))
410
457
  continue;
411
- if (Math.abs(world.posY[i] - desc.position[1]) >= rec.half[1] + half[1])
458
+ if (!axisOverlaps(desc.position[1], world.posY[i], rec.half[1] + half[1]))
412
459
  continue;
413
- if (Math.abs(world.posZ[i] - desc.position[2]) >= rec.half[2] + half[2])
460
+ if (!axisOverlaps(desc.position[2], world.posZ[i], rec.half[2] + half[2]))
414
461
  continue;
415
462
  out.push(handle);
416
463
  }
@@ -437,6 +484,7 @@ export function createPhysicsWorldBackend(options) {
437
484
  position: [world.posX[i], world.posY[i], world.posZ[i]],
438
485
  velocity: [world.velX[i], world.velY[i], world.velZ[i]],
439
486
  sleeping: world.isSleeping(i),
487
+ ...(rec.linearDamping > 0 ? { linearDamping: rec.linearDamping } : {}),
440
488
  });
441
489
  }
442
490
  return {
@@ -465,6 +513,7 @@ export function createPhysicsWorldBackend(options) {
465
513
  layers: body.layers,
466
514
  mask: body.mask,
467
515
  userData: body.userData,
516
+ ...(body.linearDamping === undefined ? {} : { linearDamping: body.linearDamping }),
468
517
  }, body.sleeping);
469
518
  }
470
519
  for (const joint of saved.joints) {
@@ -36,6 +36,6 @@ export declare class SpatialGrid {
36
36
  rebuild(count: number, xs: Float32Array, zs: Float32Array): void;
37
37
  /** Fill `out` with every entity within `radius` of (x,z); returns the hit count (capped at `out.length`). */
38
38
  queryCircle(x: number, z: number, radius: number, out: Int32Array): number;
39
- /** Invoke `cb(a, b)` once for each unordered pair within `maxDistance` (mutual separation/overlap). */
39
+ /** Invoke `cb(a, b)` once for each unordered pair within finite, nonnegative `maxDistance`, including distances larger than `cellSize`. */
40
40
  forEachPair(maxDistance: number, cb: (a: number, b: number) => void): void;
41
41
  }
@@ -103,25 +103,24 @@ export class SpatialGrid {
103
103
  }
104
104
  return n;
105
105
  }
106
- /** Invoke `cb(a, b)` once for each unordered pair within `maxDistance` (mutual separation/overlap). */
106
+ /** Invoke `cb(a, b)` once for each unordered pair within finite, nonnegative `maxDistance`, including distances larger than `cellSize`. */
107
107
  forEachPair(maxDistance, cb) {
108
+ if (!Number.isFinite(maxDistance) || maxDistance < 0) {
109
+ throw new RangeError("SpatialGrid: maxDistance must be finite and nonnegative");
110
+ }
108
111
  const xs = this.xs;
109
112
  const zs = this.zs;
110
113
  if (xs === null || zs === null)
111
114
  return;
112
115
  const d2 = maxDistance * maxDistance;
113
116
  const nx = this.nx;
114
- const nz = this.nz;
115
117
  for (let i = 0; i < this.entityCount; i += 1) {
116
- const c = this.cellOfBody[i];
117
- const cz = (c / nx) | 0;
118
- const cx = c - cz * nx;
119
118
  const x = xs[i];
120
119
  const z = zs[i];
121
- const z0 = cz > 0 ? cz - 1 : cz;
122
- const z1 = cz < nz - 1 ? cz + 1 : cz;
123
- const x0 = cx > 0 ? cx - 1 : cx;
124
- const x1 = cx < nx - 1 ? cx + 1 : cx;
120
+ const z0 = this.cellZ(z - maxDistance);
121
+ const z1 = this.cellZ(z + maxDistance);
122
+ const x0 = this.cellX(x - maxDistance);
123
+ const x1 = this.cellX(x + maxDistance);
125
124
  for (let gz = z0; gz <= z1; gz += 1) {
126
125
  const rowBase = gz * nx;
127
126
  for (let gx = x0; gx <= x1; gx += 1) {
@@ -0,0 +1,49 @@
1
+ import type { BodyHandle, PhysicsBackend, PhysicsVec3 } from "./physicsBackend.js";
2
+ /** Options for {@link createVehicleBackendLink}. */
3
+ export interface VehicleBackendLinkOptions {
4
+ /** Chassis collision box half extents, m (width/2, height/2, length/2), heading along +z. */
5
+ halfExtents: PhysicsVec3;
6
+ /** Gap kept between the box bottom and the car's base height, so the cast never scrapes the ground (default `0.1`). */
7
+ clearance?: number;
8
+ /** Collision layer bitmask of the chassis body (default `1`). */
9
+ layers?: number;
10
+ /** Layers the chassis body collides with and shoves in the solver (default all). */
11
+ mask?: number;
12
+ /** Layers that stop the car in `clampMove` (default all). Leave props off it so the car shoves them instead of stopping. */
13
+ blockMask?: number;
14
+ /** Contact normals steeper than this `y` count as ground, not walls, and never block (default `0.7`). */
15
+ groundNormalY?: number;
16
+ }
17
+ /** The pose fields {@link VehicleBackendLink.sync} reads; any vehicle step (`VehicleDynamicsStep`, `KinematicVehicleStep`) fits. */
18
+ export interface VehicleLinkPose {
19
+ position: readonly [number, number, number];
20
+ heading: number;
21
+ bodyPitch?: number;
22
+ bodyRoll?: number;
23
+ }
24
+ /** A wall the last clamped move ran into. */
25
+ export interface VehicleLinkHit {
26
+ body: BodyHandle;
27
+ normal: PhysicsVec3;
28
+ /** Metres of this move the wall absorbed; divide by `dt` for the impact speed. */
29
+ blocked: number;
30
+ }
31
+ /** A vehicle sim's presence in a physics backend: a kinematic chassis that shoves props, and a wall-aware move clamp. */
32
+ export interface VehicleBackendLink {
33
+ readonly body: BodyHandle;
34
+ /** Pass as the sim's `clampMove` option: sweeps the chassis box and stops at walls, sliding along them. */
35
+ clampMove(from: readonly [number, number], to: readonly [number, number]): readonly [number, number];
36
+ /** Move the kinematic chassis to the sim's pose; call after each sim tick, before the backend steps. */
37
+ sync(pose: VehicleLinkPose): void;
38
+ /** The wall the most recent `clampMove` hit, or `null`. */
39
+ lastHit(): VehicleLinkHit | null;
40
+ dispose(): void;
41
+ }
42
+ /**
43
+ * Puts a vehicle sim (`createVehicleDynamics`, `createKinematicVehicle`) into a {@link PhysicsBackend} world without
44
+ * handing its handling to the rigid-body solver: the sim still owns grip and balance, while a kinematic chassis box
45
+ * follows it so Rapier or `PhysicsWorld` props get shoved, and `clampMove` sweeps that box so walls and parked cars
46
+ * stop it and it slides along them.
47
+ * @capability vehicle-backend-link collide a vehicle sim with a physics backend — shove props, stop at walls, slide along them
48
+ */
49
+ export declare function createVehicleBackendLink(backend: PhysicsBackend, options: VehicleBackendLinkOptions): VehicleBackendLink;
@@ -0,0 +1,102 @@
1
+ /** Yaw about +y (heading), then pitch about the car's x, then roll about its z, as a quaternion. */
2
+ function poseQuat(heading, pitch, roll) {
3
+ const hy = heading / 2;
4
+ const hp = pitch / 2;
5
+ const hr = roll / 2;
6
+ const cy = Math.cos(hy);
7
+ const sy = Math.sin(hy);
8
+ const cp = Math.cos(hp);
9
+ const sp = Math.sin(hp);
10
+ const cr = Math.cos(hr);
11
+ const sr = Math.sin(hr);
12
+ return [
13
+ cy * sp * cr + sy * cp * sr,
14
+ sy * cp * cr - cy * sp * sr,
15
+ cy * cp * sr - sy * sp * cr,
16
+ cy * cp * cr + sy * sp * sr,
17
+ ];
18
+ }
19
+ /**
20
+ * Puts a vehicle sim (`createVehicleDynamics`, `createKinematicVehicle`) into a {@link PhysicsBackend} world without
21
+ * handing its handling to the rigid-body solver: the sim still owns grip and balance, while a kinematic chassis box
22
+ * follows it so Rapier or `PhysicsWorld` props get shoved, and `clampMove` sweeps that box so walls and parked cars
23
+ * stop it and it slides along them.
24
+ * @capability vehicle-backend-link collide a vehicle sim with a physics backend — shove props, stop at walls, slide along them
25
+ */
26
+ export function createVehicleBackendLink(backend, options) {
27
+ const half = options.halfExtents;
28
+ const clearance = options.clearance ?? 0.1;
29
+ const groundNormalY = options.groundNormalY ?? 0.7;
30
+ const shape = { kind: "box", halfExtents: half };
31
+ const pose = { baseY: 0, heading: 0, pitch: 0, roll: 0 };
32
+ const hit = { active: false, value: { body: -1, normal: [0, 0, 0], blocked: 0 } };
33
+ const body = backend.addBody({
34
+ shape,
35
+ kind: "kinematic",
36
+ position: [0, half[1] + clearance, 0],
37
+ layers: options.layers ?? 1,
38
+ ...(options.mask === undefined ? {} : { mask: options.mask }),
39
+ });
40
+ function sweep(x, z, dx, dz) {
41
+ return backend.shapecast({
42
+ shape,
43
+ position: [x, pose.baseY + half[1] + clearance, z],
44
+ rotation: poseQuat(pose.heading, 0, 0),
45
+ motion: [dx, 0, dz],
46
+ exclude: body,
47
+ ...(options.blockMask === undefined ? {} : { mask: options.blockMask }),
48
+ });
49
+ }
50
+ return {
51
+ body,
52
+ clampMove(from, to) {
53
+ hit.active = false;
54
+ let x = from[0];
55
+ let z = from[1];
56
+ let dx = to[0] - x;
57
+ let dz = to[1] - z;
58
+ for (let pass = 0; pass < 2; pass += 1) {
59
+ const length = Math.hypot(dx, dz);
60
+ if (length < 1e-9)
61
+ break;
62
+ const found = sweep(x, z, dx, dz);
63
+ if (found === null || found.normal[1] > groundNormalY) {
64
+ x += dx;
65
+ z += dz;
66
+ break;
67
+ }
68
+ const travel = Math.max(0, found.toi - 0.02 / length);
69
+ x += dx * travel;
70
+ z += dz * travel;
71
+ const nx = found.normal[0];
72
+ const nz = found.normal[2];
73
+ const flat = Math.hypot(nx, nz) || 1;
74
+ const restX = dx * (1 - travel);
75
+ const restZ = dz * (1 - travel);
76
+ const into = (restX * nx + restZ * nz) / flat;
77
+ hit.active = true;
78
+ hit.value = { body: found.body, normal: [nx / flat, 0, nz / flat], blocked: Math.abs(into) };
79
+ dx = restX - (into * nx) / flat;
80
+ dz = restZ - (into * nz) / flat;
81
+ }
82
+ return [x, z];
83
+ },
84
+ sync(next) {
85
+ pose.baseY = next.position[1];
86
+ pose.heading = next.heading;
87
+ pose.pitch = next.bodyPitch ?? 0;
88
+ pose.roll = next.bodyRoll ?? 0;
89
+ const center = [next.position[0], next.position[1] + half[1] + clearance, next.position[2]];
90
+ const rotation = poseQuat(pose.heading, pose.pitch, pose.roll);
91
+ backend.setKinematicTarget(body, center, rotation);
92
+ // A resting prop sleeps, and a kinematic body moved by target does not wake it on every backend.
93
+ for (const touched of backend.overlap({ shape, position: center, rotation, exclude: body }))
94
+ backend.wake(touched);
95
+ },
96
+ lastHit: () => (hit.active ? hit.value : null),
97
+ dispose() {
98
+ if (backend.hasBody(body))
99
+ backend.removeBody(body);
100
+ },
101
+ };
102
+ }