@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
@@ -12,6 +12,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
12
12
 
13
13
  - `advanceLedger` (function) · `import { advanceLedger } from "@jgengine/core/gameplay"`
14
14
 
15
+ ## affix-roller — roll randomized gear (guns, armor, loot) with rarity tiers, stat scaling and prefix/suffix affixes from a seed
16
+
17
+ - `createAffixRoller` (function) · `import { createAffixRoller } from "@jgengine/core/gameplay"`
18
+
15
19
  ## auction-book — timed bid auctions with escrowed bids, buyout, anti-snipe close extension, and collection-box settlement
16
20
 
17
21
  - `createAuctionBook` (function) · `import { createAuctionBook } from "@jgengine/core/gameplay"`
@@ -40,6 +44,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
40
44
 
41
45
  - `totalLoad` (function) · `import { totalLoad } from "@jgengine/core/inventory/encumbrance"`
42
46
 
47
+ ## cell-grid — immutable 2D board of cells with row clears, gravity collapse, and match runs
48
+
49
+ - `createCellGrid` (function) · `import { createCellGrid } from "@jgengine/core/puzzle/cellGrid"`
50
+
43
51
  ## clip-roles — map a rigged asset's pack-specific clip name to its semantic animation role
44
52
 
45
53
  - `classifyClip` (function) · `import { classifyClip } from "@jgengine/core/game/clipRoles"`
@@ -57,6 +65,18 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
57
65
 
58
66
  - `createItemUse` (function) · `import { createItemUse } from "@jgengine/core/gameplay"`
59
67
 
68
+ ## content-progression-validation — diagnose unavailable sources and blocked cross-content progression under explicit game-owned rules without rejecting intentional seeded cycles
69
+
70
+ - `validateContentProgression` (function) · `import { validateContentProgression } from "@jgengine/core/game/contentValidation"`
71
+
72
+ ## content-reference-validation — locate duplicate content ids and broken references across game-owned catalog namespaces with repair guidance
73
+
74
+ - `validateContentReferences` (function) · `import { validateContentReferences } from "@jgengine/core/game/contentValidation"`
75
+
76
+ ## control-suspension — Suspend player input across overlapping menus without stopping shared simulation.
77
+
78
+ - `suspendPlayControls` (function) · `import { suspendPlayControls } from "@jgengine/core/game/controlGate"`
79
+
60
80
  ## cosmetics — equip cosmetic skins and customizations by slot
61
81
 
62
82
  - `createCosmetics` (function) · `import { createCosmetics } from "@jgengine/core/gameplay"`
@@ -104,6 +124,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
104
124
 
105
125
  - `createGameDialogue` (function) · `import { createGameDialogue } from "@jgengine/core/gameplay"`
106
126
 
127
+ ## dialogue-graph-validation — diagnose duplicate ids, broken branches, unreachable dialogue, and nodes without a route to an ending before play
128
+
129
+ - `validateDialogueGraph` (function) · `import { validateDialogueGraph } from "@jgengine/core/game/dialogueGraph"`
130
+
107
131
  ## dialogue-graph-view — select a conversation node's render-ready view (speaker, line, branching choices) from a serializable dialogue graph
108
132
 
109
133
  - `selectDialogueView` (function) · `import { selectDialogueView } from "@jgengine/core/gameplay"`
@@ -136,6 +160,26 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
136
160
 
137
161
  - `createEventTicker` (function) · `import { createEventTicker } from "@jgengine/core/gameplay"`
138
162
 
163
+ ## expedition-dispatch — dispatch a saveable timed away mission with finite supplies and a seeded RNG cursor
164
+
165
+ - `dispatchExpedition` (function) · `import { dispatchExpedition } from "@jgengine/core/gameplay"`
166
+
167
+ ## expedition-recall — begin a safe fractional-time return after settling the pending expedition frontier
168
+
169
+ - `recallExpedition` (function) · `import { recallExpedition } from "@jgengine/core/gameplay"`
170
+
171
+ ## expedition-return — complete an authorized instant return without skipping unsettled ticks or reviving death
172
+
173
+ - `returnExpeditionNow` (function) · `import { returnExpeditionNow } from "@jgengine/core/gameplay"`
174
+
175
+ ## expedition-settle — settle bounded offline expedition ticks without losing supplies rewards or RNG continuity
176
+
177
+ - `settleExpedition` (function) · `import { settleExpedition } from "@jgengine/core/gameplay"`
178
+
179
+ ## 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
180
+
181
+ - `ActionCodesMap` (type) · `import { ActionCodesMap } from "@jgengine/core/gameplay"`
182
+
139
183
  ## game-save — save/load game state with a pluggable backend, autosave, slots, and migration
140
184
 
141
185
  - `createSaveStore` (function) · `import { createSaveStore } from "@jgengine/core/gameplay"`
@@ -239,6 +283,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
239
283
 
240
284
  - `localSaveBackend` (function) · `import { localSaveBackend } from "@jgengine/core/gameplay"`
241
285
 
286
+ ## loot-beams — rarity-colored loot beams and labels on dropped items via defineGame({ worldItem: { rarityStyle, beamHeight } })
287
+
288
+ - `RarityStyle` (interface) · `import { RarityStyle } from "@jgengine/core/gameplay"`
289
+
242
290
  ## loot-filter — filter and highlight drops by rarity/name rules
243
291
 
244
292
  - `evaluateLootFilter` (function) · `import { evaluateLootFilter } from "@jgengine/core/gameplay"`
@@ -273,6 +321,14 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
273
321
 
274
322
  - `createNameGenerator` (function) · `import { createNameGenerator } from "@jgengine/core/gameplay"`
275
323
 
324
+ ## narrative-batch-validation — locate stale canon, oversized batches, explicit character/fact/chronology contradictions and dialogue graph issues for bounded repair
325
+
326
+ - `validateNarrativeBatch` (function) · `import { validateNarrativeBatch } from "@jgengine/core/game/narrativeAuthoring"`
327
+
328
+ ## narrative-canon — freeze caller-reviewed character ids, facts and chronology for a narrative authoring revision
329
+
330
+ - `freezeNarrativeCanon` (function) · `import { freezeNarrativeCanon } from "@jgengine/core/game/narrativeAuthoring"`
331
+
276
332
  ## notice-feed — serializable tone-tagged notice/event feed bounded by count and age (toasts, life events, killfeed)
277
333
 
278
334
  - `appendFeed` (function) · `import { appendFeed } from "@jgengine/core/gameplay"`
@@ -312,6 +368,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
312
368
 
313
369
  - `createPingSystem` (function) · `import { createPingSystem } from "@jgengine/core/gameplay"`
314
370
 
371
+ ## pointer-commands — click an entity or the ground to run a command (click-to-target, click-to-move, grab loot, marquee select)
372
+
373
+ - `PointerConfig` (interface) · `import { PointerConfig } from "@jgengine/core/gameplay"`
374
+
315
375
  ## policy-annotate — tag a scheduled transaction's provenance without changing its value
316
376
 
317
377
  - `annotate` (function) · `import { annotate } from "@jgengine/core/gameplay"`
@@ -352,6 +412,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
352
412
 
353
413
  - `evalCurve` (function) · `import { evalCurve } from "@jgengine/core/gameplay"`
354
414
 
415
+ ## quest-catalog-validation — diagnose authored quest ids, prerequisite and reward references, quantities, and blocked dependencies with caller-declared external unlocks
416
+
417
+ - `validateQuestCatalog` (function) · `import { validateQuestCatalog } from "@jgengine/core/game/questCatalog"`
418
+
355
419
  ## quest-log — track accepted quests and their per-objective progress
356
420
 
357
421
  - `createQuestJournal` (function) · `import { createQuestJournal } from "@jgengine/core/gameplay"`
@@ -373,6 +437,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
373
437
 
374
438
  - `createRaceState` (function) · `import { createRaceState } from "@jgengine/core/gameplay"`
375
439
 
440
+ ## recipe-catalog-validation — locate broken crafting item/station/unlock references, duplicate inputs and invalid authored quantities with repair guidance
441
+
442
+ - `validateRecipeCatalog` (function) · `import { validateRecipeCatalog } from "@jgengine/core/crafting/recipeCatalog"`
443
+
376
444
  ## resolve — the current tier/band label for a keyed or scalar value
377
445
 
378
446
  - `tierAt` (function) · `import { tierAt } from "@jgengine/core/gameplay"`
@@ -423,6 +491,18 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
423
491
 
424
492
  - `setValue` (function) · `import { setValue } from "@jgengine/core/gameplay"`
425
493
 
494
+ ## shaped-grid — a serializable inventory board with arbitrary cell footprints, rotation, overlap checks, and immutable placement
495
+
496
+ - `canPlace` (function) · `import { canPlace } from "@jgengine/core/inventory/shapedGrid"`
497
+ - `createShapedGrid` (function) · `import { createShapedGrid } from "@jgengine/core/inventory/shapedGrid"`
498
+ - `moveShaped` (function) · `import { moveShaped } from "@jgengine/core/inventory/shapedGrid"`
499
+ - `placeShaped` (function) · `import { placeShaped } from "@jgengine/core/inventory/shapedGrid"`
500
+ - `removeShaped` (function) · `import { removeShaped } from "@jgengine/core/inventory/shapedGrid"`
501
+
502
+ ## shaped-placement — find a shaped inventory item's first fit with ordered rotations, row-major placement, and an explicit search budget
503
+
504
+ - `findShapedPlacement` (function) · `import { findShapedPlacement } from "@jgengine/core/inventory/shapedGrid"`
505
+
426
506
  ## shop-grid — serializable vendor/shop stock with buy/sell over a caller-owned wallet — finite/unlimited qty, sell-back, restock/setPrice, snapshot/restore
427
507
 
428
508
  - `createShopStock` (function) · `import { createShopStock } from "@jgengine/core/gameplay"`
@@ -435,6 +515,14 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
435
515
 
436
516
  - `createSpawnPoints` (function) · `import { createSpawnPoints } from "@jgengine/core/gameplay"`
437
517
 
518
+ ## split-screen — Split the screen into one viewport per local seat, each with its own follow camera.
519
+
520
+ - `splitViewports` (function) · `import { splitViewports } from "@jgengine/core/game/viewports"`
521
+
522
+ ## staffed-station-rate — turn bounded caller-owned worker stats and efficiency into a production rate
523
+
524
+ - `stationOutputRate` (function) · `import { stationOutputRate } from "@jgengine/core/gameplay"`
525
+
438
526
  ## stat-graph — derive game-defined stats from named inputs with provenance and preview
439
527
 
440
528
  - `createStatGraph` (function) · `import { createStatGraph } from "@jgengine/core/gameplay"`
@@ -459,6 +547,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
459
547
 
460
548
  - `tick` (function) · `import { tick } from "@jgengine/core/gameplay"`
461
549
 
550
+ ## time-scaled-loot — shape loot odds by elapsed hours with caller-authored curves and pipeline provenance
551
+
552
+ - `timeScaledRarity` (function) · `import { timeScaledRarity } from "@jgengine/core/gameplay"`
553
+
462
554
  ## toast-feed — queue of transient self-expiring on-screen messages (toasts, announcer, kill-feed)
463
555
 
464
556
  - `appendToast` (function) · `import { appendToast } from "@jgengine/core/gameplay"`
@@ -0,0 +1,46 @@
1
+ # Truthful device saves
2
+
3
+ Use `ctx.game.save` for the authoritative world or `createSaveStore` for a caller-owned,
4
+ serializable checkpoint. Default offline context saves use strict local storage:
5
+ denied reads, quota errors, unavailable storage, and denied removals report
6
+ `status() === "error"`. Explicitly injected backends retain their own policy.
7
+
8
+ For an existing Wayfarer/OddOrbit-style run checkpoint, keep the game's checkpoint
9
+ schema and validation, and replace only its persistence adapter:
10
+
11
+ ```ts
12
+ import { createSaveStore, localSaveBackend } from "@jgengine/core/game/saveStore";
13
+
14
+ const checkpoint = createSaveStore({
15
+ key: "my-game:run",
16
+ initial: { roomId: "start", cleared: [] as string[] },
17
+ backend: localSaveBackend(undefined, { errorMode: "throw" }),
18
+ onError: (error) => console.error("Checkpoint storage failed", error),
19
+ });
20
+
21
+ checkpoint.set({ roomId: "harbor", cleared: ["intro"] });
22
+ await checkpoint.save();
23
+ const message = checkpoint.status() === "saved"
24
+ ? "Checkpoint saved"
25
+ : "Checkpoint could not be saved. Keep playing and retry.";
26
+ ```
27
+
28
+ `save()` handles backend rejection; awaiting it alone does not prove success.
29
+ Subscribe to status changes for autosave UI. A rejected payload write preserves
30
+ the previous stored checkpoint while live edits remain available for retry.
31
+ Denied load/remove leaves the current value intact. Whole-world load/hasSave/slot
32
+ switches return false after a denied payload read and do not restore cached state. Slot metadata is a separate
33
+ storage operation: a payload can be written before its slot index fails. That
34
+ reports `error` too; there is no cross-key rollback or transaction guarantee.
35
+ Async index updates after load also report through status and `onError`.
36
+
37
+ `createKeyValueStore` and `localSaveBackend` preserve their legacy silent fallback
38
+ by default. Set `errorMode: "throw"` when persistence must succeed; optionally
39
+ provide `onError({ operation, key, error })` to observe reads, writes and removals.
40
+ Strict key-value writes/removals preserve the prior cell on rejection. Observers
41
+ must not throw. Shape validation and corruption recovery still belong to the
42
+ game's checkpoint schema or a supplied deserializer.
43
+
44
+ For intentional session-only/headless success, use `memorySaveBackend()`,
45
+ `persist: { storage: "memory" }`, or a key-value cell with `storage: null`.
46
+ Memory success does not promise persistence across reloads.
@@ -0,0 +1,108 @@
1
+ # Timed away missions
2
+
3
+ Use the pure transitions in `@jgengine/core/work/expedition` to orchestrate a timed
4
+ away mission. The caller owns its clock, store, identities, authored content and
5
+ policy. Core owns event ordering and the exploring → returning → home / dead
6
+ lifecycle, not a scheduler, genre preset or persistence backend.
7
+
8
+ All `*Ms` fields and log `at` timestamps are epoch milliseconds. Persist the entire
9
+ `ExpeditionState`, including `cursor`, supplies, carried rewards and the completed
10
+ tick frontier. `settleExpedition(config, state, nowMs)` uses `accrueSince` and
11
+ `steppedCatchUp`; a multi-day gap follows the same loot → hazard → ordered supplies
12
+ → death → capacity recall sequence as incremental calls. Each callback is pure
13
+ and receives the state at that event's time, with the persisted RNG stream.
14
+
15
+ ```ts
16
+ import { createLootPipeline } from "@jgengine/core/game/lootPipeline";
17
+ import { timeScaledRarity } from "@jgengine/core/game/lootModifiers";
18
+ import {
19
+ dispatchExpedition, settleExpedition, recallExpedition, returnExpeditionNow,
20
+ type ExpeditionConfig, type ExpeditionState,
21
+ } from "@jgengine/core/work/expedition";
22
+ import { defineStore } from "@jgengine/core/store/defineStore";
23
+ import { defineSystem } from "@jgengine/core/game/defineSystem";
24
+
25
+ const rewards = createLootPipeline<{ elapsedMs: number }>({
26
+ id: "survey-rewards",
27
+ stages: [{ id: "survey", table: {
28
+ id: "survey-pool", entries: [
29
+ { item: "sample", count: 1, weight: 9 },
30
+ { item: "artifact", count: 1, weight: 1 },
31
+ ],
32
+ }, modifiers: [timeScaledRarity({
33
+ id: "survey-depth",
34
+ ramp: (entry, hours) => entry.item === "artifact" ? 1 + hours : 1,
35
+ })] }],
36
+ });
37
+ const survey: ExpeditionConfig<{ elapsedMs: number }> = {
38
+ tickMs: 20 * 60_000,
39
+ loot: rewards,
40
+ lootCtx: state => ({ elapsedMs: state.elapsedMs }),
41
+ maxHp: state => Math.max(1, 100 - state.exposure),
42
+ hazard: (_state, rng) => ({ damage: rng() < 0.25 ? 4 : 1, exposure: 1 }),
43
+ supplies: [
44
+ { key: "filter", when: state => state.exposure >= 10, clearExposure: 10 },
45
+ { key: "medicine", when: state => state.hp < 30, restoreHp: 25 },
46
+ ],
47
+ returnFraction: 0.5,
48
+ carryCap: 100,
49
+ maxTicksPerSettle: 1000,
50
+ logLimit: 200,
51
+ describe: event => `Survey: ${event.kind}`, // Replace with authored/localized prose.
52
+ };
53
+
54
+ export function awayMission(nowMs: () => number) {
55
+ const mission = defineStore<ExpeditionState | null>("away-mission", null);
56
+ const dispatch = () => dispatchExpedition({
57
+ id: "surveyor", startAtMs: nowMs(), hp: 100,
58
+ supplies: { medicine: 5, filter: 2 }, seed: "caller-campaign:surveyor",
59
+ });
60
+ const system = defineSystem({
61
+ id: "away-mission", tick: { type: "frame" },
62
+ create(ctx) { mission.write(ctx, dispatch()); },
63
+ update(ctx) { mission.update(ctx, state => state === null ? null : settleExpedition(survey, state, nowMs())); },
64
+ reset(ctx) { mission.write(ctx, dispatch()); },
65
+ });
66
+ return { mission, system, recallExpedition, returnExpeditionNow };
67
+ }
68
+ ```
69
+
70
+ Install the returned system with `defineGame({ systems: [system] })`. Store writes
71
+ and `ctx.state()` / `ctx.restore()` keep the ordinary native save boundary; no
72
+ expedition-specific backend is required. A caller-owned store may instead call
73
+ the same pure transitions and atomically persist each returned value. Decode and
74
+ validate your saved schema at that boundary. Keep policy callbacks free of side
75
+ effects; a rejected policy transition does not write through the pure API.
76
+
77
+ A courier composition can use a parcel-only loot pipeline, no exposure,
78
+ `maxHp: () => 20`, rations restoring health, and `returnFraction: 0.25`. Both
79
+ compositions use the same orchestration. Item names, encounter notices, quality
80
+ curves, hazard magnitudes, immunity and supply thresholds are authored data and
81
+ callbacks; they are not SDK content presets. `timeScaledRarity` multiplies weighted
82
+ odds or independent chances (clamped to one) and preserves the loot pipeline's
83
+ original/effective odds and modifier ids. There is no built-in rarity taxonomy.
84
+
85
+ `carryCap` counts **item units**, not drop records. Rolled item quantities are
86
+ admitted in pipeline order up to the remaining cap; currency does not consume
87
+ item capacity and is carried in full. Carried counts merge by identity. Each loot
88
+ event's `drops` contains accepted quantities, while `loot` contains the full rolled
89
+ resolution and provenance, including quantities excluded by capacity. Core starts
90
+ auto-return at the event time that filled capacity. Returning never rolls rewards,
91
+ consumes supplies or takes hazard damage. Arrival is recorded at its exact deadline
92
+ even when one offline call observes it much later. Dead missions never revive.
93
+
94
+ `maxTicksPerSettle` bounds work without dropping elapsed ticks. If the budget runs
95
+ out, the completed frontier remains behind `nowMs`. Recall and instant return use
96
+ that same settlement pipeline and postpone the transition while due ticks remain;
97
+ repeat at the same target until caught up. The caller owns instant-return payment
98
+ and authorization. Count-bound `appendFeed` keeps the newest log events without
99
+ discarding carried rewards or RNG progress. Supply rules run in array order and
100
+ spend one unit only when restoration/cleansing changes a vital; health can recover
101
+ from zero during that tick before the death decision.
102
+
103
+ Configuration is runtime policy, never saved into state. Retuning applies the
104
+ current policy to unsettled ticks; a changed cadence anchors at the last completed
105
+ tick. Persist a policy/version id alongside your state if historical policy must
106
+ be replayed exactly. A lower carry cap preserves already-owned rewards and recalls
107
+ after the next due event (or immediately when no tick is due). Monotonic settlement
108
+ does not replay time when the caller's clock moves backward.
@@ -0,0 +1,76 @@
1
+ # Repair dialogue batches against reviewed canon
2
+
3
+ `npx jgengine recipe narrative-authoring` prints an SDK-typechecked function with
4
+ injected `generate` and `review` callbacks. Use it for a bounded chapter's
5
+ conversations, then stage the accepted batch for playtesting. It selects no
6
+ model, network service, assets or genre.
7
+
8
+ The author supplies a chapter brief: intended experience, voices, prerequisites,
9
+ player choices and their consequences. Keep reviewed canon separate from draft
10
+ prose. `freezeNarrativeCanon` detaches and freezes a revision, character ids,
11
+ exact string facts and a strict event order. Include only events whose order is
12
+ settled; branching or uncertain chronology belongs in semantic review. A new
13
+ fact or changed fact requires a separately reviewed canon revision.
14
+
15
+ ```ts
16
+ import {
17
+ freezeNarrativeCanon,
18
+ validateNarrativeBatch,
19
+ type NarrativeBatch,
20
+ } from "@jgengine/core/game/narrativeAuthoring";
21
+
22
+ const canon = freezeNarrativeCanon({
23
+ revision: "inquiry-r7",
24
+ characterIds: ["diver"],
25
+ facts: { hatch: "sealed" },
26
+ eventIds: ["descent", "alarm"],
27
+ });
28
+ const candidate: NarrativeBatch = {
29
+ canonRevision: canon.revision,
30
+ dialogues: [{
31
+ id: "deposition",
32
+ graph: {
33
+ start: "witness",
34
+ nodes: [{ id: "witness", speaker: "Witness 4", text: "The hatch stayed sealed." }],
35
+ },
36
+ claims: [{ nodeId: "witness", kind: "fact", factId: "hatch", value: "sealed" }],
37
+ }],
38
+ };
39
+ const issues = validateNarrativeBatch(canon, candidate, {
40
+ maxDialogues: 1, maxNodes: 20, maxChoices: 40, maxClaims: 30, maxTextCharacters: 6000,
41
+ });
42
+ ```
43
+
44
+ These numbers and content demonstrate caller choices. A procedural undersea
45
+ inquiry can hinge on preserving a witness's anonymity versus publishing evidence.
46
+ An absurd rooftop tea negotiation can hinge on saluting a pigeon's ceremonial
47
+ hat versus offering biscuits. Each game writes its own canon, voices, style and
48
+ consequences; neither slice supplies a reusable story preset.
49
+
50
+ The typed batch contains graphs plus explicit node-attached `character`, `fact`
51
+ and `before` claims. `validateNarrativeBatch` checks those claims and reuses
52
+ `validateDialogueGraph` for topology. Diagnostics include a JSON-pointer path,
53
+ for example `/dialogues/0/claims/1/value` or
54
+ `/dialogues/0/graph/nodes/2/choices/0/to`. Speaker labels and prose remain display
55
+ data. Omitted or misleading claims cannot prove prose is coherent.
56
+
57
+ The CLI function snapshots canon, limits, brief and callback selection before
58
+ awaiting generation. It checks revision and budgets before copying a returned
59
+ candidate; oversized and stale candidates are discarded with diagnostics.
60
+ Within-budget drafts and repair diagnostics are detached and frozen across
61
+ callbacks. Fix the located candidate, preserving the reviewed canon. Attempts
62
+ stop at the caller's positive `maxAttempts`; exhausting it returns `needs-review`.
63
+
64
+ Generators return typed `NarrativeBatch` data. Decode external JSON before this
65
+ boundary and enforce response byte/token budgets in the generator adapter; the
66
+ SDK's node/choice/claim/text limits are not a transport-size limiter. Callback
67
+ failures propagate. A pending reviewer stays pending, and rejection never implies
68
+ approval. A reviewer must return `approved: true` and no error diagnostics for
69
+ acceptance. Review topology warnings explicitly against intended play.
70
+
71
+ Semantic review compares the actual prose with canon and the chapter brief,
72
+ including unannotated statements, character motivations, causality, chronology,
73
+ lasting consequences and meaningful playable choices. Return located issues to
74
+ the next repair attempt. Play representative branches before promoting accepted
75
+ drafts to the game's authored catalog. Acceptance stages data; this function
76
+ never publishes content or replaces canon.
@@ -0,0 +1,101 @@
1
+ # Save generated items in bounded pages
2
+
3
+ Use this recipe when an existing project owns generated item storage and wants
4
+ runtime ids and incremental save export without mirroring its items into another
5
+ registry. The interface is synchronous; it does not provide a database adapter.
6
+
7
+ ```ts
8
+ import {
9
+ createItemInstanceRegistry,
10
+ type ItemInstanceStorage,
11
+ type ItemInstanceRegistryState,
12
+ } from "@jgengine/core/item/itemInstanceRegistry";
13
+
14
+ type RolledItem = { damage: number; affixes: string[] };
15
+
16
+ let existingItems = new Map<string, RolledItem>();
17
+ const storage: ItemInstanceStorage<RolledItem> = {
18
+ get: (id) => existingItems.get(id),
19
+ has: (id) => existingItems.has(id),
20
+ set: (id, definition) => void existingItems.set(id, definition),
21
+ delete: (id) => void existingItems.delete(id),
22
+ count: () => existingItems.size,
23
+ entries: () => existingItems.entries(),
24
+ replace(entries) {
25
+ const staged = new Map(entries);
26
+ existingItems = staged;
27
+ },
28
+ };
29
+
30
+ const registry = createItemInstanceRegistry("rolled", { storage });
31
+ const runtimeId = registry.register("blade", { damage: 17, affixes: ["keen"] });
32
+ const item = registry.get(runtimeId);
33
+ ```
34
+
35
+ The example adapter stages replacement in another Map. A larger caller-owned
36
+ store can stage in its own transaction or generation and switch on success.
37
+ `replace` must consume the entire iterable synchronously and preserve old data
38
+ when iteration, cloning, validation, or commit fails. Do not clear live data and
39
+ then populate it. Stream `entries()` in stable order; materializing all entries
40
+ inside the adapter defeats bounded export.
41
+
42
+ ## Export and restore
43
+
44
+ ```ts
45
+ function saveItems(writePage: (json: string) => void): void {
46
+ for (const page of registry.statePages(256)) {
47
+ writePage(JSON.stringify(page));
48
+ }
49
+ }
50
+
51
+ function restoreItems(readPages: Iterable<string>): boolean {
52
+ function* decodedPages(): IterableIterator<ItemInstanceRegistryState<RolledItem>> {
53
+ for (const json of readPages) {
54
+ yield JSON.parse(json) as ItemInstanceRegistryState<RolledItem>;
55
+ }
56
+ }
57
+ return registry.restorePages(decodedPages());
58
+ }
59
+ ```
60
+
61
+ Each page retains the existing `{ prefix, sequence, entries }` save shape.
62
+ An empty registry exports one empty page so released ids still retain their
63
+ allocation sequence. Every restored page must have the same prefix and sequence.
64
+ Each page's definitions are detached independently; cross-page object identity
65
+ is not preserved. Keep definitions JSON-compatible for JSON saves.
66
+
67
+ `statePages` captures the allocator when called, reads at most `pageSize` entries
68
+ per step, and clones only that batch. Registry mutations invalidate the export
69
+ and the next step throws. Abandoning an iterator never blocks writes; call its
70
+ `return()` when stopping early to release adapter resources. Keep direct writes
71
+ to injected storage and definition objects quiescent during export: the registry
72
+ cannot detect them. The caller owns immutable capture or staging if saves must
73
+ overlap live changes. Publish a set of pages only after the complete export
74
+ succeeds; discard partial saves on failure.
75
+
76
+ `restorePages` returns false for an empty iterable or an invalid first header.
77
+ Later inconsistent headers, clone failures, and storage failures throw. A
78
+ conforming adapter keeps both the old collection and allocator intact on failure.
79
+ Restore consumes and clones one input page at a time; storage staging cost belongs
80
+ to the adapter. Existing `state()` and `restore(state)` remain whole-collection
81
+ operations and retain their original save shape.
82
+
83
+ ## Reattach existing storage
84
+
85
+ If the caller restores storage separately, persist the page's allocator header
86
+ alongside it and attach with its original prefix and sequence:
87
+
88
+ ```ts
89
+ function reattach(savedHeader: { prefix: string; sequence: number }) {
90
+ return createItemInstanceRegistry(savedHeader.prefix, {
91
+ storage,
92
+ sequence: savedHeader.sequence,
93
+ });
94
+ }
95
+ ```
96
+
97
+ Never derive sequence from count: released ids must remain consumed. Allocation
98
+ checks collisions against attached storage, but saving the real sequence avoids
99
+ searching through old ids. Allocation stops at `Number.MAX_SAFE_INTEGER` instead
100
+ of reusing ids through numeric rounding. The default registry remains in memory;
101
+ injected storage owns retention, indexing, transaction policy, and persistence.
@@ -0,0 +1,63 @@
1
+ # Validate generated quest batches against reviewed facts
2
+
3
+ Run `npx jgengine recipe quest-authoring` for the SDK-typechecked composition.
4
+ Supply your reviewed canonical facts, established quests, catalog ids, external
5
+ unlock sources, and authoring limits. Inject your generator as the async callback;
6
+ the recipe does not choose a model, story, genre, or assets.
7
+
8
+ The generator receives detached, deeply frozen snapshots of the facts, established
9
+ definitions, and limits for each bounded batch. Mutating caller inputs while the
10
+ generator awaits cannot change the reviewed revision or reference lookups. It returns only new quest definitions. Decode untrusted JSON
11
+ into `QuestDef` before returning it; this validator checks authored structure, not
12
+ arbitrary JSON shape. The recipe rejects oversized quest/objective/reference
13
+ batches before structural validation. Keep the existing catalog bounded too;
14
+ partition larger worlds into reviewed dependency-complete catalogs.
15
+
16
+ ```ts
17
+ import { validateQuestCatalog } from "@jgengine/core/game/questCatalog";
18
+ import type { QuestDef } from "@jgengine/core/game/quest";
19
+
20
+ function reviewBatch(existing: readonly QuestDef[], generated: readonly QuestDef[]) {
21
+ return validateQuestCatalog([...existing, ...generated], {
22
+ externalUnlocks: ["caller-declared-world-entry"],
23
+ });
24
+ }
25
+ ```
26
+
27
+ The CLI recipe returns `factsRevision`, `candidate`, `diagnostics`, and `repair`.
28
+ `candidate` is null when structural errors exist. Each issue includes a JSON
29
+ Pointer into the combined catalog, such as `/4/rewards/quests/0`, plus a stable
30
+ code, severity, and referenced id when relevant. Send `repair` with the same
31
+ canonical facts and definitions to your generator for a bounded correction;
32
+ correct the identified fields and validate the whole candidate again. Never
33
+ replace reviewed facts with a model's remembered version. The recipe leaves all
34
+ warnings visible for review before registering the candidate with the journal.
35
+
36
+ Declare unlock ids that other game systems may grant in `externalUnlocks`.
37
+ Unlocks produced by catalog quest rewards are recognized automatically.
38
+ `externallyStartedQuests` declares catalog quests your game may grant active,
39
+ bypassing acceptance prerequisites. These declarations describe possible entry
40
+ points, without asserting when they happen. A requirement may be fulfilled by
41
+ completion of that quest id **or** an unlock with the same id; every requirement
42
+ must be met. Any eligible quest may be accepted directly, so a quest does not
43
+ need an incoming reward follow-up to be reachable.
44
+
45
+ Blocked prerequisites and cycles are warnings under this potential-progression
46
+ model. Objectives are assumed completable; external grants, custom acceptance
47
+ rules, or unavailable objectives can change actual play. Unknown requirement and
48
+ follow-up ids are errors: declare legitimate external unlock ids rather than
49
+ silencing missing references. Use `hasReference` for item, target, inventory, and
50
+ currency namespaces. Its objective context lets a game route custom objective
51
+ kinds to different catalogs. Omitted lookup policy leaves those ids unchecked.
52
+
53
+ Objective and item counts may be fractional or zero; zero can mean an already
54
+ complete objective or a no-op reward. Negative/nonfinite counts and nonfinite
55
+ reward amounts reject. Discrete quantity rules, XP loss, economy balance, story
56
+ contradictions, character motivation, chronology, and consequences remain
57
+ caller-owned review. Play representative paths before treating a valid catalog
58
+ as a coherent world.
59
+
60
+ For item supply and skill gates outside the quest journal, compose the
61
+ [content validation](../reference-content-validation.md) checks. Declare source
62
+ rules explicitly rather than assuming every defined item is obtainable or that
63
+ catalog order proves an item reward can precede a collection objective.
@@ -0,0 +1,50 @@
1
+ # Place shaped inventory items
2
+
3
+ Use `@jgengine/core/inventory/shapedGrid` for discrete cell footprints, including
4
+ rectangles and irregular shapes. Item definitions, values, authored dimensions,
5
+ save schemas, drag controls, and presentation stay in the game.
6
+
7
+ ```ts
8
+ import {
9
+ createShapedGrid,
10
+ findShapedPlacement,
11
+ moveShaped,
12
+ placeShaped,
13
+ type ShapedItem,
14
+ } from "@jgengine/core/inventory/shapedGrid";
15
+
16
+ const item: ShapedItem<{ name: string }> = {
17
+ id: "probe-7",
18
+ value: { name: "Survey probe" },
19
+ footprint: [[0, 0], [0, 1], [1, 1]],
20
+ };
21
+ const board = createShapedGrid<{ name: string }>(4, 3);
22
+ const fit = findShapedPlacement(board, item.footprint, {
23
+ rotations: [0, 1],
24
+ maxChecks: 24,
25
+ });
26
+ if (fit.status === "found") {
27
+ const placed = placeShaped(board, item, fit.origin, fit.rotation);
28
+ if (placed.status === "ok") moveShaped(placed.grid, item.id, [1, 0], 1);
29
+ }
30
+ ```
31
+
32
+ Rotations are quarter turns. Search visits the supplied rotation order, then
33
+ rows and columns; the default rotation order is `[0, 1, 2, 3]`. Coordinates and
34
+ dimensions must be finite safe integers; dimensions are positive and footprints
35
+ are nonempty. Footprints normalize their minimum cell to `[0, 0]` after rotation.
36
+ `canPlace` reports invalid cells before bounds and overlap checks. Pass `ignoreId`
37
+ to either check or search when repositioning an existing item.
38
+
39
+ Search indexes current occupancy once and limits candidate origins to
40
+ `maxChecks` (default 4096). `budget-exceeded` means the search is incomplete;
41
+ `no-space` means every requested origin and rotation was checked. Do not report
42
+ a full bag for an exhausted budget. Indexing and footprint checks scale with
43
+ the caller's board data; the budget does not limit their cell count.
44
+
45
+ Placement operations return new board data. Serialize that data in the caller's
46
+ save format; the engine does not keep a parallel bag or impose item nouns.
47
+ Deepward's prepared adopter maps its existing rectangular item sizes and saved
48
+ `x`, `y`, `rotated` fields into this contract, preserving its `[0, 1]` rotation
49
+ preference and row-major packing. Its UI and extraction rules remain local.
50
+ The prepared patch waits for a coordinator-verified containing package release.