@jgengine/core 0.18.0 → 0.19.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 (318) hide show
  1. package/CHANGELOG.md +265 -2
  2. package/dist/ai/decisionGraph.d.ts +96 -0
  3. package/dist/ai/decisionGraph.js +212 -0
  4. package/dist/ai/groupAssist.d.ts +10 -0
  5. package/dist/ai/groupAssist.js +11 -0
  6. package/dist/ai/mobBrainGraph.d.ts +3 -0
  7. package/dist/ai/mobBrainGraph.js +25 -0
  8. package/dist/ai/pursuit.d.ts +1 -1
  9. package/dist/ai/pursuit.js +1 -1
  10. package/dist/ai/tacticalQueries.d.ts +30 -0
  11. package/dist/ai/tacticalQueries.js +45 -0
  12. package/dist/ai/threat.d.ts +8 -0
  13. package/dist/ai/threat.js +10 -0
  14. package/dist/anim/animGraph.d.ts +155 -0
  15. package/dist/anim/animGraph.js +414 -0
  16. package/dist/anim/boneTexture.d.ts +55 -0
  17. package/dist/anim/boneTexture.js +43 -0
  18. package/dist/anim/footPlacement.d.ts +57 -0
  19. package/dist/anim/footPlacement.js +67 -0
  20. package/dist/anim/ikSolver.d.ts +51 -0
  21. package/dist/anim/ikSolver.js +124 -0
  22. package/dist/anim/locomotionGraph.d.ts +35 -0
  23. package/dist/anim/locomotionGraph.js +62 -0
  24. package/dist/assets/spriteAtlas.d.ts +21 -0
  25. package/dist/assets/spriteAtlas.js +103 -0
  26. package/dist/audio/audioFalloff.d.ts +12 -0
  27. package/dist/audio/doppler.d.ts +21 -0
  28. package/dist/audio/doppler.js +32 -0
  29. package/dist/audio/engineLayers.d.ts +83 -0
  30. package/dist/audio/engineLayers.js +115 -0
  31. package/dist/combat/abilityKit.d.ts +1 -0
  32. package/dist/combat/abilityKit.js +1 -0
  33. package/dist/combat/death.d.ts +7 -0
  34. package/dist/combat/shotOrigin.d.ts +6 -0
  35. package/dist/combat/shotOrigin.js +6 -0
  36. package/dist/combat/weaponFeedback.d.ts +36 -0
  37. package/dist/combat/weaponFeedback.js +42 -0
  38. package/dist/combat/weaponHandling.d.ts +161 -0
  39. package/dist/combat/weaponHandling.js +202 -0
  40. package/dist/combat/weaponPresentation.d.ts +115 -0
  41. package/dist/combat/weaponPresentation.js +96 -0
  42. package/dist/devtools/warnOnce.d.ts +8 -0
  43. package/dist/devtools/warnOnce.js +18 -0
  44. package/dist/economy/currency.d.ts +16 -2
  45. package/dist/economy/currency.js +55 -14
  46. package/dist/economy/wallet.d.ts +5 -4
  47. package/dist/economy/wallet.js +8 -4
  48. package/dist/editor/commandApply.js +1 -0
  49. package/dist/editor/commands.d.ts +4 -1
  50. package/dist/editor/document.d.ts +2 -1
  51. package/dist/editor/document.js +92 -1
  52. package/dist/editor/environment.d.ts +3 -0
  53. package/dist/editor/environment.js +24 -0
  54. package/dist/editor/index.d.ts +4 -2
  55. package/dist/editor/index.js +1 -1
  56. package/dist/editor/types.d.ts +20 -0
  57. package/dist/faction/reputation.d.ts +6 -0
  58. package/dist/faction/reputation.js +11 -0
  59. package/dist/game/cameraConfig.d.ts +59 -1
  60. package/dist/game/chat.d.ts +22 -1
  61. package/dist/game/chat.js +48 -19
  62. package/dist/game/controlGate.d.ts +6 -0
  63. package/dist/game/controlGate.js +30 -1
  64. package/dist/game/defineGame.d.ts +10 -1
  65. package/dist/game/events.d.ts +14 -1
  66. package/dist/game/lootTable.d.ts +4 -0
  67. package/dist/game/lootTable.js +16 -0
  68. package/dist/game/pixelPerfect.d.ts +25 -0
  69. package/dist/game/pixelPerfect.js +29 -0
  70. package/dist/game/playableGame.d.ts +167 -6
  71. package/dist/game/systemRuntime.js +6 -1
  72. package/dist/game/toasts.d.ts +8 -0
  73. package/dist/game/toasts.js +7 -0
  74. package/dist/game/vfxInstance.d.ts +9 -0
  75. package/dist/game/vfxInstance.js +12 -1
  76. package/dist/game/viewports.d.ts +43 -0
  77. package/dist/game/viewports.js +46 -0
  78. package/dist/game/worldItem.d.ts +5 -1
  79. package/dist/game/worldItem.js +0 -1
  80. package/dist/input/actionBindings.d.ts +17 -2
  81. package/dist/input/actionBindings.js +24 -11
  82. package/dist/input/actionContexts.d.ts +45 -0
  83. package/dist/input/actionContexts.js +113 -0
  84. package/dist/input/axisInput.d.ts +4 -0
  85. package/dist/input/axisInput.js +4 -0
  86. package/dist/input/axisShaper.d.ts +73 -0
  87. package/dist/input/axisShaper.js +105 -0
  88. package/dist/input/gamepadModel.d.ts +79 -0
  89. package/dist/input/gamepadModel.js +108 -0
  90. package/dist/input/haptics.d.ts +50 -0
  91. package/dist/input/haptics.js +74 -0
  92. package/dist/input/inputBuffer.d.ts +40 -0
  93. package/dist/input/inputBuffer.js +59 -0
  94. package/dist/input/lookChannel.d.ts +10 -0
  95. package/dist/input/lookChannel.js +6 -0
  96. package/dist/input/pointer.d.ts +2 -0
  97. package/dist/input/pointer.js +4 -0
  98. package/dist/item/affix.d.ts +6 -0
  99. package/dist/item/affix.js +6 -0
  100. package/dist/meta/changelog.d.ts +1 -1
  101. package/dist/meta/changelog.js +196 -1
  102. package/dist/movement/characterController.d.ts +76 -0
  103. package/dist/movement/characterController.js +216 -0
  104. package/dist/movement/freeFlight.d.ts +119 -0
  105. package/dist/movement/freeFlight.js +294 -0
  106. package/dist/movement/glideModel.d.ts +3 -0
  107. package/dist/movement/glideModel.js +6 -0
  108. package/dist/movement/grappleSwing.d.ts +2 -0
  109. package/dist/movement/grappleSwing.js +5 -1
  110. package/dist/movement/movementModel.d.ts +39 -0
  111. package/dist/movement/movementModel.js +62 -19
  112. package/dist/movement/movementProbe.d.ts +51 -0
  113. package/dist/movement/movementProbe.js +119 -0
  114. package/dist/movement/playerMovement.d.ts +30 -3
  115. package/dist/movement/playerMovement.js +304 -7
  116. package/dist/movement/serverStep.d.ts +5 -0
  117. package/dist/movement/serverStep.js +6 -0
  118. package/dist/movement/solidObstacles.d.ts +39 -8
  119. package/dist/movement/solidObstacles.js +71 -6
  120. package/dist/movement/voxelController.js +7 -7
  121. package/dist/multiplayer/poseSyncGate.d.ts +3 -1
  122. package/dist/multiplayer/poseSyncGate.js +9 -1
  123. package/dist/multiplayer/presenceContract.d.ts +6 -0
  124. package/dist/multiplayer/presenceModel.d.ts +2 -0
  125. package/dist/multiplayer/presenceModel.js +11 -0
  126. package/dist/nav/navFromEnvironment.d.ts +9 -3
  127. package/dist/nav/navFromEnvironment.js +18 -12
  128. package/dist/nav/navMesh.d.ts +74 -0
  129. package/dist/nav/navMesh.js +570 -0
  130. package/dist/physics/aircraftDynamics.d.ts +390 -0
  131. package/dist/physics/aircraftDynamics.js +704 -0
  132. package/dist/physics/boatDynamics.d.ts +116 -0
  133. package/dist/physics/boatDynamics.js +160 -0
  134. package/dist/physics/drivableVehicle.d.ts +22 -8
  135. package/dist/physics/drivableVehicle.js +4 -2
  136. package/dist/physics/flightDynamics.d.ts +29 -4
  137. package/dist/physics/flightDynamics.js +28 -5
  138. package/dist/physics/handlingProbe.d.ts +249 -0
  139. package/dist/physics/handlingProbe.js +606 -0
  140. package/dist/physics/kinematicVehicle.d.ts +37 -2
  141. package/dist/physics/kinematicVehicle.js +15 -0
  142. package/dist/physics/physicsBackend.d.ts +203 -0
  143. package/dist/physics/physicsBackend.js +49 -0
  144. package/dist/physics/physicsBackendConformance.d.ts +27 -0
  145. package/dist/physics/physicsBackendConformance.js +151 -0
  146. package/dist/physics/physicsWorldBackend.d.ts +45 -0
  147. package/dist/physics/physicsWorldBackend.js +518 -0
  148. package/dist/physics/vehicleBackendLink.d.ts +49 -0
  149. package/dist/physics/vehicleBackendLink.js +102 -0
  150. package/dist/physics/vehicleDynamics.d.ts +374 -0
  151. package/dist/physics/vehicleDynamics.js +737 -0
  152. package/dist/physics/worldColliders.d.ts +13 -0
  153. package/dist/physics/worldColliders.js +128 -0
  154. package/dist/puzzle/cellGrid.d.ts +15 -9
  155. package/dist/puzzle/cellGrid.js +13 -9
  156. package/dist/random/rng.d.ts +12 -1
  157. package/dist/random/rng.js +20 -3
  158. package/dist/render/environment.d.ts +23 -0
  159. package/dist/render/environment.js +1 -0
  160. package/dist/render/lookPreset.d.ts +19 -6
  161. package/dist/render/lookPreset.js +56 -4
  162. package/dist/render/postProcessing.d.ts +40 -1
  163. package/dist/render/postProcessing.js +1 -1
  164. package/dist/render/sprite2d.d.ts +38 -0
  165. package/dist/render/sprite2d.js +40 -0
  166. package/dist/runtime/adapter.d.ts +20 -1
  167. package/dist/runtime/adapter.js +8 -1
  168. package/dist/runtime/cameraDirector.d.ts +17 -3
  169. package/dist/runtime/cameraDirector.js +25 -0
  170. package/dist/runtime/commandInput.d.ts +52 -0
  171. package/dist/runtime/commandInput.js +71 -0
  172. package/dist/runtime/commandRunner.d.ts +39 -7
  173. package/dist/runtime/commandRunner.js +35 -5
  174. package/dist/runtime/context/combat.d.ts +1 -0
  175. package/dist/runtime/context/combat.js +2 -1
  176. package/dist/runtime/context/deathLoot.d.ts +2 -0
  177. package/dist/runtime/context/deathLoot.js +1 -0
  178. package/dist/runtime/gameContext.js +18 -1
  179. package/dist/runtime/gameContextTypes.d.ts +33 -7
  180. package/dist/runtime/gameRuntime.d.ts +23 -5
  181. package/dist/runtime/gameRuntime.js +21 -4
  182. package/dist/runtime/headlessRunner.d.ts +10 -0
  183. package/dist/runtime/headlessRunner.js +26 -8
  184. package/dist/runtime/hostPolicy.d.ts +3 -2
  185. package/dist/runtime/hostPolicy.js +8 -10
  186. package/dist/runtime/hostedGameRunner.js +34 -6
  187. package/dist/runtime/hostedWorldSession.d.ts +12 -16
  188. package/dist/runtime/hostedWorldSession.js +29 -6
  189. package/dist/runtime/hostedWorldStore.d.ts +20 -0
  190. package/dist/runtime/hostedWorldStore.js +1 -0
  191. package/dist/runtime/inputRecorder.d.ts +30 -0
  192. package/dist/runtime/inputRecorder.js +59 -0
  193. package/dist/runtime/inputSnapshot.d.ts +14 -0
  194. package/dist/runtime/inputSnapshot.js +11 -0
  195. package/dist/runtime/localPlayers.d.ts +73 -0
  196. package/dist/runtime/localPlayers.js +119 -0
  197. package/dist/runtime/poseInterpolation.d.ts +36 -0
  198. package/dist/runtime/poseInterpolation.js +91 -0
  199. package/dist/runtime/prediction.d.ts +45 -0
  200. package/dist/runtime/prediction.js +62 -0
  201. package/dist/runtime/simContext.d.ts +45 -0
  202. package/dist/runtime/simContext.js +65 -0
  203. package/dist/runtime/simLoop.d.ts +52 -0
  204. package/dist/runtime/simLoop.js +72 -0
  205. package/dist/runtime/simSnapshot.d.ts +32 -0
  206. package/dist/runtime/simSnapshot.js +60 -0
  207. package/dist/runtime/snapshot.d.ts +8 -0
  208. package/dist/runtime/snapshotBuffer.d.ts +33 -0
  209. package/dist/runtime/snapshotBuffer.js +58 -0
  210. package/dist/runtime/transport.d.ts +41 -2
  211. package/dist/runtime/transport.js +7 -1
  212. package/dist/runtime/worldChunks.d.ts +2 -0
  213. package/dist/runtime/worldChunks.js +14 -0
  214. package/dist/runtime/worldProjection.d.ts +9 -0
  215. package/dist/runtime/worldProjection.js +17 -1
  216. package/dist/runtime/worldSnapshot.d.ts +6 -0
  217. package/dist/runtime/worldSnapshot.js +3 -0
  218. package/dist/scene/behaviorRuntime.d.ts +29 -1
  219. package/dist/scene/behaviorRuntime.js +71 -4
  220. package/dist/scene/behaviors.d.ts +12 -1
  221. package/dist/scene/builtinSceneKinds.js +10 -0
  222. package/dist/scene/sceneKinds.d.ts +9 -0
  223. package/dist/scene/vehicleSeat.d.ts +5 -1
  224. package/dist/scene/vehicleSeat.js +5 -1
  225. package/dist/sensor/concealment.d.ts +6 -0
  226. package/dist/sensor/concealment.js +8 -0
  227. package/dist/sensor/freezeMonitor.d.ts +6 -0
  228. package/dist/sensor/freezeMonitor.js +8 -0
  229. package/dist/sensor/perception.d.ts +70 -0
  230. package/dist/sensor/perception.js +189 -0
  231. package/dist/sensor/recordingBuffer.d.ts +3 -0
  232. package/dist/sensor/recordingBuffer.js +8 -0
  233. package/dist/session/roundState.d.ts +4 -0
  234. package/dist/session/roundState.js +11 -0
  235. package/dist/settings/frameRateLimit.d.ts +16 -0
  236. package/dist/settings/frameRateLimit.js +37 -0
  237. package/dist/settings/graphicsProfile.d.ts +21 -0
  238. package/dist/settings/graphicsProfile.js +48 -0
  239. package/dist/settings/graphicsSettings.d.ts +40 -0
  240. package/dist/settings/graphicsSettings.js +81 -0
  241. package/dist/settings/settingsModel.d.ts +9 -1
  242. package/dist/settings/settingsModel.js +10 -0
  243. package/dist/stats/eventMeter.d.ts +1 -1
  244. package/dist/stats/eventMeter.js +1 -1
  245. package/dist/time/accrueSince.d.ts +10 -0
  246. package/dist/time/accrueSince.js +9 -0
  247. package/dist/time/rateWindow.d.ts +16 -0
  248. package/dist/time/rateWindow.js +14 -0
  249. package/dist/time/serverTick.d.ts +21 -3
  250. package/dist/time/serverTick.js +14 -13
  251. package/dist/vfx/feedbackMixer.d.ts +61 -0
  252. package/dist/vfx/feedbackMixer.js +106 -0
  253. package/dist/vfx/particleDirector.d.ts +68 -0
  254. package/dist/vfx/particleDirector.js +81 -0
  255. package/dist/visibility/simulationCulling.d.ts +8 -1
  256. package/dist/visibility/simulationCulling.js +7 -0
  257. package/dist/visibility/spatialIndex.d.ts +21 -0
  258. package/dist/visibility/spatialIndex.js +16 -0
  259. package/dist/world/authoredSolids.d.ts +46 -0
  260. package/dist/world/authoredSolids.js +75 -0
  261. package/dist/world/buildingGenerator.js +2 -2
  262. package/dist/world/buildingKit.d.ts +3 -1
  263. package/dist/world/buildings.d.ts +46 -2
  264. package/dist/world/buildings.js +53 -3
  265. package/dist/world/cityContent.js +103 -25
  266. package/dist/world/cityKind.d.ts +139 -0
  267. package/dist/world/cityKind.js +367 -16
  268. package/dist/world/features.d.ts +56 -3
  269. package/dist/world/features.js +13 -2
  270. package/dist/world/footprintGrid.d.ts +7 -0
  271. package/dist/world/footprintGrid.js +16 -0
  272. package/dist/world/interiors.d.ts +1 -0
  273. package/dist/world/interiors.js +5 -3
  274. package/dist/world/lod.d.ts +6 -0
  275. package/dist/world/lod.js +8 -0
  276. package/dist/world/placement.d.ts +7 -0
  277. package/dist/world/placement.js +2 -0
  278. package/dist/world/placementController.d.ts +1 -1
  279. package/dist/world/placementController.js +7 -0
  280. package/dist/world/raceCircuit.d.ts +168 -0
  281. package/dist/world/raceCircuit.js +797 -0
  282. package/dist/world/terraform.d.ts +8 -0
  283. package/dist/world/terraform.js +7 -0
  284. package/dist/world/terrain.d.ts +5 -1
  285. package/dist/world/terrain.js +5 -0
  286. package/dist/world/territory.d.ts +97 -0
  287. package/dist/world/territory.js +182 -0
  288. package/dist/world/walls.d.ts +7 -0
  289. package/dist/world/walls.js +7 -0
  290. package/dist/world/worldSolids.d.ts +84 -0
  291. package/dist/world/worldSolids.js +240 -0
  292. package/dist/world.d.ts +5 -3
  293. package/dist/world.js +4 -2
  294. package/package.json +1 -1
  295. package/skills/ce-handoff/SKILL.md +2 -2
  296. package/skills/game-design/SKILL.md +11 -1
  297. package/skills/jgengine/SKILL.md +9 -5
  298. package/skills/jgengine/api.md +205 -101
  299. package/skills/jgengine/capabilities.md +56 -0
  300. package/skills/jgengine/recipes/minimal-game.md +3 -0
  301. package/skills/jgengine-combat/SKILL.md +5 -0
  302. package/skills/jgengine-combat/api.md +138 -103
  303. package/skills/jgengine-combat/capabilities.md +29 -1
  304. package/skills/jgengine-combat/recipes/weapon-feel.md +50 -0
  305. package/skills/jgengine-gameplay/SKILL.md +10 -0
  306. package/skills/jgengine-gameplay/api.md +501 -453
  307. package/skills/jgengine-gameplay/capabilities.md +36 -0
  308. package/skills/jgengine-gameplay/reference-systems.md +4 -0
  309. package/skills/jgengine-world/SKILL.md +21 -3
  310. package/skills/jgengine-world/api.md +1172 -625
  311. package/skills/jgengine-world/capabilities.md +229 -3
  312. package/skills/jgengine-world/recipes/character-animation.md +39 -0
  313. package/skills/jgengine-world/recipes/controls.md +44 -0
  314. package/skills/jgengine-world/recipes/flight-feel.md +76 -0
  315. package/skills/jgengine-world/recipes/npc-senses.md +43 -0
  316. package/skills/jgengine-world/recipes/vehicle-feel.md +85 -0
  317. package/skills/jgengine-world/reference.md +31 -2
  318. package/skills/jgengine-world/visibility.md +112 -0
@@ -4,6 +4,14 @@
4
4
 
5
5
  Reach for these before hand-rolling. Each row is *the thing you need* → *the primitive that already does it*.
6
6
 
7
+ ## action-contexts — Swap bindings live for on-foot, driving, menu or build modes by pushing and popping layered action maps.
8
+
9
+ - `createActionContextStack` (function) · `import { createActionContextStack } from "@jgengine/core/input/actionContexts"`
10
+
11
+ ## ai-decision-graph — Evaluate serializable selector, sequence, condition, action, utility, timer, and random AI decisions with interrupt hooks.
12
+
13
+ - `createDecisionGraphRuntime` (function) · `import { createDecisionGraphRuntime } from "@jgengine/core/ai/decisionGraph"`
14
+
7
15
  ## ai-driver — difficulty-aware chase/route driving step producing throttle/brake/steer for the vehicle sim
8
16
 
9
17
  - `driveStep` (function) · `import { driveStep } from "@jgengine/core/ai/driver"`
@@ -16,6 +24,25 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
16
24
 
17
25
  - `createDriverState` (function) · `import { createDriverState } from "@jgengine/core/ai/driver"`
18
26
 
27
+ ## air-metrics — measure a vehicle's jumps and air control — apex height and time, double jump, air rotation rates
28
+
29
+ - `measureAir` (function) · `import { measureAir } from "@jgengine/core/physics/handlingProbe"`
30
+
31
+ ## anim-graph — data-first animation state machine with blend trees, crossfades, layers, and clip events
32
+
33
+ - `createAnimGraphRuntime` (function) · `import { createAnimGraphRuntime } from "@jgengine/core/anim/animGraph"`
34
+ - `parseAnimGraph` (function) · `import { parseAnimGraph } from "@jgengine/core/anim/animGraph"`
35
+
36
+ ## animation — find thigh, shin and foot bones on a humanoid rig for foot IK
37
+
38
+ - `inferLegChains` (function) · `import { inferLegChains } from "@jgengine/core/anim/footPlacement"`
39
+ - `lookAt` (function) · `import { lookAt } from "@jgengine/core/anim/ikSolver"`
40
+ - `placeFeet` (function) · `import { placeFeet } from "@jgengine/core/anim/footPlacement"`
41
+ - `planBoneTexture` (function) · `import { planBoneTexture } from "@jgengine/core/anim/boneTexture"`
42
+ - `sampleBoneTexture` (function) · `import { sampleBoneTexture } from "@jgengine/core/anim/boneTexture"`
43
+ - `solveFabrik` (function) · `import { solveFabrik } from "@jgengine/core/anim/ikSolver"`
44
+ - `solveTwoBone` (function) · `import { solveTwoBone } from "@jgengine/core/anim/ikSolver"`
45
+
19
46
  ## area-effect-field — source-following area membership with enter/refresh/leave edges, stacking, and cleanup
20
47
 
21
48
  - `createAreaEffectField` (function) · `import { createAreaEffectField } from "@jgengine/core/world"`
@@ -87,6 +114,11 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
87
114
  - `placeAuthoredObjects` (function) · `import { placeAuthoredObjects } from "@jgengine/core/world"`
88
115
  - `resolveAuthoredObjects` (function) · `import { resolveAuthoredObjects } from "@jgengine/core/world"`
89
116
 
117
+ ## authored-solids — collision for studio-authored world content such as city volumes
118
+
119
+ - `resolveAuthoredSolids` (function) · `import { resolveAuthoredSolids } from "@jgengine/core/world/authoredSolids"`
120
+ - `syncAuthoredSolids` (function) · `import { syncAuthoredSolids } from "@jgengine/core/world/authoredSolids"`
121
+
90
122
  ## authored-spawn — read spawn points and markers from the editor document
91
123
 
92
124
  - `authoredSpawnPosition` (function) · `import { authoredSpawnPosition } from "@jgengine/core/world/authoredSpawn"`
@@ -102,6 +134,14 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
102
134
  - `registerBuiltinTriggerActions` (function) · `import { registerBuiltinTriggerActions } from "@jgengine/core/scene/authoredTriggers"`
103
135
  - `registerTriggerAction` (function) · `import { registerTriggerAction } from "@jgengine/core/world"`
104
136
 
137
+ ## axis-shaping — shape raw input per axis — deadzone, response curve, keyboard ramp, self-centring, speed-sensitive scaling
138
+
139
+ - `createAxisShaper` (function) · `import { createAxisShaper } from "@jgengine/core/input/axisShaper"`
140
+
141
+ ## behavior-actions — register named callbacks for decision graph behavior descriptors
142
+
143
+ - `registerBehaviorActions` (function) · `import { registerBehaviorActions } from "@jgengine/core/world"`
144
+
105
145
  ## behavior-control — pause/disable/resume/seek/inspect behavior instances per entity
106
146
 
107
147
  - `behaviorControl` (function) · `import { behaviorControl } from "@jgengine/core/world"`
@@ -110,6 +150,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
110
150
 
111
151
  - `advanceBehaviors` (function) · `import { advanceBehaviors } from "@jgengine/core/world"`
112
152
 
153
+ ## boat-dynamics — force-based powered hull — hull-speed drag hump, planing, keel, rudder that needs flow or outboard thrust
154
+
155
+ - `createBoatDynamics` (function) · `import { createBoatDynamics } from "@jgengine/core/physics/boatDynamics"`
156
+
113
157
  ## body-bind — mirror sim-body snapshots onto scene entities each tick, no per-body setPose
114
158
 
115
159
  - `createBodyBind` (function) · `import { createBodyBind } from "@jgengine/core/world"`
@@ -142,9 +186,14 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
142
186
 
143
187
  - `createCameraShake` (function) · `import { createCameraShake } from "@jgengine/core/world"`
144
188
 
189
+ ## character-controller — capsule walk over the physics backend — slide, step-up, slopes, crouch, platforms
190
+
191
+ - `createCharacterController` (function) · `import { createCharacterController } from "@jgengine/core/movement/characterController"`
192
+
145
193
  ## city-district — polygon math for road-derived blocks, parcels, and buildable footprints
146
194
 
147
195
  - `Vec2` (type) · `import { Vec2 } from "@jgengine/core/world/cityGeometry"`
196
+ - `citySolids` (function) · `import { citySolids } from "@jgengine/core/world/cityKind"`
148
197
  - `extractGraphBlocks` (function) · `import { extractGraphBlocks } from "@jgengine/core/world/cityBlocks"`
149
198
  - `rectClearsPolyline` (function) · `import { rectClearsPolyline } from "@jgengine/core/world/cityGeometry"`
150
199
  - `rectsSeparated` (function) · `import { rectsSeparated } from "@jgengine/core/world/cityGeometry"`
@@ -171,6 +220,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
171
220
 
172
221
  - `createTimerSet` (function) · `import { createTimerSet } from "@jgengine/core/world"`
173
222
 
223
+ ## course-metrics — measure a vehicle on driven tests — understeer gradient, skidpad g, slalom speed
224
+
225
+ - `measureCourse` (function) · `import { measureCourse } from "@jgengine/core/physics/handlingProbe"`
226
+
174
227
  ## crouch-prone — stance/pose transitions that change the hitbox
175
228
 
176
229
  - `createPoseState` (function) · `import { createPoseState } from "@jgengine/core/world"`
@@ -219,6 +272,14 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
219
272
 
220
273
  - `DIFFICULTY_TIERS` (const) · `import { DIFFICULTY_TIERS } from "@jgengine/core/ai/difficulty"`
221
274
 
275
+ ## doppler-pitch — pitch a moving sound up as it approaches and down as it passes, from listener and emitter velocity
276
+
277
+ - `dopplerRate` (function) · `import { dopplerRate } from "@jgengine/core/audio/doppler"`
278
+
279
+ ## drivable-vehicle — drive a car from throttle/steer/handbrake input each tick: step the vehicle sim and pose its entity
280
+
281
+ - `tickDrivableVehicle` (function) · `import { tickDrivableVehicle } from "@jgengine/core/world"`
282
+
222
283
  ## elevation-readout — cursor height and delta-from-reference feedback
223
284
 
224
285
  - `sampleElevation` (function) · `import { sampleElevation } from "@jgengine/core/world"`
@@ -227,6 +288,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
227
288
 
228
289
  - `summarizeElevation` (function) · `import { summarizeElevation } from "@jgengine/core/world"`
229
290
 
291
+ ## engine-layers — layered engine audio: rpm-keyed samples crossfaded, with on-load and off-load sets picked by engine load
292
+
293
+ - `createEngineLayers` (function) · `import { createEngineLayers } from "@jgengine/core/audio/engineLayers"`
294
+
230
295
  ## entity-meta — cast-free narrow of SceneEntity.meta via a type guard
231
296
 
232
297
  - `entityMetaOf` (function) · `import { entityMetaOf } from "@jgengine/core/world"`
@@ -235,9 +300,17 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
235
300
 
236
301
  - `createFastTravelNetwork` (function) · `import { createFastTravelNetwork } from "@jgengine/core/world"`
237
302
 
238
- ## flight-dynamics — simulate fixed-wing, helicopter, and VTOL aircraft
303
+ ## feedback-mixer — map sim telemetry to camera, audio and haptic parameters through curves, smoothing and threshold events
304
+
305
+ - `createFeedbackMixer` (function) · `import { createFeedbackMixer } from "@jgengine/core/vfx/feedbackMixer"`
306
+
307
+ ## 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
308
+
309
+ - `ActionCodesMap` (type) · `import { ActionCodesMap } from "@jgengine/core/input/actionBindings"`
239
310
 
240
- - `createAircraftDynamics` (function) · `import { createAircraftDynamics } from "@jgengine/core/world"`
311
+ ## flight-metrics — measure an aircraft's feel as numbers — roll rate, sustained turn, stall speed, climb, throttle response, hover drift
312
+
313
+ - `measureFlight` (function) · `import { measureFlight } from "@jgengine/core/physics/handlingProbe"`
241
314
 
242
315
  ## follow-trail — trailing follower/snake formation that chases a leader
243
316
 
@@ -279,6 +352,14 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
279
352
 
280
353
  - `DEFAULT_FORWARD` (const) · `import { DEFAULT_FORWARD } from "@jgengine/core/world"`
281
354
 
355
+ ## free-flight — camera-relative creative/spectator/noclip/hover flight with correct strafe, sprint, and optional gravity
356
+
357
+ - `createFreeFlightController` (function) · `import { createFreeFlightController } from "@jgengine/core/world"`
358
+
359
+ ## gamepad-input — Resolve sampled gamepad input into actions with deadzones and response curves.
360
+
361
+ - `resolveGamepadFrame` (function) · `import { resolveGamepadFrame } from "@jgengine/core/input/gamepadModel"`
362
+
282
363
  ## glide — gliding/wingsuit descent control from a launch
283
364
 
284
365
  - `createGlideModel` (function) · `import { createGlideModel } from "@jgengine/core/world"`
@@ -297,10 +378,22 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
297
378
 
298
379
  - `grounded` (function) · `import { grounded } from "@jgengine/core/world"`
299
380
 
381
+ ## handling-metrics — measure a vehicle's feel as numbers — 0–100, braking, lateral g, turn-in, sideslip, spin
382
+
383
+ - `measureHandling` (function) · `import { measureHandling } from "@jgengine/core/physics/handlingProbe"`
384
+
385
+ ## haptics — Continuous gamepad rumble from named channels (engine, road, impact) mixed by priority, plus fading pulses.
386
+
387
+ - `createHapticChannels` (function) · `import { createHapticChannels } from "@jgengine/core/input/haptics"`
388
+
300
389
  ## hold-order — stand-ground order that holds position until preempted or canceled
301
390
 
302
391
  - `defineHoldOrder` (function) · `import { defineHoldOrder } from "@jgengine/core/world"`
303
392
 
393
+ ## input-buffer — buffer early presses, coyote time, hold duration and double tap for responsive controls
394
+
395
+ - `createInputBuffer` (function) · `import { createInputBuffer } from "@jgengine/core/input/inputBuffer"`
396
+
304
397
  ## interaction-prompt — observable nearest-interactable prompt registry over the proximity-prompt resolver — register/resolve/active with priority+range and change-notified snapshot/restore
305
398
 
306
399
  - `createPromptRegistry` (function) · `import { createPromptRegistry } from "@jgengine/core/world"`
@@ -325,6 +418,14 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
325
418
 
326
419
  - `createRebindSession` (function) · `import { createRebindSession } from "@jgengine/core/input/rebindSession"`
327
420
 
421
+ ## kinematic-vehicle — arcade car/kart driving physics with throttle, brake, steer and retunable handling
422
+
423
+ - `createKinematicVehicle` (function) · `import { createKinematicVehicle } from "@jgengine/core/world"`
424
+
425
+ ## lean-metrics — measure a motorcycle's lean — steady lean, time to lean, countersteer tip-in
426
+
427
+ - `measureLean` (function) · `import { measureLean } from "@jgengine/core/physics/handlingProbe"`
428
+
328
429
  ## limb-health — per-body-part/region health tracked separately
329
430
 
330
431
  - `createMultiRegionHealth` (function) · `import { createMultiRegionHealth } from "@jgengine/core/procedural"`
@@ -337,6 +438,11 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
337
438
 
338
439
  - `generateLock` (function) · `import { generateLock } from "@jgengine/core/world"`
339
440
 
441
+ ## locomotion-graph — resolve the animation graph a model config plays
442
+
443
+ - `animGraphFromConfig` (function) · `import { animGraphFromConfig } from "@jgengine/core/anim/locomotionGraph"`
444
+ - `locomotionGraph` (function) · `import { locomotionGraph } from "@jgengine/core/anim/locomotionGraph"`
445
+
340
446
  ## map-annotations — player-drawn map annotation layer — freehand strokes, area shapes, and pinned notes, projected into the map's routes/zones props and serializable
341
447
 
342
448
  - `createAnnotationLayer` (function) · `import { createAnnotationLayer } from "@jgengine/core/world"`
@@ -366,6 +472,22 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
366
472
 
367
473
  - `defineMoveOrder` (function) · `import { defineMoveOrder } from "@jgengine/core/world"`
368
474
 
475
+ ## movement-metrics — measure a walking character's feel as numbers — time to top speed, stop distance, jump height and apex, air control, turn-around
476
+
477
+ - `measureMovement` (function) · `import { measureMovement } from "@jgengine/core/movement/movementProbe"`
478
+
479
+ ## nav-from-solids — block world solids on a nav grid
480
+
481
+ - `populateNavGridFromSolids` (function) · `import { populateNavGridFromSolids } from "@jgengine/core/nav/navFromEnvironment"`
482
+
483
+ ## navmesh-bake — voxelize scene triangles into an agent-sized polygon nav mesh
484
+
485
+ - `bakeNavMesh` (function) · `import { bakeNavMesh } from "@jgengine/navbake"`
486
+
487
+ ## navmesh-query — route, snap, and raycast on a polygon navmesh with retunable area costs and a bounded search
488
+
489
+ - `createNavMeshQuery` (function) · `import { createNavMeshQuery } from "@jgengine/core/world"`
490
+
369
491
  ## object-slot-inventory — placed objects hold validated per-instance container contents that replicate and save with the placement
370
492
 
371
493
  - `SceneObjectSlots` (interface) · `import { SceneObjectSlots } from "@jgengine/core/world"`
@@ -378,6 +500,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
378
500
 
379
501
  - `createOrderRegistry` (function) · `import { createOrderRegistry } from "@jgengine/core/world"`
380
502
 
503
+ ## particle-director — game-reachable VFX seam: queue one-shot particle bursts and keyed follow-capable standing emitters as serializable data for the shell to render
504
+
505
+ - `createParticleDirector` (function) · `import { createParticleDirector } from "@jgengine/core/vfx/particleDirector"`
506
+
381
507
  ## particle-system — deterministic pooled particle emitter with SoA render buffers, burst/continuous emission, gravity/drag, size/color/alpha-over-life, and serializable state
382
508
 
383
509
  - `createParticleSystem` (function) · `import { createParticleSystem } from "@jgengine/core/world"`
@@ -403,6 +529,22 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
403
529
 
404
530
  - `definePatrolOrder` (function) · `import { definePatrolOrder } from "@jgengine/core/world"`
405
531
 
532
+ ## perception-service — retain sight, sound, and damage observations with range, cone, occlusion, and decay policy
533
+
534
+ - `createPerception` (function) · `import { createPerception } from "@jgengine/core/sensor/perception"`
535
+
536
+ ## physics-backend — one interface over interchangeable rigid-body solvers — bodies, joints, raycast, shapecast, snapshot
537
+
538
+ - `PhysicsBackend` (interface) · `import { PhysicsBackend } from "@jgengine/core/physics/physicsBackend"`
539
+
540
+ ## physics-backend-conformance — the shared test contract a physics adapter runs to prove it matches the seam
541
+
542
+ - `runPhysicsBackendConformance` (function) · `import { runPhysicsBackendConformance } from "@jgengine/core/physics/physicsBackendConformance"`
543
+
544
+ ## physics-world-backend — the zero-dependency PhysicsWorld solver exposed through the PhysicsBackend seam
545
+
546
+ - `createPhysicsWorldBackend` (function) · `import { createPhysicsWorldBackend } from "@jgengine/core/physics/physicsWorldBackend"`
547
+
406
548
  ## piece-sockets-from-model — derive build-piece snap sockets from a model's named attach points
407
549
 
408
550
  - `pieceSocketsFromModel` (function) · `import { pieceSocketsFromModel } from "@jgengine/core/world/buildSockets"`
@@ -443,7 +585,7 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
443
585
 
444
586
  - `resolveActivePrompt` (function) · `import { resolveActivePrompt } from "@jgengine/core/world"`
445
587
 
446
- ## pursuit — chase-to-reach cooldown-gated attack with optional leash-to-home; owns the attack cooldown
588
+ ## pursuit — enemy/mob aggro chase-to-reach with a cooldown-gated attack and optional leash-to-home; owns the attack cooldown
447
589
 
448
590
  - `advancePursuit` (function) · `import { advancePursuit } from "@jgengine/core/ai/pursuit"`
449
591
 
@@ -451,6 +593,14 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
451
593
 
452
594
  - `evaluateQteSequence` (function) · `import { evaluateQteSequence } from "@jgengine/core/world"`
453
595
 
596
+ ## rapier-physics-backend — native capsules, shapecasts, rotation, CCD, joints, and collision queries
597
+
598
+ - `createRapierBackend` (function) · `import { createRapierBackend } from "@jgengine/rapier"`
599
+
600
+ ## rate-window-policy — Evaluate deterministic request windows with retry timing.
601
+
602
+ - `decideRateWindow` (function) · `import { decideRateWindow } from "@jgengine/core/time/rateWindow"`
603
+
454
604
  ## reputation — faction standing that crosses named reputation tiers
455
605
 
456
606
  - `tierForStanding` (function) · `import { tierForStanding } from "@jgengine/core/world"`
@@ -459,6 +609,14 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
459
609
 
460
610
  - `createResourceNodeField` (function) · `import { createResourceNodeField } from "@jgengine/core/world/resourceNode"`
461
611
 
612
+ ## ride-metrics — measure a sprung vehicle's ride — settle time, dive, roll per g, landing bounce
613
+
614
+ - `measureRide` (function) · `import { measureRide } from "@jgengine/core/physics/handlingProbe"`
615
+
616
+ ## rigid-aircraft — force-and-torque aircraft — lifting surfaces with stall and drag polar, control surfaces, inertia, loops and rolls
617
+
618
+ - `createRigidAircraft` (function) · `import { createRigidAircraft } from "@jgengine/core/physics/aircraftDynamics"`
619
+
462
620
  ## rts-control-groups — map Ctrl+digit / digit / double-tap presses onto selection-bookmark bind / recall / focus intents
463
621
 
464
622
  - `controlGroupKey` (function) · `import { controlGroupKey } from "@jgengine/core/world"`
@@ -498,6 +656,7 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
498
656
 
499
657
  ## solid-obstacles — the one blocking-geometry query the player resolver and NPC movers share
500
658
 
659
+ - `obstacleFromSolid` (function) · `import { obstacleFromSolid } from "@jgengine/core/movement/solidObstacles"`
501
660
  - `solidObstaclesNear` (function) · `import { solidObstaclesNear } from "@jgengine/core/movement/solidObstacles"`
502
661
 
503
662
  ## spatial-region-box — sample uniformly within an axis-aligned 3D box
@@ -560,6 +719,14 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
560
719
 
561
720
  - `surfaceRing` (function) · `import { surfaceRing } from "@jgengine/core/world"`
562
721
 
722
+ ## tactical-cover-points — derive cover candidates from navigation-mesh boundaries
723
+
724
+ - `coverPoints` (function) · `import { coverPoints } from "@jgengine/core/ai/tacticalQueries"`
725
+
726
+ ## tactical-position-score — rank bounded tactical positions with caller-defined weights
727
+
728
+ - `scorePositions` (function) · `import { scorePositions } from "@jgengine/core/ai/tacticalQueries"`
729
+
563
730
  ## target-acquirer — stateful held-target wrapper over an acquisition policy with retention
564
731
 
565
732
  - `createTargetAcquirer` (function) · `import { createTargetAcquirer } from "@jgengine/core/world"`
@@ -584,11 +751,47 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
584
751
 
585
752
  - `raycastHeightField` (function) · `import { raycastHeightField } from "@jgengine/core/world/terrain"`
586
753
 
754
+ ## territory-cell-chunk — Map ownership cells to persisted world chunks.
755
+
756
+ - `territoryChunkKey` (function) · `import { territoryChunkKey } from "@jgengine/core/world/territory"`
757
+
758
+ ## territory-footprint — Resolve exactly the ownership cells touched by a placement footprint.
759
+
760
+ - `territoryFootprintCells` (function) · `import { territoryFootprintCells } from "@jgengine/core/world/territory"`
761
+
762
+ ## territory-owner-query — Read a loaded land cell owner for authoritative placement checks.
763
+
764
+ - `territoryOwnerOf` (function) · `import { territoryOwnerOf } from "@jgengine/core/world/territory"`
765
+
766
+ ## territory-ownership — Claim, release and query land through host-owned snapshot storage.
767
+
768
+ - `createTerritory` (function) · `import { createTerritory } from "@jgengine/core/world/territory"`
769
+
770
+ ## territory-placement — Purchase footprint cells atomically with object placement.
771
+
772
+ - `placeWithTerritory` (function) · `import { placeWithTerritory } from "@jgengine/core/world/territory"`
773
+
774
+ ## territory-read-scope — Load the footprint and foreign-owner gap without scanning the world.
775
+
776
+ - `territoryChunkKeys` (function) · `import { territoryChunkKeys } from "@jgengine/core/world/territory"`
777
+
587
778
  ## touch-controls — switch the on-screen touch control set when gameplay context changes (enter/exit vehicle, mount, build mode)
588
779
 
589
780
  - `setTouchControlsMode` (function) · `import { setTouchControlsMode } from "@jgengine/core/input/touchControlsMode"`
590
781
  - `touchButtonShape` (function) · `import { touchButtonShape } from "@jgengine/core/input/touchScheme"`
591
782
 
783
+ ## vehicle-backend-link — collide a vehicle sim with a physics backend — shove props, stop at walls, slide along them
784
+
785
+ - `createVehicleBackendLink` (function) · `import { createVehicleBackendLink } from "@jgengine/core/physics/vehicleBackendLink"`
786
+
787
+ ## vehicle-dynamics — force-based car handling — tires, load transfer, drivetrain, assists — tuned with physical numbers
788
+
789
+ - `createVehicleDynamics` (function) · `import { createVehicleDynamics } from "@jgengine/core/physics/vehicleDynamics"`
790
+
791
+ ## vehicle-seats — enter, exit and swap seats in a car or mount (GTA-style boarding) with driver and passenger slots
792
+
793
+ - `createVehicleSeats` (function) · `import { createVehicleSeats } from "@jgengine/core/world"`
794
+
592
795
  ## visibility-field — per-viewer-group observed/remembered/hidden fog-of-war knowledge with terrain memory and authoritative entity disclosure
593
796
 
594
797
  - `createVisibilityField` (function) · `import { createVisibilityField } from "@jgengine/core/world"`
@@ -601,6 +804,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
601
804
 
602
805
  - `resolveWalkerStep` (function) · `import { resolveWalkerStep } from "@jgengine/core/movement/solidObstacles"`
603
806
 
807
+ ## walker-solid-step-source — slide a step against solid geometry from a pure rules package
808
+
809
+ - `resolveSourceWalkerStep` (function) · `import { resolveSourceWalkerStep } from "@jgengine/core/movement/solidObstacles"`
810
+
604
811
  ## wave-runner — observable serializable wave/spawn runner over the seeded spawn director — WAVE N, wave progress, budget/alert readout, onSpawn sink, snapshot/restore
605
812
 
606
813
  - `createWaveRunner` (function) · `import { createWaveRunner } from "@jgengine/core/world"`
@@ -620,6 +827,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
620
827
  - `buildTrimmedIntersections` (function) · `import { buildTrimmedIntersections } from "@jgengine/core/world"`
621
828
  - `trimBandAtJunctions` (function) · `import { trimBandAtJunctions } from "@jgengine/core/world"`
622
829
 
830
+ ## world-physics-colliders — Synchronize terrain and authored static colliders with a physics backend.
831
+
832
+ - `syncWorldColliders` (function) · `import { syncWorldColliders } from "@jgengine/core/physics/worldColliders"`
833
+
623
834
  ## world-place — declare the place a game happens in — flat/round/voxel/board ground, surface laws, per-place physics
624
835
 
625
836
  - `world` (function) · `import { world } from "@jgengine/core/world"`
@@ -627,3 +838,18 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
627
838
  ## world-seed — derive a deterministic generator/scatter seed from world id + save/run seed
628
839
 
629
840
  - `seedForPlace` (function) · `import { seedForPlace } from "@jgengine/core/world"`
841
+
842
+ ## world-solid-bounds — world AABB and nav footprint of a world solid
843
+
844
+ - `worldSolidBounds` (function) · `import { worldSolidBounds } from "@jgengine/core/world/worldSolids"`
845
+ - `worldSolidFootprint` (function) · `import { worldSolidFootprint } from "@jgengine/core/world/worldSolids"`
846
+
847
+ ## world-solids — collision for generated and authored world geometry (buildings, city lots, walls)
848
+
849
+ - `createWorldSolids` (function) · `import { createWorldSolids } from "@jgengine/core/world/worldSolids"`
850
+
851
+ ## world-solids-from-data — derive collision boxes from generated buildings and wall runs
852
+
853
+ - `buildingSolids` (function) · `import { buildingSolids } from "@jgengine/core/world/worldSolids"`
854
+ - `structureSolids` (function) · `import { structureSolids } from "@jgengine/core/world/worldSolids"`
855
+ - `wallSolids` (function) · `import { wallSolids } from "@jgengine/core/world/worldSolids"`
@@ -0,0 +1,39 @@
1
+ # Recipe — character animation (graph → rig → ground)
2
+
3
+ **What this wires:** a rigged character that moves the way the game plays: clips chosen by data, feet that meet the ground, and gameplay moments keyed to animation frames. There is no default hero, soldier or NPC rig: every game picks its clips, parameters and corrections.
4
+
5
+ ## The seams
6
+
7
+ - **Clips on a model.** `ModelConfig.animation` on an entity kind. A catalog model with indexed `clips` animates automatically through semantic clip roles; `"none"` holds the bind pose. `{ clip, paused, time }` holds one pose.
8
+ - **Graph.** `animation.graph` is an `AnimGraph` (`@jgengine/core/anim/animGraph`): layers, each a state machine of `clip`, `blend1D` and `blend2D` states joined by transitions with `when` conditions, `trigger`s, crossfade `duration` and `exitTime`. It is plain data, so it saves, diffs and can be authored in the editor.
9
+ - `locomotionGraph({ idle, walk, run, walkSpeed, runSpeed, fadeSec, oneShots })` builds the idle/walk/run blend plus one-shots; `states`/`oneShots` configs are converted through it.
10
+ - Upper-body actions (aim, reload, wave) go on a second layer with `mask: ["spine", "arm", ...]` bone prefixes, `additive` for recoil or breathing on top of the base.
11
+ - `createAnimGraphRuntime(graph)` runs the same graph headless: `advance(dt, params, clips)`, `trigger`, `snapshot`/`restore`, `retune`. Test transitions with it before looking at pixels.
12
+ - **Authoring in the editor.** A placement's graph is inspected, previewed and stored from the editor's Animation workspace (see `jgengine-editor` reference, "Placement animation"). `animGraphFromConfig(config)` gives the graph any config plays, so tools and tests see what the shell plays. `parseAnimGraph(json)` validates a stored one.
13
+ - **Parameters.** The shell feeds the entity's smoothed ground speed as `speed`. Anything else (aiming, crouched, strafe x/y) goes on the entity blackboard under `ANIM_PARAMS_KEY`.
14
+ - **Triggers.** `ctx.game.playEntityAnimation(id, name)` arms a trigger; `hit` and `death` arm from their combat events.
15
+ - **Events.** `graph.events: [{ clip, atSec, name }]` emit `animation.event` when a clip crosses that time. Hook footstep audio, the damage frame of a swing or the magazine swap of a reload to the event, not to a timer.
16
+ - **Root motion.** `rootMotion: true` on a state moves the entity by the clip's horizontal root-bone travel, turned by the entity's facing and scaled with the model, instead of playing it in place. The root bone's vertical motion stays in the clip, and other states keep their root animation untouched. Use it for dodges, vaults and attacks with lunges whose distance must match the feet. `output.rootMotion` reports when a root-motion state is current.
17
+ - **Foot IK.** `ModelConfig.ik` runs after the mixer each frame.
18
+ - `ik: "auto"` finds thigh → shin → foot chains by bone name. Pass `{ feet: [{ root, mid, tip }] }` when the rig's names are unusual, and `feet: []` to keep only `lookAt`.
19
+ - Each foot keeps its animated swing and moves by its ground's height above or below the model origin. The pelvis drops so the lower leg can reach, soles never end under the ground, and planted feet tilt to the ground normal (`alignToGround`, `0..1`). The correction fades out while the feet are clear of the ground.
20
+ - `maxAdjust` caps the correction as a fraction of leg length (default `0.4`). Probes hit terrain and blocking physical objects, never the model's own entity.
21
+ - The pure pieces are `placeFeet` and `inferLegChains` (`@jgengine/core/anim/footPlacement`) and `solveTwoBone`/`solveFabrik`/`lookAt` (`@jgengine/core/anim/ikSolver`), for a custom host or a non-leg chain (a hand on a ledge, a tail).
22
+ - **Crowds.** `<SkinnedInstances url instances clips once fps modelScale />` (`@jgengine/shell/render/SkinnedInstances`) draws hundreds of animated copies of one rig in one instanced draw per material. Each `CrowdInstance` has `position`, `rotationY`, `scale`, `clip`, `timeOffset` and `speed`.
23
+ - Every clip is baked once to a bone-matrix texture (layout from `planBoneTexture`, `@jgengine/core/anim/boneTexture`) and sampled on the GPU. Clips in `once` hold their last pose. A member changes clip when the game passes a new `instances` array.
24
+ - Only skinned meshes are baked. Static props parented to bones (a weapon in a hand slot, a hat) are dropped, and a mesh with several materials keeps its first.
25
+ - Crowd members have no graph, IK or attachments. Promote the few near the camera or near the player to entity models.
26
+ - `bakeSkinnedCrowd` and `patchCrowdMaterial` are exported for a custom renderer.
27
+
28
+ ## Symptom → knob
29
+
30
+ | Symptom | Knob |
31
+ | --- | --- |
32
+ | Feet slide at walk or run speed | `walkSpeed`/`runSpeed` to the clip's authored stride speed, or blend1D `at` points |
33
+ | Pop between states | transition `duration`; `exitTime` on one-shots returning to locomotion |
34
+ | Feet float or sink on slopes and steps | `ik: "auto"` |
35
+ | Knees buckle on steep ground | lower `ik.maxAdjust` |
36
+ | Feet tilt wrongly on rubble or props | lower `ik.alignToGround` |
37
+ | Attack damage lands before the swing | `graph.events` on the hit frame instead of a timer |
38
+ | Lunge distance doesn't match the feet | `rootMotion: true` on that state |
39
+ | Hundreds of animated characters cost thousands of draw calls | `SkinnedInstances` for the background crowd |
@@ -0,0 +1,44 @@
1
+ # Recipe — controls (bindings → contexts → devices → feel)
2
+
3
+ **What this wires:** a game's controls from small parts: named actions, layered contexts that swap bindings live, keyboard/mouse and gamepad on the same actions, and per-axis feel. There are no control presets; every game names its own actions and codes.
4
+
5
+ ## The seams
6
+
7
+ - **Actions.** `defineGame({ input: { action: codes } })` maps action names to codes. Keyboard codes are `KeyboardEvent.code` (`KeyW`), pad buttons are `pad:<index>` and stick or trigger directions are `padaxis:<index>+`/`-` in the standard mapping. `bindingLabel(code, "xbox")` gives the glyph text for prompts. Sim code reads actions only: `ctx.input.isDown`, `justPressed`, `value` (0..1, analog when a stick or trigger drives it) and `axis(bindings)`.
8
+ - **Contexts.** `actionContextStack(ctx)` (`@jgengine/core/game/controlGate`) layers action maps over the base input. `push({ id, codes, passthrough, axes?, shaping? })` swaps bindings immediately: the shell re-binds keyboard, touch and pad on the next frame, and a key held across the swap keeps counting toward whatever it maps to now. `passthrough: false` hides everything below (menus, a parked vehicle); `passthrough: true` adds or overrides a few actions (a build mode on top of on-foot). The stack has `snapshot`/`restore`, so the current mode survives a save.
9
+ - **Axes and feel per context.** A context can carry `axes` (axis bindings by action name) and `shaping` (serializable `createAxisShaper` profiles). Read them with `activeAxes()` when `version()` changes and `retune` the shaper, so on-foot movement and driving steer each get their own ramp and curve without a branch per mode.
10
+ - **Pad feel.** `defineGame({ gamepad: { deadzone, curve, triggerDeadzone } })` shapes sticks and triggers before values reach `ctx.input`; the axis shaper only adds ramps and speed scaling on top.
11
+ - **Timing.** `createInputBuffer` (`@jgengine/core/input/inputBuffer`) buffers early presses, coyote time, hold duration and double taps.
12
+ - **Local seats (couch co-op).** `defineGame({ localPlayers: { maxSlots: 4 } })` opens seats on one screen. The first pad to press a button shares the primary seat with keyboard and touch (`claimPrimary: "none"` keeps the primary keyboard-only); each further pad hot-joins its own seat, and the shell calls `loop.onNewPlayer(ctx, { userId, isNew: true })` for it, so spawn with `player?.userId ?? ctx.player.userId`. Read a seat with `localPlayers(ctx).local(slotId)` → `{ userId, input }` (`@jgengine/core/runtime/localPlayers`); iterate `slots()` in `onTick`. Seats use the same bindings and contexts as the primary, and the shell's walk controller moves every seat's entity. The table has `snapshot`/`restore`/`retune`.
13
+ - **Split-screen.** `defineGame({ viewports: { layout: "auto", split: "vertical" } })` draws one viewport per joined seat (`@jgengine/core/game/viewports`: `splitViewports`, `resolveViewports`, `viewportPixels`). The primary seat keeps the main camera rig; other seats get a chase or top-down follow camera (`viewports.camera`, defaulting to top-down when the main rig is), and the walk controller moves each seat relative to its own camera. `ViewportHuds` (`@jgengine/shell/camera/Viewports`) gives each seat its own HUD root. Post-processing is skipped while the screen is split. `bun run drive couch --param gamepad=2` shows two seats.
14
+ - **Haptics.** `ctx.input.haptics(userId)` holds named rumble channels: `set(name, { strong, weak }, priority?)` each tick for continuous feel, `pulse(name, { strong, weak, ms }, priority?)` for a fading hit. Higher priority ducks lower channels. The shell drives each seat's own pad; channels are local and never saved or sent.
15
+ - **Rebinding.** `createRebindSession` drives a conflict-aware "press a key" flow; overrides persist per game and apply under the context stack.
16
+
17
+ ## Wiring a mode swap
18
+
19
+ ```ts
20
+ const DRIVING: ActionContext = {
21
+ id: "driving",
22
+ codes: { exit: ["KeyF", "pad:3"] },
23
+ passthrough: true,
24
+ axes: { steer: { positive: ["steerRight"], negative: ["steerLeft"] } },
25
+ shaping: { steer: { digital: { riseRate: 3, returnRate: 6 }, analog: { deadzone: 0.08, curve: 1.5 } } },
26
+ };
27
+
28
+ // enter: actionContextStack(ctx).push(DRIVING); leave: actionContextStack(ctx).pop("driving")
29
+ if (state.contextVersion !== contexts.version()) {
30
+ state.contextVersion = contexts.version();
31
+ const layer = contexts.activeAxes();
32
+ state.bindings = { steer: layer.bindings.steer ?? { positive: [] } };
33
+ state.shaper.retune({ axes: { steer: layer.shaping.steer ?? {} } });
34
+ }
35
+ const raw = ctx.input.axis(state.bindings);
36
+ const axis = state.shaper.shape(dt, raw, { analog: analogAxes(state.bindings, ctx.input.analog()) });
37
+ ```
38
+
39
+ `bun run drive handling` runs this: F pops the driving context and a held W stops reaching the engine.
40
+
41
+ ## Pitfalls
42
+
43
+ - Actions that exist only inside a context are published to `ctx.input` while the context is active, but the base `input` map is what the touch dock and settings menu list. Keep an action in the base map when touch players need it.
44
+ - `shaping` has no `scale` callback because contexts are serializable; apply speed-sensitive scaling on the shaper the game owns.
@@ -0,0 +1,76 @@
1
+ # Recipe — flight feel (aircraft as physical numbers)
2
+
3
+ **What this wires:** an aircraft that flies the way the brief says. There is no default jet or plane: every aircraft is a mass, an inertia tensor, a set of surfaces and an engine the game picks, checked against numbers it writes down first.
4
+
5
+ ## The seams
6
+
7
+ - **Sim.** `createRigidAircraft(tuning, { position, velocity, heading | orientation, wind?, groundHeight?, gravityField?, airDensity? })` from `@jgengine/core/physics/aircraftDynamics`. It is a quaternion rigid body: surface forces act at their mount points on the inertia tensor, so nothing commands a rotation rate. `snapshot()`/`restore()` for prediction and replay, `retune()` for damage, upgrades and staging, per-tick `modifiers` (`thrustScale`, `liftScale`, `dragScale`, extra `force`/`torque`).
8
+ - **Body frame.** `[left, up, forward]` about the centre of mass. A wing panel sits at `[±semiSpan/2, 0, 0]`, a tailplane at `[0, 0, -tailArm]`, a fin at `[0, finHeight, -tailArm]` with `normal: [1, 0, 0]`.
9
+ - **Surfaces.** Each one has `area` (m²), `liftSlope` (per rad), `stallAngle` (rad), `postStallLift` (share of peak kept past the stall), `incidence` (trim), and the drag polar `cd0` + `inducedDrag·CL²`. Split the wing into left and right panels so ailerons have a roll arm.
10
+ - **Controls.** `control: { pitch?, roll?, yaw? }` on a surface is AoA change per rad of that channel's deflection. The sim signs it from the lever arm, so positive input is always nose up, right roll, nose right. Elevons are one surface with both `pitch` and `roll`. `controls.<channel>` sets `maxDeflection` and actuator `rate`.
11
+ - **Rotors.** A `rotor` block makes the same body a helicopter: `maxThrust` (N, full collective and rotor speed), `radius` (m), hub `at`, `torque` (N·m at full collective, positive yaws the nose left), `cyclic` disc tilt, `damping`, `spoolRate` and an optional `tail` rotor (`maxThrust`, `at`, `sideDamping`).
12
+ - Throttle spins the rotor up; `input.collective` sets thrust; the pitch and roll channels tilt the disc; yaw drives the tail rotor.
13
+ - Neutral pedal gives no tail thrust, so the nose swings with the torque until the pilot (or SAS) holds it. Hover pedal is `torque·collective / (tail.maxThrust · tailArm)`.
14
+ - Thrust follows momentum theory: translational lift once clean air reaches the induced velocity `√(T/2ρA)`, less thrust while climbing through the disc, and Cheeseman–Bennett ground effect below about one rotor diameter.
15
+ - The step's `rotor` telemetry (`speed`, `thrust`, `torque`, `groundEffect`, `translationalLift`) drives rotor sound, dust and HUD.
16
+ - **Rockets.** A `motor` block: `thrustCurve` (`[seconds, N]` points over burn time), `propellantKg` on top of `massKg`, `massFlow` (kg/s at peak thrust; it scales with thrust), nozzle `at` and `gimbal` (rad, on the pitch and yaw actuators).
17
+ - Throttle above zero burns and advances the burn clock; throttle `0` coasts with the clock held.
18
+ - Mass and inertia fall as propellant leaves (inertia scales with total over dry mass), so acceleration and turn rate rise through the burn.
19
+ - Stage by `retune(nextStage)`: a new `motor` object loads its propellant and restarts its clock; the same object keeps both. Move the pose to the new stage's centre of mass with `restore` first if the stages differ in length.
20
+ - The step reports `massKg` and `motor` (`thrust`, `burnTime`, `propellantKg`, `burnedOut`).
21
+ - **Assists.** An `assists` block is a flight computer on the same actuators, so it never exceeds the airframe's authority, and full stick on an axis is always the pilot's.
22
+ - `sas: { pitch, roll, yaw }` (`0..1`) damps body rates and, hands-off, holds the attitude or heading at release with a learned trim. `sasRate` sets how firm (rad/s for full control). On a helicopter, `sas.yaw` holds heading against rotor torque with no pedal.
23
+ - `autoLevel` (`0..1`) flies the wings level and the nose to the horizon hands-off.
24
+ - `maxAngleOfAttack` (rad) and `maxG` cut the pitch command before the limit; `step.limited` reports it.
25
+ - `hoverHold` (`0..1`) flies hands-off cyclic to a stop over the ground: position → velocity → attitude → rate.
26
+ - `policy(context, command)` runs last and returns the command the actuators get: replace any built-in with the game's own.
27
+ - `step.command` is what the actuators got. Toggle assists with `retune({ ...aircraft.tuning(), assists })`.
28
+ - **Pose.** Set the entity's `rotationY` from `step.heading` and apply the rest of `step.orientation` to the mesh (the heading-free part: `qY(-heading) · orientation`). `step.pitch`/`step.bank` match three.js `Euler(-pitch, heading, bank, "YXZ")`; `aircraftAttitudeQuaternion(heading, pitch, bank)` goes the other way for spawns.
29
+ - **Camera.** The chase rig (`rig: "chase"`) follows heading; give it `frustum: { far }` in the thousands and a `yawResponse` around 4 so the boom swings smoothly over the top of a loop.
30
+ - **Telemetry.** `airspeed`, `angleOfAttack`, `sideslip`, `gLoad`, `stalled`/`stallFraction`, `thrust`, body rates and `deflection` drive the HUD, stall horn, wind noise and camera shake.
31
+
32
+ ## Workflow
33
+
34
+ 1. **Write the feel as numbers** on the `FlightReport` fields. Some examples:
35
+ - "Nimble aerobatic" is `rollRateDeg > 300` and `sustainedTurnRateDeg > 18`.
36
+ - "Heavy transport" is `rollRateDeg` 40–80 and `stallSpeed` 50–70.
37
+ - "Responsive jet" is `throttleResponse < 2`.
38
+ - "Easy hover" is `hoverDriftMeters < 5` with the game's assists on.
39
+ 2. **Put them in a test** next to the tuning: `measureFlight((spawn) => createRigidAircraft(tuning, spawn), { cruiseSpeed })` from `@jgengine/core/physics/handlingProbe`. It flies deterministic autopilots through a full-aileron roll, full-throttle banked turns, an idle deceleration, a speed-held climb, a throttle step and a hands-off hover. Fields that don't apply are `NaN`: turn and stall need wings, hover drift needs a rotor.
40
+ 3. **Start from the real aircraft**: mass, wing area, span, tail arm, thrust. Inertia is roughly `m·(span/4)²` for roll and `m·(length/4)²` for pitch and yaw.
41
+ 4. **Trim it.** With the wing on the centre of mass, set wing `incidence` to the cruise lift coefficient over the lift slope, `W / (½ρV²·S·a)`, and leave the tail at `0`. It then flies level hands-off at that speed.
42
+ 5. **Drive it**: `bun run drive flight --key KeyR+KeyS:10500 --key KeyD:1500 --record <name> --record-fps 20`, or `bun run drive flight-heli --key KeyR:1500 --wait 3000 --key KeyE:3500 --record <name> --record-fps 20` for the helicopter, or `bun run drive flight-rocket --key KeyR:400 --wait 12000 --record <name> --record-fps 20` for the two-stage rocket.
43
+
44
+ ## Symptom → knob
45
+
46
+ | Symptom | Knob (direction) |
47
+ | --- | --- |
48
+ | Pitches up or down hands-off | wing `incidence` ↓ / ↑; wing further aft of the centre of mass ↑ stability |
49
+ | Loops too slowly | tail `control.pitch` ↑ (more AoA), entry speed or thrust ↑, `inertia.pitch` ↓ |
50
+ | Stalls in every hard pull | tail `control.pitch` ↓ so the elevator can't reach the stall angle, or wing `stallAngle` ↑ |
51
+ | Stall is a cliff / too gentle | `postStallLift` ↓ / ↑ |
52
+ | Rolls too slowly / too fast | aileron `control.roll` ↑ / ↓, wing panel `at` further out ↑ damping; `inertia.roll` sets how fast it gets there |
53
+ | Nose wanders, won't track | fin `area` ↑ or fin further aft |
54
+ | Controls feel laggy | `controls.<channel>.rate` ↑ |
55
+ | Top speed wrong | `engine.maxThrust`, `dragArea`, surface `cd0` |
56
+ | Bleeds speed in turns | `inducedDrag` ↓, wing `area` ↑ (lower CL for the same lift) |
57
+ | Throttle feels sluggish | `engine.spoolRate` ↑ |
58
+ | Helicopter won't hover / climbs away | hover collective is `weight / maxThrust` out of ground effect; `rotor.maxThrust` sets how much margin there is |
59
+ | Nose swings too hard without pedal | `rotor.torque` ↓, `inertia.yaw` ↑, `tail.sideDamping` ↑ |
60
+ | Pedal too weak to stop a swing | `tail.maxThrust` ↑ or tail rotor further aft |
61
+ | Cyclic twitchy / sluggish | `rotor.cyclic` ↓ / ↑, hub height (`rotor.at[1]`) ↓ / ↑, `rotor.damping` ↑ / ↓ |
62
+ | Pedal also rolls the body | tail rotor `at[1]` above the centre of mass; move it down or trim with cyclic |
63
+ | Rocket weathercocks too hard / tumbles | fin `area` ↓ / ↑, fins further aft for more stability |
64
+ | Gimbal too twitchy / can't steer | `motor.gimbal` ↓ / ↑; lighter late in the burn it turns faster on its own |
65
+ | Burns too long / short | `propellantKg` over `massFlow` sets burn time; the curve's last point cuts it off first |
66
+ | Helicopter too hard to hover on a keyboard | `assists.sas.yaw` 1 for heading hold, `assists.hoverHold` for cyclic; collective stays the pilot's |
67
+ | Assisted aircraft oscillates | `assists.sasRate` ↑ (softer), strengths ↓ |
68
+ | Players stall it pulling hard | `assists.maxAngleOfAttack` just under the wing `stallAngle`, or `maxG` |
69
+ | Sinks when it slows down | that is translational lift going away; `rotor.translationalLift` sets how much |
70
+
71
+ ## Traps
72
+
73
+ - `createAircraftDynamics` (`physics/flightDynamics`) is deprecated: it commands rotation and can't loop. The CHANGELOG Migrate note maps its fields onto `createRigidAircraft`.
74
+ - Setting orientation or angular velocity by hand defeats the model. Change a surface, the inertia or the control authority.
75
+ - A single centreline wing has no roll arm, so ailerons on it do nothing. Split it into left and right panels.
76
+ - For multiplayer, send inputs, not poses. Predict with the same tuning and `restore(serverState)` on correction.
@@ -0,0 +1,43 @@
1
+ # Recipe — NPC senses (perceive → remember → decide → route)
2
+
3
+ **What this wires:** an NPC that notices things, remembers where they were, acts on
4
+ that memory, and gives up when it goes stale. A guard that hears a sprint and
5
+ checks it out, a mob that leashes home, a pedestrian that flees a gunshot and a
6
+ patrol car that drives to the last sighting are all this loop with different
7
+ facts, graphs and speeds. No archetypes: the NPC is data plus a few named actions.
8
+
9
+ Working example: the `guard-probe` dev demo (`apps/dev/src/demo/guardProbe.ts`),
10
+ `bun run drive guard-probe`.
11
+
12
+ ## The seams
13
+
14
+ - **Perceive → memory.** One `createPerception` per group of NPCs that share
15
+ senses. Each tick, push the stimuli your game produces (`sound` with a
16
+ `loudness` that scales range, `damage`) and call `observe(npc, candidates, nowMs)`
17
+ with a *bounded* candidate list from a spatial query, never every entity. Pass
18
+ `occluded` for walls. Memory decays linearly over `memorySeconds`; each observer
19
+ judges a stimulus once.
20
+ - **Memory → blackboard.** A small system copies the facts the graph needs into
21
+ the NPC's persistent blackboard: `behaviorControl(ctx).blackboard(id)`. Write
22
+ plain scalars (`alerted`, `lastKnownX`, `lastKnownZ`, `confidence`). Actions
23
+ and conditions read facts; they do not query the world for them.
24
+ - **Blackboard → decision.** The NPC's `decisionGraph` behavior is data:
25
+ conditions on blackboard keys, `memory` sequences for multi-step errands
26
+ (go → `wait` → give up), `cooldown` for barks, `random` (drawn from `ctx.rng`)
27
+ for idle variety. Register actions by name with `registerBehaviorActions`, and
28
+ release movement or claims in `onAbort` when a higher branch pre-empts them.
29
+ Set `thinkInterval` so a crowd thinks a few times a second.
30
+ - **Decision → route.** Actions that move ask one `createNavMeshQuery(mesh)` per
31
+ mesh for `findPath` and replan only when the goal moves. Bake the mesh from
32
+ scene geometry with `@jgengine/navbake` (`await initNavBake()`, then
33
+ `bakeNavMesh`) and store it as an editor `nav` bake; price hazards or lock doors
34
+ with `areaCosts` through `retune`.
35
+
36
+ ## Gotchas
37
+
38
+ - Hand the graph facts, not objects: the blackboard serializes with the behavior,
39
+ so saves and replays keep an NPC mid-investigation.
40
+ - Judge footstep noise on speed averaged over a window, not one frame; frame-time
41
+ jitter otherwise reads as a sprint.
42
+ - Record the stimulus you handled (`handledAt`) so giving up does not
43
+ re-alert on the same memory next tick.