@jgengine/core 0.10.0 → 0.12.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 (727) hide show
  1. package/CHANGELOG.md +39 -2
  2. package/LICENSE +203 -661
  3. package/README.md +1 -1
  4. package/dist/ai/crowd.d.ts +55 -0
  5. package/dist/ai/crowd.js +114 -0
  6. package/dist/ai/flock.d.ts +5 -2
  7. package/dist/ai/flock.js +5 -2
  8. package/dist/ai/groupAssist.d.ts +1 -0
  9. package/dist/ai/groupAssist.js +1 -0
  10. package/dist/ai/heatSystem.d.ts +76 -0
  11. package/dist/ai/heatSystem.js +97 -0
  12. package/dist/ai/interestScheduler.d.ts +107 -0
  13. package/dist/ai/interestScheduler.js +118 -0
  14. package/dist/ai/jobBoard.d.ts +1 -0
  15. package/dist/ai/jobBoard.js +1 -0
  16. package/dist/ai/laneSelect.d.ts +5 -2
  17. package/dist/ai/laneSelect.js +5 -2
  18. package/dist/ai/mobBrain.d.ts +1 -0
  19. package/dist/ai/mobBrain.js +1 -0
  20. package/dist/ai/populationDirector.d.ts +103 -0
  21. package/dist/ai/populationDirector.js +190 -0
  22. package/dist/ai/pursuit.d.ts +56 -0
  23. package/dist/ai/pursuit.js +53 -0
  24. package/dist/ai/spawnDirector.d.ts +7 -2
  25. package/dist/ai/spawnDirector.js +18 -15
  26. package/dist/ai/spawnPoint.d.ts +3 -1
  27. package/dist/ai/spawnPoint.js +3 -1
  28. package/dist/ai/targetAcquisition.d.ts +84 -0
  29. package/dist/ai/targetAcquisition.js +92 -0
  30. package/dist/anim/easing.d.ts +19 -0
  31. package/dist/anim/easing.js +19 -0
  32. package/dist/anim/oscillator.d.ts +12 -0
  33. package/dist/anim/oscillator.js +25 -0
  34. package/dist/area/areaEffectField.d.ts +127 -0
  35. package/dist/area/areaEffectField.js +175 -0
  36. package/dist/area/stackPolicy.d.ts +47 -0
  37. package/dist/area/stackPolicy.js +99 -0
  38. package/dist/authoring.d.ts +2 -1
  39. package/dist/authoring.js +2 -1
  40. package/dist/board/laneBoard.d.ts +13 -0
  41. package/dist/board/laneBoard.js +13 -0
  42. package/dist/board/timelineBoard.d.ts +10 -0
  43. package/dist/board/timelineBoard.js +10 -0
  44. package/dist/combat/abilityKit.d.ts +18 -0
  45. package/dist/combat/abilityKit.js +24 -9
  46. package/dist/combat/animationState.d.ts +5 -0
  47. package/dist/combat/animationState.js +5 -0
  48. package/dist/combat/breakMeters.d.ts +10 -0
  49. package/dist/combat/breakMeters.js +10 -0
  50. package/dist/combat/castRunner.d.ts +5 -0
  51. package/dist/combat/castRunner.js +5 -0
  52. package/dist/combat/comboPoints.d.ts +5 -0
  53. package/dist/combat/comboPoints.js +5 -0
  54. package/dist/combat/comboString.d.ts +5 -0
  55. package/dist/combat/comboString.js +5 -0
  56. package/dist/combat/damageInterceptors.d.ts +192 -0
  57. package/dist/combat/damageInterceptors.js +251 -0
  58. package/dist/combat/damageMatchup.d.ts +68 -0
  59. package/dist/combat/damageMatchup.js +42 -0
  60. package/dist/combat/damageResolution.d.ts +63 -0
  61. package/dist/combat/damageResolution.js +50 -0
  62. package/dist/combat/death.d.ts +7 -1
  63. package/dist/combat/death.js +5 -0
  64. package/dist/combat/deathReason.d.ts +14 -0
  65. package/dist/combat/deathReason.js +1 -0
  66. package/dist/combat/defensiveWindow.d.ts +5 -0
  67. package/dist/combat/defensiveWindow.js +5 -0
  68. package/dist/combat/dotField.d.ts +5 -1
  69. package/dist/combat/dotField.js +5 -1
  70. package/dist/combat/downed.d.ts +5 -0
  71. package/dist/combat/downed.js +5 -0
  72. package/dist/combat/effects.d.ts +5 -0
  73. package/dist/combat/effects.js +5 -0
  74. package/dist/combat/encounterSequence.d.ts +258 -0
  75. package/dist/combat/encounterSequence.js +426 -0
  76. package/dist/combat/hitReaction.d.ts +57 -1
  77. package/dist/combat/hitReaction.js +45 -5
  78. package/dist/combat/magazine.d.ts +54 -0
  79. package/dist/combat/magazine.js +96 -0
  80. package/dist/combat/projectiles.d.ts +5 -0
  81. package/dist/combat/projectiles.js +32 -14
  82. package/dist/combat/receivedDamage.d.ts +117 -0
  83. package/dist/combat/receivedDamage.js +92 -0
  84. package/dist/combat/regenShield.d.ts +5 -1
  85. package/dist/combat/regenShield.js +5 -1
  86. package/dist/combat/renderCues.d.ts +51 -0
  87. package/dist/combat/renderCues.js +44 -0
  88. package/dist/combat/resourcePool.d.ts +5 -0
  89. package/dist/combat/resourcePool.js +5 -0
  90. package/dist/combat/rewardAllocation.d.ts +121 -0
  91. package/dist/combat/rewardAllocation.js +251 -0
  92. package/dist/combat/shotOrigin.d.ts +18 -1
  93. package/dist/combat/shotOrigin.js +39 -0
  94. package/dist/combat/statusApplication.d.ts +96 -0
  95. package/dist/combat/statusApplication.js +81 -0
  96. package/dist/combat/telegraph.d.ts +11 -2
  97. package/dist/combat/telegraph.js +11 -2
  98. package/dist/combat/thresholdCrossings.d.ts +85 -0
  99. package/dist/combat/thresholdCrossings.js +116 -0
  100. package/dist/combat.d.ts +35 -0
  101. package/dist/combat.js +35 -0
  102. package/dist/commands/commandRegistry.d.ts +11 -0
  103. package/dist/commands/commandRegistry.js +9 -2
  104. package/dist/crafting/crop.d.ts +18 -0
  105. package/dist/crafting/crop.js +18 -0
  106. package/dist/crafting/production.d.ts +5 -0
  107. package/dist/crafting/production.js +5 -0
  108. package/dist/devtools/controls.d.ts +38 -0
  109. package/dist/devtools/controls.js +181 -0
  110. package/dist/devtools/devtools.d.ts +5 -197
  111. package/dist/devtools/devtools.js +38 -774
  112. package/dist/devtools/discover.d.ts +32 -0
  113. package/dist/devtools/discover.js +240 -0
  114. package/dist/devtools/fallbackSeams.d.ts +50 -0
  115. package/dist/devtools/fallbackSeams.js +111 -0
  116. package/dist/devtools/frame.d.ts +15 -0
  117. package/dist/devtools/frame.js +247 -0
  118. package/dist/devtools/logs.d.ts +15 -0
  119. package/dist/devtools/logs.js +86 -0
  120. package/dist/devtools/overrides.d.ts +16 -0
  121. package/dist/devtools/overrides.js +66 -0
  122. package/dist/devtools/rewriteTunables.d.ts +17 -0
  123. package/dist/devtools/rewriteTunables.js +276 -0
  124. package/dist/devtools/saveEndpoint.d.ts +46 -0
  125. package/dist/devtools/saveEndpoint.js +48 -0
  126. package/dist/devtools/transformTunables.d.ts +3 -0
  127. package/dist/devtools/transformTunables.js +3 -0
  128. package/dist/devtools/tunableSchema.d.ts +31 -0
  129. package/dist/devtools/tunableSchema.js +31 -0
  130. package/dist/devtools/types.d.ts +212 -0
  131. package/dist/devtools/types.js +1 -0
  132. package/dist/economy/auctionBook.d.ts +122 -0
  133. package/dist/economy/auctionBook.js +213 -0
  134. package/dist/economy/currency.d.ts +12 -1
  135. package/dist/economy/currency.js +12 -1
  136. package/dist/economy/listingBook.d.ts +91 -0
  137. package/dist/economy/listingBook.js +144 -0
  138. package/dist/economy/priceHistory.d.ts +44 -0
  139. package/dist/economy/priceHistory.js +89 -0
  140. package/dist/economy/resourceLedger.d.ts +222 -0
  141. package/dist/economy/resourceLedger.js +353 -0
  142. package/dist/economy/sharedWallet.d.ts +14 -0
  143. package/dist/economy/sharedWallet.js +14 -0
  144. package/dist/economy/techTree.d.ts +12 -0
  145. package/dist/economy/techTree.js +12 -0
  146. package/dist/economy/unlockPoints.d.ts +92 -0
  147. package/dist/economy/unlockPoints.js +103 -0
  148. package/dist/economy/wallet.d.ts +26 -2
  149. package/dist/economy/wallet.js +35 -6
  150. package/dist/editor/commandApply.d.ts +5 -0
  151. package/dist/editor/commandApply.js +755 -0
  152. package/dist/editor/commands.d.ts +235 -7
  153. package/dist/editor/commands.js +82 -124
  154. package/dist/editor/directives.d.ts +94 -0
  155. package/dist/editor/directives.js +206 -0
  156. package/dist/editor/document.d.ts +194 -13
  157. package/dist/editor/document.js +1166 -19
  158. package/dist/editor/entityCatalog.d.ts +42 -0
  159. package/dist/editor/entityCatalog.js +101 -0
  160. package/dist/editor/environment.d.ts +74 -0
  161. package/dist/editor/environment.js +169 -0
  162. package/dist/editor/grid.d.ts +229 -0
  163. package/dist/editor/grid.js +366 -0
  164. package/dist/editor/gridAdapters.d.ts +80 -0
  165. package/dist/editor/gridAdapters.js +134 -0
  166. package/dist/editor/index.d.ts +98 -6
  167. package/dist/editor/index.js +13 -2
  168. package/dist/editor/liveSync.d.ts +127 -0
  169. package/dist/editor/liveSync.js +249 -0
  170. package/dist/editor/minimap.d.ts +31 -0
  171. package/dist/editor/minimap.js +73 -0
  172. package/dist/editor/runtimeInspector.d.ts +88 -0
  173. package/dist/editor/runtimeInspector.js +239 -0
  174. package/dist/editor/streamer.d.ts +66 -0
  175. package/dist/editor/streamer.js +68 -0
  176. package/dist/editor/types.d.ts +250 -1
  177. package/dist/editor/world.d.ts +136 -0
  178. package/dist/editor/world.js +303 -0
  179. package/dist/faction/reputation.d.ts +5 -0
  180. package/dist/faction/reputation.js +5 -0
  181. package/dist/format/distance.d.ts +17 -0
  182. package/dist/format/distance.js +18 -0
  183. package/dist/format/duration.d.ts +27 -0
  184. package/dist/format/duration.js +45 -0
  185. package/dist/format/speed.d.ts +17 -0
  186. package/dist/format/speed.js +25 -0
  187. package/dist/game/breeding.d.ts +161 -0
  188. package/dist/game/breeding.js +181 -0
  189. package/dist/game/cameraConfig.d.ts +451 -0
  190. package/dist/game/cameraConfig.js +1 -0
  191. package/dist/game/connectedPlayers.d.ts +6 -4
  192. package/dist/game/connectedPlayers.js +2 -2
  193. package/dist/game/cosmetics.d.ts +9 -0
  194. package/dist/game/cosmetics.js +16 -0
  195. package/dist/game/defineGame.d.ts +110 -5
  196. package/dist/game/defineGame.js +49 -4
  197. package/dist/game/defineSystem.d.ts +90 -0
  198. package/dist/game/defineSystem.js +47 -0
  199. package/dist/game/dialogue.d.ts +33 -0
  200. package/dist/game/dialogue.js +21 -0
  201. package/dist/game/events.d.ts +63 -13
  202. package/dist/game/events.js +5 -0
  203. package/dist/game/feed.d.ts +33 -0
  204. package/dist/game/feed.js +35 -2
  205. package/dist/game/gamePhase.d.ts +11 -0
  206. package/dist/game/gamePhase.js +16 -0
  207. package/dist/game/leaderboard.d.ts +5 -0
  208. package/dist/game/leaderboard.js +5 -0
  209. package/dist/game/loadout.d.ts +5 -0
  210. package/dist/game/loadout.js +5 -0
  211. package/dist/game/lootFilter.d.ts +2 -0
  212. package/dist/game/lootFilter.js +2 -0
  213. package/dist/game/lootPipeline.d.ts +164 -0
  214. package/dist/game/lootPipeline.js +216 -0
  215. package/dist/game/lootTable.d.ts +19 -2
  216. package/dist/game/lootTable.js +15 -5
  217. package/dist/game/objectives.d.ts +43 -0
  218. package/dist/game/objectives.js +36 -0
  219. package/dist/game/paramLayers.d.ts +148 -0
  220. package/dist/game/paramLayers.js +185 -0
  221. package/dist/game/ping.d.ts +5 -0
  222. package/dist/game/ping.js +5 -0
  223. package/dist/game/playableGame.d.ts +116 -432
  224. package/dist/game/playableGame.js +1 -1
  225. package/dist/game/progression.d.ts +35 -0
  226. package/dist/game/progression.js +19 -0
  227. package/dist/game/quest.d.ts +8 -0
  228. package/dist/game/quest.js +30 -3
  229. package/dist/game/race.d.ts +141 -0
  230. package/dist/game/race.js +169 -0
  231. package/dist/game/recordBook.d.ts +3 -1
  232. package/dist/game/recordBook.js +4 -1
  233. package/dist/game/ruleSelection.d.ts +75 -0
  234. package/dist/game/ruleSelection.js +127 -0
  235. package/dist/game/runDraft.d.ts +5 -0
  236. package/dist/game/runDraft.js +5 -0
  237. package/dist/game/saveStore.d.ts +111 -0
  238. package/dist/game/saveStore.js +312 -0
  239. package/dist/game/snapshotHistory.d.ts +67 -0
  240. package/dist/game/snapshotHistory.js +95 -0
  241. package/dist/game/social.d.ts +38 -1
  242. package/dist/game/social.js +71 -0
  243. package/dist/game/spawnPoints.d.ts +5 -0
  244. package/dist/game/spawnPoints.js +5 -0
  245. package/dist/game/systemRuntime.d.ts +50 -0
  246. package/dist/game/systemRuntime.js +157 -0
  247. package/dist/game/systemSchedule.d.ts +72 -0
  248. package/dist/game/systemSchedule.js +242 -0
  249. package/dist/game/toasts.d.ts +43 -0
  250. package/dist/game/toasts.js +44 -0
  251. package/dist/game/trade.d.ts +10 -0
  252. package/dist/game/trade.js +10 -0
  253. package/dist/game/tribe.d.ts +184 -0
  254. package/dist/game/tribe.js +432 -0
  255. package/dist/game/unlocks.d.ts +8 -0
  256. package/dist/game/unlocks.js +16 -0
  257. package/dist/game/vfxInstance.d.ts +138 -0
  258. package/dist/game/vfxInstance.js +96 -0
  259. package/dist/game/worldItem.d.ts +17 -4
  260. package/dist/game/worldItem.js +17 -4
  261. package/dist/gameplay.d.ts +93 -0
  262. package/dist/gameplay.js +93 -0
  263. package/dist/index.d.ts +5 -14
  264. package/dist/index.js +5 -14
  265. package/dist/input/actionBindings.d.ts +23 -6
  266. package/dist/input/actionBindings.js +23 -6
  267. package/dist/input/axisInput.d.ts +7 -2
  268. package/dist/input/axisInput.js +7 -2
  269. package/dist/input/controlGroups.d.ts +67 -0
  270. package/dist/input/controlGroups.js +41 -0
  271. package/dist/input/touchScheme.d.ts +82 -6
  272. package/dist/input/touchScheme.js +123 -10
  273. package/dist/interaction/lockpick.d.ts +61 -0
  274. package/dist/interaction/lockpick.js +168 -0
  275. package/dist/interaction/proximityPrompt.d.ts +2 -0
  276. package/dist/interaction/proximityPrompt.js +2 -0
  277. package/dist/interaction/qte.d.ts +5 -0
  278. package/dist/interaction/qte.js +5 -0
  279. package/dist/interaction/skillCheck.d.ts +5 -0
  280. package/dist/interaction/skillCheck.js +5 -0
  281. package/dist/inventory/encumbrance.d.ts +69 -0
  282. package/dist/inventory/encumbrance.js +68 -0
  283. package/dist/inventory/inventoryModel.d.ts +12 -0
  284. package/dist/inventory/inventoryModel.js +12 -0
  285. package/dist/inventory/shapedGrid.d.ts +16 -0
  286. package/dist/inventory/shapedGrid.js +16 -0
  287. package/dist/inventory/slotModel.d.ts +10 -0
  288. package/dist/inventory/slotModel.js +10 -0
  289. package/dist/item/durability.d.ts +5 -0
  290. package/dist/item/durability.js +6 -3
  291. package/dist/item/itemIdentity.d.ts +171 -0
  292. package/dist/item/itemIdentity.js +138 -0
  293. package/dist/item/itemInstanceRegistry.d.ts +32 -0
  294. package/dist/item/itemInstanceRegistry.js +34 -0
  295. package/dist/item/itemgen.d.ts +163 -0
  296. package/dist/item/itemgen.js +151 -0
  297. package/dist/item/modularItem.d.ts +5 -0
  298. package/dist/item/modularItem.js +5 -0
  299. package/dist/item/use.d.ts +5 -0
  300. package/dist/item/use.js +5 -0
  301. package/dist/item/useBehavior.d.ts +142 -0
  302. package/dist/item/useBehavior.js +105 -0
  303. package/dist/item/weapon.d.ts +5 -0
  304. package/dist/item/weapon.js +5 -0
  305. package/dist/math/scalar.d.ts +25 -0
  306. package/dist/math/scalar.js +47 -0
  307. package/dist/meta/changelog.d.ts +4 -1
  308. package/dist/meta/changelog.js +39 -2
  309. package/dist/movement/avoidance.d.ts +45 -0
  310. package/dist/movement/avoidance.js +123 -0
  311. package/dist/movement/cameraRig.d.ts +8 -0
  312. package/dist/movement/cameraRig.js +8 -0
  313. package/dist/movement/dash.d.ts +11 -0
  314. package/dist/movement/dash.js +11 -0
  315. package/dist/movement/formation.d.ts +135 -0
  316. package/dist/movement/formation.js +194 -0
  317. package/dist/movement/glideModel.d.ts +5 -0
  318. package/dist/movement/glideModel.js +5 -0
  319. package/dist/movement/grappleSwing.d.ts +5 -0
  320. package/dist/movement/grappleSwing.js +5 -0
  321. package/dist/movement/leaderTrail.d.ts +5 -0
  322. package/dist/movement/leaderTrail.js +5 -0
  323. package/dist/movement/movementModel.d.ts +50 -9
  324. package/dist/movement/movementModel.js +75 -29
  325. package/dist/movement/playerMovement.js +167 -12
  326. package/dist/movement/poseState.d.ts +13 -0
  327. package/dist/movement/poseState.js +16 -0
  328. package/dist/movement/steering.d.ts +8 -0
  329. package/dist/movement/steering.js +18 -0
  330. package/dist/multiplayer/combatSnapshot.d.ts +3 -0
  331. package/dist/multiplayer/combatSnapshot.js +16 -8
  332. package/dist/multiplayer/lagCompensation.d.ts +5 -0
  333. package/dist/multiplayer/lagCompensation.js +5 -0
  334. package/dist/multiplayer/presenceModel.d.ts +11 -1
  335. package/dist/multiplayer/presenceModel.js +17 -1
  336. package/dist/multiplayer/simultaneousCommit.d.ts +3 -0
  337. package/dist/multiplayer/simultaneousCommit.js +3 -0
  338. package/dist/multiplayer.d.ts +11 -0
  339. package/dist/multiplayer.js +11 -0
  340. package/dist/nav/pathFollow.d.ts +57 -0
  341. package/dist/nav/pathFollow.js +109 -0
  342. package/dist/orders/orderKinds.d.ts +156 -0
  343. package/dist/orders/orderKinds.js +197 -0
  344. package/dist/orders/orderQueue.d.ts +208 -0
  345. package/dist/orders/orderQueue.js +211 -0
  346. package/dist/physics/drivableVehicle.d.ts +31 -0
  347. package/dist/physics/drivableVehicle.js +16 -0
  348. package/dist/physics/flightDynamics.d.ts +82 -0
  349. package/dist/physics/flightDynamics.js +165 -0
  350. package/dist/physics/gravityField.d.ts +25 -0
  351. package/dist/physics/gravityField.js +41 -0
  352. package/dist/physics/kinematicVehicle.d.ts +108 -1
  353. package/dist/physics/kinematicVehicle.js +212 -15
  354. package/dist/physics/physicsWorld.d.ts +50 -12
  355. package/dist/physics/physicsWorld.js +120 -122
  356. package/dist/physics/radialImpulse.d.ts +25 -0
  357. package/dist/physics/radialImpulse.js +27 -0
  358. package/dist/physics/vehicleObstacles.d.ts +44 -0
  359. package/dist/physics/vehicleObstacles.js +115 -0
  360. package/dist/procedural.d.ts +3 -0
  361. package/dist/procedural.js +3 -0
  362. package/dist/progression/statGraph.d.ts +116 -0
  363. package/dist/progression/statGraph.js +318 -0
  364. package/dist/puzzle/cellGrid.d.ts +9 -0
  365. package/dist/puzzle/cellGrid.js +9 -0
  366. package/dist/puzzle/fallingPiece.d.ts +9 -0
  367. package/dist/puzzle/fallingPiece.js +9 -0
  368. package/dist/puzzle/nonogram.d.ts +39 -0
  369. package/dist/puzzle/nonogram.js +136 -0
  370. package/dist/random/nameGen.d.ts +5 -0
  371. package/dist/random/nameGen.js +5 -0
  372. package/dist/random/pick.d.ts +8 -0
  373. package/dist/random/pick.js +31 -0
  374. package/dist/random/rng.d.ts +26 -1
  375. package/dist/random/rng.js +32 -9
  376. package/dist/random/seedLink.d.ts +4 -0
  377. package/dist/random/seedLink.js +4 -0
  378. package/dist/relation/keyedValues.d.ts +54 -0
  379. package/dist/relation/keyedValues.js +115 -0
  380. package/dist/relation/thresholds.d.ts +62 -0
  381. package/dist/relation/thresholds.js +62 -0
  382. package/dist/render/color.d.ts +12 -0
  383. package/dist/render/color.js +26 -0
  384. package/dist/render/lookPreset.d.ts +41 -0
  385. package/dist/render/lookPreset.js +29 -0
  386. package/dist/render/postProcessing.d.ts +20 -0
  387. package/dist/render/postProcessing.js +21 -1
  388. package/dist/rules/predicate.d.ts +53 -0
  389. package/dist/rules/predicate.js +64 -0
  390. package/dist/rules/ruleEffects.d.ts +38 -0
  391. package/dist/rules/ruleEffects.js +37 -0
  392. package/dist/rules/triggeredRules.d.ts +160 -0
  393. package/dist/rules/triggeredRules.js +241 -0
  394. package/dist/runtime/adapter.d.ts +55 -3
  395. package/dist/runtime/adapter.js +57 -1
  396. package/dist/runtime/cameraDirector.d.ts +1 -0
  397. package/dist/runtime/cameraDirector.js +1 -0
  398. package/dist/runtime/commandRunner.d.ts +1 -0
  399. package/dist/runtime/commandRunner.js +1 -0
  400. package/dist/runtime/context/combat.d.ts +51 -0
  401. package/dist/runtime/context/combat.js +130 -0
  402. package/dist/runtime/context/combatFx.d.ts +23 -0
  403. package/dist/runtime/context/combatFx.js +161 -0
  404. package/dist/runtime/context/deathLoot.d.ts +30 -0
  405. package/dist/runtime/context/deathLoot.js +39 -0
  406. package/dist/runtime/context/player.d.ts +48 -0
  407. package/dist/runtime/context/player.js +125 -0
  408. package/dist/runtime/context/registries.d.ts +13 -0
  409. package/dist/runtime/context/registries.js +84 -0
  410. package/dist/runtime/context/scene.d.ts +59 -0
  411. package/dist/runtime/context/scene.js +291 -0
  412. package/dist/runtime/context/snapshotCodecs.d.ts +12 -0
  413. package/dist/runtime/context/snapshotCodecs.js +26 -0
  414. package/dist/runtime/context/worldItems.d.ts +21 -0
  415. package/dist/runtime/context/worldItems.js +37 -0
  416. package/dist/runtime/descriptors/baseline.d.ts +48 -0
  417. package/dist/runtime/descriptors/baseline.js +157 -0
  418. package/dist/runtime/descriptors/features.d.ts +79 -0
  419. package/dist/runtime/descriptors/features.js +299 -0
  420. package/dist/runtime/gameContext.d.ts +2 -327
  421. package/dist/runtime/gameContext.js +273 -778
  422. package/dist/runtime/gameContextTypes.d.ts +467 -0
  423. package/dist/runtime/gameContextTypes.js +1 -0
  424. package/dist/runtime/gameRuntime.d.ts +1 -0
  425. package/dist/runtime/gameRuntime.js +1 -0
  426. package/dist/runtime/headlessRunner.d.ts +62 -0
  427. package/dist/runtime/headlessRunner.js +45 -0
  428. package/dist/runtime/hostPersistence.d.ts +12 -0
  429. package/dist/runtime/hostPersistence.js +12 -0
  430. package/dist/runtime/hostPolicy.d.ts +51 -0
  431. package/dist/runtime/hostPolicy.js +68 -0
  432. package/dist/runtime/hostedGameRunner.d.ts +11 -4
  433. package/dist/runtime/hostedGameRunner.js +27 -3
  434. package/dist/runtime/hostedWorldSession.d.ts +15 -5
  435. package/dist/runtime/hostedWorldSession.js +12 -4
  436. package/dist/runtime/inputSnapshot.d.ts +8 -3
  437. package/dist/runtime/inputSnapshot.js +6 -2
  438. package/dist/runtime/motionIntents.d.ts +11 -2
  439. package/dist/runtime/motionIntents.js +16 -2
  440. package/dist/runtime/perContext.d.ts +15 -0
  441. package/dist/runtime/perContext.js +21 -0
  442. package/dist/runtime/runtimeSave.d.ts +79 -0
  443. package/dist/runtime/runtimeSave.js +105 -0
  444. package/dist/runtime/save.d.ts +4 -0
  445. package/dist/runtime/save.js +4 -0
  446. package/dist/runtime/snapshot.d.ts +7 -0
  447. package/dist/runtime/snapshot.js +7 -0
  448. package/dist/runtime/visibility.d.ts +2 -1
  449. package/dist/runtime/visibility.js +2 -1
  450. package/dist/runtime/worldChannel.d.ts +7 -3
  451. package/dist/runtime/worldChannel.js +51 -6
  452. package/dist/runtime/worldMirror.d.ts +14 -2
  453. package/dist/runtime/worldMirror.js +15 -2
  454. package/dist/runtime/worldProjection.d.ts +47 -0
  455. package/dist/runtime/worldProjection.js +52 -0
  456. package/dist/runtime/worldReplication.d.ts +30 -7
  457. package/dist/runtime/worldReplication.js +41 -9
  458. package/dist/runtime/worldSnapshot.d.ts +42 -3
  459. package/dist/runtime/worldSnapshot.js +34 -7
  460. package/dist/scene/assetCatalog.d.ts +26 -0
  461. package/dist/scene/assetCatalog.js +4 -0
  462. package/dist/scene/assetDiagnostics.d.ts +69 -0
  463. package/dist/scene/assetDiagnostics.js +110 -0
  464. package/dist/scene/assetGenerator.d.ts +65 -0
  465. package/dist/scene/assetGenerator.js +64 -0
  466. package/dist/scene/assetSpace.d.ts +137 -0
  467. package/dist/scene/assetSpace.js +257 -0
  468. package/dist/scene/authoredTriggers.d.ts +219 -0
  469. package/dist/scene/authoredTriggers.js +454 -0
  470. package/dist/scene/behaviorRuntime.d.ts +75 -0
  471. package/dist/scene/behaviorRuntime.js +217 -0
  472. package/dist/scene/behaviors.d.ts +14 -2
  473. package/dist/scene/behaviors.js +7 -2
  474. package/dist/scene/bodyBind.d.ts +60 -0
  475. package/dist/scene/bodyBind.js +51 -0
  476. package/dist/scene/builtinSceneKinds.d.ts +5 -0
  477. package/dist/scene/builtinSceneKinds.js +67 -0
  478. package/dist/scene/colliders.d.ts +71 -1
  479. package/dist/scene/colliders.js +145 -1
  480. package/dist/scene/collisionMesh.d.ts +94 -0
  481. package/dist/scene/collisionMesh.js +699 -0
  482. package/dist/scene/companion.d.ts +147 -0
  483. package/dist/scene/companion.js +193 -0
  484. package/dist/scene/entityStats.d.ts +18 -11
  485. package/dist/scene/entityStats.js +21 -16
  486. package/dist/scene/entityStore.d.ts +12 -1
  487. package/dist/scene/entityStore.js +15 -0
  488. package/dist/scene/facing.d.ts +18 -0
  489. package/dist/scene/facing.js +26 -0
  490. package/dist/scene/modelSockets.d.ts +33 -0
  491. package/dist/scene/modelSockets.js +29 -0
  492. package/dist/scene/objectQuery.d.ts +2 -0
  493. package/dist/scene/objectQuery.js +2 -0
  494. package/dist/scene/objectStore.d.ts +4 -0
  495. package/dist/scene/objectStore.js +6 -0
  496. package/dist/scene/possession.d.ts +8 -0
  497. package/dist/scene/possession.js +14 -0
  498. package/dist/scene/roster.d.ts +3 -0
  499. package/dist/scene/roster.js +12 -0
  500. package/dist/scene/sceneKinds.d.ts +256 -0
  501. package/dist/scene/sceneKinds.js +218 -0
  502. package/dist/scene/sceneOwnership.d.ts +169 -0
  503. package/dist/scene/sceneOwnership.js +179 -0
  504. package/dist/scene/sceneRaycast.js +15 -0
  505. package/dist/scene/selectionBookmarks.d.ts +93 -0
  506. package/dist/scene/selectionBookmarks.js +122 -0
  507. package/dist/scene/vehicleSeat.d.ts +68 -0
  508. package/dist/scene/vehicleSeat.js +59 -0
  509. package/dist/sensor/concealment.d.ts +3 -0
  510. package/dist/sensor/concealment.js +3 -0
  511. package/dist/sensor/freezeMonitor.d.ts +1 -0
  512. package/dist/sensor/freezeMonitor.js +1 -0
  513. package/dist/sensor/frustumSensor.d.ts +7 -3
  514. package/dist/sensor/frustumSensor.js +7 -3
  515. package/dist/sensor/hiddenStateProbe.d.ts +5 -2
  516. package/dist/sensor/hiddenStateProbe.js +5 -2
  517. package/dist/sensor/recordingBuffer.d.ts +2 -1
  518. package/dist/sensor/recordingBuffer.js +2 -1
  519. package/dist/sensor/replayLoop.d.ts +6 -2
  520. package/dist/sensor/replayLoop.js +6 -2
  521. package/dist/sensor/revealQuery.d.ts +2 -1
  522. package/dist/sensor/revealQuery.js +2 -1
  523. package/dist/sensor/visionCone.d.ts +10 -3
  524. package/dist/sensor/visionCone.js +10 -3
  525. package/dist/session/contestedChannel.d.ts +1 -0
  526. package/dist/session/contestedChannel.js +1 -0
  527. package/dist/session/extraction.d.ts +1 -0
  528. package/dist/session/extraction.js +1 -0
  529. package/dist/session/roles.d.ts +6 -0
  530. package/dist/session/roles.js +6 -0
  531. package/dist/session/roundState.d.ts +8 -0
  532. package/dist/session/roundState.js +2 -0
  533. package/dist/settings/settingsModel.d.ts +1 -0
  534. package/dist/settings/settingsModel.js +1 -0
  535. package/dist/stats/accumulatorMeter.d.ts +6 -0
  536. package/dist/stats/accumulatorMeter.js +6 -0
  537. package/dist/stats/eventMeter.d.ts +5 -0
  538. package/dist/stats/eventMeter.js +5 -0
  539. package/dist/stats/rollCheck.d.ts +5 -0
  540. package/dist/stats/rollCheck.js +5 -0
  541. package/dist/stats/spawnLevelStats.d.ts +110 -0
  542. package/dist/stats/spawnLevelStats.js +140 -0
  543. package/dist/stats/statModifiers.d.ts +5 -0
  544. package/dist/stats/statModifiers.js +5 -0
  545. package/dist/stats/statPool.d.ts +70 -0
  546. package/dist/stats/statPool.js +59 -0
  547. package/dist/store/changeSignal.d.ts +3 -0
  548. package/dist/store/changeSignal.js +2 -0
  549. package/dist/store/defineKeyedStore.d.ts +38 -0
  550. package/dist/store/defineKeyedStore.js +42 -0
  551. package/dist/store/defineStore.d.ts +33 -0
  552. package/dist/store/defineStore.js +35 -0
  553. package/dist/store/observableKeyedStore.d.ts +7 -0
  554. package/dist/store/observableKeyedStore.js +35 -2
  555. package/dist/survival/decayMeter.d.ts +55 -0
  556. package/dist/survival/decayMeter.js +130 -57
  557. package/dist/survival/moodle.d.ts +2 -0
  558. package/dist/survival/regionHealth.d.ts +6 -0
  559. package/dist/survival/regionHealth.js +6 -3
  560. package/dist/tactics/fallingGrid.d.ts +4 -2
  561. package/dist/tactics/fallingGrid.js +4 -2
  562. package/dist/tactics/predictiveQuery.d.ts +3 -0
  563. package/dist/tactics/predictiveQuery.js +3 -0
  564. package/dist/tactics/snapshot.d.ts +2 -0
  565. package/dist/tactics/snapshot.js +2 -0
  566. package/dist/tactics/surface.d.ts +1 -0
  567. package/dist/tactics/surface.js +1 -0
  568. package/dist/tactics/tacticalGrid.d.ts +1 -0
  569. package/dist/tactics/tacticalGrid.js +1 -0
  570. package/dist/time/beatClock.d.ts +14 -4
  571. package/dist/time/beatClock.js +14 -4
  572. package/dist/time/calendarClock.d.ts +3 -0
  573. package/dist/time/calendarClock.js +3 -0
  574. package/dist/time/idleProgress.d.ts +4 -0
  575. package/dist/time/idleProgress.js +4 -0
  576. package/dist/time/simClock.d.ts +2 -0
  577. package/dist/time/simClock.js +20 -5
  578. package/dist/ui/actionModel.d.ts +110 -0
  579. package/dist/ui/actionModel.js +159 -0
  580. package/dist/ui/gameLayout.d.ts +30 -11
  581. package/dist/ui/gameLayout.js +30 -11
  582. package/dist/ui/hudDocument.d.ts +114 -0
  583. package/dist/ui/hudDocument.js +220 -0
  584. package/dist/ui/hudLayout.d.ts +38 -1
  585. package/dist/ui/hudLayout.js +220 -17
  586. package/dist/ui/selectionModel.d.ts +92 -0
  587. package/dist/ui/selectionModel.js +88 -0
  588. package/dist/ui/tooltipModel.d.ts +63 -0
  589. package/dist/ui/tooltipModel.js +82 -0
  590. package/dist/ui.d.ts +15 -0
  591. package/dist/ui.js +15 -0
  592. package/dist/visibility/bounds.d.ts +8 -1
  593. package/dist/visibility/bounds.js +8 -1
  594. package/dist/visibility/frustum.d.ts +12 -3
  595. package/dist/visibility/frustum.js +12 -3
  596. package/dist/work/jobQueue.d.ts +201 -0
  597. package/dist/work/jobQueue.js +253 -0
  598. package/dist/work/unitTraining.d.ts +58 -0
  599. package/dist/work/unitTraining.js +52 -0
  600. package/dist/world/authoredEntities.d.ts +29 -0
  601. package/dist/world/authoredEntities.js +29 -0
  602. package/dist/world/authoredObjects.d.ts +72 -0
  603. package/dist/world/authoredObjects.js +81 -0
  604. package/dist/world/authoredSpawn.d.ts +39 -0
  605. package/dist/world/authoredSpawn.js +37 -0
  606. package/dist/world/buildSockets.d.ts +152 -0
  607. package/dist/world/buildSockets.js +188 -0
  608. package/dist/world/buildingGenerator.d.ts +18 -0
  609. package/dist/world/buildingGenerator.js +62 -0
  610. package/dist/world/buildings.d.ts +5 -0
  611. package/dist/world/buildings.js +5 -0
  612. package/dist/world/catenary.d.ts +22 -0
  613. package/dist/world/catenary.js +69 -0
  614. package/dist/world/cityBlocks.d.ts +152 -0
  615. package/dist/world/cityBlocks.js +670 -0
  616. package/dist/world/cityContent.d.ts +77 -0
  617. package/dist/world/cityContent.js +261 -0
  618. package/dist/world/cityGeometry.d.ts +114 -0
  619. package/dist/world/cityGeometry.js +573 -0
  620. package/dist/world/cityKind.d.ts +314 -0
  621. package/dist/world/cityKind.js +1578 -0
  622. package/dist/world/features.d.ts +133 -10
  623. package/dist/world/features.js +66 -6
  624. package/dist/world/footprintGrid.d.ts +59 -0
  625. package/dist/world/footprintGrid.js +141 -0
  626. package/dist/world/geometry.d.ts +64 -0
  627. package/dist/world/geometry.js +94 -0
  628. package/dist/world/grassKind.d.ts +51 -0
  629. package/dist/world/grassKind.js +101 -0
  630. package/dist/world/markers.d.ts +50 -4
  631. package/dist/world/markers.js +49 -0
  632. package/dist/world/massing.js +1 -1
  633. package/dist/world/minimapBake.d.ts +78 -0
  634. package/dist/world/minimapBake.js +208 -0
  635. package/dist/world/minimapTrack.d.ts +17 -0
  636. package/dist/world/minimapTrack.js +22 -0
  637. package/dist/world/pathInstances.d.ts +27 -0
  638. package/dist/world/pathInstances.js +27 -0
  639. package/dist/world/pathNetwork.d.ts +159 -0
  640. package/dist/world/pathNetwork.js +684 -0
  641. package/dist/world/pathTerrain.d.ts +80 -0
  642. package/dist/world/pathTerrain.js +142 -0
  643. package/dist/world/place.d.ts +166 -0
  644. package/dist/world/place.js +96 -0
  645. package/dist/world/placeAsset.d.ts +81 -0
  646. package/dist/world/placeAsset.js +79 -0
  647. package/dist/world/placement.d.ts +4 -0
  648. package/dist/world/placement.js +4 -0
  649. package/dist/world/placementController.d.ts +6 -0
  650. package/dist/world/placementController.js +6 -0
  651. package/dist/world/poleLineKind.d.ts +40 -0
  652. package/dist/world/poleLineKind.js +97 -0
  653. package/dist/world/polyline.d.ts +34 -0
  654. package/dist/world/polyline.js +88 -0
  655. package/dist/world/resourceNode.d.ts +109 -0
  656. package/dist/world/resourceNode.js +140 -0
  657. package/dist/world/roads.d.ts +8 -0
  658. package/dist/world/roads.js +38 -0
  659. package/dist/world/scatter.js +2 -20
  660. package/dist/world/scatterRegion.d.ts +164 -0
  661. package/dist/world/scatterRegion.js +409 -0
  662. package/dist/world/sceneShapes.d.ts +43 -0
  663. package/dist/world/sceneShapes.js +1 -0
  664. package/dist/world/segment.d.ts +27 -0
  665. package/dist/world/segment.js +43 -0
  666. package/dist/world/soilKind.d.ts +48 -0
  667. package/dist/world/soilKind.js +92 -0
  668. package/dist/world/spatialSample.d.ts +241 -0
  669. package/dist/world/spatialSample.js +465 -0
  670. package/dist/world/streets.d.ts +29 -0
  671. package/dist/world/streets.js +55 -0
  672. package/dist/world/terraform.d.ts +213 -1
  673. package/dist/world/terraform.js +627 -22
  674. package/dist/world/terrain.d.ts +164 -20
  675. package/dist/world/terrain.js +325 -41
  676. package/dist/world/terrainGuides.d.ts +182 -0
  677. package/dist/world/terrainGuides.js +343 -0
  678. package/dist/world/vec2.d.ts +81 -0
  679. package/dist/world/vec2.js +128 -0
  680. package/dist/world/vec3.d.ts +49 -0
  681. package/dist/world/vec3.js +76 -0
  682. package/dist/world/vegetation.d.ts +71 -0
  683. package/dist/world/vegetation.js +151 -0
  684. package/dist/world/visibilityField.d.ts +154 -0
  685. package/dist/world/visibilityField.js +271 -0
  686. package/dist/world/volumetricClouds.d.ts +52 -0
  687. package/dist/world/volumetricClouds.js +43 -0
  688. package/dist/world/walls.d.ts +9 -0
  689. package/dist/world/walls.js +9 -0
  690. package/dist/world/waterKind.d.ts +55 -0
  691. package/dist/world/waterKind.js +99 -0
  692. package/dist/world.d.ts +141 -0
  693. package/dist/world.js +141 -0
  694. package/package.json +32 -8
  695. package/skills/game-design/SKILL.md +28 -0
  696. package/skills/game-design/agents/openai.yaml +4 -0
  697. package/skills/game-design/references/application-playbook.md +170 -0
  698. package/skills/game-design/references/field-guide.md +288 -0
  699. package/skills/jgengine/SKILL.md +54 -0
  700. package/skills/jgengine/api.md +527 -0
  701. package/skills/jgengine/capabilities.md +131 -0
  702. package/skills/jgengine/recipes/minimal-game.md +88 -0
  703. package/skills/jgengine-combat/SKILL.md +38 -0
  704. package/skills/jgengine-combat/api.md +585 -0
  705. package/skills/jgengine-combat/capabilities.md +213 -0
  706. package/skills/jgengine-combat/recipes/portable-stat-pools.md +122 -0
  707. package/skills/jgengine-combat/reference.md +108 -0
  708. package/skills/jgengine-gameplay/SKILL.md +39 -0
  709. package/skills/jgengine-gameplay/api.md +1920 -0
  710. package/skills/jgengine-gameplay/capabilities.md +454 -0
  711. package/skills/jgengine-gameplay/reference-systems.md +56 -0
  712. package/skills/jgengine-world/SKILL.md +51 -0
  713. package/skills/jgengine-world/api.md +3056 -0
  714. package/skills/jgengine-world/capabilities.md +476 -0
  715. package/skills/jgengine-world/recipes/commanding-entities.md +128 -0
  716. package/skills/jgengine-world/reference.md +261 -0
  717. package/dist/cartridge/runtime.d.ts +0 -15
  718. package/dist/cartridge/runtime.js +0 -556
  719. package/dist/cartridge/spec.d.ts +0 -188
  720. package/dist/cartridge/spec.js +0 -19
  721. package/dist/cartridge/validate.d.ts +0 -2
  722. package/dist/cartridge/validate.js +0 -78
  723. package/dist/store/engineStore.d.ts +0 -22
  724. package/dist/store/engineStore.js +0 -50
  725. package/dist/store/features.d.ts +0 -10
  726. package/dist/store/features.js +0 -23
  727. package/llms.txt +0 -5389
@@ -12,6 +12,7 @@
12
12
  */
13
13
  export type MovementKey = "w" | "a" | "s" | "d" | "shift" | "control" | "c" | "space";
14
14
  export type MovementKeysState = Record<MovementKey, boolean>;
15
+ /** @internal */
15
16
  export declare function createEmptyMovementKeys(): MovementKeysState;
16
17
  /**
17
18
  * Frame-independent description of what the player is asking the avatar to do.
@@ -31,7 +32,8 @@ export interface MovementIntent {
31
32
  * Translate the set of held keys into an intent. When `canMove` is false (a
32
33
  * menu is open, the world is paused) the avatar is fully idle so it never
33
34
  * drifts behind an overlay.
34
- */
35
+ * @internal
36
+ */
35
37
  export declare function resolveMovementIntent(keys: MovementKeysState, canMove: boolean): MovementIntent;
36
38
  /**
37
39
  * Kinematics + feel tuning for the first-person controller. Centralised here so
@@ -43,6 +45,8 @@ export declare const MOVEMENT_TUNING: {
43
45
  readonly walkSpeedMultiplier: 1.75;
44
46
  readonly runSpeedMultiplier: 2.25;
45
47
  readonly crouchSpeedMultiplier: 0.45;
48
+ /** Backward (S) travels this fraction of walk speed so retreating reads slower than advancing. */
49
+ readonly backpedalSpeedMultiplier: 0.65;
46
50
  readonly groundAcceleration: 26;
47
51
  readonly airAcceleration: 12;
48
52
  readonly groundFriction: 18;
@@ -74,6 +78,7 @@ export interface PlayerMotionState {
74
78
  grounded: boolean;
75
79
  jumpHeld: boolean;
76
80
  }
81
+ /** @internal */
77
82
  export declare function createPlayerMotionState(): PlayerMotionState;
78
83
  /** Horizontal step (world units) the avatar should commit this frame. */
79
84
  export interface MovementFrameStep {
@@ -87,6 +92,19 @@ export interface MovementFrameStep {
87
92
  export interface MovementTuningOverrides {
88
93
  gravityAcceleration?: number;
89
94
  jumpVelocity?: number;
95
+ /** Fraction of walk speed while backpedalling; overrides {@link MOVEMENT_TUNING.backpedalSpeedMultiplier}. */
96
+ backpedalSpeedMultiplier?: number;
97
+ }
98
+ /**
99
+ * Per-frame dynamic modifiers for {@link advancePlayerMotion} — state the caller
100
+ * recomputes each tick (unlike the static {@link MovementTuningOverrides}). Omitted
101
+ * fields leave the integrator at its default behavior.
102
+ */
103
+ export interface MotionFrameOptions {
104
+ /** Extra multiplier applied on top of the resolved target speed (e.g. swimming). Default 1. */
105
+ speedScale?: number;
106
+ /** Suppress gravity/jump integration and hold the avatar afloat this frame (e.g. swimming). Default false. */
107
+ floating?: boolean;
90
108
  }
91
109
  /**
92
110
  * Advance one frame of avatar kinematics. Mutates `motion` (velocity, jump,
@@ -97,14 +115,29 @@ export interface MovementTuningOverrides {
97
115
  * (need not be normalized); the avatar's right axis is derived from it. Pulling
98
116
  * this out of the render loop is what lets us assert "held forward for N frames
99
117
  * crosses the room" in a unit test instead of only in the live game.
100
- */
101
- export declare function advancePlayerMotion(motion: PlayerMotionState, intent: MovementIntent, forwardX: number, forwardZ: number, baseSpeed: number, rawDeltaSeconds: number, tuning?: MovementTuningOverrides): MovementFrameStep;
118
+ * @internal
119
+ */
120
+ export declare function advancePlayerMotion(motion: PlayerMotionState, intent: MovementIntent, forwardX: number, forwardZ: number, baseSpeed: number, rawDeltaSeconds: number, tuning?: MovementTuningOverrides, options?: MotionFrameOptions): MovementFrameStep;
102
121
  /** A placed scene object the walking player collides against as a circle-vs-AABB obstacle. */
103
122
  export interface CollisionObstacle {
104
123
  position: readonly [number, number, number];
105
- /** Box half-size on each axis. Default `[0.5, 0.5, 0.5]` (unit-box scene objects). */
124
+ /** Box half-size on each axis. Default `[0.5, 0.5, 0.5]` (unit-box scene objects). Ignored when `boxes` is set. */
106
125
  halfExtents?: readonly [number, number, number];
126
+ /** Local center shift added to `position` for the single-AABB path (the collider's yaw-rotated offset). Ignored when `boxes` is set. */
127
+ offset?: readonly [number, number, number];
128
+ /**
129
+ * Optional compound solid: axis-aligned sub-boxes as `min`/`max` **relative to `position`** (add
130
+ * `position` for world space), already yaw-rotated and conservatively expanded by the caller. When
131
+ * present the capsule slides against EACH sub-box instead of the single `halfExtents` AABB, so a
132
+ * concave opening (the gap between an archway's pillars/lintel) lets the capsule pass through.
133
+ */
134
+ boxes?: readonly {
135
+ min: readonly [number, number, number];
136
+ max: readonly [number, number, number];
137
+ }[];
107
138
  }
139
+ /** @internal Player radius the walking obstruction inflates obstacles by; callers gather within reach of it. */
140
+ export declare const DEFAULT_OBSTACLE_PLAYER_RADIUS = 0.3;
108
141
  /**
109
142
  * Resolve a horizontal step against nearby placed objects with classic
110
143
  * axis-separated sliding (try X, then Z against the post-X position) so
@@ -113,21 +146,29 @@ export interface CollisionObstacle {
113
146
  * by `playerRadius`; an obstacle whose vertical span misses the player's
114
147
  * feet-to-head span is skipped entirely. Callers should pre-filter to nearby
115
148
  * objects — this also early-exits per obstacle on horizontal distance.
116
- */
149
+ * @internal
150
+ */
117
151
  export declare function resolveObstacleStep(current: readonly [number, number, number], stepX: number, stepZ: number, obstacles: readonly CollisionObstacle[], playerRadius?: number): MovementFrameStep;
118
- /** Placed objects within `radius` (XZ) of `center`, as {@link CollisionObstacle}s to pre-filter for {@link resolveObstacleStep}. */
152
+ /** Placed objects within `radius` (XZ) of `center`, as {@link CollisionObstacle}s to pre-filter for {@link resolveObstacleStep}.
153
+ * @internal
154
+ */
119
155
  export declare function nearbyObstacles(objects: readonly {
120
156
  position: readonly [number, number, number];
121
157
  }[], center: readonly [number, number, number], radius?: number): CollisionObstacle[];
122
- /** Zeroes the off-axis component so travel is locked to a single world axis (`PlayerMovementConfig.mode: "axis"`). */
158
+ /** Zeroes the off-axis component so travel is locked to a single world axis (`PlayerMovementConfig.mode: "axis"`).
159
+ * @internal
160
+ */
123
161
  export declare function constrainStepToAxis(stepX: number, stepZ: number, axis: "x" | "z"): MovementFrameStep;
124
162
  /**
125
163
  * Snap a world position to its containing cell's center, matching the
126
164
  * bounds-free form of `navGrid`'s and `tacticalGrid`'s `floor` + half-cell
127
165
  * convention (`PlayerMovementConfig.mode: "grid"`).
128
- */
166
+ * @internal
167
+ */
129
168
  export declare function snapPositionToGrid(x: number, z: number, cellSize: number): [number, number];
130
169
  /** Peak jump height from MOVEMENT_TUNING.jumpVelocity + gravity, with small buffer. */
131
170
  export declare const MAX_JUMP_OFFSET = 1.15;
132
- /** Camera yaw looks along -Z; character mesh faces +Z at body rotation.y = 0. */
171
+ /** Camera yaw looks along -Z; character mesh faces +Z at body rotation.y = 0.
172
+ * @internal
173
+ */
133
174
  export declare function cameraYawToAvatarBodyYaw(cameraYaw: number): number;
@@ -10,6 +10,7 @@
10
10
  * touch controls) and its camera rigs (pointer lock, the per-frame look
11
11
  * channel).
12
12
  */
13
+ /** @internal */
13
14
  export function createEmptyMovementKeys() {
14
15
  return { w: false, a: false, s: false, d: false, shift: false, control: false, c: false, space: false };
15
16
  }
@@ -25,7 +26,8 @@ const IDLE_INTENT = {
25
26
  * Translate the set of held keys into an intent. When `canMove` is false (a
26
27
  * menu is open, the world is paused) the avatar is fully idle so it never
27
28
  * drifts behind an overlay.
28
- */
29
+ * @internal
30
+ */
29
31
  export function resolveMovementIntent(keys, canMove) {
30
32
  if (!canMove)
31
33
  return IDLE_INTENT;
@@ -52,6 +54,8 @@ export const MOVEMENT_TUNING = {
52
54
  walkSpeedMultiplier: 1.75,
53
55
  runSpeedMultiplier: 2.25,
54
56
  crouchSpeedMultiplier: 0.45,
57
+ /** Backward (S) travels this fraction of walk speed so retreating reads slower than advancing. */
58
+ backpedalSpeedMultiplier: 0.65,
55
59
  groundAcceleration: 26,
56
60
  airAcceleration: 12,
57
61
  groundFriction: 18,
@@ -70,15 +74,24 @@ export const MOVEMENT_TUNING = {
70
74
  /** Largest physics step we integrate so a stalled tab can't teleport the player. */
71
75
  maxFrameSeconds: 0.05,
72
76
  };
73
- /** Speed (units/s) the avatar should target given the active intent. */
74
- function resolveTargetSpeed(intent, baseSpeed) {
77
+ /**
78
+ * Speed (units/s) the avatar should target given the active intent. Crouch and run
79
+ * take precedence over backpedal; a plain backward intent (`forward < 0`) slows to
80
+ * {@link MOVEMENT_TUNING.backpedalSpeedMultiplier} (overridable via `tuning`), while a
81
+ * pure strafe keeps full speed.
82
+ */
83
+ function resolveTargetSpeed(intent, baseSpeed, tuning) {
84
+ const backpedal = tuning?.backpedalSpeedMultiplier ?? MOVEMENT_TUNING.backpedalSpeedMultiplier;
75
85
  const speedMultiplier = intent.crouching
76
86
  ? MOVEMENT_TUNING.crouchSpeedMultiplier
77
87
  : intent.running
78
88
  ? MOVEMENT_TUNING.runSpeedMultiplier
79
- : 1;
89
+ : intent.forward < 0
90
+ ? backpedal
91
+ : 1;
80
92
  return baseSpeed * MOVEMENT_TUNING.walkSpeedMultiplier * speedMultiplier;
81
93
  }
94
+ /** @internal */
82
95
  export function createPlayerMotionState() {
83
96
  return {
84
97
  horizontalVelocityX: 0,
@@ -98,10 +111,11 @@ export function createPlayerMotionState() {
98
111
  * (need not be normalized); the avatar's right axis is derived from it. Pulling
99
112
  * this out of the render loop is what lets us assert "held forward for N frames
100
113
  * crosses the room" in a unit test instead of only in the live game.
101
- */
102
- export function advancePlayerMotion(motion, intent, forwardX, forwardZ, baseSpeed, rawDeltaSeconds, tuning) {
114
+ * @internal
115
+ */
116
+ export function advancePlayerMotion(motion, intent, forwardX, forwardZ, baseSpeed, rawDeltaSeconds, tuning, options) {
103
117
  const deltaSeconds = Math.min(rawDeltaSeconds, MOVEMENT_TUNING.maxFrameSeconds);
104
- const targetSpeed = resolveTargetSpeed(intent, baseSpeed);
118
+ const targetSpeed = resolveTargetSpeed(intent, baseSpeed, tuning) * (options?.speedScale ?? 1);
105
119
  const gravityAcceleration = tuning?.gravityAcceleration ?? MOVEMENT_TUNING.gravityAcceleration;
106
120
  const jumpVelocity = tuning?.jumpVelocity ?? MOVEMENT_TUNING.jumpVelocity;
107
121
  let targetVelocityX = 0;
@@ -146,13 +160,19 @@ export function advancePlayerMotion(motion, intent, forwardX, forwardZ, baseSpee
146
160
  motion.horizontalVelocityX *= friction;
147
161
  motion.horizontalVelocityZ *= friction;
148
162
  }
163
+ const floating = options?.floating === true;
149
164
  const jumpPressed = intent.jumping;
150
- if (jumpPressed && !motion.jumpHeld && motion.grounded && !intent.crouching) {
165
+ if (jumpPressed && !motion.jumpHeld && motion.grounded && !intent.crouching && !floating) {
151
166
  motion.verticalVelocity = jumpVelocity;
152
167
  motion.grounded = false;
153
168
  }
154
169
  motion.jumpHeld = jumpPressed;
155
- if (!motion.grounded || motion.verticalVelocity > 0) {
170
+ if (floating) {
171
+ motion.verticalVelocity = 0;
172
+ motion.jumpOffset = 0;
173
+ motion.grounded = true;
174
+ }
175
+ else if (!motion.grounded || motion.verticalVelocity > 0) {
156
176
  motion.verticalVelocity -= gravityAcceleration * deltaSeconds;
157
177
  motion.jumpOffset += motion.verticalVelocity * deltaSeconds;
158
178
  if (motion.jumpOffset <= 0) {
@@ -167,7 +187,8 @@ export function advancePlayerMotion(motion, intent, forwardX, forwardZ, baseSpee
167
187
  };
168
188
  }
169
189
  const DEFAULT_OBSTACLE_HALF_EXTENTS = [0.5, 0.5, 0.5];
170
- const DEFAULT_OBSTACLE_PLAYER_RADIUS = 0.3;
190
+ /** @internal Player radius the walking obstruction inflates obstacles by; callers gather within reach of it. */
191
+ export const DEFAULT_OBSTACLE_PLAYER_RADIUS = 0.3;
171
192
  /** Feet-to-head span used for the obstacle's vertical overlap test; matches DEFAULT_VOXEL_DIMS.height. */
172
193
  const OBSTACLE_PLAYER_HEIGHT = 1.8;
173
194
  function clampAxisStep(originalCoord, step, min, max) {
@@ -188,29 +209,47 @@ function clampAxisStep(originalCoord, step, min, max) {
188
209
  * by `playerRadius`; an obstacle whose vertical span misses the player's
189
210
  * feet-to-head span is skipped entirely. Callers should pre-filter to nearby
190
211
  * objects — this also early-exits per obstacle on horizontal distance.
191
- */
212
+ * @internal
213
+ */
192
214
  export function resolveObstacleStep(current, stepX, stepZ, obstacles, playerRadius = DEFAULT_OBSTACLE_PLAYER_RADIUS) {
193
215
  const feetY = current[1];
194
216
  const headY = feetY + OBSTACLE_PLAYER_HEIGHT;
195
217
  const reachX = Math.abs(stepX) + playerRadius;
196
218
  const reachZ = Math.abs(stepZ) + playerRadius;
197
219
  const nearby = [];
198
- for (const obstacle of obstacles) {
199
- const halfExtents = obstacle.halfExtents ?? DEFAULT_OBSTACLE_HALF_EXTENTS;
200
- const obstacleBottom = obstacle.position[1] - halfExtents[1];
201
- const obstacleTop = obstacle.position[1] + halfExtents[1];
202
- if (obstacleTop <= feetY || obstacleBottom >= headY)
203
- continue;
204
- const dx = obstacle.position[0] - current[0];
205
- const dz = obstacle.position[2] - current[2];
206
- if (Math.abs(dx) > halfExtents[0] + reachX || Math.abs(dz) > halfExtents[2] + reachZ)
207
- continue;
220
+ const considerBox = (minX, maxX, minY, maxY, minZ, maxZ) => {
221
+ // Vertical-span cull: a sub-box entirely above the head (walk under the lintel) or below the feet is skipped.
222
+ if (maxY <= feetY || minY >= headY)
223
+ return;
224
+ const centerX = (minX + maxX) / 2;
225
+ const centerZ = (minZ + maxZ) / 2;
226
+ const halfX = (maxX - minX) / 2;
227
+ const halfZ = (maxZ - minZ) / 2;
228
+ if (Math.abs(centerX - current[0]) > halfX + reachX || Math.abs(centerZ - current[2]) > halfZ + reachZ)
229
+ return;
208
230
  nearby.push({
209
- minX: obstacle.position[0] - halfExtents[0] - playerRadius,
210
- maxX: obstacle.position[0] + halfExtents[0] + playerRadius,
211
- minZ: obstacle.position[2] - halfExtents[2] - playerRadius,
212
- maxZ: obstacle.position[2] + halfExtents[2] + playerRadius,
231
+ minX: minX - playerRadius,
232
+ maxX: maxX + playerRadius,
233
+ minZ: minZ - playerRadius,
234
+ maxZ: maxZ + playerRadius,
213
235
  });
236
+ };
237
+ for (const obstacle of obstacles) {
238
+ const px = obstacle.position[0];
239
+ const py = obstacle.position[1];
240
+ const pz = obstacle.position[2];
241
+ if (obstacle.boxes !== undefined && obstacle.boxes.length > 0) {
242
+ for (const box of obstacle.boxes) {
243
+ considerBox(px + box.min[0], px + box.max[0], py + box.min[1], py + box.max[1], pz + box.min[2], pz + box.max[2]);
244
+ }
245
+ continue;
246
+ }
247
+ const halfExtents = obstacle.halfExtents ?? DEFAULT_OBSTACLE_HALF_EXTENTS;
248
+ const offset = obstacle.offset;
249
+ const cx = px + (offset !== undefined ? offset[0] : 0);
250
+ const cy = py + (offset !== undefined ? offset[1] : 0);
251
+ const cz = pz + (offset !== undefined ? offset[2] : 0);
252
+ considerBox(cx - halfExtents[0], cx + halfExtents[0], cy - halfExtents[1], cy + halfExtents[1], cz - halfExtents[2], cz + halfExtents[2]);
214
253
  }
215
254
  let resultX = stepX;
216
255
  for (const box of nearby) {
@@ -228,7 +267,9 @@ export function resolveObstacleStep(current, stepX, stepZ, obstacles, playerRadi
228
267
  return { stepX: resultX, stepZ: resultZ };
229
268
  }
230
269
  const OBSTACLE_GATHER_RADIUS = 3;
231
- /** Placed objects within `radius` (XZ) of `center`, as {@link CollisionObstacle}s to pre-filter for {@link resolveObstacleStep}. */
270
+ /** Placed objects within `radius` (XZ) of `center`, as {@link CollisionObstacle}s to pre-filter for {@link resolveObstacleStep}.
271
+ * @internal
272
+ */
232
273
  export function nearbyObstacles(objects, center, radius = OBSTACLE_GATHER_RADIUS) {
233
274
  const radiusSq = radius * radius;
234
275
  const result = [];
@@ -240,7 +281,9 @@ export function nearbyObstacles(objects, center, radius = OBSTACLE_GATHER_RADIUS
240
281
  }
241
282
  return result;
242
283
  }
243
- /** Zeroes the off-axis component so travel is locked to a single world axis (`PlayerMovementConfig.mode: "axis"`). */
284
+ /** Zeroes the off-axis component so travel is locked to a single world axis (`PlayerMovementConfig.mode: "axis"`).
285
+ * @internal
286
+ */
244
287
  export function constrainStepToAxis(stepX, stepZ, axis) {
245
288
  return axis === "x" ? { stepX, stepZ: 0 } : { stepX: 0, stepZ };
246
289
  }
@@ -248,13 +291,16 @@ export function constrainStepToAxis(stepX, stepZ, axis) {
248
291
  * Snap a world position to its containing cell's center, matching the
249
292
  * bounds-free form of `navGrid`'s and `tacticalGrid`'s `floor` + half-cell
250
293
  * convention (`PlayerMovementConfig.mode: "grid"`).
251
- */
294
+ * @internal
295
+ */
252
296
  export function snapPositionToGrid(x, z, cellSize) {
253
297
  return [(Math.floor(x / cellSize) + 0.5) * cellSize, (Math.floor(z / cellSize) + 0.5) * cellSize];
254
298
  }
255
299
  /** Peak jump height from MOVEMENT_TUNING.jumpVelocity + gravity, with small buffer. */
256
300
  export const MAX_JUMP_OFFSET = 1.15;
257
- /** Camera yaw looks along -Z; character mesh faces +Z at body rotation.y = 0. */
301
+ /** Camera yaw looks along -Z; character mesh faces +Z at body rotation.y = 0.
302
+ * @internal
303
+ */
258
304
  export function cameraYawToAvatarBodyYaw(cameraYaw) {
259
305
  return cameraYaw + Math.PI;
260
306
  }
@@ -1,10 +1,16 @@
1
1
  import { applyHorizontalImpulses, applyMotionImpulses } from "../runtime/motionIntents.js";
2
- import { groundFieldFor, hasEnvironmentTerrain } from "../world/terrain.js";
3
- import { advancePlayerMotion, constrainStepToAxis, createEmptyMovementKeys, createPlayerMotionState, nearbyObstacles, resolveMovementIntent, resolveObstacleStep, snapPositionToGrid, } from "./movementModel.js";
4
- import { steerYaw } from "./steering.js";
2
+ import { groundFieldFor, hasEnvironmentTerrain, sampleSlope } from "../world/terrain.js";
3
+ import { resolveColliders } from "../scene/colliders.js";
4
+ import { advancePlayerMotion, constrainStepToAxis, createEmptyMovementKeys, createPlayerMotionState, DEFAULT_OBSTACLE_PLAYER_RADIUS, resolveMovementIntent, resolveObstacleStep, snapPositionToGrid, } from "./movementModel.js";
5
+ import { approachYaw, steerYaw } from "./steering.js";
5
6
  import { advanceVoxelPlayer, createVoxelPlayerBody, } from "./voxelController.js";
6
7
  const DEFAULT_TURN_SPEED = 2.4;
7
8
  const DEFAULT_WALK_SPEED = 2;
9
+ const DEFAULT_SWIM_SPEED_MULTIPLIER = 0.65;
10
+ /** Minimum ground-normal `y` the player can stand on before slope-slide kicks in — cos(50°) ≈ 0.643. */
11
+ const DEFAULT_MAX_CLIMB_NORMAL_Y = Math.cos((50 * Math.PI) / 180);
12
+ /** Downhill slide speed (units/s) per unit of slope steepness while on too-steep ground. */
13
+ const SLOPE_SLIDE_SPEED = 4;
8
14
  /**
9
15
  * Maps a game's declared `physics` onto the movement controllers' tuning. `PhysicsConfig.gravity` is a signed
10
16
  * world acceleration (negative points down), but the controllers integrate `velocityY -= gravityAcceleration * dt`
@@ -25,10 +31,12 @@ export function resolvePhysicsTuning(physics) {
25
31
  /** Gather a game's collision/movement/physics/world config into a {@link PlayerMovementTuning} — call once per world; both the shell and a host pass the result to {@link stepPlayerMovement}. */
26
32
  export function resolvePlayerMovementTuning(opts) {
27
33
  const physics = resolvePhysicsTuning(opts.physics);
34
+ const backpedal = opts.movement?.backpedalMult;
35
+ const overrides = backpedal === undefined ? physics : { ...(physics ?? {}), backpedalSpeedMultiplier: backpedal };
28
36
  return {
29
37
  ...(opts.collision === undefined ? {} : { collision: opts.collision }),
30
38
  ...(opts.movement === undefined ? {} : { movement: opts.movement }),
31
- ...(physics === undefined ? {} : { physics }),
39
+ ...(overrides === undefined ? {} : { physics: overrides }),
32
40
  ground: groundFieldFor(opts.world),
33
41
  hasTerrain: hasEnvironmentTerrain(opts.world),
34
42
  };
@@ -45,7 +53,7 @@ function storeFor(ctx) {
45
53
  function stateFor(store, userId) {
46
54
  let state = store.players.get(userId);
47
55
  if (state === undefined) {
48
- state = { heading: 0, voxelBody: null, motion: null };
56
+ state = { heading: 0, facing: null, voxelBody: null, motion: null };
49
57
  store.players.set(userId, state);
50
58
  }
51
59
  return state;
@@ -58,6 +66,25 @@ export function playerMovementHeading(ctx, userId) {
58
66
  export function forgetPlayerMovement(ctx, userId) {
59
67
  stores.get(ctx)?.players.delete(userId);
60
68
  }
69
+ /**
70
+ * Rendered body yaw to commit this frame. With `turnSpeed` unset the body snaps
71
+ * to its movement heading exactly as before (no opt-in, no behavior change); with
72
+ * `turnSpeed` set it rotates toward that heading at `turnSpeed` rad/s along the
73
+ * shortest arc, so strafing and backpedalling read as a turning body rather than
74
+ * an instant flip. Retained per player so the smoothing is continuous across frames.
75
+ */
76
+ function resolveBodyFacing(state, moving, velocityX, velocityZ, fallbackYaw, turnSpeed, dt) {
77
+ if (turnSpeed === undefined) {
78
+ const snapped = moving ? Math.atan2(velocityX, velocityZ) : fallbackYaw;
79
+ state.facing = snapped;
80
+ return snapped;
81
+ }
82
+ const from = state.facing ?? fallbackYaw;
83
+ const target = moving ? Math.atan2(velocityX, velocityZ) : from;
84
+ const next = approachYaw(from, target, turnSpeed, dt);
85
+ state.facing = next;
86
+ return next;
87
+ }
61
88
  /**
62
89
  * Integrate one player's movement for a tick from their held-input frame and commit the pose — the single
63
90
  * genre-agnostic controller both the shell (its local player) and a host (each connected player in `onTick`) call,
@@ -80,8 +107,9 @@ export function stepPlayerMovement(ctx, userId, input, dt, tuning, heading) {
80
107
  }
81
108
  else {
82
109
  const turnInput = (isDown("turnRight") ? 1 : 0) - (isDown("turnLeft") ? 1 : 0);
110
+ const turnSpeed = tuning.movement?.turnSpeed ?? DEFAULT_TURN_SPEED;
83
111
  if (turnInput !== 0)
84
- state.heading = steerYaw(state.heading, turnInput, DEFAULT_TURN_SPEED, dt);
112
+ state.heading = steerYaw(state.heading, turnInput, turnSpeed, dt);
85
113
  }
86
114
  const forwardX = Math.sin(state.heading);
87
115
  const forwardZ = Math.cos(state.heading);
@@ -120,7 +148,7 @@ export function stepPlayerMovement(ctx, userId, input, dt, tuning, heading) {
120
148
  body.y = motionBatch.y;
121
149
  ctx.scene.entity.setPose(playerId, {
122
150
  position: [body.x, body.y, body.z],
123
- rotationY: intent.moving ? Math.atan2(body.velocityX, body.velocityZ) : player.rotationY,
151
+ rotationY: resolveBodyFacing(state, intent.moving, body.velocityX, body.velocityZ, player.rotationY, tuning.movement?.turnSpeed, dt),
124
152
  dt,
125
153
  });
126
154
  return;
@@ -132,7 +160,19 @@ export function stepPlayerMovement(ctx, userId, input, dt, tuning, heading) {
132
160
  }
133
161
  motion.verticalVelocity = applyMotionImpulses(motion.verticalVelocity, motionBatch);
134
162
  [motion.horizontalVelocityX, motion.horizontalVelocityZ] = applyHorizontalImpulses(motion.horizontalVelocityX, motion.horizontalVelocityZ, motionBatch);
135
- const step = advancePlayerMotion(motion, intent, forwardX, forwardZ, walkSpeed, dt, tuning.physics);
163
+ const swimCfg = tuning.movement?.swim;
164
+ const swimEnabled = swimCfg === true || (typeof swimCfg === "object" && swimCfg !== null);
165
+ const swimSpeedMultiplier = typeof swimCfg === "object" && swimCfg !== null
166
+ ? swimCfg.speedMultiplier ?? DEFAULT_SWIM_SPEED_MULTIPLIER
167
+ : DEFAULT_SWIM_SPEED_MULTIPLIER;
168
+ const waterLevel = tuning.ground.waterLevel;
169
+ const submerged = swimEnabled &&
170
+ waterLevel !== undefined &&
171
+ tuning.ground.sampleHeight(player.position[0], player.position[2]) < waterLevel;
172
+ const motionOptions = submerged
173
+ ? { speedScale: swimSpeedMultiplier, floating: true }
174
+ : undefined;
175
+ const step = advancePlayerMotion(motion, intent, forwardX, forwardZ, walkSpeed, dt, tuning.physics, motionOptions);
136
176
  let stepX = step.stepX;
137
177
  let stepZ = step.stepZ;
138
178
  if (tuning.movement?.mode === "axis") {
@@ -141,7 +181,7 @@ export function stepPlayerMovement(ctx, userId, input, dt, tuning, heading) {
141
181
  stepZ = constrained.stepZ;
142
182
  }
143
183
  if (tuning.movement?.collideObjects === true) {
144
- const obstacles = nearbyObstacles(ctx.scene.object.list(), player.position);
184
+ const obstacles = gatherMovementObstacles(ctx, player.position, stepX, stepZ);
145
185
  const resolved = resolveObstacleStep(player.position, stepX, stepZ, obstacles);
146
186
  stepX = resolved.stepX;
147
187
  stepZ = resolved.stepZ;
@@ -153,10 +193,26 @@ export function stepPlayerMovement(ctx, userId, input, dt, tuning, heading) {
153
193
  nextX = snapped[0];
154
194
  nextZ = snapped[1];
155
195
  }
156
- let nextY = tuning.ground.sampleHeight(nextX, nextZ) + motion.jumpOffset;
196
+ const slideCfg = tuning.movement?.slopeSlide;
197
+ if (slideCfg === true || (typeof slideCfg === "object" && slideCfg !== null)) {
198
+ const maxClimbNormalY = typeof slideCfg === "object" && slideCfg.maxClimbSlope !== undefined
199
+ ? slideCfg.maxClimbSlope
200
+ : DEFAULT_MAX_CLIMB_NORMAL_Y;
201
+ if (tuning.ground.sampleNormal(nextX, nextZ)[1] < maxClimbNormalY) {
202
+ const slope = sampleSlope(tuning.ground, nextX, nextZ);
203
+ const slide = SLOPE_SLIDE_SPEED * slope.steepness * dt;
204
+ nextX += slope.downhill[0] * slide;
205
+ nextZ += slope.downhill[1] * slide;
206
+ }
207
+ }
208
+ const groundAtNext = tuning.ground.sampleHeight(nextX, nextZ);
209
+ let nextY = groundAtNext + motion.jumpOffset;
157
210
  if (motionBatch !== null && motionBatch.y !== null) {
158
211
  nextY = motionBatch.y;
159
- motion.jumpOffset = motionBatch.y - tuning.ground.sampleHeight(nextX, nextZ);
212
+ motion.jumpOffset = motionBatch.y - groundAtNext;
213
+ }
214
+ else if (swimEnabled && waterLevel !== undefined && groundAtNext < waterLevel) {
215
+ nextY = waterLevel;
160
216
  }
161
217
  if (tuning.movement?.beforeCommit !== undefined) {
162
218
  const frame = {
@@ -175,7 +231,106 @@ export function stepPlayerMovement(ctx, userId, input, dt, tuning, heading) {
175
231
  }
176
232
  ctx.scene.entity.setPose(playerId, {
177
233
  position: [nextX, nextY, nextZ],
178
- rotationY: intent.moving ? Math.atan2(motion.horizontalVelocityX, motion.horizontalVelocityZ) : player.rotationY,
234
+ rotationY: resolveBodyFacing(state, intent.moving, motion.horizontalVelocityX, motion.horizontalVelocityZ, player.rotationY, tuning.movement?.turnSpeed, dt),
179
235
  dt,
180
236
  });
181
237
  }
238
+ /** Feet-to-head span the obstruction test uses; matches `movementModel`'s `OBSTACLE_PLAYER_HEIGHT`. */
239
+ const OBSTACLE_PLAYER_HEIGHT = 1.8;
240
+ /**
241
+ * Largest collider half-extent the bounded broadphase assumes, so an object whose blocking box reaches
242
+ * the player still has its center inside the `inBox` query — a ~4m wall (half-extent ~2m) is caught. An
243
+ * object wider than this on an axis could be missed at its far edge; raise it if a wider blocker exists.
244
+ */
245
+ const OBSTACLE_MAX_HALF_EXTENT = 4;
246
+ /**
247
+ * Gather the player's nearby blocking obstacles from resolved colliders — the mesh-accurate replacement
248
+ * for the old `nearbyObstacles(list(), …)` which emitted a default 1×1×1 box per object. Bounded by an
249
+ * `inBox` broadphase (reach ≥ player radius + max half-extent so wide walls can't be missed), then, per
250
+ * candidate, keeps only blocking physical colliders and emits their real half-extents/offset (or, for a
251
+ * mesh collider carrying `boxes`, the compound sub-boxes), conservatively yaw-rotated by the object's
252
+ * `rotationY`. Objects with no resolved collider fall back to the default box; objects whose colliders
253
+ * don't block stop obstructing. Runs for both the shell's local player and a host, so parity is automatic.
254
+ */
255
+ function gatherMovementObstacles(ctx, position, stepX, stepZ) {
256
+ const reachX = Math.abs(stepX) + DEFAULT_OBSTACLE_PLAYER_RADIUS + OBSTACLE_MAX_HALF_EXTENT;
257
+ const reachZ = Math.abs(stepZ) + DEFAULT_OBSTACLE_PLAYER_RADIUS + OBSTACLE_MAX_HALF_EXTENT;
258
+ const min = [
259
+ position[0] - reachX,
260
+ position[1] - OBSTACLE_MAX_HALF_EXTENT,
261
+ position[2] - reachZ,
262
+ ];
263
+ const max = [
264
+ position[0] + reachX,
265
+ position[1] + OBSTACLE_PLAYER_HEIGHT + OBSTACLE_MAX_HALF_EXTENT,
266
+ position[2] + reachZ,
267
+ ];
268
+ const obstacles = [];
269
+ for (const object of ctx.scene.object.inBox(min, max)) {
270
+ const set = ctx.scene.object.collidersOf(object.instanceId);
271
+ if (set === null) {
272
+ // No resolved collider: preserve today's default 1×1×1 box.
273
+ obstacles.push({ position: object.position });
274
+ continue;
275
+ }
276
+ for (const collider of resolveColliders(set)) {
277
+ if (!collider.blocks || collider.purpose !== "physical")
278
+ continue;
279
+ obstacles.push(obstacleFromCollider(collider, object.position, object.rotationY));
280
+ }
281
+ }
282
+ return obstacles;
283
+ }
284
+ /** One blocking collider as a {@link CollisionObstacle}: a compound `boxes` obstacle for a mesh collider
285
+ * carrying sub-boxes, else a single half-extents/offset AABB. Yaw is applied by conservatively expanding
286
+ * each AABB to its rotated extent (mirroring `worldOffset`), so the obstruction never under-covers. */
287
+ function obstacleFromCollider(collider, position, rotationY) {
288
+ const cos = Math.cos(rotationY);
289
+ const sin = Math.sin(rotationY);
290
+ const shape = collider.shape;
291
+ if (shape.kind === "mesh" && shape.boxes !== undefined && shape.boxes.length > 0) {
292
+ return {
293
+ position,
294
+ boxes: shape.boxes.map((b) => yawExpandLocalBox(b.min, b.max, cos, sin)),
295
+ };
296
+ }
297
+ let hx;
298
+ let hy;
299
+ let hz;
300
+ if (shape.kind === "sphere") {
301
+ hx = hy = hz = shape.radius;
302
+ }
303
+ else {
304
+ hx = shape.halfExtents[0];
305
+ hy = shape.halfExtents[1];
306
+ hz = shape.halfExtents[2];
307
+ }
308
+ const offset = shape.offset;
309
+ const ox = offset !== undefined ? offset[0] : 0;
310
+ const oy = offset !== undefined ? offset[1] : 0;
311
+ const oz = offset !== undefined ? offset[2] : 0;
312
+ return {
313
+ position,
314
+ // Rotate the offset by yaw (mirror of colliders.worldOffset), expand the extents to the rotated AABB.
315
+ offset: [ox * cos + oz * sin, oy, -ox * sin + oz * cos],
316
+ halfExtents: [Math.abs(hx * cos) + Math.abs(hz * sin), hy, Math.abs(hx * sin) + Math.abs(hz * cos)],
317
+ };
318
+ }
319
+ /** Yaw-rotate an entity-local AABB about the object origin and re-fit it to an axis-aligned box, expressed
320
+ * as `min`/`max` relative to `position`. Conservative: the rotated box is grown to its enclosing AABB. */
321
+ function yawExpandLocalBox(boxMin, boxMax, cos, sin) {
322
+ const centerX = (boxMin[0] + boxMax[0]) / 2;
323
+ const centerY = (boxMin[1] + boxMax[1]) / 2;
324
+ const centerZ = (boxMin[2] + boxMax[2]) / 2;
325
+ const halfX = (boxMax[0] - boxMin[0]) / 2;
326
+ const halfY = (boxMax[1] - boxMin[1]) / 2;
327
+ const halfZ = (boxMax[2] - boxMin[2]) / 2;
328
+ const rcX = centerX * cos + centerZ * sin;
329
+ const rcZ = -centerX * sin + centerZ * cos;
330
+ const rhX = Math.abs(halfX * cos) + Math.abs(halfZ * sin);
331
+ const rhZ = Math.abs(halfX * sin) + Math.abs(halfZ * cos);
332
+ return {
333
+ min: [rcX - rhX, centerY - halfY, rcZ - rhZ],
334
+ max: [rcX + rhX, centerY + halfY, rcZ + rhZ],
335
+ };
336
+ }
@@ -13,11 +13,24 @@ export interface PoseAllowedStates {
13
13
  export interface PoseRejection {
14
14
  reason: string;
15
15
  }
16
+ export interface PoseSnapshot {
17
+ poses: Record<string, MovementPose>;
18
+ aims: Record<string, AimMode>;
19
+ }
16
20
  export interface PoseState {
17
21
  getPose(instanceId: string): MovementPose;
18
22
  setPose(instanceId: string, pose: MovementPose): PoseRejection | null;
19
23
  getAim(instanceId: string): AimMode;
20
24
  setAim(instanceId: string, mode: AimMode): PoseRejection | null;
21
25
  clear(instanceId: string): void;
26
+ /** Snapshot every entity's pose and aim for a whole-world save. */
27
+ snapshotAll(): PoseSnapshot;
28
+ /** Restore every entity's pose and aim from a {@link snapshotAll} payload. */
29
+ hydrateAll(state: PoseSnapshot): void;
22
30
  }
31
+ /**
32
+ * Stance/pose transitions — stand, crouch, prone — that change the hitbox and movement.
33
+ *
34
+ * @capability crouch-prone stance/pose transitions that change the hitbox
35
+ */
23
36
  export declare function createPoseState(resolveAllowed: (instanceId: string) => PoseAllowedStates | null | undefined): PoseState;
@@ -23,6 +23,11 @@ export const POSE_HITBOX = {
23
23
  speedMultiplier: MOVEMENT_TUNING.runSpeedMultiplier,
24
24
  },
25
25
  };
26
+ /**
27
+ * Stance/pose transitions — stand, crouch, prone — that change the hitbox and movement.
28
+ *
29
+ * @capability crouch-prone stance/pose transitions that change the hitbox
30
+ */
26
31
  export function createPoseState(resolveAllowed) {
27
32
  const poses = new Map();
28
33
  const aims = new Map();
@@ -57,5 +62,16 @@ export function createPoseState(resolveAllowed) {
57
62
  poses.delete(instanceId);
58
63
  aims.delete(instanceId);
59
64
  },
65
+ snapshotAll() {
66
+ return { poses: Object.fromEntries(poses), aims: Object.fromEntries(aims) };
67
+ },
68
+ hydrateAll(state) {
69
+ poses.clear();
70
+ for (const [id, pose] of Object.entries(state.poses))
71
+ poses.set(id, pose);
72
+ aims.clear();
73
+ for (const [id, aim] of Object.entries(state.aims))
74
+ aims.set(id, aim);
75
+ },
60
76
  };
61
77
  }
@@ -25,3 +25,11 @@ export declare function steerYaw(yaw: number, steerRight: number, turnRatePerSec
25
25
  * it into [-1, 1] at the call site.
26
26
  */
27
27
  export declare function steerToward(yaw: number, desiredYaw: number): number;
28
+ /**
29
+ * @internal Rotate `yaw` toward `targetYaw` by at most `turnRatePerSecond * dt`
30
+ * radians, always along the shortest arc, snapping exactly onto the target once
31
+ * within a single step so it converges without overshoot or jitter — the
32
+ * smoothing behind a rendered body-turn (`PlayerMovementConfig.turnSpeed`). A
33
+ * large enough rate (or `dt`) reaches the target in one call, matching a snap.
34
+ */
35
+ export declare function approachYaw(yaw: number, targetYaw: number, turnRatePerSecond: number, dt: number): number;