@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,47 +1,45 @@
1
- import { defaultKeyValueStorage } from "./keyValueStore.js";
2
- function readSafe(storage, key) {
3
- if (storage === null)
4
- return null;
5
- try {
6
- return storage.getItem(key);
7
- }
8
- catch {
9
- return null;
10
- }
11
- }
1
+ import { defaultKeyValueStorage, reportStorageFailure } from "./keyValueStore.js";
12
2
  /**
13
3
  * A {@link SaveBackend} over a synchronous {@link KeyValueStorage} — the
14
4
  * browser's `localStorage` by default (offline, on-device saves), a test stub,
15
- * or `null` for memory-only. Storage errors (quota exceeded, private mode, no
16
- * DOM) degrade to no-ops, so a save never throws into a game tick.
5
+ * or `null` for a no-op backend. Default fallback ignores failures; `errorMode: "throw"`
6
+ * rejects failed operations and unavailable storage, so SaveStore status/onError can report them.
7
+ * Use {@link memorySaveBackend} for deliberate ephemeral persistence.
17
8
  *
18
9
  * @capability local-save persist a game to on-device localStorage (offline)
19
10
  */
20
- export function localSaveBackend(storage) {
21
- const store = storage === undefined ? defaultKeyValueStorage() : storage;
11
+ export function localSaveBackend(storage, policy = {}) {
12
+ const resolve = () => {
13
+ const store = storage === undefined ? defaultKeyValueStorage((error) => { throw error; }) : storage;
14
+ if (store === null)
15
+ throw new Error("Local storage is unavailable");
16
+ return store;
17
+ };
22
18
  return {
23
- read: (key) => Promise.resolve(readSafe(store, key)),
24
- write: (key, value) => {
25
- if (store !== null) {
26
- try {
27
- store.setItem(key, value);
28
- }
29
- catch {
30
- // storage rejected the write; the store keeps the value in memory
31
- }
19
+ async read(key) {
20
+ try {
21
+ return resolve().getItem(key);
22
+ }
23
+ catch (error) {
24
+ reportStorageFailure(policy, { operation: "read", key, error });
25
+ return null;
32
26
  }
33
- return Promise.resolve();
34
27
  },
35
- remove: (key) => {
36
- if (store !== null) {
37
- try {
38
- store.removeItem(key);
39
- }
40
- catch {
41
- // ignore removal failure
42
- }
28
+ async write(key, value) {
29
+ try {
30
+ resolve().setItem(key, value);
31
+ }
32
+ catch (error) {
33
+ reportStorageFailure(policy, { operation: "write", key, error });
34
+ }
35
+ },
36
+ async remove(key) {
37
+ try {
38
+ resolve().removeItem(key);
39
+ }
40
+ catch (error) {
41
+ reportStorageFailure(policy, { operation: "remove", key, error });
43
42
  }
44
- return Promise.resolve();
45
43
  },
46
44
  };
47
45
  }
@@ -143,10 +141,10 @@ export function createSaveStore(config) {
143
141
  return parsed;
144
142
  }
145
143
  async function readSlots() {
144
+ const raw = await backend.read(indexKey());
145
+ if (raw === null)
146
+ return [];
146
147
  try {
147
- const raw = await backend.read(indexKey());
148
- if (raw === null)
149
- return [];
150
148
  const parsed = JSON.parse(raw);
151
149
  return Array.isArray(parsed) ? parsed.filter((entry) => typeof entry === "string") : [];
152
150
  }
@@ -155,26 +153,16 @@ export function createSaveStore(config) {
155
153
  }
156
154
  }
157
155
  async function rememberSlot(slot) {
158
- try {
159
- const list = await readSlots();
160
- if (list.includes(slot))
161
- return;
162
- await backend.write(indexKey(), JSON.stringify([...list, slot]));
163
- }
164
- catch (error) {
165
- reportError(error);
166
- }
156
+ const list = await readSlots();
157
+ if (list.includes(slot))
158
+ return;
159
+ await backend.write(indexKey(), JSON.stringify([...list, slot]));
167
160
  }
168
161
  async function forgetSlot(slot) {
169
- try {
170
- const list = await readSlots();
171
- if (!list.includes(slot))
172
- return;
173
- await backend.write(indexKey(), JSON.stringify(list.filter((entry) => entry !== slot)));
174
- }
175
- catch (error) {
176
- reportError(error);
177
- }
162
+ const list = await readSlots();
163
+ if (!list.includes(slot))
164
+ return;
165
+ await backend.write(indexKey(), JSON.stringify(list.filter((entry) => entry !== slot)));
178
166
  }
179
167
  function clearTimer() {
180
168
  if (pendingTimer !== null) {
@@ -243,7 +231,10 @@ export function createSaveStore(config) {
243
231
  firstDirtyAt = null;
244
232
  setStatus("idle");
245
233
  emit();
246
- void rememberSlot(currentSlot);
234
+ void rememberSlot(currentSlot).catch((error) => {
235
+ setStatus("error");
236
+ reportError(error);
237
+ });
247
238
  return current;
248
239
  }
249
240
  catch (error) {
@@ -298,7 +289,16 @@ export function createSaveStore(config) {
298
289
  firstDirtyAt = null;
299
290
  return load();
300
291
  },
301
- slots: readSlots,
292
+ async slots() {
293
+ try {
294
+ return await readSlots();
295
+ }
296
+ catch (error) {
297
+ setStatus("error");
298
+ reportError(error);
299
+ return [];
300
+ }
301
+ },
302
302
  subscribe(listener) {
303
303
  listeners.add(listener);
304
304
  return () => listeners.delete(listener);
@@ -15,14 +15,14 @@ export interface InstalledSystems {
15
15
  tick(ctx: GameContext, dt: number): void;
16
16
  /** Invoke every system's `reset` (scenario/run wipe). */
17
17
  reset(ctx: GameContext): void;
18
- /** Invoke every system's `dispose` in reverse install order. */
18
+ /** Retire once in reverse order; finish cleanup before rethrowing the first error. */
19
19
  dispose(ctx: GameContext): void;
20
20
  /** Run one manual system by id (no-op if missing or not manual). */
21
21
  runManual(ctx: GameContext, id: string, dt?: number): void;
22
22
  }
23
23
  /**
24
24
  * Install systems on a live context: `create` → bind events → register save/replicate → `start`.
25
- * Call once per world boot (from the composed loop's `onInit`).
25
+ * Call once per world boot. Failed installation retires entered systems and listeners, then rethrows the startup error.
26
26
  * @internal
27
27
  */
28
28
  export declare function installSystems(ctx: GameContext, systems: readonly SystemDefinition[], options?: CompileSystemScheduleOptions & {
@@ -7,7 +7,7 @@ function resolveModule(spec, ctx) {
7
7
  }
8
8
  /**
9
9
  * Install systems on a live context: `create` → bind events → register save/replicate → `start`.
10
- * Call once per world boot (from the composed loop's `onInit`).
10
+ * Call once per world boot. Failed installation retires entered systems and listeners, then rethrows the startup error.
11
11
  * @internal
12
12
  */
13
13
  export function installSystems(ctx, systems, options) {
@@ -16,29 +16,41 @@ export function installSystems(ctx, systems, options) {
16
16
  const unsubs = [];
17
17
  const intervalAcc = new Map();
18
18
  const fixedAcc = new Map();
19
- for (const system of list) {
20
- system.create?.(ctx);
21
- }
22
- for (const system of list) {
23
- if (system.events !== undefined) {
24
- for (const [name, handler] of Object.entries(system.events)) {
25
- if (handler === undefined)
26
- continue;
27
- const off = ctx.game.events.on(name, ((event) => {
28
- handler(ctx, event);
29
- }));
30
- unsubs.push(off);
19
+ const acquired = [];
20
+ let disposed = false;
21
+ try {
22
+ for (const system of list) {
23
+ acquired.push(system);
24
+ system.create?.(ctx);
25
+ }
26
+ for (const system of list) {
27
+ if (system.events !== undefined) {
28
+ for (const [name, handler] of Object.entries(system.events)) {
29
+ if (handler === undefined)
30
+ continue;
31
+ const off = ctx.game.events.on(name, ((event) => {
32
+ handler(ctx, event);
33
+ }));
34
+ unsubs.push(off);
35
+ }
31
36
  }
37
+ const save = resolveModule(system.save, ctx);
38
+ if (save !== undefined)
39
+ options?.modules?.registerSave(save);
40
+ const replicate = resolveModule(system.replicate, ctx);
41
+ if (replicate !== undefined)
42
+ options?.modules?.registerReplicate(replicate);
43
+ }
44
+ for (const system of list) {
45
+ system.start?.(ctx);
32
46
  }
33
- const save = resolveModule(system.save, ctx);
34
- if (save !== undefined)
35
- options?.modules?.registerSave(save);
36
- const replicate = resolveModule(system.replicate, ctx);
37
- if (replicate !== undefined)
38
- options?.modules?.registerReplicate(replicate);
39
47
  }
40
- for (const system of list) {
41
- system.start?.(ctx);
48
+ catch (error) {
49
+ try {
50
+ dispose(ctx);
51
+ }
52
+ catch { /* The startup error remains the first failure. */ }
53
+ throw error;
42
54
  }
43
55
  for (const group of schedule.fixed) {
44
56
  fixedAcc.set(group.rate, 0);
@@ -52,7 +64,7 @@ export function installSystems(ctx, systems, options) {
52
64
  }
53
65
  }
54
66
  function tick(ctx, dt) {
55
- if (!(dt > 0))
67
+ if (disposed || !(dt > 0))
56
68
  return;
57
69
  for (const group of schedule.fixed) {
58
70
  const step = 1 / group.rate;
@@ -79,6 +91,8 @@ export function installSystems(ctx, systems, options) {
79
91
  }
80
92
  }
81
93
  function reset(ctx) {
94
+ if (disposed)
95
+ return;
82
96
  for (const system of list)
83
97
  system.reset?.(ctx);
84
98
  for (const group of schedule.fixed)
@@ -87,14 +101,33 @@ export function installSystems(ctx, systems, options) {
87
101
  intervalAcc.set(interval.id, 0);
88
102
  }
89
103
  function dispose(ctx) {
90
- for (const off of unsubs)
91
- off();
104
+ if (disposed)
105
+ return;
106
+ disposed = true;
107
+ let failure;
108
+ for (const off of unsubs) {
109
+ try {
110
+ off();
111
+ }
112
+ catch (error) {
113
+ failure ??= { error };
114
+ }
115
+ }
92
116
  unsubs.length = 0;
93
- for (let i = list.length - 1; i >= 0; i -= 1) {
94
- list[i]?.dispose?.(ctx);
117
+ for (let i = acquired.length - 1; i >= 0; i -= 1) {
118
+ try {
119
+ acquired[i]?.dispose?.(ctx);
120
+ }
121
+ catch (error) {
122
+ failure ??= { error };
123
+ }
95
124
  }
125
+ if (failure !== undefined)
126
+ throw failure.error;
96
127
  }
97
128
  function runManual(ctx, id, dt = 0) {
129
+ if (disposed)
130
+ return;
98
131
  if (!schedule.manual.includes(id))
99
132
  return;
100
133
  schedule.systemsById.get(id)?.update?.(ctx, dt);
@@ -119,17 +152,18 @@ export function composeGameLoop(systems, loop, options) {
119
152
  if (systems === undefined || systems.length === 0) {
120
153
  return loop ?? {};
121
154
  }
122
- let installed;
155
+ const retired = new WeakSet();
123
156
  const loopBefore = options?.loopBeforeSystems === true;
124
157
  return {
125
158
  onInit(ctx) {
159
+ retired.delete(ctx);
126
160
  const modules = ctx.game.registerSave !== undefined && ctx.game.registerReplicate !== undefined
127
161
  ? {
128
162
  registerSave: (m) => ctx.game.registerSave(m),
129
163
  registerReplicate: (m) => ctx.game.registerReplicate(m),
130
164
  }
131
165
  : undefined;
132
- installed = installSystems(ctx, systems, { ...options, modules });
166
+ const installed = installSystems(ctx, systems, { ...options, modules });
133
167
  installedByCtx.set(ctx, installed);
134
168
  loop?.onInit?.(ctx);
135
169
  },
@@ -139,10 +173,10 @@ export function composeGameLoop(systems, loop, options) {
139
173
  onTick(ctx, dt) {
140
174
  if (loopBefore) {
141
175
  loop?.onTick?.(ctx, dt);
142
- installed?.tick(ctx, dt);
176
+ installedByCtx.get(ctx)?.tick(ctx, dt);
143
177
  }
144
178
  else {
145
- installed?.tick(ctx, dt);
179
+ installedByCtx.get(ctx)?.tick(ctx, dt);
146
180
  loop?.onTick?.(ctx, dt);
147
181
  }
148
182
  },
@@ -150,13 +184,30 @@ export function composeGameLoop(systems, loop, options) {
150
184
  loop?.onPlayerLeave?.(ctx, player);
151
185
  },
152
186
  onReset(ctx) {
153
- installed?.reset(ctx);
187
+ installedByCtx.get(ctx)?.reset(ctx);
154
188
  loop?.onReset?.(ctx);
155
189
  },
156
190
  onDispose(ctx) {
157
- installed?.dispose(ctx);
191
+ if (retired.has(ctx))
192
+ return;
193
+ retired.add(ctx);
194
+ const installed = installedByCtx.get(ctx);
158
195
  installedByCtx.delete(ctx);
159
- loop?.onDispose?.(ctx);
196
+ let failure;
197
+ try {
198
+ installed?.dispose(ctx);
199
+ }
200
+ catch (error) {
201
+ failure = { error };
202
+ }
203
+ try {
204
+ loop?.onDispose?.(ctx);
205
+ }
206
+ catch (error) {
207
+ failure ??= { error };
208
+ }
209
+ if (failure !== undefined)
210
+ throw failure.error;
160
211
  },
161
212
  };
162
213
  }
@@ -15,6 +15,11 @@ export interface Toast<T = string> {
15
15
  export declare function appendToast<T>(toasts: readonly Toast<T>[], toast: Toast<T>, cap: number): readonly Toast<T>[];
16
16
  /** Drop every toast whose `expiresAt` is at or before `now`. Returns the same array when nothing expired. */
17
17
  export declare function pruneToasts<T>(toasts: readonly Toast<T>[], now: number): readonly Toast<T>[];
18
+ /** Plain JSON state of a {@link ToastQueue}: live toasts plus the id counter. */
19
+ export interface ToastQueueState<T = string> {
20
+ toasts: Toast<T>[];
21
+ counter: number;
22
+ }
18
23
  /** Stateful transient-toast list with a size cap and time-to-live eviction. */
19
24
  export interface ToastQueue<T = string> {
20
25
  /** Raise a toast at `now` living `ttlSeconds` (falling back to the queue default); returns it. */
@@ -25,6 +30,9 @@ export interface ToastQueue<T = string> {
25
30
  list(): readonly Toast<T>[];
26
31
  /** Remove all toasts. */
27
32
  clear(): void;
33
+ /** Copies the queue; each toast's `body` is shared, since the queue never mutates it. */
34
+ snapshot(): ToastQueueState<T>;
35
+ restore(next: ToastQueueState<T>): void;
28
36
  }
29
37
  /** Options for {@link createToastQueue}. */
30
38
  export interface ToastQueueOptions {
@@ -40,5 +40,12 @@ export function createToastQueue(options = {}) {
40
40
  clear() {
41
41
  toasts = [];
42
42
  },
43
+ snapshot() {
44
+ return { toasts: toasts.map((toast) => ({ ...toast })), counter };
45
+ },
46
+ restore(next) {
47
+ toasts = next.toasts.map((toast) => ({ ...toast }));
48
+ counter = next.counter;
49
+ },
43
50
  };
44
51
  }
@@ -115,6 +115,15 @@ export interface VfxInstanceStore {
115
115
  tick(nowMs?: number): number;
116
116
  /** Dispose every instance (scene change / reset), emitting a `stop` op for each. */
117
117
  clear(): void;
118
+ /** Plain JSON copy of every live instance plus the id counter. */
119
+ snapshot(): VfxInstanceStoreState;
120
+ /** Replaces the live set: emits `stop` for each current instance, then `upsert` for each restored one. */
121
+ restore(next: VfxInstanceStoreState): void;
122
+ }
123
+ /** Plain JSON state of a {@link VfxInstanceStore}. */
124
+ export interface VfxInstanceStoreState {
125
+ instances: VfxInstanceState[];
126
+ seq: number;
118
127
  }
119
128
  /** Options for {@link createVfxInstanceStore}. */
120
129
  export interface VfxInstanceStoreOptions {
@@ -92,5 +92,16 @@ export function createVfxInstanceStore(options = {}) {
92
92
  for (const id of [...instances.keys()])
93
93
  stop(id);
94
94
  }
95
- return { upsert, update, stop, get, list, count, tick, clear };
95
+ function snapshot() {
96
+ return { instances: structuredClone([...instances.values()]), seq };
97
+ }
98
+ function restore(next) {
99
+ clear();
100
+ seq = next.seq;
101
+ for (const state of structuredClone(next.instances)) {
102
+ instances.set(state.id, state);
103
+ emit({ op: "upsert", id: state.id, instance: state });
104
+ }
105
+ }
106
+ return { upsert, update, stop, get, list, count, tick, clear, snapshot, restore };
96
107
  }
@@ -0,0 +1,43 @@
1
+ import type { ChaseCameraConfig, TopDownCameraConfig } from "./cameraConfig.js";
2
+ /** A screen region as fractions of the canvas, `[x, y, width, height]` in `0..1` with the origin at the top left. */
3
+ export type ViewportRect = readonly [number, number, number, number];
4
+ /** The follow camera an extra viewport runs; the primary seat keeps the game's main camera rig. */
5
+ export interface ViewportCameraConfig {
6
+ /** Default: `"topDown"` when the game's main rig is top-down, otherwise `"chase"`. */
7
+ rig?: "chase" | "topDown";
8
+ chase?: ChaseCameraConfig;
9
+ topDown?: TopDownCameraConfig;
10
+ }
11
+ /** One viewport: which local seat it shows and where. */
12
+ export interface ViewportDef {
13
+ /** Local seat id (`"slot:0"`, `"slot:1"`, …) from `localPlayers(ctx)`. */
14
+ slot: string;
15
+ rect: ViewportRect;
16
+ camera?: ViewportCameraConfig;
17
+ }
18
+ /** `defineGame({ viewports })`: split-screen layout for local seats. */
19
+ export interface ViewportsConfig {
20
+ /** `"auto"` (default) splits the screen evenly across the seats currently joined; a list pins explicit rects. */
21
+ layout?: "auto" | readonly ViewportDef[];
22
+ /** Two-seat split: `"vertical"` puts them side by side (default), `"horizontal"` stacks them. */
23
+ split?: "vertical" | "horizontal";
24
+ /** Follow camera for seats whose viewport has none. */
25
+ camera?: ViewportCameraConfig;
26
+ }
27
+ /** A viewport in canvas pixels with a bottom-left origin, ready for `gl.setViewport`/`setScissor`. */
28
+ export interface ViewportPixels {
29
+ x: number;
30
+ y: number;
31
+ width: number;
32
+ height: number;
33
+ }
34
+ /**
35
+ * Even split-screen rects for seats in order: one fills the screen, two split side by side (or stacked),
36
+ * three put the first on a full-width top half, four make a 2×2 grid, more fill a grid row by row.
37
+ * @capability split-screen Split the screen into one viewport per local seat, each with its own follow camera.
38
+ */
39
+ export declare function splitViewports(slots: readonly string[], split?: "vertical" | "horizontal"): ViewportDef[];
40
+ /** The viewports to draw for the seats currently joined: the explicit layout filtered to joined seats, or an even split. */
41
+ export declare function resolveViewports(config: ViewportsConfig | undefined, slots: readonly string[]): ViewportDef[];
42
+ /** Convert a top-left fractional rect into whole canvas pixels with a bottom-left origin; neighbours share edges exactly. */
43
+ export declare function viewportPixels(rect: ViewportRect, width: number, height: number): ViewportPixels;
@@ -0,0 +1,46 @@
1
+ function gridRects(count) {
2
+ const columns = Math.ceil(Math.sqrt(count));
3
+ const rows = Math.ceil(count / columns);
4
+ const rects = [];
5
+ for (let index = 0; index < count; index += 1) {
6
+ const row = Math.floor(index / columns);
7
+ const inRow = row === rows - 1 ? count - row * columns : columns;
8
+ const column = index - row * columns;
9
+ rects.push([column / inRow, row / rows, 1 / inRow, 1 / rows]);
10
+ }
11
+ return rects;
12
+ }
13
+ /**
14
+ * Even split-screen rects for seats in order: one fills the screen, two split side by side (or stacked),
15
+ * three put the first on a full-width top half, four make a 2×2 grid, more fill a grid row by row.
16
+ * @capability split-screen Split the screen into one viewport per local seat, each with its own follow camera.
17
+ */
18
+ export function splitViewports(slots, split = "vertical") {
19
+ const count = slots.length;
20
+ let rects;
21
+ if (count <= 1)
22
+ rects = [[0, 0, 1, 1]];
23
+ else if (count === 2) {
24
+ rects = split === "vertical" ? [[0, 0, 0.5, 1], [0.5, 0, 0.5, 1]] : [[0, 0, 1, 0.5], [0, 0.5, 1, 0.5]];
25
+ }
26
+ else if (count === 3)
27
+ rects = [[0, 0, 1, 0.5], [0, 0.5, 0.5, 0.5], [0.5, 0.5, 0.5, 0.5]];
28
+ else
29
+ rects = gridRects(count);
30
+ return slots.map((slot, index) => ({ slot, rect: rects[index] }));
31
+ }
32
+ /** The viewports to draw for the seats currently joined: the explicit layout filtered to joined seats, or an even split. */
33
+ export function resolveViewports(config, slots) {
34
+ const layout = config?.layout ?? "auto";
35
+ if (layout === "auto")
36
+ return splitViewports(slots, config?.split);
37
+ return layout.filter((def) => slots.includes(def.slot));
38
+ }
39
+ /** Convert a top-left fractional rect into whole canvas pixels with a bottom-left origin; neighbours share edges exactly. */
40
+ export function viewportPixels(rect, width, height) {
41
+ const left = Math.round(rect[0] * width);
42
+ const right = Math.round((rect[0] + rect[2]) * width);
43
+ const top = Math.round(rect[1] * height);
44
+ const bottom = Math.round((rect[1] + rect[3]) * height);
45
+ return { x: left, y: height - bottom, width: Math.max(1, right - left), height: Math.max(1, bottom - top) };
46
+ }
@@ -62,7 +62,6 @@ export declare function selectNearestWorldItem(candidates: readonly {
62
62
  /**
63
63
  * Spawn and track pickup-able items scattered in the world, including drops that scatter on death.
64
64
  *
65
- * @capability world-drops spawn pickup-able items in the world, including death drops
66
65
  * @internal
67
66
  */
68
67
  export declare function createWorldItemStore(deps: WorldItemStoreDeps): WorldItemStore;
@@ -87,6 +86,11 @@ export interface ResolvedDeathDrops {
87
86
  * @internal
88
87
  */
89
88
  export declare function resolveDeathDrops(drops: readonly Drop[], options: ResolveDeathDropsOptions): ResolvedDeathDrops;
89
+ /**
90
+ * Per-rarity look of a dropped item: beam, color and label. Set via `defineGame({ worldItem: { rarityStyle } })`.
91
+ *
92
+ * @capability loot-beams rarity-colored loot beams and labels on dropped items via defineGame({ worldItem: { rarityStyle, beamHeight } })
93
+ */
90
94
  export interface RarityStyle {
91
95
  color?: string;
92
96
  beam?: boolean;
@@ -39,7 +39,6 @@ export function selectNearestWorldItem(candidates, from, radius) {
39
39
  /**
40
40
  * Spawn and track pickup-able items scattered in the world, including drops that scatter on death.
41
41
  *
42
- * @capability world-drops spawn pickup-able items in the world, including death drops
43
42
  * @internal
44
43
  */
45
44
  export function createWorldItemStore(deps) {
@@ -31,6 +31,7 @@ export { medalFor, rankLeaderboard, type RankLeaderboardOptions, type RankableRo
31
31
  export { createLevelSequence, type LevelSequence } from "./game/levelSequence.js";
32
32
  export { createLoadouts, type LoadoutDef } from "./game/loadout.js";
33
33
  export { evaluateLootFilter, lootFilter, type LootFilterRule } from "./game/lootFilter.js";
34
+ export { timeScaledRarity, type TimeScaledRarityOptions } from "./game/lootModifiers.js";
34
35
  export { createLootPipeline, defineLootPipeline, type LootDropProvenance, type LootModifier, type LootPipeline, type LootPipelineDef, type LootPipelineDeps, type LootPlanEntry, type LootResolution, type LootResolveContext, type LootRollPlan, type LootStage, type LootStageKind, type LootStageStatus, type LootStageTrace, } from "./game/lootPipeline.js";
35
36
  export { createLootRegistry, lootTable, type Drop, type LootTableDef } from "./game/lootTable.js";
36
37
  export { resolveOneShotClip } from "./game/modelAnimation.js";
@@ -100,5 +101,7 @@ export { createMultiRegionHealth, type MultiRegionHealth } from "./survival/regi
100
101
  export { createCommitController } from "./turn/commit.js";
101
102
  export { createIntentBoard } from "./turn/intent.js";
102
103
  export { createTurnLoop, type TurnLoop } from "./turn/turnLoop.js";
104
+ export { dispatchExpedition, recallExpedition, returnExpeditionNow, settleExpedition, type ExpeditionConfig, type ExpeditionDispatch, type ExpeditionEvent, type ExpeditionHazard, type ExpeditionState, type ExpeditionSupplyRule, } from "./work/expedition.js";
103
105
  export { activeJobs, cancelJob, createWorkQueue, enqueue, fifoOrdering, jobById, jobProgress, pauseJob, priorityOrdering, queueSize, queuedJobs, resumeJob, tick, type CancelResult, type EnqueueOptions, type EnqueueResult, type Job, type JobId, type JobOrdering, type JobStatus, type JobValidation, type TickResult, type WorkQueueConfig, type WorkQueueEvent, type WorkQueueState, } from "./work/jobQueue.js";
106
+ export { stationOutputRate, type StaffedStation, type StationRateTuning, type StationWorker, } from "./work/staffedStation.js";
104
107
  export { unitTrainingConfig, type ResourceCost, type TrainableUnitDef, type UnitReservation, type UnitSpawnOrder, type UnitTrainingOptions, type UnitTrainingSpec, } from "./work/unitTraining.js";
package/dist/gameplay.js CHANGED
@@ -31,6 +31,7 @@ export { medalFor, rankLeaderboard, } from "./game/leaderboardRank.js";
31
31
  export { createLevelSequence } from "./game/levelSequence.js";
32
32
  export { createLoadouts } from "./game/loadout.js";
33
33
  export { evaluateLootFilter, lootFilter } from "./game/lootFilter.js";
34
+ export { timeScaledRarity } from "./game/lootModifiers.js";
34
35
  export { createLootPipeline, defineLootPipeline, } from "./game/lootPipeline.js";
35
36
  export { createLootRegistry, lootTable } from "./game/lootTable.js";
36
37
  export { resolveOneShotClip } from "./game/modelAnimation.js";
@@ -100,5 +101,7 @@ export { createMultiRegionHealth } from "./survival/regionHealth.js";
100
101
  export { createCommitController } from "./turn/commit.js";
101
102
  export { createIntentBoard } from "./turn/intent.js";
102
103
  export { createTurnLoop } from "./turn/turnLoop.js";
104
+ export { dispatchExpedition, recallExpedition, returnExpeditionNow, settleExpedition, } from "./work/expedition.js";
103
105
  export { activeJobs, cancelJob, createWorkQueue, enqueue, fifoOrdering, jobById, jobProgress, pauseJob, priorityOrdering, queueSize, queuedJobs, resumeJob, tick, } from "./work/jobQueue.js";
106
+ export { stationOutputRate, } from "./work/staffedStation.js";
104
107
  export { unitTrainingConfig, } from "./work/unitTraining.js";
@@ -34,7 +34,13 @@ export type ActionCodes<TCode extends string = string> = readonly TCode[] | {
34
34
  toggle?: readonly TCode[];
35
35
  repeatMs?: number;
36
36
  };
37
- /** Maps each game action name to the input codes (hold/toggle keys, repeat rate) that trigger it. */
37
+ /**
38
+ * Maps each game action name to the input codes (hold/toggle keys, repeat rate) that trigger it. In a
39
+ * `defineGame({ input })`, pressing an action runs the same-named command with `{ yaw, pitch, aim }`;
40
+ * `repeatMs` re-fires it while held (automatic weapons).
41
+ *
42
+ * @capability fire-input bind a key or mouse button to a named action that runs the same-named command with the aim; repeatMs auto-fires while held
43
+ */
38
44
  export type ActionCodesMap<TAction extends string = string, TCode extends string = string> = Record<TAction, ActionCodes<TCode>>;
39
45
  /** @internal */
40
46
  export declare function actionRepeatMs(codes: ActionCodes | undefined): number | undefined;
@@ -82,6 +88,14 @@ export interface ActionStateTracker<TAction extends string> {
82
88
  wasPressed(action: TAction): boolean;
83
89
  endFrame(): void;
84
90
  reset(): void;
91
+ /**
92
+ * Swap the binding map without dropping what is physically held: a key still down counts toward
93
+ * whatever action it maps to now, with no fresh press edge. Toggle state survives for actions that
94
+ * come back later.
95
+ */
96
+ rebind(map: ActionStateBindingMap<TAction, string>): void;
97
+ /** The action names in the current binding map. */
98
+ actions(): readonly TAction[];
85
99
  }
86
100
  /** @internal */
87
101
  export declare function createActionStateTracker<TAction extends string, TCode extends string = string>(map: ActionStateBindingMap<TAction, TCode>): ActionStateTracker<TAction>;
@@ -179,9 +179,9 @@ function resolveActionBindingModes(config) {
179
179
  }
180
180
  /** @internal */
181
181
  export function createActionStateTracker(map) {
182
- const actions = Object.keys(map);
183
- const modesByAction = new Map(actions.map((action) => [action, resolveActionBindingModes(map[action])]));
184
- const heldCodesByAction = new Map(actions.map((action) => [action, new Set()]));
182
+ let actions = Object.keys(map);
183
+ let modesByAction = new Map(actions.map((action) => [action, resolveActionBindingModes(map[action])]));
184
+ let heldCodesByAction = new Map(actions.map((action) => [action, new Set()]));
185
185
  const toggledActions = new Set();
186
186
  const pressedThisFrame = new Set();
187
187
  const activeCodes = new Set();
@@ -240,5 +240,16 @@ export function createActionStateTracker(map) {
240
240
  pressedThisFrame.clear();
241
241
  activeCodes.clear();
242
242
  },
243
+ rebind(next) {
244
+ actions = Object.keys(next);
245
+ modesByAction = new Map(actions.map((action) => [action, resolveActionBindingModes(next[action])]));
246
+ heldCodesByAction = new Map(actions.map((action) => [action, new Set()]));
247
+ for (const code of activeCodes) {
248
+ const holdAction = findAction(code, (modes) => modes.hold);
249
+ if (holdAction !== null)
250
+ heldCodesByAction.get(holdAction).add(code);
251
+ }
252
+ },
253
+ actions: () => actions,
243
254
  };
244
255
  }