@jgengine/core 0.11.0 → 0.13.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 (256) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/dist/ai/populationDirector.d.ts +103 -0
  3. package/dist/ai/populationDirector.js +190 -0
  4. package/dist/ai/pursuit.d.ts +56 -0
  5. package/dist/ai/pursuit.js +53 -0
  6. package/dist/authoring.d.ts +1 -1
  7. package/dist/authoring.js +1 -1
  8. package/dist/combat/effects.d.ts +11 -4
  9. package/dist/combat/effects.js +24 -17
  10. package/dist/combat/magazine.d.ts +13 -0
  11. package/dist/combat/magazine.js +27 -0
  12. package/dist/combat/vfxPresets.d.ts +176 -0
  13. package/dist/combat/vfxPresets.js +74 -0
  14. package/dist/combat/weaponFire.d.ts +114 -0
  15. package/dist/combat/weaponFire.js +96 -0
  16. package/dist/combat.d.ts +5 -2
  17. package/dist/combat.js +4 -1
  18. package/dist/devtools/controls.d.ts +38 -0
  19. package/dist/devtools/controls.js +181 -0
  20. package/dist/devtools/devtools.d.ts +5 -197
  21. package/dist/devtools/devtools.js +38 -774
  22. package/dist/devtools/discover.d.ts +32 -0
  23. package/dist/devtools/discover.js +240 -0
  24. package/dist/devtools/fallbackSeams.d.ts +50 -0
  25. package/dist/devtools/fallbackSeams.js +111 -0
  26. package/dist/devtools/frame.d.ts +15 -0
  27. package/dist/devtools/frame.js +247 -0
  28. package/dist/devtools/logs.d.ts +15 -0
  29. package/dist/devtools/logs.js +86 -0
  30. package/dist/devtools/overrides.d.ts +16 -0
  31. package/dist/devtools/overrides.js +66 -0
  32. package/dist/devtools/types.d.ts +212 -0
  33. package/dist/devtools/types.js +1 -0
  34. package/dist/devtools/urlFlags.d.ts +22 -0
  35. package/dist/devtools/urlFlags.js +62 -0
  36. package/dist/economy/auctionBook.d.ts +122 -0
  37. package/dist/economy/auctionBook.js +213 -0
  38. package/dist/economy/priceHistory.d.ts +44 -0
  39. package/dist/economy/priceHistory.js +89 -0
  40. package/dist/economy/unlockPoints.d.ts +92 -0
  41. package/dist/economy/unlockPoints.js +103 -0
  42. package/dist/editor/commandApply.d.ts +5 -0
  43. package/dist/editor/commandApply.js +755 -0
  44. package/dist/editor/commands.d.ts +48 -3
  45. package/dist/editor/commands.js +40 -754
  46. package/dist/editor/directives.d.ts +94 -0
  47. package/dist/editor/directives.js +206 -0
  48. package/dist/editor/document.d.ts +37 -2
  49. package/dist/editor/document.js +422 -6
  50. package/dist/editor/entityCatalog.d.ts +42 -0
  51. package/dist/editor/entityCatalog.js +101 -0
  52. package/dist/editor/environment.d.ts +74 -0
  53. package/dist/editor/environment.js +169 -0
  54. package/dist/editor/index.d.ts +26 -1
  55. package/dist/editor/index.js +8 -1
  56. package/dist/editor/minimap.d.ts +31 -0
  57. package/dist/editor/minimap.js +73 -0
  58. package/dist/editor/streamer.d.ts +66 -0
  59. package/dist/editor/streamer.js +68 -0
  60. package/dist/editor/types.d.ts +143 -0
  61. package/dist/editor/world.d.ts +136 -0
  62. package/dist/editor/world.js +303 -0
  63. package/dist/game/breeding.d.ts +161 -0
  64. package/dist/game/breeding.js +181 -0
  65. package/dist/game/cameraConfig.d.ts +451 -0
  66. package/dist/game/cameraConfig.js +1 -0
  67. package/dist/game/clipRoles.d.ts +51 -0
  68. package/dist/game/clipRoles.js +142 -0
  69. package/dist/game/defineGame.d.ts +16 -9
  70. package/dist/game/defineGame.js +14 -6
  71. package/dist/game/defineSystem.d.ts +2 -0
  72. package/dist/game/defineSystem.js +2 -0
  73. package/dist/game/events.d.ts +28 -0
  74. package/dist/game/partAnimation.d.ts +78 -0
  75. package/dist/game/partAnimation.js +147 -0
  76. package/dist/game/playableGame.d.ts +72 -440
  77. package/dist/game/playableGame.js +1 -1
  78. package/dist/game/progression.d.ts +35 -0
  79. package/dist/game/progression.js +22 -1
  80. package/dist/game/quest.js +1 -3
  81. package/dist/game/tribe.d.ts +184 -0
  82. package/dist/game/tribe.js +432 -0
  83. package/dist/gameplay.d.ts +3 -1
  84. package/dist/gameplay.js +3 -1
  85. package/dist/inventory/encumbrance.d.ts +69 -0
  86. package/dist/inventory/encumbrance.js +68 -0
  87. package/dist/inventory/inventoryModel.d.ts +24 -8
  88. package/dist/inventory/inventoryModel.js +47 -8
  89. package/dist/item/durability.js +1 -3
  90. package/dist/meta/changelog.d.ts +1 -1
  91. package/dist/meta/changelog.js +48 -1
  92. package/dist/movement/movementModel.d.ts +15 -1
  93. package/dist/movement/movementModel.js +33 -15
  94. package/dist/movement/playerMovement.js +102 -2
  95. package/dist/multiplayer/combatSnapshot.js +13 -8
  96. package/dist/multiplayer/presenceModel.d.ts +2 -0
  97. package/dist/multiplayer/presenceModel.js +8 -0
  98. package/dist/physics/drivableVehicle.d.ts +2 -0
  99. package/dist/physics/drivableVehicle.js +1 -1
  100. package/dist/physics/flightDynamics.d.ts +82 -0
  101. package/dist/physics/flightDynamics.js +165 -0
  102. package/dist/physics/gravityField.d.ts +25 -0
  103. package/dist/physics/gravityField.js +41 -0
  104. package/dist/physics/kinematicVehicle.d.ts +87 -0
  105. package/dist/physics/kinematicVehicle.js +193 -11
  106. package/dist/physics/physicsWorld.d.ts +26 -0
  107. package/dist/physics/physicsWorld.js +109 -117
  108. package/dist/physics/vehicleObstacles.d.ts +44 -0
  109. package/dist/physics/vehicleObstacles.js +115 -0
  110. package/dist/random/rng.d.ts +4 -1
  111. package/dist/random/rng.js +4 -1
  112. package/dist/runtime/adapter.d.ts +13 -0
  113. package/dist/runtime/adapter.js +13 -0
  114. package/dist/runtime/context/combat.d.ts +51 -0
  115. package/dist/runtime/context/combat.js +131 -0
  116. package/dist/runtime/context/combatFx.js +15 -5
  117. package/dist/runtime/context/deathLoot.d.ts +30 -0
  118. package/dist/runtime/context/deathLoot.js +39 -0
  119. package/dist/runtime/context/player.d.ts +48 -0
  120. package/dist/runtime/context/player.js +125 -0
  121. package/dist/runtime/context/scene.d.ts +60 -0
  122. package/dist/runtime/context/scene.js +329 -0
  123. package/dist/runtime/context/snapshotCodecs.d.ts +12 -0
  124. package/dist/runtime/context/snapshotCodecs.js +26 -0
  125. package/dist/runtime/descriptors/baseline.js +26 -3
  126. package/dist/runtime/descriptors/features.d.ts +5 -0
  127. package/dist/runtime/descriptors/features.js +20 -2
  128. package/dist/runtime/gameContext.d.ts +2 -428
  129. package/dist/runtime/gameContext.js +133 -500
  130. package/dist/runtime/gameContextTypes.d.ts +500 -0
  131. package/dist/runtime/gameContextTypes.js +1 -0
  132. package/dist/runtime/headlessRunner.d.ts +17 -2
  133. package/dist/runtime/headlessRunner.js +15 -0
  134. package/dist/runtime/hostPolicy.d.ts +51 -0
  135. package/dist/runtime/hostPolicy.js +68 -0
  136. package/dist/runtime/hostedGameRunner.d.ts +3 -1
  137. package/dist/runtime/hostedGameRunner.js +1 -0
  138. package/dist/runtime/hostedWorldSession.d.ts +3 -1
  139. package/dist/runtime/hostedWorldSession.js +1 -0
  140. package/dist/runtime/worldSnapshot.d.ts +13 -1
  141. package/dist/runtime/worldSnapshot.js +15 -3
  142. package/dist/scene/assetCatalog.d.ts +7 -0
  143. package/dist/scene/assetSpace.js +1 -3
  144. package/dist/scene/authoredTriggers.d.ts +50 -0
  145. package/dist/scene/authoredTriggers.js +114 -0
  146. package/dist/scene/builtinSceneKinds.js +8 -3
  147. package/dist/scene/colliders.d.ts +79 -0
  148. package/dist/scene/colliders.js +170 -0
  149. package/dist/scene/collisionMesh.d.ts +94 -0
  150. package/dist/scene/collisionMesh.js +699 -0
  151. package/dist/scene/companion.d.ts +147 -0
  152. package/dist/scene/companion.js +193 -0
  153. package/dist/scene/entityStats.d.ts +7 -9
  154. package/dist/scene/entityStats.js +19 -15
  155. package/dist/scene/sceneKinds.d.ts +16 -0
  156. package/dist/scene/sceneKinds.js +4 -0
  157. package/dist/scene/sceneRaycast.js +15 -0
  158. package/dist/stats/spawnLevelStats.d.ts +110 -0
  159. package/dist/stats/spawnLevelStats.js +140 -0
  160. package/dist/stats/statModifiers.d.ts +16 -0
  161. package/dist/stats/statModifiers.js +30 -1
  162. package/dist/stats/statPool.d.ts +70 -0
  163. package/dist/stats/statPool.js +62 -0
  164. package/dist/store/changeSignal.d.ts +3 -0
  165. package/dist/store/changeSignal.js +2 -0
  166. package/dist/store/observableKeyedStore.d.ts +2 -0
  167. package/dist/store/observableKeyedStore.js +1 -0
  168. package/dist/survival/decayMeter.js +1 -3
  169. package/dist/survival/regionHealth.js +1 -3
  170. package/dist/ui/hudLayout.d.ts +5 -0
  171. package/dist/ui/hudLayout.js +17 -1
  172. package/dist/ui/panelModel.d.ts +120 -0
  173. package/dist/ui/panelModel.js +168 -0
  174. package/dist/ui.d.ts +1 -0
  175. package/dist/ui.js +1 -0
  176. package/dist/world/authoredEntities.d.ts +29 -0
  177. package/dist/world/authoredEntities.js +29 -0
  178. package/dist/world/authoredObjects.d.ts +15 -1
  179. package/dist/world/authoredObjects.js +13 -1
  180. package/dist/world/authoredSpawn.d.ts +39 -0
  181. package/dist/world/authoredSpawn.js +37 -0
  182. package/dist/world/buildSockets.d.ts +152 -0
  183. package/dist/world/buildSockets.js +188 -0
  184. package/dist/world/buildingLots.d.ts +84 -0
  185. package/dist/world/buildingLots.js +119 -0
  186. package/dist/world/buildings.d.ts +6 -0
  187. package/dist/world/cityBlocks.d.ts +152 -0
  188. package/dist/world/cityBlocks.js +670 -0
  189. package/dist/world/cityContent.d.ts +77 -0
  190. package/dist/world/cityContent.js +261 -0
  191. package/dist/world/cityGenerator.d.ts +41 -0
  192. package/dist/world/cityGenerator.js +58 -0
  193. package/dist/world/cityGeometry.d.ts +114 -0
  194. package/dist/world/cityGeometry.js +573 -0
  195. package/dist/world/cityKind.d.ts +314 -0
  196. package/dist/world/cityKind.js +1578 -0
  197. package/dist/world/environmentSummary.js +57 -1
  198. package/dist/world/features.d.ts +53 -5
  199. package/dist/world/features.js +15 -3
  200. package/dist/world/grassKind.js +2 -2
  201. package/dist/world/markers.d.ts +50 -4
  202. package/dist/world/markers.js +49 -0
  203. package/dist/world/massing.js +1 -1
  204. package/dist/world/minimapBake.d.ts +78 -0
  205. package/dist/world/minimapBake.js +208 -0
  206. package/dist/world/minimapTrack.d.ts +17 -0
  207. package/dist/world/minimapTrack.js +22 -0
  208. package/dist/world/place.d.ts +166 -0
  209. package/dist/world/place.js +96 -0
  210. package/dist/world/placeAsset.js +3 -1
  211. package/dist/world/poleLineKind.d.ts +40 -0
  212. package/dist/world/poleLineKind.js +97 -0
  213. package/dist/world/polyline.js +9 -2
  214. package/dist/world/resourceNode.d.ts +109 -0
  215. package/dist/world/resourceNode.js +140 -0
  216. package/dist/world/roads.d.ts +19 -2
  217. package/dist/world/roads.js +60 -3
  218. package/dist/world/scatter.js +2 -20
  219. package/dist/world/streetGenerator.d.ts +159 -0
  220. package/dist/world/streetGenerator.js +684 -0
  221. package/dist/world/streets.d.ts +103 -1
  222. package/dist/world/streets.js +273 -0
  223. package/dist/world/terraform.js +58 -88
  224. package/dist/world/terrain.d.ts +65 -1
  225. package/dist/world/terrain.js +119 -2
  226. package/dist/world.d.ts +14 -5
  227. package/dist/world.js +12 -3
  228. package/package.json +1 -1
  229. package/skills/game-design/SKILL.md +28 -0
  230. package/skills/game-design/agents/openai.yaml +4 -0
  231. package/skills/game-design/references/application-playbook.md +170 -0
  232. package/skills/game-design/references/field-guide.md +288 -0
  233. package/skills/jgengine/SKILL.md +8 -4
  234. package/skills/jgengine/api.md +182 -81
  235. package/skills/jgengine/capabilities.md +104 -2
  236. package/skills/jgengine/recipes/minimal-game.md +88 -0
  237. package/skills/jgengine-combat/SKILL.md +19 -1
  238. package/skills/jgengine-combat/api.md +75 -2
  239. package/skills/jgengine-combat/capabilities.md +61 -1
  240. package/skills/jgengine-combat/recipes/portable-damage-effects.md +197 -0
  241. package/skills/jgengine-combat/recipes/portable-runtime-state.md +154 -0
  242. package/skills/jgengine-combat/recipes/portable-stat-pools.md +122 -0
  243. package/skills/jgengine-combat/recipes/portable-weapon-plumbing.md +156 -0
  244. package/skills/jgengine-combat/recipes/visible-attacks.md +81 -0
  245. package/skills/jgengine-combat/reference.md +33 -4
  246. package/skills/jgengine-gameplay/SKILL.md +8 -0
  247. package/skills/jgengine-gameplay/api.md +182 -26
  248. package/skills/jgengine-gameplay/capabilities.md +96 -2
  249. package/skills/jgengine-gameplay/recipes/portable-xp-leveling.md +168 -0
  250. package/skills/jgengine-gameplay/reference-systems.md +10 -5
  251. package/skills/jgengine-world/SKILL.md +13 -0
  252. package/skills/jgengine-world/api.md +453 -21
  253. package/skills/jgengine-world/capabilities.md +112 -2
  254. package/skills/jgengine-world/recipes/commanding-entities.md +128 -0
  255. package/skills/jgengine-world/reference.md +43 -4
  256. package/skills/jgengine/reference-cartridge.md +0 -72
@@ -8,6 +8,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
8
8
 
9
9
  - `advanceLedger` (function) · `import { advanceLedger } from "@jgengine/core/gameplay"`
10
10
 
11
+ ## auction-book — timed bid auctions with escrowed bids, buyout, anti-snipe close extension, and collection-box settlement
12
+
13
+ - `createAuctionBook` (function) · `import { createAuctionBook } from "@jgengine/core/gameplay"`
14
+
11
15
  ## best-record — persist personal-best times/scores with safe storage fallback
12
16
 
13
17
  - `createRecordBook` (function) · `import { createRecordBook } from "@jgengine/core/gameplay"`
@@ -24,6 +28,19 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
24
28
 
25
29
  - `cancelJob` (function) · `import { cancelJob } from "@jgengine/core/gameplay"`
26
30
 
31
+ ## carry-encumbrance — carried mass vs capacity into movement penalty tiers and a speed multiplier
32
+
33
+ - `resolveEncumbrance` (function) · `import { resolveEncumbrance } from "@jgengine/core/inventory/encumbrance"`
34
+
35
+ ## carry-load — sum carried mass from item stacks via an injected mass resolver
36
+
37
+ - `totalLoad` (function) · `import { totalLoad } from "@jgengine/core/inventory/encumbrance"`
38
+
39
+ ## clip-roles — map a rigged asset's pack-specific clip name to its semantic animation role
40
+
41
+ - `classifyClip` (function) · `import { classifyClip } from "@jgengine/core/game/clipRoles"`
42
+ - `defaultAnimationForClips` (function) · `import { defaultAnimationForClips } from "@jgengine/core/game/clipRoles"`
43
+
27
44
  ## compose-game-loop — fold composable systems into the game loop without a manual tick fan-out
28
45
 
29
46
  - `composeGameLoop` (function) · `import { composeGameLoop } from "@jgengine/core/gameplay"`
@@ -36,6 +53,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
36
53
 
37
54
  - `createCosmetics` (function) · `import { createCosmetics } from "@jgengine/core/gameplay"`
38
55
 
56
+ ## creature-breeding — deterministic stat inheritance, mutation, and lineage genetics for taming games
57
+
58
+ - `breedOffspring` (function) · `import { breedOffspring } from "@jgengine/core/game/breeding"`
59
+
39
60
  ## decay-meter — survival meters that drain/refill over game time (hunger, water, oxygen, stamina)
40
61
 
41
62
  - `createDecayMeterSet` (function) · `import { createDecayMeterSet } from "@jgengine/core/gameplay"`
@@ -51,9 +72,9 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
51
72
  - `evaluatePredicate` (function) · `import { evaluatePredicate } from "@jgengine/core/gameplay"`
52
73
  - `readPath` (function) · `import { readPath } from "@jgengine/core/gameplay"`
53
74
 
54
- ## define-game — single public game-authoring path — compose systems, world, and loop in one definition
75
+ ## define-system — composable game behavior — pure data + hooks the engine schedules; pass via `defineGame({ systems })`
55
76
 
56
- - `defineGame` (function) · `import { defineGame } from "@jgengine/core/gameplay"`
77
+ - `defineSystem` (function) · `import { defineSystem } from "@jgengine/core/gameplay"`
57
78
 
58
79
  ## detect — fire-once threshold/milestone/tier crossings when a value moves
59
80
 
@@ -67,6 +88,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
67
88
 
68
89
  - `applyWear` (function) · `import { applyWear } from "@jgengine/core/gameplay"`
69
90
 
91
+ ## encumbrance-move-curve — progressive move-speed multiplier from a carry-load fraction
92
+
93
+ - `encumbranceMoveMultiplier` (function) · `import { encumbranceMoveMultiplier } from "@jgengine/core/inventory/encumbrance"`
94
+
70
95
  ## enqueue-work — reserve inputs and add a timed job, honoring capacity and validation policy
71
96
 
72
97
  - `enqueue` (function) · `import { enqueue } from "@jgengine/core/gameplay"`
@@ -87,6 +112,30 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
87
112
 
88
113
  - `SystemDefinition` (interface) · `import { SystemDefinition } from "@jgengine/core/gameplay"`
89
114
 
115
+ ## imprint-bonus — apply imprint stat and handler bonuses at an imprint fraction
116
+
117
+ - `applyImprintBonus` (function) · `import { applyImprintBonus } from "@jgengine/core/game/breeding"`
118
+
119
+ ## imprint-increment — per-care-request imprint gain across a maturation schedule
120
+
121
+ - `imprintIncrementPerRequest` (function) · `import { imprintIncrementPerRequest } from "@jgengine/core/game/breeding"`
122
+
123
+ ## incubation-tick — advance egg incubation health inside/outside its temperature range
124
+
125
+ - `tickIncubation` (function) · `import { tickIncubation } from "@jgengine/core/game/breeding"`
126
+
127
+ ## incubation-viability — test whether an egg incubation still has health to hatch
128
+
129
+ - `incubationViable` (function) · `import { incubationViable } from "@jgengine/core/game/breeding"`
130
+
131
+ ## inventory-grid — a bag of stackable items with add, remove, and move
132
+
133
+ - `createEmptyInventory` (function) · `import { createEmptyInventory } from "@jgengine/core/inventory/inventoryModel"`
134
+
135
+ ## inventory-split — split a stack into a target or first-empty slot
136
+
137
+ - `splitStack` (function) · `import { splitStack } from "@jgengine/core/inventory/inventoryModel"`
138
+
90
139
  ## item-generation — compose constraints, weighted pools, transforms, and provenance into a deterministic generated item
91
140
 
92
141
  - `generate` (function) · `import { generate } from "@jgengine/core/gameplay"`
@@ -126,6 +175,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
126
175
 
127
176
  - `createLeaderboard` (function) · `import { createLeaderboard } from "@jgengine/core/gameplay"`
128
177
 
178
+ ## leveling-track — grant XP, resolve level-ups against a curve, and write level+xp stats back through a stat adapter
179
+
180
+ - `leveling` (function) · `import { leveling } from "@jgengine/core/gameplay"`
181
+
129
182
  ## lifecycle — declarative start/restart run flow — the engine owns the command glue and phase sync, the game supplies pure state transitions
130
183
 
131
184
  - `LifecycleConfig` (interface) · `import { LifecycleConfig } from "@jgengine/core/gameplay"`
@@ -160,6 +213,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
160
213
  - `createLootRegistry` (function) · `import { createLootRegistry } from "@jgengine/core/gameplay"`
161
214
  - `lootTable` (function) · `import { lootTable } from "@jgengine/core/gameplay"`
162
215
 
216
+ ## maturation-stage — resolve the baby/juvenile/adolescent/adult stage from elapsed maturation
217
+
218
+ - `maturationStage` (function) · `import { maturationStage } from "@jgengine/core/game/breeding"`
219
+
163
220
  ## modular-item — attach parts into item mount slots to compute combined stats
164
221
 
165
222
  - `slotAccepts` (function) · `import { slotAccepts } from "@jgengine/core/gameplay"`
@@ -190,6 +247,11 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
190
247
  - `resolveSelection` (function) · `import { resolveSelection } from "@jgengine/core/gameplay"`
191
248
  - `validateLayers` (function) · `import { validateLayers } from "@jgengine/core/gameplay"`
192
249
 
250
+ ## part-motion — sample the root bob/sway/squash/flinch/death pose for a rig-less part-composed character
251
+
252
+ - `sampleBodyPose` (function) · `import { sampleBodyPose } from "@jgengine/core/game/partAnimation"`
253
+ - `samplePartPose` (function) · `import { samplePartPose } from "@jgengine/core/game/partAnimation"`
254
+
193
255
  ## pause-scheduled-rule — stop a scheduled rule from firing while retaining its cursor
194
256
 
195
257
  - `pauseRule` (function) · `import { pauseRule } from "@jgengine/core/gameplay"`
@@ -230,10 +292,18 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
230
292
 
231
293
  - `thresholdScale` (function) · `import { thresholdScale } from "@jgengine/core/gameplay"`
232
294
 
295
+ ## price-history — rolling per-item sale records aggregated into market price stats
296
+
297
+ - `createPriceHistory` (function) · `import { createPriceHistory } from "@jgengine/core/gameplay"`
298
+
233
299
  ## production-building — a factory building converting inputs to outputs over time
234
300
 
235
301
  - `createProductionState` (function) · `import { createProductionState } from "@jgengine/core/gameplay"`
236
302
 
303
+ ## progression-curve — evaluate a level/rank curve (const, linear, power, geometric, steps, piecewise) at a point
304
+
305
+ - `evalCurve` (function) · `import { evalCurve } from "@jgengine/core/gameplay"`
306
+
237
307
  ## quest-log — track accepted quests and their per-objective progress
238
308
 
239
309
  - `createQuestJournal` (function) · `import { createQuestJournal } from "@jgengine/core/gameplay"`
@@ -281,6 +351,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
281
351
 
282
352
  - `createRunDraft` (function) · `import { createRunDraft } from "@jgengine/core/gameplay"`
283
353
 
354
+ ## seeded-random — injected deterministic randomness for game logic — never `Math.random` in a simulation
355
+
356
+ - `seededRng` (function) · `import { seededRng } from "@jgengine/core/gameplay"`
357
+
284
358
  ## set — a bounded keyed value in a caller-owned serializable record
285
359
 
286
360
  - `setValue` (function) · `import { setValue } from "@jgengine/core/gameplay"`
@@ -318,6 +392,14 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
318
392
 
319
393
  - `touchButtonShape` (function) · `import { touchButtonShape } from "@jgengine/core/gameplay"`
320
394
 
395
+ ## tribe-group — shared-ownership group (tribe/guild/clan) with ranked permissions, group vs personal assets, alliances, and a bounded event log
396
+
397
+ - `createTribe` (function) · `import { createTribe } from "@jgengine/core/game/tribe"`
398
+
399
+ ## tribe-registry — registry of tribes with mutual alliances and cross-tribe relation lookup
400
+
401
+ - `createTribeRegistry` (function) · `import { createTribeRegistry } from "@jgengine/core/game/tribe"`
402
+
321
403
  ## triggered-rules — event-conditioned effects with declarative predicates, lifetimes, and stacking
322
404
 
323
405
  - `createTriggeredRuleEngine` (function) · `import { createTriggeredRuleEngine } from "@jgengine/core/gameplay"`
@@ -325,10 +407,22 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
325
407
  - `listRuleEffects` (function) · `import { listRuleEffects } from "@jgengine/core/gameplay"`
326
408
  - `registerRuleEffect` (function) · `import { registerRuleEffect } from "@jgengine/core/gameplay"`
327
409
 
410
+ ## typed-keyed-store — a cast-free typed handle onto a per-owner keyed family of reactive game-store slots, replayable and host-authoritative
411
+
412
+ - `defineKeyedStore` (function) · `import { defineKeyedStore } from "@jgengine/core/store/defineKeyedStore"`
413
+
414
+ ## typed-store — a cast-free typed handle onto one reactive game-store slot, replayable and host-authoritative
415
+
416
+ - `defineStore` (function) · `import { defineStore } from "@jgengine/core/store/defineStore"`
417
+
328
418
  ## unit-training — compose a timed work queue that trains catalog units into spawn orders
329
419
 
330
420
  - `unitTrainingConfig` (function) · `import { unitTrainingConfig } from "@jgengine/core/gameplay"`
331
421
 
422
+ ## unlock-points — spendable point pool earned per level and spent to unlock nodes, with refund and respec
423
+
424
+ - `createUnlockPoints` (function) · `import { createUnlockPoints } from "@jgengine/core/economy/unlockPoints"`
425
+
332
426
  ## unlockables — gate content behind unlock conditions the player earns
333
427
 
334
428
  - `createUnlockCatalog` (function) · `import { createUnlockCatalog } from "@jgengine/core/gameplay"`
@@ -0,0 +1,168 @@
1
+ # Add XP and leveling to an existing store
2
+
3
+ Use this recipe when an existing JavaScript/TypeScript project owns its player
4
+ state and save format but wants JGengine's deterministic XP curves,
5
+ multi-level grants, and per-level events. No `GameContext`, renderer, React,
6
+ entity store, or JGengine loop is required.
7
+
8
+ ## Install and focused imports
9
+
10
+ ```sh
11
+ bun add @jgengine/core
12
+ ```
13
+
14
+ ```ts
15
+ import {
16
+ leveling,
17
+ type LevelingStatAccess,
18
+ } from "@jgengine/core/game/progression";
19
+ ```
20
+
21
+ ## Caller-owned save data
22
+
23
+ ```ts
24
+ interface SavedPool {
25
+ current: number;
26
+ max: number;
27
+ }
28
+
29
+ interface PlayerSave {
30
+ experience: SavedPool;
31
+ rank: SavedPool;
32
+ unlockedAbilities: string[];
33
+ }
34
+
35
+ interface ExistingSave {
36
+ players: Record<string, PlayerSave>;
37
+ }
38
+
39
+ let save: ExistingSave = {
40
+ players: {
41
+ player_1: {
42
+ experience: { current: 0, max: 100 },
43
+ rank: { current: 1, max: 20 },
44
+ unlockedAbilities: [],
45
+ },
46
+ },
47
+ };
48
+ ```
49
+
50
+ ## Minimal adapter
51
+
52
+ ```ts
53
+ const stats: LevelingStatAccess = {
54
+ get(userId, statId) {
55
+ const player = save.players[userId];
56
+ if (player === undefined) return null;
57
+ if (statId === "experience") return player.experience;
58
+ if (statId === "rank") return player.rank;
59
+ return null;
60
+ },
61
+ set(userId, statId, patch) {
62
+ const player = save.players[userId];
63
+ if (player === undefined) return false;
64
+ const previous = statId === "experience"
65
+ ? player.experience
66
+ : statId === "rank"
67
+ ? player.rank
68
+ : null;
69
+ if (previous === null) return false;
70
+ const next = {
71
+ current: patch.current ?? previous.current,
72
+ max: patch.max ?? previous.max,
73
+ };
74
+ const nextPlayer = statId === "experience"
75
+ ? { ...player, experience: next }
76
+ : { ...player, rank: next };
77
+ save = {
78
+ ...save,
79
+ players: {
80
+ ...save.players,
81
+ [userId]: nextPlayer,
82
+ },
83
+ };
84
+ return true;
85
+ },
86
+ };
87
+ ```
88
+
89
+ The adapter may call a Zustand `set`, dispatch a Redux action, write an ECS
90
+ component, or replace plain data as above. It never mirrors the player into a
91
+ JGengine store.
92
+
93
+ ## Configure and grant XP
94
+
95
+ ```ts
96
+ const emitted: unknown[] = [];
97
+ const existingEvents = {
98
+ emit(name: string, payload: unknown) {
99
+ emitted.push({ name, payload });
100
+ },
101
+ };
102
+
103
+ const playerLevel = leveling({
104
+ xpForLevel: {
105
+ kind: "power",
106
+ base: 80,
107
+ exponent: 1.35,
108
+ round: "floor",
109
+ },
110
+ maxLevel: 20,
111
+ xpStat: "experience",
112
+ levelStat: "rank",
113
+ });
114
+
115
+ const levelEvents: number[] = [];
116
+ const levelsGained = playerLevel.grantXp(
117
+ stats,
118
+ "player_1",
119
+ 900,
120
+ (level) => {
121
+ levelEvents.push(level);
122
+ existingEvents.emit("player-level-up", { playerId: "player_1", level });
123
+ },
124
+ );
125
+ ```
126
+
127
+ `levelsGained` is the count returned to the caller. The callback fires once for
128
+ every reached level in ascending order, so a large reward can unlock each
129
+ milestone deterministically instead of collapsing them into one final event.
130
+
131
+ Use `playerLevel.resolve(level, xp)` when prediction/UI needs the same math
132
+ without writing through the adapter.
133
+
134
+ ## Save and restore
135
+
136
+ The adapter's authoritative values already live in the existing save:
137
+
138
+ ```ts
139
+ const encoded = JSON.stringify(save);
140
+ save = JSON.parse(encoded) as ExistingSave;
141
+
142
+ // Recreate an adapter over the restored store, then continue normally.
143
+ playerLevel.grantXp(stats, "player_1", 75, (level) => {
144
+ existingEvents.emit("player-level-up", { playerId: "player_1", level });
145
+ });
146
+ ```
147
+
148
+ The leveling track is immutable configuration; recreate it from the same curve
149
+ after load. Version curve changes in the existing project's migration layer.
150
+
151
+ ## Ownership
152
+
153
+ The existing project retains player identity, store updates, save schema,
154
+ curve/content choice, reward sources, event bus, unlock handling, UI, backend,
155
+ and when XP is granted. JGengine owns curve evaluation, surplus rollover,
156
+ level caps, ordered level events, and the two-method stat adapter contract.
157
+
158
+ ## Common traps
159
+
160
+ - Initialize both configured stat ids; `grantXp` returns `0` when either is
161
+ missing.
162
+ - Treat positive and negative XP policy as game-owned input validation;
163
+ sanitize rewards before calling when XP loss is not allowed.
164
+ - The default threshold mode is `perLevel`. Use `cumulative` only when the curve
165
+ represents total lifetime XP required to reach each level.
166
+ - Keep `rank.max` aligned with `maxLevel` in the caller's schema.
167
+ - Apply milestone rewards from each callback invocation; do not assume a large
168
+ grant emits only the final reached level.
@@ -1,6 +1,15 @@
1
1
  # Composable systems (`defineSystem`)
2
2
 
3
- Single public authoring path: `defineGame({ systems, loop?, … })` from `@jgengine/shell/defineGame` (or core). Systems are meaningful capabilities — not micro-ticks.
3
+ Single public authoring path: `defineGame({ systems, loop?, … })` from `@jgengine/shell/defineGame`. Systems are meaningful capabilities — not micro-ticks.
4
+
5
+ ## Portable XP and leveling
6
+
7
+ `leveling` (`@jgengine/core/game/progression`) is usable without a scheduled
8
+ system or game context. Its `LevelingStatAccess` reads and replaces two
9
+ caller-named pools in an existing store; `grantXp` writes settled XP/threshold
10
+ and level state, then emits every reached level in ascending order. State stays
11
+ in the caller's save format and round-trips as ordinary JSON. See the
12
+ [portable XP/leveling recipe](recipes/portable-xp-leveling.md).
4
13
 
5
14
  ## API
6
15
 
@@ -50,10 +59,6 @@ Multiple systems may share a channel. Order is stage tables + optional `before`/
50
59
 
51
60
  Default stages: fixed `input → movement → combat → ai → activities → cleanup`; frame includes those plus `animation → camera → effects`.
52
61
 
53
- ## Cartridge path
54
-
55
- Cartridge remains a convenience wrapper. Express the same shape as ordinary systems (`runFlow`, wave director, combat) under `defineGame({ systems })` when you outgrow the cartridge schema — one authoring model, no rewrite cliff.
56
-
57
62
  ## Ownership
58
63
 
59
64
  - **Save / replicate** — system modules register via `ctx.game.registerSave` / `registerReplicate` at install.
@@ -11,8 +11,16 @@ This skill owns runtime spatial behavior: authored-scene consumption, environmen
11
11
 
12
12
  Search [capabilities.md](capabilities.md) by intent before designing a primitive. Use [api.md](api.md) for exact exports and [reference.md](reference.md) for environment, camera, navigation, physics, audio, and authored-scene recipes.
13
13
 
14
+ For a *connected* walkthrough that wires several primitives into a running loop, read the goal-oriented recipes under [recipes/](recipes/) — organized by composition seam (what connects to what), never by genre. Start there before reading a game's source to learn how pieces fit; a hybrid game is just a different composition of the same primitives.
15
+
16
+ - [recipes/commanding-entities.md](recipes/commanding-entities.md) — select → order → tick → resolve: direct command over a group of entities (the command loop behind squad control, base-defense, herding, autobattle, or a mutating game's real-time phase).
17
+
14
18
  ## Canonical workflows
15
19
 
20
+ ### Declare the place
21
+
22
+ A world is the place you play in: substrate + laws, via `world()` from `@jgengine/core/world/place` — `{ id, ground: { mode: flat|round|voxel|board, size, surface?, generator? }, physics? }`. Thin is complete: flat with `Infinity` axes for 3D, `board` for 2D, or omit `world` for pure UI/rules games. Never put seed, sky, or vegetation in a world definition — seeds derive from world id + save/run (`seedForPlace`); sky look and foliage scatter are editor-authored scene content, with the engine default sky when the document has none. `ground.surface` carries matter/feel laws (metal vs slime vs felt) that the same rule systems read — swap the surface, not the rules. See [reference.md](reference.md) for the full shape; `environment()` is the legacy consumption path for editor/preset-written scene data, never the starter.
23
+
16
24
  ### Authored world
17
25
 
18
26
  1. Load the scene document through the shared authored-scene feature.
@@ -28,8 +36,13 @@ Choose input intent, controller/motor, collision/navigation, and camera as separ
28
36
 
29
37
  Compose perception, selection, planning/behavior, movement, and lifecycle independently. Inject randomness and scheduling. Use spatial indexes, interest tiers, or bounded candidate sets for scale.
30
38
 
39
+ ### Fallback-seam diagnostics
40
+
41
+ Render seams fall back to placeholders when content is unauthored: default green ground (no environment), primitive capsule/box actors (no `entityModels`/`objectModels` mapping), stylized proxy foliage (no scatter `resolveItem`). These placeholders are sometimes intended, so runtime never changes — but a dev-only, opt-in probe (`@jgengine/core/devtools/fallbackSeams`) tells "unauthored/misconfigured" apart from "intended". The shell arms it via `armFallbackSeams(devtoolsEnabled)`; each seam reports at its choice site (`reportFallbackSeam`), and counts surface through the devtools `fallbacks` probe. Disarmed (production) it is a pure, allocation-free no-op.
42
+
31
43
  ## Traps
32
44
 
45
+ - Do not put seed, sky presets, or vegetation/scatter fields in a world definition — `world()` rejects them; dressing belongs to the editor's scene document.
33
46
  - Do not put UI layout, touch chrome, combat resolution, or asset acquisition here.
34
47
  - Do not duplicate editor coordinates in runtime state.
35
48
  - Public APIs accept semantic policies and caller data, not renderer mechanics or internal tuning rolls.