@jgengine/core 0.9.0 → 0.11.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 (631) hide show
  1. package/CHANGELOG.md +34 -3
  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/spawnDirector.d.ts +19 -1
  21. package/dist/ai/spawnDirector.js +53 -26
  22. package/dist/ai/spawnPoint.d.ts +7 -0
  23. package/dist/ai/spawnPoint.js +7 -0
  24. package/dist/ai/targetAcquisition.d.ts +84 -0
  25. package/dist/ai/targetAcquisition.js +92 -0
  26. package/dist/anim/easing.d.ts +19 -0
  27. package/dist/anim/easing.js +19 -0
  28. package/dist/anim/oscillator.d.ts +12 -0
  29. package/dist/anim/oscillator.js +25 -0
  30. package/dist/area/areaEffectField.d.ts +127 -0
  31. package/dist/area/areaEffectField.js +175 -0
  32. package/dist/area/stackPolicy.d.ts +47 -0
  33. package/dist/area/stackPolicy.js +99 -0
  34. package/dist/audio/audioFalloff.d.ts +5 -2
  35. package/dist/audio/music.d.ts +53 -0
  36. package/dist/audio/music.js +34 -0
  37. package/dist/audio/synth.d.ts +60 -0
  38. package/dist/audio/synth.js +8 -0
  39. package/dist/authoring.d.ts +15 -0
  40. package/dist/authoring.js +15 -0
  41. package/dist/board/laneBoard.d.ts +13 -0
  42. package/dist/board/laneBoard.js +13 -0
  43. package/dist/board/timelineBoard.d.ts +10 -0
  44. package/dist/board/timelineBoard.js +10 -0
  45. package/dist/combat/abilityKit.d.ts +18 -0
  46. package/dist/combat/abilityKit.js +24 -9
  47. package/dist/combat/animationState.d.ts +5 -0
  48. package/dist/combat/animationState.js +5 -0
  49. package/dist/combat/breakMeters.d.ts +10 -0
  50. package/dist/combat/breakMeters.js +10 -0
  51. package/dist/combat/castRunner.d.ts +5 -0
  52. package/dist/combat/castRunner.js +5 -0
  53. package/dist/combat/comboPoints.d.ts +5 -0
  54. package/dist/combat/comboPoints.js +5 -0
  55. package/dist/combat/comboString.d.ts +5 -0
  56. package/dist/combat/comboString.js +5 -0
  57. package/dist/combat/damageInterceptors.d.ts +192 -0
  58. package/dist/combat/damageInterceptors.js +251 -0
  59. package/dist/combat/damageMatchup.d.ts +68 -0
  60. package/dist/combat/damageMatchup.js +42 -0
  61. package/dist/combat/damageResolution.d.ts +63 -0
  62. package/dist/combat/damageResolution.js +50 -0
  63. package/dist/combat/death.d.ts +7 -1
  64. package/dist/combat/death.js +5 -0
  65. package/dist/combat/deathReason.d.ts +14 -0
  66. package/dist/combat/deathReason.js +1 -0
  67. package/dist/combat/defensiveWindow.d.ts +5 -0
  68. package/dist/combat/defensiveWindow.js +5 -0
  69. package/dist/combat/dotField.d.ts +47 -0
  70. package/dist/combat/dotField.js +52 -0
  71. package/dist/combat/downed.d.ts +5 -0
  72. package/dist/combat/downed.js +5 -0
  73. package/dist/combat/effects.d.ts +5 -0
  74. package/dist/combat/effects.js +5 -0
  75. package/dist/combat/encounterSequence.d.ts +258 -0
  76. package/dist/combat/encounterSequence.js +426 -0
  77. package/dist/combat/hitReaction.d.ts +57 -1
  78. package/dist/combat/hitReaction.js +45 -5
  79. package/dist/combat/magazine.d.ts +54 -0
  80. package/dist/combat/magazine.js +96 -0
  81. package/dist/combat/projectiles.d.ts +7 -2
  82. package/dist/combat/projectiles.js +48 -17
  83. package/dist/combat/receivedDamage.d.ts +117 -0
  84. package/dist/combat/receivedDamage.js +92 -0
  85. package/dist/combat/regenShield.d.ts +39 -0
  86. package/dist/combat/regenShield.js +49 -0
  87. package/dist/combat/renderCues.d.ts +51 -0
  88. package/dist/combat/renderCues.js +44 -0
  89. package/dist/combat/resourcePool.d.ts +5 -0
  90. package/dist/combat/resourcePool.js +5 -0
  91. package/dist/combat/rewardAllocation.d.ts +121 -0
  92. package/dist/combat/rewardAllocation.js +251 -0
  93. package/dist/combat/shotOrigin.d.ts +30 -1
  94. package/dist/combat/shotOrigin.js +70 -2
  95. package/dist/combat/statusApplication.d.ts +96 -0
  96. package/dist/combat/statusApplication.js +81 -0
  97. package/dist/combat/telegraph.d.ts +11 -2
  98. package/dist/combat/telegraph.js +11 -2
  99. package/dist/combat/thresholdCrossings.d.ts +85 -0
  100. package/dist/combat/thresholdCrossings.js +116 -0
  101. package/dist/combat.d.ts +34 -0
  102. package/dist/combat.js +34 -0
  103. package/dist/commands/commandRegistry.d.ts +11 -0
  104. package/dist/commands/commandRegistry.js +9 -2
  105. package/dist/crafting/crop.d.ts +18 -0
  106. package/dist/crafting/crop.js +18 -0
  107. package/dist/crafting/production.d.ts +5 -0
  108. package/dist/crafting/production.js +5 -0
  109. package/dist/devtools/rewriteTunables.d.ts +17 -0
  110. package/dist/devtools/rewriteTunables.js +276 -0
  111. package/dist/devtools/saveEndpoint.d.ts +46 -0
  112. package/dist/devtools/saveEndpoint.js +48 -0
  113. package/dist/devtools/transformTunables.d.ts +3 -0
  114. package/dist/devtools/transformTunables.js +3 -0
  115. package/dist/devtools/tunableSchema.d.ts +31 -0
  116. package/dist/devtools/tunableSchema.js +31 -0
  117. package/dist/economy/currency.d.ts +12 -1
  118. package/dist/economy/currency.js +12 -1
  119. package/dist/economy/listingBook.d.ts +91 -0
  120. package/dist/economy/listingBook.js +144 -0
  121. package/dist/economy/resourceLedger.d.ts +222 -0
  122. package/dist/economy/resourceLedger.js +353 -0
  123. package/dist/economy/sharedWallet.d.ts +14 -0
  124. package/dist/economy/sharedWallet.js +14 -0
  125. package/dist/economy/techTree.d.ts +12 -0
  126. package/dist/economy/techTree.js +12 -0
  127. package/dist/economy/wallet.d.ts +26 -2
  128. package/dist/economy/wallet.js +35 -6
  129. package/dist/editor/commands.d.ts +248 -0
  130. package/dist/editor/commands.js +865 -0
  131. package/dist/editor/document.d.ts +186 -0
  132. package/dist/editor/document.js +884 -0
  133. package/dist/editor/grid.d.ts +229 -0
  134. package/dist/editor/grid.js +366 -0
  135. package/dist/editor/gridAdapters.d.ts +80 -0
  136. package/dist/editor/gridAdapters.js +134 -0
  137. package/dist/editor/index.d.ts +97 -0
  138. package/dist/editor/index.js +10 -0
  139. package/dist/editor/liveSync.d.ts +127 -0
  140. package/dist/editor/liveSync.js +249 -0
  141. package/dist/editor/runtimeInspector.d.ts +88 -0
  142. package/dist/editor/runtimeInspector.js +239 -0
  143. package/dist/editor/types.d.ts +175 -0
  144. package/dist/editor/types.js +29 -0
  145. package/dist/faction/reputation.d.ts +5 -0
  146. package/dist/faction/reputation.js +5 -0
  147. package/dist/format/distance.d.ts +17 -0
  148. package/dist/format/distance.js +18 -0
  149. package/dist/format/duration.d.ts +27 -0
  150. package/dist/format/duration.js +45 -0
  151. package/dist/format/speed.d.ts +17 -0
  152. package/dist/format/speed.js +25 -0
  153. package/dist/game/connectedPlayers.d.ts +30 -0
  154. package/dist/game/connectedPlayers.js +21 -0
  155. package/dist/game/cosmetics.d.ts +9 -0
  156. package/dist/game/cosmetics.js +16 -0
  157. package/dist/game/defineGame.d.ts +141 -2
  158. package/dist/game/defineGame.js +39 -1
  159. package/dist/game/defineSystem.d.ts +88 -0
  160. package/dist/game/defineSystem.js +45 -0
  161. package/dist/game/dialogue.d.ts +33 -0
  162. package/dist/game/dialogue.js +21 -0
  163. package/dist/game/events.d.ts +50 -13
  164. package/dist/game/events.js +5 -0
  165. package/dist/game/feed.d.ts +33 -0
  166. package/dist/game/feed.js +35 -2
  167. package/dist/game/gamePhase.d.ts +11 -0
  168. package/dist/game/gamePhase.js +16 -0
  169. package/dist/game/keyValueStore.d.ts +25 -0
  170. package/dist/game/keyValueStore.js +61 -0
  171. package/dist/game/leaderboard.d.ts +5 -0
  172. package/dist/game/leaderboard.js +5 -0
  173. package/dist/game/levelSequence.d.ts +24 -2
  174. package/dist/game/levelSequence.js +51 -2
  175. package/dist/game/loadout.d.ts +5 -0
  176. package/dist/game/loadout.js +5 -0
  177. package/dist/game/lootFilter.d.ts +2 -0
  178. package/dist/game/lootFilter.js +2 -0
  179. package/dist/game/lootPipeline.d.ts +164 -0
  180. package/dist/game/lootPipeline.js +216 -0
  181. package/dist/game/lootTable.d.ts +21 -0
  182. package/dist/game/lootTable.js +15 -4
  183. package/dist/game/modelAnimation.d.ts +8 -0
  184. package/dist/game/modelAnimation.js +18 -0
  185. package/dist/game/objectives.d.ts +43 -0
  186. package/dist/game/objectives.js +36 -0
  187. package/dist/game/paramLayers.d.ts +148 -0
  188. package/dist/game/paramLayers.js +185 -0
  189. package/dist/game/ping.d.ts +5 -0
  190. package/dist/game/ping.js +5 -0
  191. package/dist/game/playableGame.d.ts +160 -12
  192. package/dist/game/quest.d.ts +8 -0
  193. package/dist/game/quest.js +29 -0
  194. package/dist/game/race.d.ts +141 -0
  195. package/dist/game/race.js +169 -0
  196. package/dist/game/recordBook.d.ts +5 -5
  197. package/dist/game/recordBook.js +4 -1
  198. package/dist/game/ruleSelection.d.ts +75 -0
  199. package/dist/game/ruleSelection.js +127 -0
  200. package/dist/game/runDraft.d.ts +5 -0
  201. package/dist/game/runDraft.js +5 -0
  202. package/dist/game/saveStore.d.ts +111 -0
  203. package/dist/game/saveStore.js +312 -0
  204. package/dist/game/snapshotHistory.d.ts +67 -0
  205. package/dist/game/snapshotHistory.js +95 -0
  206. package/dist/game/social.d.ts +38 -1
  207. package/dist/game/social.js +71 -0
  208. package/dist/game/spawnPoints.d.ts +5 -0
  209. package/dist/game/spawnPoints.js +5 -0
  210. package/dist/game/systemRuntime.d.ts +50 -0
  211. package/dist/game/systemRuntime.js +157 -0
  212. package/dist/game/systemSchedule.d.ts +72 -0
  213. package/dist/game/systemSchedule.js +242 -0
  214. package/dist/game/toasts.d.ts +43 -0
  215. package/dist/game/toasts.js +44 -0
  216. package/dist/game/trade.d.ts +10 -0
  217. package/dist/game/trade.js +10 -0
  218. package/dist/game/unlocks.d.ts +8 -0
  219. package/dist/game/unlocks.js +16 -0
  220. package/dist/game/vfxInstance.d.ts +138 -0
  221. package/dist/game/vfxInstance.js +96 -0
  222. package/dist/game/worldItem.d.ts +17 -4
  223. package/dist/game/worldItem.js +17 -4
  224. package/dist/gameplay.d.ts +91 -0
  225. package/dist/gameplay.js +91 -0
  226. package/dist/index.d.ts +5 -7
  227. package/dist/index.js +5 -7
  228. package/dist/input/actionBindings.d.ts +24 -5
  229. package/dist/input/actionBindings.js +23 -5
  230. package/dist/input/axisInput.d.ts +13 -1
  231. package/dist/input/axisInput.js +22 -1
  232. package/dist/input/controlGroups.d.ts +67 -0
  233. package/dist/input/controlGroups.js +41 -0
  234. package/dist/input/touchScheme.d.ts +82 -6
  235. package/dist/input/touchScheme.js +123 -10
  236. package/dist/interaction/contextMenu.d.ts +1 -0
  237. package/dist/interaction/contextMenu.js +1 -0
  238. package/dist/interaction/lockpick.d.ts +61 -0
  239. package/dist/interaction/lockpick.js +168 -0
  240. package/dist/interaction/proximityPrompt.d.ts +5 -1
  241. package/dist/interaction/proximityPrompt.js +5 -3
  242. package/dist/interaction/qte.d.ts +5 -0
  243. package/dist/interaction/qte.js +5 -0
  244. package/dist/interaction/skillCheck.d.ts +5 -0
  245. package/dist/interaction/skillCheck.js +5 -0
  246. package/dist/inventory/inventoryModel.d.ts +12 -0
  247. package/dist/inventory/inventoryModel.js +12 -0
  248. package/dist/inventory/shapedGrid.d.ts +16 -0
  249. package/dist/inventory/shapedGrid.js +16 -0
  250. package/dist/inventory/slotModel.d.ts +10 -0
  251. package/dist/inventory/slotModel.js +10 -0
  252. package/dist/item/durability.d.ts +5 -0
  253. package/dist/item/durability.js +5 -0
  254. package/dist/item/itemIdentity.d.ts +171 -0
  255. package/dist/item/itemIdentity.js +138 -0
  256. package/dist/item/itemInstanceRegistry.d.ts +32 -0
  257. package/dist/item/itemInstanceRegistry.js +34 -0
  258. package/dist/item/itemgen.d.ts +163 -0
  259. package/dist/item/itemgen.js +151 -0
  260. package/dist/item/modularItem.d.ts +5 -0
  261. package/dist/item/modularItem.js +5 -0
  262. package/dist/item/use.d.ts +5 -0
  263. package/dist/item/use.js +5 -0
  264. package/dist/item/useBehavior.d.ts +142 -0
  265. package/dist/item/useBehavior.js +105 -0
  266. package/dist/item/weapon.d.ts +5 -0
  267. package/dist/item/weapon.js +5 -0
  268. package/dist/math/scalar.d.ts +25 -0
  269. package/dist/math/scalar.js +47 -0
  270. package/dist/meta/changelog.d.ts +4 -1
  271. package/dist/meta/changelog.js +36 -1
  272. package/dist/movement/avoidance.d.ts +45 -0
  273. package/dist/movement/avoidance.js +123 -0
  274. package/dist/movement/cameraRig.d.ts +8 -0
  275. package/dist/movement/cameraRig.js +8 -0
  276. package/dist/movement/dash.d.ts +11 -0
  277. package/dist/movement/dash.js +11 -0
  278. package/dist/movement/formation.d.ts +135 -0
  279. package/dist/movement/formation.js +194 -0
  280. package/dist/movement/glideModel.d.ts +5 -0
  281. package/dist/movement/glideModel.js +5 -0
  282. package/dist/movement/grappleSwing.d.ts +5 -0
  283. package/dist/movement/grappleSwing.js +5 -0
  284. package/dist/movement/leaderTrail.d.ts +5 -0
  285. package/dist/movement/leaderTrail.js +5 -0
  286. package/dist/movement/movementModel.d.ts +38 -7
  287. package/dist/movement/movementModel.js +54 -13
  288. package/dist/movement/playerMovement.d.ts +41 -0
  289. package/dist/movement/playerMovement.js +236 -0
  290. package/dist/movement/poseState.d.ts +13 -0
  291. package/dist/movement/poseState.js +16 -0
  292. package/dist/movement/steering.d.ts +8 -0
  293. package/dist/movement/steering.js +18 -0
  294. package/dist/multiplayer/combatSnapshot.d.ts +3 -0
  295. package/dist/multiplayer/combatSnapshot.js +3 -0
  296. package/dist/multiplayer/lagCompensation.d.ts +5 -0
  297. package/dist/multiplayer/lagCompensation.js +5 -0
  298. package/dist/multiplayer/presenceModel.d.ts +9 -1
  299. package/dist/multiplayer/presenceModel.js +9 -1
  300. package/dist/multiplayer/simultaneousCommit.d.ts +3 -0
  301. package/dist/multiplayer/simultaneousCommit.js +3 -0
  302. package/dist/multiplayer.d.ts +11 -0
  303. package/dist/multiplayer.js +11 -0
  304. package/dist/nav/pathFollow.d.ts +57 -0
  305. package/dist/nav/pathFollow.js +109 -0
  306. package/dist/orders/orderKinds.d.ts +156 -0
  307. package/dist/orders/orderKinds.js +197 -0
  308. package/dist/orders/orderQueue.d.ts +208 -0
  309. package/dist/orders/orderQueue.js +211 -0
  310. package/dist/physics/drivableVehicle.d.ts +29 -0
  311. package/dist/physics/drivableVehicle.js +16 -0
  312. package/dist/physics/kinematicVehicle.d.ts +21 -1
  313. package/dist/physics/kinematicVehicle.js +23 -8
  314. package/dist/physics/physicsWorld.d.ts +24 -12
  315. package/dist/physics/physicsWorld.js +11 -5
  316. package/dist/physics/radialImpulse.d.ts +25 -0
  317. package/dist/physics/radialImpulse.js +27 -0
  318. package/dist/procedural.d.ts +3 -0
  319. package/dist/procedural.js +3 -0
  320. package/dist/progression/statGraph.d.ts +116 -0
  321. package/dist/progression/statGraph.js +318 -0
  322. package/dist/puzzle/cellGrid.d.ts +9 -0
  323. package/dist/puzzle/cellGrid.js +9 -0
  324. package/dist/puzzle/fallingPiece.d.ts +9 -0
  325. package/dist/puzzle/fallingPiece.js +9 -0
  326. package/dist/puzzle/nonogram.d.ts +39 -0
  327. package/dist/puzzle/nonogram.js +136 -0
  328. package/dist/random/nameGen.d.ts +5 -0
  329. package/dist/random/nameGen.js +5 -0
  330. package/dist/random/pick.d.ts +8 -0
  331. package/dist/random/pick.js +31 -0
  332. package/dist/random/rng.d.ts +24 -0
  333. package/dist/random/rng.js +30 -8
  334. package/dist/random/seedLink.d.ts +4 -0
  335. package/dist/random/seedLink.js +4 -0
  336. package/dist/relation/keyedValues.d.ts +54 -0
  337. package/dist/relation/keyedValues.js +115 -0
  338. package/dist/relation/thresholds.d.ts +62 -0
  339. package/dist/relation/thresholds.js +62 -0
  340. package/dist/render/color.d.ts +12 -0
  341. package/dist/render/color.js +26 -0
  342. package/dist/render/lookPreset.d.ts +41 -0
  343. package/dist/render/lookPreset.js +29 -0
  344. package/dist/render/postProcessing.d.ts +76 -0
  345. package/dist/render/postProcessing.js +21 -0
  346. package/dist/rules/predicate.d.ts +53 -0
  347. package/dist/rules/predicate.js +64 -0
  348. package/dist/rules/ruleEffects.d.ts +38 -0
  349. package/dist/rules/ruleEffects.js +37 -0
  350. package/dist/rules/triggeredRules.d.ts +160 -0
  351. package/dist/rules/triggeredRules.js +241 -0
  352. package/dist/runtime/adapter.d.ts +58 -0
  353. package/dist/runtime/adapter.js +54 -5
  354. package/dist/runtime/cameraDirector.d.ts +1 -0
  355. package/dist/runtime/cameraDirector.js +1 -0
  356. package/dist/runtime/commandRunner.d.ts +1 -0
  357. package/dist/runtime/commandRunner.js +1 -0
  358. package/dist/runtime/context/combatFx.d.ts +23 -0
  359. package/dist/runtime/context/combatFx.js +161 -0
  360. package/dist/runtime/context/registries.d.ts +13 -0
  361. package/dist/runtime/context/registries.js +84 -0
  362. package/dist/runtime/context/worldItems.d.ts +21 -0
  363. package/dist/runtime/context/worldItems.js +37 -0
  364. package/dist/runtime/descriptors/baseline.d.ts +48 -0
  365. package/dist/runtime/descriptors/baseline.js +134 -0
  366. package/dist/runtime/descriptors/features.d.ts +74 -0
  367. package/dist/runtime/descriptors/features.js +281 -0
  368. package/dist/runtime/gameContext.d.ts +163 -19
  369. package/dist/runtime/gameContext.js +385 -339
  370. package/dist/runtime/gameRuntime.d.ts +1 -0
  371. package/dist/runtime/gameRuntime.js +1 -0
  372. package/dist/runtime/headlessRunner.d.ts +60 -0
  373. package/dist/runtime/headlessRunner.js +44 -0
  374. package/dist/runtime/hostPersistence.d.ts +12 -0
  375. package/dist/runtime/hostPersistence.js +12 -0
  376. package/dist/runtime/hostedGameRunner.d.ts +54 -0
  377. package/dist/runtime/hostedGameRunner.js +85 -0
  378. package/dist/runtime/hostedWorldSession.d.ts +73 -0
  379. package/dist/runtime/hostedWorldSession.js +64 -0
  380. package/dist/runtime/inputSnapshot.d.ts +21 -3
  381. package/dist/runtime/inputSnapshot.js +8 -2
  382. package/dist/runtime/motionIntents.d.ts +12 -1
  383. package/dist/runtime/motionIntents.js +26 -1
  384. package/dist/runtime/perContext.d.ts +15 -0
  385. package/dist/runtime/perContext.js +21 -0
  386. package/dist/runtime/runtimeSave.d.ts +79 -0
  387. package/dist/runtime/runtimeSave.js +105 -0
  388. package/dist/runtime/save.d.ts +4 -0
  389. package/dist/runtime/save.js +4 -0
  390. package/dist/runtime/snapshot.d.ts +7 -0
  391. package/dist/runtime/snapshot.js +7 -0
  392. package/dist/runtime/visibility.d.ts +2 -1
  393. package/dist/runtime/visibility.js +2 -1
  394. package/dist/runtime/worldChannel.d.ts +62 -0
  395. package/dist/runtime/worldChannel.js +114 -0
  396. package/dist/runtime/worldMirror.d.ts +36 -0
  397. package/dist/runtime/worldMirror.js +44 -0
  398. package/dist/runtime/worldProjection.d.ts +47 -0
  399. package/dist/runtime/worldProjection.js +52 -0
  400. package/dist/runtime/worldReplication.d.ts +69 -0
  401. package/dist/runtime/worldReplication.js +217 -0
  402. package/dist/runtime/worldSnapshot.d.ts +44 -0
  403. package/dist/runtime/worldSnapshot.js +30 -0
  404. package/dist/scene/assetCatalog.d.ts +22 -0
  405. package/dist/scene/assetCatalog.js +4 -0
  406. package/dist/scene/assetDiagnostics.d.ts +69 -0
  407. package/dist/scene/assetDiagnostics.js +110 -0
  408. package/dist/scene/assetGenerator.d.ts +65 -0
  409. package/dist/scene/assetGenerator.js +64 -0
  410. package/dist/scene/assetSpace.d.ts +137 -0
  411. package/dist/scene/assetSpace.js +259 -0
  412. package/dist/scene/authoredTriggers.d.ts +169 -0
  413. package/dist/scene/authoredTriggers.js +340 -0
  414. package/dist/scene/behaviorRuntime.d.ts +75 -0
  415. package/dist/scene/behaviorRuntime.js +217 -0
  416. package/dist/scene/behaviors.d.ts +14 -2
  417. package/dist/scene/behaviors.js +7 -2
  418. package/dist/scene/bodyBind.d.ts +60 -0
  419. package/dist/scene/bodyBind.js +51 -0
  420. package/dist/scene/builtinSceneKinds.d.ts +5 -0
  421. package/dist/scene/builtinSceneKinds.js +62 -0
  422. package/dist/scene/colliders.d.ts +18 -0
  423. package/dist/scene/colliders.js +61 -1
  424. package/dist/scene/entityStats.d.ts +13 -0
  425. package/dist/scene/entityStats.js +30 -0
  426. package/dist/scene/entityStore.d.ts +41 -1
  427. package/dist/scene/entityStore.js +75 -0
  428. package/dist/scene/facing.d.ts +18 -0
  429. package/dist/scene/facing.js +26 -0
  430. package/dist/scene/modelSockets.d.ts +33 -0
  431. package/dist/scene/modelSockets.js +29 -0
  432. package/dist/scene/objectQuery.d.ts +2 -0
  433. package/dist/scene/objectQuery.js +2 -0
  434. package/dist/scene/objectStore.d.ts +4 -0
  435. package/dist/scene/objectStore.js +6 -0
  436. package/dist/scene/possession.d.ts +8 -0
  437. package/dist/scene/possession.js +14 -0
  438. package/dist/scene/roster.d.ts +3 -0
  439. package/dist/scene/roster.js +12 -0
  440. package/dist/scene/sceneKinds.d.ts +240 -0
  441. package/dist/scene/sceneKinds.js +214 -0
  442. package/dist/scene/sceneOwnership.d.ts +169 -0
  443. package/dist/scene/sceneOwnership.js +179 -0
  444. package/dist/scene/selectionBookmarks.d.ts +93 -0
  445. package/dist/scene/selectionBookmarks.js +122 -0
  446. package/dist/scene/vehicleSeat.d.ts +68 -0
  447. package/dist/scene/vehicleSeat.js +59 -0
  448. package/dist/sensor/concealment.d.ts +3 -0
  449. package/dist/sensor/concealment.js +3 -0
  450. package/dist/sensor/freezeMonitor.d.ts +1 -0
  451. package/dist/sensor/freezeMonitor.js +1 -0
  452. package/dist/sensor/frustumSensor.d.ts +7 -3
  453. package/dist/sensor/frustumSensor.js +7 -3
  454. package/dist/sensor/hiddenStateProbe.d.ts +5 -2
  455. package/dist/sensor/hiddenStateProbe.js +5 -2
  456. package/dist/sensor/recordingBuffer.d.ts +2 -1
  457. package/dist/sensor/recordingBuffer.js +2 -1
  458. package/dist/sensor/replayLoop.d.ts +6 -2
  459. package/dist/sensor/replayLoop.js +6 -2
  460. package/dist/sensor/revealQuery.d.ts +2 -1
  461. package/dist/sensor/revealQuery.js +2 -1
  462. package/dist/sensor/visionCone.d.ts +10 -3
  463. package/dist/sensor/visionCone.js +10 -3
  464. package/dist/session/contestedChannel.d.ts +1 -0
  465. package/dist/session/contestedChannel.js +1 -0
  466. package/dist/session/extraction.d.ts +1 -0
  467. package/dist/session/extraction.js +1 -0
  468. package/dist/session/roles.d.ts +6 -0
  469. package/dist/session/roles.js +6 -0
  470. package/dist/session/roundState.d.ts +8 -0
  471. package/dist/session/roundState.js +2 -0
  472. package/dist/settings/settingsModel.d.ts +1 -0
  473. package/dist/settings/settingsModel.js +1 -0
  474. package/dist/stats/accumulatorMeter.d.ts +6 -0
  475. package/dist/stats/accumulatorMeter.js +6 -0
  476. package/dist/stats/eventMeter.d.ts +5 -0
  477. package/dist/stats/eventMeter.js +5 -0
  478. package/dist/stats/rollCheck.d.ts +5 -0
  479. package/dist/stats/rollCheck.js +5 -0
  480. package/dist/stats/statModifiers.d.ts +5 -0
  481. package/dist/stats/statModifiers.js +5 -0
  482. package/dist/store/defineKeyedStore.d.ts +38 -0
  483. package/dist/store/defineKeyedStore.js +42 -0
  484. package/dist/store/defineStore.d.ts +33 -0
  485. package/dist/store/defineStore.js +35 -0
  486. package/dist/store/observableKeyedStore.d.ts +9 -0
  487. package/dist/store/observableKeyedStore.js +43 -2
  488. package/dist/survival/decayMeter.d.ts +55 -0
  489. package/dist/survival/decayMeter.js +130 -55
  490. package/dist/survival/moodle.d.ts +2 -0
  491. package/dist/survival/regionHealth.d.ts +6 -0
  492. package/dist/survival/regionHealth.js +5 -0
  493. package/dist/tactics/fallingGrid.d.ts +6 -4
  494. package/dist/tactics/fallingGrid.js +6 -4
  495. package/dist/tactics/predictiveQuery.d.ts +3 -0
  496. package/dist/tactics/predictiveQuery.js +3 -0
  497. package/dist/tactics/snapshot.d.ts +2 -0
  498. package/dist/tactics/snapshot.js +2 -0
  499. package/dist/tactics/surface.d.ts +1 -0
  500. package/dist/tactics/surface.js +1 -0
  501. package/dist/tactics/tacticalGrid.d.ts +1 -0
  502. package/dist/tactics/tacticalGrid.js +1 -0
  503. package/dist/time/beatClock.d.ts +14 -4
  504. package/dist/time/beatClock.js +14 -4
  505. package/dist/time/calendarClock.d.ts +3 -0
  506. package/dist/time/calendarClock.js +3 -0
  507. package/dist/time/idleProgress.d.ts +4 -0
  508. package/dist/time/idleProgress.js +4 -0
  509. package/dist/time/simClock.d.ts +2 -0
  510. package/dist/time/simClock.js +20 -5
  511. package/dist/ui/actionModel.d.ts +110 -0
  512. package/dist/ui/actionModel.js +159 -0
  513. package/dist/ui/gameLayout.d.ts +144 -0
  514. package/dist/ui/gameLayout.js +148 -0
  515. package/dist/ui/hudDocument.d.ts +114 -0
  516. package/dist/ui/hudDocument.js +220 -0
  517. package/dist/ui/hudLayout.d.ts +33 -1
  518. package/dist/ui/hudLayout.js +204 -17
  519. package/dist/ui/orientation.d.ts +39 -0
  520. package/dist/ui/orientation.js +49 -0
  521. package/dist/ui/selectionModel.d.ts +92 -0
  522. package/dist/ui/selectionModel.js +88 -0
  523. package/dist/ui/swingTimer.d.ts +42 -0
  524. package/dist/ui/swingTimer.js +38 -0
  525. package/dist/ui/tooltipModel.d.ts +63 -0
  526. package/dist/ui/tooltipModel.js +82 -0
  527. package/dist/ui.d.ts +15 -0
  528. package/dist/ui.js +15 -0
  529. package/dist/visibility/bounds.d.ts +8 -1
  530. package/dist/visibility/bounds.js +8 -1
  531. package/dist/visibility/frustum.d.ts +12 -3
  532. package/dist/visibility/frustum.js +12 -3
  533. package/dist/visibility/spatialIndex.js +34 -20
  534. package/dist/work/jobQueue.d.ts +201 -0
  535. package/dist/work/jobQueue.js +253 -0
  536. package/dist/work/unitTraining.d.ts +58 -0
  537. package/dist/work/unitTraining.js +52 -0
  538. package/dist/world/authoredObjects.d.ts +58 -0
  539. package/dist/world/authoredObjects.js +69 -0
  540. package/dist/world/buildingGenerator.d.ts +18 -0
  541. package/dist/world/buildingGenerator.js +62 -0
  542. package/dist/world/buildings.d.ts +5 -0
  543. package/dist/world/buildings.js +5 -0
  544. package/dist/world/catenary.d.ts +22 -0
  545. package/dist/world/catenary.js +69 -0
  546. package/dist/world/environmentSummary.d.ts +11 -2
  547. package/dist/world/environmentSummary.js +11 -1
  548. package/dist/world/features.d.ts +244 -7
  549. package/dist/world/features.js +92 -4
  550. package/dist/world/footprintGrid.d.ts +59 -0
  551. package/dist/world/footprintGrid.js +141 -0
  552. package/dist/world/geometry.d.ts +64 -0
  553. package/dist/world/geometry.js +94 -0
  554. package/dist/world/grassKind.d.ts +51 -0
  555. package/dist/world/grassKind.js +101 -0
  556. package/dist/world/pathInstances.d.ts +27 -0
  557. package/dist/world/pathInstances.js +27 -0
  558. package/dist/world/pathTerrain.d.ts +80 -0
  559. package/dist/world/pathTerrain.js +142 -0
  560. package/dist/world/placeAsset.d.ts +81 -0
  561. package/dist/world/placeAsset.js +77 -0
  562. package/dist/world/placement.d.ts +4 -0
  563. package/dist/world/placement.js +4 -0
  564. package/dist/world/placementController.d.ts +6 -0
  565. package/dist/world/placementController.js +6 -0
  566. package/dist/world/polyline.d.ts +34 -0
  567. package/dist/world/polyline.js +81 -0
  568. package/dist/world/roads.d.ts +55 -0
  569. package/dist/world/roads.js +184 -0
  570. package/dist/world/scatterRegion.d.ts +164 -0
  571. package/dist/world/scatterRegion.js +409 -0
  572. package/dist/world/sceneShapes.d.ts +43 -0
  573. package/dist/world/sceneShapes.js +1 -0
  574. package/dist/world/segment.d.ts +27 -0
  575. package/dist/world/segment.js +43 -0
  576. package/dist/world/soilKind.d.ts +48 -0
  577. package/dist/world/soilKind.js +92 -0
  578. package/dist/world/spatialSample.d.ts +241 -0
  579. package/dist/world/spatialSample.js +465 -0
  580. package/dist/world/streets.d.ts +80 -0
  581. package/dist/world/streets.js +144 -0
  582. package/dist/world/terraform.d.ts +213 -1
  583. package/dist/world/terraform.js +655 -20
  584. package/dist/world/terrain.d.ts +138 -12
  585. package/dist/world/terrain.js +346 -28
  586. package/dist/world/terrainGuides.d.ts +182 -0
  587. package/dist/world/terrainGuides.js +343 -0
  588. package/dist/world/vec2.d.ts +81 -0
  589. package/dist/world/vec2.js +128 -0
  590. package/dist/world/vec3.d.ts +49 -0
  591. package/dist/world/vec3.js +76 -0
  592. package/dist/world/vegetation.d.ts +71 -0
  593. package/dist/world/vegetation.js +151 -0
  594. package/dist/world/visibilityField.d.ts +154 -0
  595. package/dist/world/visibilityField.js +271 -0
  596. package/dist/world/volumetricClouds.d.ts +52 -0
  597. package/dist/world/volumetricClouds.js +43 -0
  598. package/dist/world/walls.d.ts +9 -0
  599. package/dist/world/walls.js +9 -0
  600. package/dist/world/waterKind.d.ts +55 -0
  601. package/dist/world/waterKind.js +99 -0
  602. package/dist/world.d.ts +132 -0
  603. package/dist/world.js +132 -0
  604. package/package.json +32 -8
  605. package/skills/jgengine/SKILL.md +51 -0
  606. package/skills/jgengine/api.md +434 -0
  607. package/skills/jgengine/capabilities.md +29 -0
  608. package/skills/jgengine/reference-cartridge.md +72 -0
  609. package/skills/jgengine-combat/SKILL.md +34 -0
  610. package/skills/jgengine-combat/api.md +549 -0
  611. package/skills/jgengine-combat/capabilities.md +169 -0
  612. package/skills/jgengine-combat/reference.md +98 -0
  613. package/skills/jgengine-gameplay/SKILL.md +39 -0
  614. package/skills/jgengine-gameplay/api.md +1794 -0
  615. package/skills/jgengine-gameplay/capabilities.md +378 -0
  616. package/skills/jgengine-gameplay/reference-systems.md +60 -0
  617. package/skills/jgengine-world/SKILL.md +38 -0
  618. package/skills/jgengine-world/api.md +2652 -0
  619. package/skills/jgengine-world/capabilities.md +370 -0
  620. package/skills/jgengine-world/reference.md +222 -0
  621. package/dist/cartridge/runtime.d.ts +0 -15
  622. package/dist/cartridge/runtime.js +0 -556
  623. package/dist/cartridge/spec.d.ts +0 -188
  624. package/dist/cartridge/spec.js +0 -19
  625. package/dist/cartridge/validate.d.ts +0 -2
  626. package/dist/cartridge/validate.js +0 -78
  627. package/dist/store/engineStore.d.ts +0 -22
  628. package/dist/store/engineStore.js +0 -50
  629. package/dist/store/features.d.ts +0 -10
  630. package/dist/store/features.js +0 -23
  631. package/llms.txt +0 -4983
package/dist/game/race.js CHANGED
@@ -316,6 +316,175 @@ export class RaceState {
316
316
  return events;
317
317
  }
318
318
  }
319
+ /**
320
+ * A checkpoint race state machine — laps, forks, live standings, splits, and pluggable win conditions.
321
+ *
322
+ * @capability race-track a checkpoint race with laps, standings, splits, and win conditions
323
+ */
319
324
  export function createRaceState(config) {
320
325
  return new RaceState(config);
321
326
  }
327
+ /**
328
+ * Create a {@link LapTimer} starting at lap 0 with no splits, best, or last time recorded.
329
+ *
330
+ * @capability lap-timer wall-clock current/last/best lap timing with splits
331
+ */
332
+ export function createLapTimer() {
333
+ let currentLap = 0;
334
+ let lastLap = null;
335
+ let bestLap = null;
336
+ let total = 0;
337
+ const splits = [];
338
+ return {
339
+ tick(dt) {
340
+ if (dt <= 0)
341
+ return;
342
+ currentLap += dt;
343
+ total += dt;
344
+ },
345
+ completeLap() {
346
+ const lapTime = currentLap;
347
+ lastLap = lapTime;
348
+ bestLap = bestLap === null || lapTime < bestLap ? lapTime : bestLap;
349
+ splits.push(lapTime);
350
+ currentLap = 0;
351
+ return lapTime;
352
+ },
353
+ penalize(seconds) {
354
+ if (seconds <= 0)
355
+ return;
356
+ currentLap += seconds;
357
+ total += seconds;
358
+ },
359
+ reset() {
360
+ currentLap = 0;
361
+ lastLap = null;
362
+ bestLap = null;
363
+ total = 0;
364
+ splits.length = 0;
365
+ },
366
+ snapshot() {
367
+ return { currentLap, lastLap, bestLap, total, lapCount: splits.length, splits: [...splits] };
368
+ },
369
+ };
370
+ }
371
+ const DEFAULT_COUNTDOWN_SECONDS = 3;
372
+ /**
373
+ * The pre-race session on the grid: `idle`, both clocks at zero. Call {@link startRaceCountdown} to
374
+ * light the lights, or hold here until the field is ready.
375
+ *
376
+ * @capability race-session pure idle→countdown→racing→finished clock every racer wraps around its state
377
+ */
378
+ export function idleRaceSession() {
379
+ return { phase: "idle", countdown: 0, elapsed: 0 };
380
+ }
381
+ /**
382
+ * Drop the lights: return a fresh `countdown` session of `seconds` (default 3). A non-positive length
383
+ * skips straight to `racing` for a standing start with no countdown.
384
+ */
385
+ export function startRaceCountdown(options) {
386
+ const seconds = Math.max(0, options?.seconds ?? DEFAULT_COUNTDOWN_SECONDS);
387
+ return seconds === 0 ? { phase: "racing", countdown: 0, elapsed: 0 } : { phase: "countdown", countdown: seconds, elapsed: 0 };
388
+ }
389
+ /**
390
+ * Advance the session by `dt` seconds: bleed the countdown down and flip to `racing` when it reaches
391
+ * zero, or accumulate `elapsed` while `racing`. `idle` and `finished` are inert. Overshoot past the
392
+ * countdown is dropped rather than banked into `elapsed`, so the race clock always starts from zero.
393
+ */
394
+ export function tickRaceSession(session, dt) {
395
+ if (dt <= 0)
396
+ return session;
397
+ if (session.phase === "countdown") {
398
+ const countdown = session.countdown - dt;
399
+ return countdown <= 0 ? { phase: "racing", countdown: 0, elapsed: 0 } : { phase: "countdown", countdown, elapsed: session.elapsed };
400
+ }
401
+ if (session.phase === "racing")
402
+ return { phase: "racing", countdown: 0, elapsed: session.elapsed + dt };
403
+ return session;
404
+ }
405
+ /** Cross the flag: move a `racing` session to `finished`, freezing its `elapsed`. A no-op in any other phase. */
406
+ export function finishRaceSession(session) {
407
+ return session.phase === "racing" ? { phase: "finished", countdown: 0, elapsed: session.elapsed } : session;
408
+ }
409
+ function outcomeForPlace(place, winningPlaces) {
410
+ return place <= winningPlaces ? "win" : "lose";
411
+ }
412
+ /**
413
+ * Turn a finish-order ranking (index 0 = winner, e.g. the `ranking` of a `race.finished` event) into
414
+ * per-racer {@link RacePlacement}s — the `1st/2nd/3rd` + win/lose every results screen shows.
415
+ *
416
+ * @capability race-placements finish order into per-racer place + win/lose for a results screen
417
+ */
418
+ export function racePlacements(finishOrder, options) {
419
+ const winningPlaces = Math.max(0, Math.floor(options?.winningPlaces ?? 1));
420
+ return finishOrder.map((racerId, index) => ({ racerId, place: index + 1, outcome: outcomeForPlace(index + 1, winningPlaces) }));
421
+ }
422
+ /**
423
+ * One racer's {@link RacePlacement} within a finish order, or `null` if they never crossed the line.
424
+ *
425
+ * @capability race-placements look up one racer's place + win/lose within a finish order
426
+ */
427
+ export function placementOf(finishOrder, racerId, options) {
428
+ const index = finishOrder.indexOf(racerId);
429
+ if (index < 0)
430
+ return null;
431
+ const winningPlaces = Math.max(0, Math.floor(options?.winningPlaces ?? 1));
432
+ return { racerId, place: index + 1, outcome: outcomeForPlace(index + 1, winningPlaces) };
433
+ }
434
+ /**
435
+ * The win/lose verdict for one racer in a finish order — `ranking[0] === player ? "win" : "lose"`, the
436
+ * check every racing game hand-rolls, generalized to a `winningPlaces` cutoff. A racer absent from the
437
+ * order counts as a `lose`.
438
+ */
439
+ export function raceOutcomeOf(finishOrder, racerId, options) {
440
+ return placementOf(finishOrder, racerId, options)?.outcome ?? "lose";
441
+ }
442
+ /**
443
+ * Per-segment durations from a cumulative split book (`splits[i]` = elapsed time at checkpoint `i`):
444
+ * `segments[i] = splits[i] − splits[i−1]`, the first measured from `start` (default 0). Turns the
445
+ * cumulative splits {@link RacerProgress} records into the individual leg times a results screen shows.
446
+ *
447
+ * @capability lap-splits turn cumulative split times into per-segment leg times
448
+ */
449
+ export function splitSegments(splits, start = 0) {
450
+ const segments = [];
451
+ let previous = start;
452
+ for (const split of splits) {
453
+ segments.push(split - previous);
454
+ previous = split;
455
+ }
456
+ return segments;
457
+ }
458
+ /**
459
+ * Per-lap durations from a cumulative split book with `gatesPerLap` checkpoints per lap — each lap's time
460
+ * is its finish-gate split minus the previous lap's finish. Only complete laps are returned.
461
+ *
462
+ * @capability lap-splits per-lap durations from a cumulative split book
463
+ */
464
+ export function lapDurations(splits, gatesPerLap) {
465
+ if (gatesPerLap <= 0)
466
+ return [];
467
+ const laps = [];
468
+ for (let lap = 0;; lap += 1) {
469
+ const finish = lap * gatesPerLap + (gatesPerLap - 1);
470
+ if (finish >= splits.length)
471
+ break;
472
+ const previousFinish = lap === 0 ? 0 : (splits[lap * gatesPerLap - 1] ?? 0);
473
+ laps.push(splits[finish] - previousFinish);
474
+ }
475
+ return laps;
476
+ }
477
+ /**
478
+ * Elementwise delta of a cumulative split book against a `reference` book (a personal best or par lap):
479
+ * positive means behind the reference at that checkpoint. Compared up to the shorter length — the
480
+ * `+0.3s` / `−1.2s` gap every racing HUD shows against its ghost.
481
+ *
482
+ * @capability lap-splits delta of splits against a personal-best or par reference
483
+ */
484
+ export function parDelta(splits, reference) {
485
+ const count = Math.min(splits.length, reference.length);
486
+ const deltas = [];
487
+ for (let i = 0; i < count; i += 1)
488
+ deltas.push(splits[i] - reference[i]);
489
+ return deltas;
490
+ }
@@ -1,8 +1,6 @@
1
- export interface RecordStorage {
2
- getItem(key: string): string | null;
3
- setItem(key: string, value: string): void;
4
- removeItem(key: string): void;
5
- }
1
+ import { type KeyValueStorage } from "./keyValueStore.js";
2
+ /** The structural storage backend a record book persists through — an alias of the shared {@link KeyValueStorage} seam (browser `localStorage`, a test stub, or `null`). */
3
+ export type RecordStorage = KeyValueStorage;
6
4
  export type RecordDirection = "lower" | "higher";
7
5
  export interface RecordBookConfig<K extends string> {
8
6
  readonly key: string;
@@ -25,5 +23,7 @@ export interface RecordBook<K extends string> {
25
23
  * key-value storage (pass `localStorage` in a browser, a stub in tests, or
26
24
  * `null` for in-memory only). Corrupt or unavailable storage degrades to an
27
25
  * empty book — a record write never throws into a game tick.
26
+ *
27
+ * @capability best-record persist personal-best times/scores with safe storage fallback
28
28
  */
29
29
  export declare function createRecordBook<K extends string>(config: RecordBookConfig<K>): RecordBook<K>;
@@ -1,3 +1,4 @@
1
+ import { defaultKeyValueStorage } from "./keyValueStore.js";
1
2
  function loadStored(config, storage) {
2
3
  if (storage === null)
3
4
  return {};
@@ -33,9 +34,11 @@ function loadStored(config, storage) {
33
34
  * key-value storage (pass `localStorage` in a browser, a stub in tests, or
34
35
  * `null` for in-memory only). Corrupt or unavailable storage degrades to an
35
36
  * empty book — a record write never throws into a game tick.
37
+ *
38
+ * @capability best-record persist personal-best times/scores with safe storage fallback
36
39
  */
37
40
  export function createRecordBook(config) {
38
- const storage = config.storage ?? null;
41
+ const storage = config.storage === undefined ? defaultKeyValueStorage() : config.storage;
39
42
  let best = loadStored(config, storage);
40
43
  function persist() {
41
44
  if (storage === null)
@@ -0,0 +1,75 @@
1
+ import { type ParamLayer, type LayerSelection } from "./paramLayers.js";
2
+ /**
3
+ * A selectable rule over a tagged pool. `tags` classify it for include/exclude filtering and for
4
+ * `requires`/`conflicts` matching (which match either another rule's id or one of its tags). `layers`
5
+ * are the parameter layers this rule contributes when active; `payload` is opaque caller data.
6
+ */
7
+ export interface RuleDef<TPayload = unknown> {
8
+ readonly id: string;
9
+ readonly tags?: readonly string[];
10
+ /** Relative pick weight; non-positive or omitted is treated as `1`. */
11
+ readonly weight?: number;
12
+ /** Ids/tags that must already be selected for this rule to become eligible. */
13
+ readonly requires?: readonly string[];
14
+ /** Ids/tags that, if already selected (either direction), make this rule ineligible. */
15
+ readonly conflicts?: readonly string[];
16
+ /** Parameter layers contributed when this rule is active, read back via {@link RuleRegistry.layersFor}. */
17
+ readonly layers?: readonly ParamLayer[];
18
+ readonly payload?: TPayload;
19
+ }
20
+ /** Deterministic selection inputs — plain serializable data a host can persist and replay. */
21
+ export interface RuleSelectionConfig {
22
+ readonly count: number;
23
+ readonly seed: string | number;
24
+ /** If set, only rules carrying at least one of these tags are eligible. */
25
+ readonly includeTags?: readonly string[];
26
+ /** Rules carrying any of these tags are excluded outright. */
27
+ readonly excludeTags?: readonly string[];
28
+ /** Rule ids force-selected first, in order; counts toward `count`. Unknown ids are ignored. */
29
+ readonly locked?: readonly string[];
30
+ }
31
+ /** The result of a selection — the chosen rules and their ids (a serializable {@link LayerSelection}-style list). */
32
+ export interface RuleSelection<TPayload = unknown> {
33
+ readonly rules: readonly RuleDef<TPayload>[];
34
+ readonly ids: readonly string[];
35
+ }
36
+ /**
37
+ * Select up to `count` rules from `pool` deterministically from `config.seed`. Locked ids are placed
38
+ * first (in order), then remaining slots are filled by weighted draw from tag-filtered, still-eligible
39
+ * candidates, honoring `requires`/`conflicts` after each pick. Each slot draws from an independent
40
+ * seed stream keyed by slot index, so a later slot's outcome never shifts an earlier one. Selection
41
+ * stops early when no compatible candidate remains. Pure given `pool` + `config`.
42
+ *
43
+ * @capability rule-selection deterministically select rules from a tagged pool under compatibility/exclusion constraints
44
+ */
45
+ export declare function selectRules<TPayload = unknown>(pool: readonly RuleDef<TPayload>[], config: RuleSelectionConfig): RuleSelection<TPayload>;
46
+ /**
47
+ * Reroll a prior selection: keep `keepIds` (the locked slots) and re-draw the rest under a fresh,
48
+ * still-deterministic stream derived from `config.seed` and `salt`. The same seed + salt + keep set
49
+ * reproduce the same reroll, so reroll history stays replayable.
50
+ *
51
+ * @capability rule-selection reroll unlocked slots of a selection deterministically from seed and salt
52
+ */
53
+ export declare function rerollRules<TPayload = unknown>(pool: readonly RuleDef<TPayload>[], config: RuleSelectionConfig, keepIds: readonly string[], salt?: string | number): RuleSelection<TPayload>;
54
+ /** Registered pool of {@link RuleDef}s — the install seam games register mutators/modes/events into. */
55
+ export interface RuleRegistry<TPayload = unknown> {
56
+ register(rule: RuleDef<TPayload>): void;
57
+ has(id: string): boolean;
58
+ get(id: string): RuleDef<TPayload> | undefined;
59
+ all(): readonly RuleDef<TPayload>[];
60
+ /** Run {@link selectRules} over the registered pool. */
61
+ select(config: RuleSelectionConfig): RuleSelection<TPayload>;
62
+ /**
63
+ * Collect the parameter layers contributed by the given selected rule ids, in id order. Unknown ids
64
+ * are skipped so stale/migrated selections resolve without throwing. This is the bridge from a
65
+ * rule selection to a {@link ParamLayer} stack — core reads `.layers`, never a rule's name.
66
+ */
67
+ layersFor(ids: LayerSelection): readonly ParamLayer[];
68
+ }
69
+ /**
70
+ * Create a {@link RuleRegistry}. Rules install through `register` (duplicate ids throw); selection and
71
+ * `layersFor` are data-driven, so the core never branches on which mutators exist.
72
+ *
73
+ * @capability rule-selection register rules and bridge a selection to its contributed parameter layers
74
+ */
75
+ export declare function createRuleRegistry<TPayload = unknown>(initial?: readonly RuleDef<TPayload>[]): RuleRegistry<TPayload>;
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Seeded rule selection over a tagged pool.
3
+ *
4
+ * A {@link RuleDef} is a caller-owned selectable — a mutator, game mode, event, or challenge modifier
5
+ * — carrying tags, an optional weight, compatibility (`requires`) and exclusion (`conflicts`)
6
+ * constraints, and any {@link ParamLayer}s it contributes when active. {@link selectRules} draws a
7
+ * requested count from the eligible pool using injected, deterministic randomness: the same seed and
8
+ * pool always yield the same rules, so a host can reproduce a session from `{ seed, count }` alone and
9
+ * every peer agrees without replicating the roll.
10
+ *
11
+ * Core never branches on known rule names — selection is driven entirely by tags and the caller's
12
+ * constraint data, and a selected rule's behavior is read back through its `layers`/`payload`. That
13
+ * keeps the seam genre-agnostic and lets games install rules through {@link createRuleRegistry}
14
+ * without the primitive knowing what any mutator "means".
15
+ */
16
+ import { pickWeighted } from "../random/pick.js";
17
+ import { seededStreams } from "../random/rng.js";
18
+ import {} from "./paramLayers.js";
19
+ function tokensOf(rule) {
20
+ return [rule.id, ...(rule.tags ?? [])];
21
+ }
22
+ function passesTagFilter(rule, config) {
23
+ const tags = rule.tags ?? [];
24
+ if (config.excludeTags?.some((tag) => tags.includes(tag)))
25
+ return false;
26
+ if (config.includeTags && config.includeTags.length > 0) {
27
+ return config.includeTags.some((tag) => tags.includes(tag));
28
+ }
29
+ return true;
30
+ }
31
+ function isCompatible(rule, selected) {
32
+ const selectedTokens = new Set(selected.flatMap(tokensOf));
33
+ if (rule.requires && !rule.requires.every((token) => selectedTokens.has(token)))
34
+ return false;
35
+ if (rule.conflicts?.some((token) => selectedTokens.has(token)))
36
+ return false;
37
+ const ruleTokens = new Set(tokensOf(rule));
38
+ for (const other of selected) {
39
+ if (other.conflicts?.some((token) => ruleTokens.has(token)))
40
+ return false;
41
+ }
42
+ return true;
43
+ }
44
+ /**
45
+ * Select up to `count` rules from `pool` deterministically from `config.seed`. Locked ids are placed
46
+ * first (in order), then remaining slots are filled by weighted draw from tag-filtered, still-eligible
47
+ * candidates, honoring `requires`/`conflicts` after each pick. Each slot draws from an independent
48
+ * seed stream keyed by slot index, so a later slot's outcome never shifts an earlier one. Selection
49
+ * stops early when no compatible candidate remains. Pure given `pool` + `config`.
50
+ *
51
+ * @capability rule-selection deterministically select rules from a tagged pool under compatibility/exclusion constraints
52
+ */
53
+ export function selectRules(pool, config) {
54
+ const byId = new Map(pool.map((rule) => [rule.id, rule]));
55
+ const chosen = [];
56
+ const chosenIds = new Set();
57
+ for (const id of config.locked ?? []) {
58
+ const rule = byId.get(id);
59
+ if (rule && !chosenIds.has(id)) {
60
+ chosen.push(rule);
61
+ chosenIds.add(id);
62
+ }
63
+ if (chosen.length >= config.count)
64
+ return { rules: chosen, ids: chosen.map((r) => r.id) };
65
+ }
66
+ const streams = seededStreams(config.seed);
67
+ let slot = chosen.length;
68
+ while (chosen.length < config.count) {
69
+ const candidates = pool.filter((rule) => !chosenIds.has(rule.id) && passesTagFilter(rule, config) && isCompatible(rule, chosen));
70
+ if (candidates.length === 0)
71
+ break;
72
+ const picked = pickWeighted(streams(`slot:${slot}`), candidates, (rule) => rule.weight !== undefined && rule.weight > 0 ? rule.weight : 1);
73
+ if (!picked)
74
+ break;
75
+ chosen.push(picked);
76
+ chosenIds.add(picked.id);
77
+ slot += 1;
78
+ }
79
+ return { rules: chosen, ids: chosen.map((r) => r.id) };
80
+ }
81
+ /**
82
+ * Reroll a prior selection: keep `keepIds` (the locked slots) and re-draw the rest under a fresh,
83
+ * still-deterministic stream derived from `config.seed` and `salt`. The same seed + salt + keep set
84
+ * reproduce the same reroll, so reroll history stays replayable.
85
+ *
86
+ * @capability rule-selection reroll unlocked slots of a selection deterministically from seed and salt
87
+ */
88
+ export function rerollRules(pool, config, keepIds, salt = 0) {
89
+ return selectRules(pool, {
90
+ ...config,
91
+ locked: keepIds,
92
+ seed: `${config.seed}:reroll:${salt}`,
93
+ });
94
+ }
95
+ /**
96
+ * Create a {@link RuleRegistry}. Rules install through `register` (duplicate ids throw); selection and
97
+ * `layersFor` are data-driven, so the core never branches on which mutators exist.
98
+ *
99
+ * @capability rule-selection register rules and bridge a selection to its contributed parameter layers
100
+ */
101
+ export function createRuleRegistry(initial) {
102
+ const rules = new Map();
103
+ const register = (rule) => {
104
+ if (rules.has(rule.id))
105
+ throw new Error(`rule "${rule.id}" is already registered`);
106
+ rules.set(rule.id, rule);
107
+ };
108
+ if (initial)
109
+ for (const rule of initial)
110
+ register(rule);
111
+ return {
112
+ register,
113
+ has: (id) => rules.has(id),
114
+ get: (id) => rules.get(id),
115
+ all: () => [...rules.values()],
116
+ select: (config) => selectRules([...rules.values()], config),
117
+ layersFor(ids) {
118
+ const layers = [];
119
+ for (const id of ids) {
120
+ const rule = rules.get(id);
121
+ if (rule?.layers)
122
+ layers.push(...rule.layers);
123
+ }
124
+ return layers;
125
+ },
126
+ };
127
+ }
@@ -31,4 +31,9 @@ export interface RunDraft<TStat extends string = string, TData = unknown> {
31
31
  choose(offerId: string): boolean;
32
32
  stack(): RunModifierStack<TStat, TData>;
33
33
  }
34
+ /**
35
+ * A roguelike run built from stacking drafted modifier picks that reshape the run.
36
+ *
37
+ * @capability run-modifiers a roguelike run built from stacking drafted modifier picks
38
+ */
34
39
  export declare function createRunDraft<TStat extends string = string, TData = unknown>(config: RunDraftConfig<TStat, TData>): RunDraft<TStat, TData>;
@@ -68,6 +68,11 @@ export function createRunModifierStack(offers) {
68
68
  },
69
69
  };
70
70
  }
71
+ /**
72
+ * A roguelike run built from stacking drafted modifier picks that reshape the run.
73
+ *
74
+ * @capability run-modifiers a roguelike run built from stacking drafted modifier picks
75
+ */
71
76
  export function createRunDraft(config) {
72
77
  const stack = createRunModifierStack(config.offers);
73
78
  const defaultRng = config.rng ?? Math.random;
@@ -0,0 +1,111 @@
1
+ import { type KeyValueStorage } from "./keyValueStore.js";
2
+ /**
3
+ * The one async storage seam a save store persists through. Every backend
4
+ * satisfies this same three-method shape — the browser's `localStorage`
5
+ * (offline), an in-memory map (tests/SSR), or a database/Convex/HTTP endpoint
6
+ * (cloud) — so a game switches offline saves for cloud saves by swapping the
7
+ * backend and changing nothing else. Keys are opaque namespaced strings; values
8
+ * are already-serialized strings, so a backend never needs to know the save
9
+ * shape.
10
+ */
11
+ export interface SaveBackend {
12
+ read(key: string): Promise<string | null>;
13
+ write(key: string, value: string): Promise<void>;
14
+ remove(key: string): Promise<void>;
15
+ }
16
+ /**
17
+ * A {@link SaveBackend} over a synchronous {@link KeyValueStorage} — the
18
+ * browser's `localStorage` by default (offline, on-device saves), a test stub,
19
+ * or `null` for memory-only. Storage errors (quota exceeded, private mode, no
20
+ * DOM) degrade to no-ops, so a save never throws into a game tick.
21
+ *
22
+ * @capability local-save persist a game to on-device localStorage (offline)
23
+ */
24
+ export declare function localSaveBackend(storage?: KeyValueStorage | null): SaveBackend;
25
+ /** A memory-only {@link SaveBackend} — saves survive a reload only within the same session. For tests, SSR, or a "no persistence" mode that still exercises the same save code path. */
26
+ export declare function memorySaveBackend(): SaveBackend;
27
+ /** Adopt any async `read`/`write`/`remove` trio as a {@link SaveBackend} — the seam for cloud saves backed by a database, an HTTP endpoint, or Convex (see `@jgengine/convex/convexSaveBackend`). Reads/writes may reject; the save store surfaces the failure as `"error"` status instead of throwing. */
28
+ export declare function remoteSaveBackend(backend: SaveBackend): SaveBackend;
29
+ /** Lifecycle of the last save/load — drive a "Saving…"/"Saved" indicator or a loading gate off it. `"error"` means the backend rejected a read or write. */
30
+ export type SaveStatus = "idle" | "loading" | "saving" | "saved" | "error";
31
+ /** Autosave cadence: debounce writes `debounceMs` after the last edit, but never wait longer than `maxWaitMs` while edits keep coming (`0` removes the ceiling). `autosave: true` uses `{ debounceMs: 1000, maxWaitMs: 15000 }`. */
32
+ export interface SaveAutoConfig {
33
+ debounceMs?: number;
34
+ maxWaitMs?: number;
35
+ }
36
+ /** The timer seam autosave schedules through — defaults to the ambient `setTimeout`/`clearTimeout`; inject fakes in tests. */
37
+ export interface SaveTimers {
38
+ set(handler: () => void, ms: number): unknown;
39
+ clear(handle: unknown): void;
40
+ }
41
+ /** The versioned envelope written to the backend — `version` lets a later game build detect and {@link SaveStoreConfig.migrate | migrate} an older payload. */
42
+ export interface SaveEnvelope<T> {
43
+ version: number;
44
+ savedAt: number;
45
+ value: T;
46
+ }
47
+ /** How a {@link createSaveStore} is wired — the backend it persists through, the initial value, and optional slot/versioning/autosave/serialization knobs. */
48
+ export interface SaveStoreConfig<T> {
49
+ /** Where saves live — {@link localSaveBackend}, {@link memorySaveBackend}, or a {@link remoteSaveBackend}. */
50
+ backend: SaveBackend;
51
+ /** The value a fresh save starts from (nothing stored yet, or after `clear`). */
52
+ initial: T;
53
+ /** Storage namespace, joined with the slot into the backend key. Default `"save"`. */
54
+ key?: string;
55
+ /** Active save slot — pass distinct slots for multiple parallel saves. Default `"default"`. */
56
+ slot?: string;
57
+ /** Current save-format version. Default `1`. Bump it on a breaking shape change and pass {@link migrate}. */
58
+ version?: number;
59
+ /** Upgrade an older payload to the current shape. Receives the stored value and the version it was written at (`0` for a pre-versioned/raw payload). */
60
+ migrate?: (data: unknown, fromVersion: number) => T;
61
+ /** Autosave on every `set`/`patch`. `false` (default) saves only on an explicit `save()`. */
62
+ autosave?: boolean | SaveAutoConfig;
63
+ serialize?: (envelope: SaveEnvelope<T>) => string;
64
+ deserialize?: (raw: string) => unknown;
65
+ /** Injectable clock (ms). Default `Date.now`. */
66
+ now?: () => number;
67
+ /** Injectable timers for autosave. Default ambient `setTimeout`/`clearTimeout`. */
68
+ timers?: SaveTimers;
69
+ /** Notified on any backend read/write failure — status is already `"error"` when it runs. */
70
+ onError?: (error: unknown) => void;
71
+ }
72
+ /**
73
+ * A pluggable-backend game save with autosave, named slots, and versioned
74
+ * migration. `value()`/`patch()` hold the live state; `load()` hydrates it from
75
+ * the backend; `save()` (or autosave) writes it back. Backend failures surface
76
+ * as `"error"` status and through `onError` — a save never throws into a tick.
77
+ */
78
+ export interface SaveStore<T> {
79
+ value(): T;
80
+ status(): SaveStatus;
81
+ slot(): string;
82
+ /** Monotonic change counter — bumps on any value or status change; drive `useSyncExternalStore` off it. */
83
+ revision(): number;
84
+ /** Hydrate the active slot from the backend into `value()`; corrupt payloads fall back to `initial`. */
85
+ load(): Promise<T>;
86
+ /** Replace the value and mark it dirty (schedules autosave). */
87
+ set(value: T): void;
88
+ /** Read-modify-write the value and mark it dirty; returns the next value. */
89
+ patch(mutate: (previous: T) => T): T;
90
+ /** Flush the current value to the backend now (serialized against in-flight saves). */
91
+ save(): Promise<void>;
92
+ /** Delete the active slot and reset `value()` to `initial`. */
93
+ clear(): Promise<void>;
94
+ /** Flush any dirty state, switch to `slot`, and load it. */
95
+ switchSlot(slot: string): Promise<T>;
96
+ /** The slots this store has written (for a load/save menu). */
97
+ slots(): Promise<string[]>;
98
+ subscribe(listener: () => void): () => void;
99
+ /** Stop autosave timers and drop listeners. */
100
+ dispose(): void;
101
+ }
102
+ /**
103
+ * Create a {@link SaveStore}. Same call for offline and cloud — only the
104
+ * `backend` differs (localStorage, memory, or an async DB/Convex endpoint). Turn
105
+ * on `autosave` and every `set`/`patch` persists on a debounce; leave it off and
106
+ * call `save()` at checkpoints. Bump `version` + pass `migrate` when the save
107
+ * shape changes so old players keep their progress.
108
+ *
109
+ * @capability game-save save/load game state with a pluggable backend, autosave, slots, and migration
110
+ */
111
+ export declare function createSaveStore<T>(config: SaveStoreConfig<T>): SaveStore<T>;