@jgengine/core 0.27.0 → 0.28.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 (108) hide show
  1. package/CHANGELOG.md +130 -0
  2. package/dist/ai/policeResponse.d.ts +253 -0
  3. package/dist/ai/policeResponse.js +421 -0
  4. package/dist/anim/actionLayer.d.ts +47 -0
  5. package/dist/anim/actionLayer.js +63 -0
  6. package/dist/anim/ikSolver.d.ts +14 -0
  7. package/dist/anim/ikSolver.js +37 -0
  8. package/dist/anim/locomotionGraph.d.ts +58 -3
  9. package/dist/anim/locomotionGraph.js +142 -15
  10. package/dist/anim/ragdollRig.d.ts +53 -0
  11. package/dist/anim/ragdollRig.js +132 -0
  12. package/dist/anim/skeletonStandard.d.ts +140 -0
  13. package/dist/anim/skeletonStandard.js +326 -0
  14. package/dist/combat/damageRuntime.d.ts +14 -1
  15. package/dist/combat/damageRuntime.js +62 -4
  16. package/dist/combat/death.js +7 -3
  17. package/dist/combat/deathReason.d.ts +7 -1
  18. package/dist/combat/effects.d.ts +25 -0
  19. package/dist/combat/effects.js +16 -0
  20. package/dist/combat/statusRuntime.d.ts +2 -0
  21. package/dist/combat/statusRuntime.js +1 -0
  22. package/dist/economy/loanBook.d.ts +182 -0
  23. package/dist/economy/loanBook.js +303 -0
  24. package/dist/economy/market.d.ts +161 -0
  25. package/dist/economy/market.js +248 -0
  26. package/dist/economy/transactionLedger.d.ts +162 -0
  27. package/dist/economy/transactionLedger.js +301 -0
  28. package/dist/editor/creatorStorage.js +3 -1
  29. package/dist/editor/simulation.d.ts +5 -2
  30. package/dist/editor/simulation.js +29 -19
  31. package/dist/game/cameraConfig.d.ts +14 -0
  32. package/dist/game/clipRoles.d.ts +5 -4
  33. package/dist/game/clipRoles.js +34 -4
  34. package/dist/game/events.d.ts +27 -0
  35. package/dist/game/gameSave.d.ts +142 -0
  36. package/dist/game/gameSave.js +174 -0
  37. package/dist/game/playableGame.d.ts +55 -4
  38. package/dist/game/saveSlotIndex.d.ts +54 -0
  39. package/dist/game/saveSlotIndex.js +71 -0
  40. package/dist/game/saveSlots.d.ts +2 -0
  41. package/dist/game/saveSlots.js +1 -1
  42. package/dist/game/saveStore.d.ts +8 -0
  43. package/dist/game/saveStore.js +16 -0
  44. package/dist/gameplay.d.ts +12 -2
  45. package/dist/gameplay.js +10 -0
  46. package/dist/meta/changelog.d.ts +1 -1
  47. package/dist/meta/changelog.js +120 -1
  48. package/dist/mission/compositions.d.ts +85 -0
  49. package/dist/mission/compositions.js +130 -0
  50. package/dist/mission/mission.d.ts +435 -0
  51. package/dist/mission/mission.js +770 -0
  52. package/dist/mission/triggers.d.ts +61 -0
  53. package/dist/mission/triggers.js +108 -0
  54. package/dist/nav/traffic.d.ts +0 -1
  55. package/dist/nav/traffic.js +1 -2
  56. package/dist/runtime/context/combat.js +11 -0
  57. package/dist/settings/accessibilitySettings.d.ts +81 -0
  58. package/dist/settings/accessibilitySettings.js +152 -0
  59. package/dist/settings/graphicsProfile.d.ts +3 -0
  60. package/dist/settings/graphicsProfile.js +4 -0
  61. package/dist/settings/settingsModel.d.ts +1 -1
  62. package/dist/settings/settingsModel.js +1 -0
  63. package/dist/sim/nearAgents.d.ts +8 -1
  64. package/dist/sim/nearAgents.js +34 -17
  65. package/dist/sim/offScreen.d.ts +2 -1
  66. package/dist/sim/offScreen.js +14 -3
  67. package/dist/sim/population.d.ts +147 -0
  68. package/dist/sim/population.js +182 -0
  69. package/dist/sim/queue.d.ts +90 -0
  70. package/dist/sim/queue.js +171 -0
  71. package/dist/sim/rides.d.ts +287 -0
  72. package/dist/sim/rides.js +471 -0
  73. package/dist/sim/routine.js +19 -11
  74. package/dist/ui/loadingTips.d.ts +41 -0
  75. package/dist/ui/loadingTips.js +64 -0
  76. package/dist/ui/menuNav.d.ts +79 -0
  77. package/dist/ui/menuNav.js +147 -0
  78. package/dist/ui/menuTransition.d.ts +50 -0
  79. package/dist/ui/menuTransition.js +67 -0
  80. package/dist/ui.d.ts +2 -0
  81. package/dist/ui.js +2 -0
  82. package/dist/vfx/effectLibrary.d.ts +98 -0
  83. package/dist/vfx/effectLibrary.js +181 -0
  84. package/dist/vfx/particleBudget.d.ts +40 -0
  85. package/dist/vfx/particleBudget.js +39 -0
  86. package/dist/vfx/particleDirector.d.ts +16 -0
  87. package/dist/vfx/particleDirector.js +18 -0
  88. package/dist/world/authoredSimulation.js +7 -1
  89. package/dist/world/captureCamera.d.ts +30 -0
  90. package/dist/world/captureCamera.js +19 -0
  91. package/dist/world/features.d.ts +15 -4
  92. package/dist/world/terrain.d.ts +1 -0
  93. package/dist/world/terrain.js +2 -0
  94. package/dist/world.d.ts +2 -1
  95. package/dist/world.js +1 -0
  96. package/package.json +1 -1
  97. package/skills/jgengine/api.md +2 -2
  98. package/skills/jgengine-combat/SKILL.md +6 -0
  99. package/skills/jgengine-combat/api.md +5 -4
  100. package/skills/jgengine-combat/reference.md +4 -0
  101. package/skills/jgengine-gameplay/SKILL.md +55 -0
  102. package/skills/jgengine-gameplay/api.md +335 -10
  103. package/skills/jgengine-gameplay/capabilities.md +83 -1
  104. package/skills/jgengine-world/SKILL.md +4 -0
  105. package/skills/jgengine-world/api.md +128 -6
  106. package/skills/jgengine-world/capabilities.md +41 -1
  107. package/skills/jgengine-world/recipes/character-animation.md +25 -2
  108. package/skills/jgengine-world/reference.md +1 -1
@@ -187,10 +187,15 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
187
187
 
188
188
  - `ActionCodesMap` (type) · `import { ActionCodesMap } from "@jgengine/core/gameplay"`
189
189
 
190
- ## game-save — save/load game state with a pluggable backend, autosave, slots, and migration
190
+ ## game-save — one versioned save document with per-system sections, named slots, caller-policy autosave, migration and a state digest
191
191
 
192
+ - `createGameSave` (function) · `import { createGameSave } from "@jgengine/core/gameplay"`
192
193
  - `createSaveStore` (function) · `import { createSaveStore } from "@jgengine/core/gameplay"`
193
194
 
195
+ ## game-save-digest — hash a save document's state for a determinism test or a diff
196
+
197
+ - `gameSaveDigest` (function) · `import { gameSaveDigest } from "@jgengine/core/gameplay"`
198
+
194
199
  ## game-system — declare a composable capability with its own schedule and lifecycle
195
200
 
196
201
  - `SystemDefinition` (interface) · `import { SystemDefinition } from "@jgengine/core/gameplay"`
@@ -290,6 +295,14 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
290
295
 
291
296
  - `createLoadouts` (function) · `import { createLoadouts } from "@jgengine/core/gameplay"`
292
297
 
298
+ ## loan-schedule — build a loan's interest and principal repayment schedule in integer minor units
299
+
300
+ - `buildLoanSchedule` (function) · `import { buildLoanSchedule } from "@jgengine/core/gameplay"`
301
+
302
+ ## loans — draw, schedule, service and default a loan with interest, posted to a ledger
303
+
304
+ - `createLoanBook` (function) · `import { createLoanBook } from "@jgengine/core/gameplay"`
305
+
293
306
  ## local-save — persist a game to on-device localStorage (offline)
294
307
 
295
308
  - `localSaveBackend` (function) · `import { localSaveBackend } from "@jgengine/core/gameplay"`
@@ -345,6 +358,36 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
345
358
 
346
359
  - `mergeCredits` (function) · `import { mergeCredits } from "@jgengine/core/game/credits"`
347
360
 
361
+ ## mission-compositions — build escort, chase and defend missions from the shared objective kinds
362
+
363
+ - `chaseMission` (function) · `import { chaseMission } from "@jgengine/core/gameplay"`
364
+ - `defendMission` (function) · `import { defendMission } from "@jgengine/core/gameplay"`
365
+ - `escortMission` (function) · `import { escortMission } from "@jgengine/core/gameplay"`
366
+
367
+ ## mission-events — count constrained kills and fail a mission on a matching world event
368
+
369
+ - `MissionWorldEvent` (interface) · `import { MissionWorldEvent } from "@jgengine/core/gameplay"`
370
+
371
+ ## mission-rewards — pay mission rewards through the quest appliers and the loot registry
372
+
373
+ - `applyMissionRewards` (function) · `import { applyMissionRewards } from "@jgengine/core/gameplay"`
374
+
375
+ ## mission-runtime — missions as a data-driven objective state machine with fail states, timers, branches and rewards
376
+
377
+ - `createMissionRuntime` (function) · `import { createMissionRuntime } from "@jgengine/core/gameplay"`
378
+
379
+ ## mission-triggers — start, advance and fail a mission from authored scene markers
380
+
381
+ - `MISSION_TRIGGER_ACTIONS` (const) · `import { MISSION_TRIGGER_ACTIONS } from "@jgengine/core/gameplay"`
382
+ - `createMissionTriggerBridge` (function) · `import { createMissionTriggerBridge } from "@jgengine/core/gameplay"`
383
+ - `missionMarkers` (function) · `import { missionMarkers } from "@jgengine/core/gameplay"`
384
+ - `registerMissionTriggerActions` (function) · `import { registerMissionTriggerActions } from "@jgengine/core/gameplay"`
385
+
386
+ ## mission-validation — tell whether an objective kind completes from a counter alone
387
+
388
+ - `isCounterObjective` (function) · `import { isCounterObjective } from "@jgengine/core/gameplay"`
389
+ - `validateMissionCatalog` (function) · `import { validateMissionCatalog } from "@jgengine/core/gameplay"`
390
+
348
391
  ## modular-item — attach parts into item mount slots to compute combined stats
349
392
 
350
393
  - `slotAccepts` (function) · `import { slotAccepts } from "@jgengine/core/gameplay"`
@@ -518,6 +561,21 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
518
561
 
519
562
  - `createVfxInstanceStore` (function) · `import { createVfxInstanceStore } from "@jgengine/core/gameplay"`
520
563
 
564
+ ## ride-appeal — how much one guest likes one ride, from its ratings and their taste
565
+
566
+ - `rideAppeal` (function) · `import { rideAppeal } from "@jgengine/core/gameplay"`
567
+
568
+ ## ride-choice — guests pick rides by taste and queue tolerance through the utility brain
569
+
570
+ - `createRideChooser` (function) · `import { createRideChooser } from "@jgengine/core/gameplay"`
571
+ - `rideChoiceCandidates` (function) · `import { rideChoiceCandidates } from "@jgengine/core/gameplay"`
572
+ - `rideChoiceOptions` (function) · `import { rideChoiceOptions } from "@jgengine/core/gameplay"`
573
+ - `rideQueueCostWeight` (function) · `import { rideQueueCostWeight } from "@jgengine/core/gameplay"`
574
+
575
+ ## ride-sim — rides with capacity, cycles, queues, ratings, breakdowns and maintenance
576
+
577
+ - `createRideSystem` (function) · `import { createRideSystem } from "@jgengine/core/gameplay"`
578
+
521
579
  ## routine-timeline — authored daily routines (home, work, meal) resolved against the world clock
522
580
 
523
581
  - `resolveRoutine` (function) · `import { resolveRoutine } from "@jgengine/core/gameplay"`
@@ -532,6 +590,14 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
532
590
 
533
591
  - `createRunDraft` (function) · `import { createRunDraft } from "@jgengine/core/gameplay"`
534
592
 
593
+ ## save-slot-index — refresh a save-select index from the game's save documents
594
+
595
+ - `refreshSaveSlots` (function) · `import { refreshSaveSlots } from "@jgengine/core/game/saveSlotIndex"`
596
+
597
+ ## save-slot-meta — project a save document onto save-select display metadata
598
+
599
+ - `saveSlotMeta` (function) · `import { saveSlotMeta } from "@jgengine/core/game/saveSlotIndex"`
600
+
535
601
  ## save-slots — serializable save-slot / profile metadata index — write/clear/rename/mostRecent over free-string per-slot meta, powering New/Continue/Load/Delete menus, with snapshot/restore
536
602
 
537
603
  - `createSaveSlots` (function) · `import { createSaveSlots } from "@jgengine/core/gameplay"`
@@ -593,6 +659,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
593
659
 
594
660
  - `statModifierContributions` (function) · `import { statModifierContributions } from "@jgengine/core/gameplay"`
595
661
 
662
+ ## supply-demand-pricing — price goods from finite stock and live demand on a stated curve
663
+
664
+ - `createMarket` (function) · `import { createMarket } from "@jgengine/core/gameplay"`
665
+
596
666
  ## system-schedule — compile fixed/frame/interval system ticks into deterministic ordered stages
597
667
 
598
668
  - `compileSystemSchedule` (function) · `import { compileSystemSchedule } from "@jgengine/core/gameplay"`
@@ -623,6 +693,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
623
693
  - `setTouchControlsMode` (function) · `import { setTouchControlsMode } from "@jgengine/core/gameplay"`
624
694
  - `touchButtonShape` (function) · `import { touchButtonShape } from "@jgengine/core/gameplay"`
625
695
 
696
+ ## transaction-ledger — record every money movement with its reason over per-account integer minor-unit balances
697
+
698
+ - `createTransactionLedger` (function) · `import { createTransactionLedger } from "@jgengine/core/gameplay"`
699
+
626
700
  ## tribe-group — shared-ownership group (tribe/guild/clan) with ranked permissions, group vs personal assets, alliances, and a bounded event log
627
701
 
628
702
  - `createTribe` (function) · `import { createTribe } from "@jgengine/core/game/tribe"`
@@ -658,6 +732,14 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
658
732
 
659
733
  - `createUnlockCatalog` (function) · `import { createUnlockCatalog } from "@jgengine/core/gameplay"`
660
734
 
735
+ ## visitor-population — a crowd whose on-site and daily counts are the bodies the game made
736
+
737
+ - `createVisitorPopulation` (function) · `import { createVisitorPopulation } from "@jgengine/core/gameplay"`
738
+
739
+ ## wait-queue — a real line whose wait estimate comes from its own measured throughput
740
+
741
+ - `createWaitQueue` (function) · `import { createWaitQueue } from "@jgengine/core/gameplay"`
742
+
661
743
  ## wallet — hold currency balances with charge and affordability checks
662
744
 
663
745
  - `createEmptyWallet` (function) · `import { createEmptyWallet } from "@jgengine/core/gameplay"`
@@ -67,6 +67,10 @@ Move many agents across that mesh with `createNavCrowd` (`nav/navCrowd`): it pla
67
67
 
68
68
  Vehicles on roads do not path-find: they follow authored lane spans. `createLaneGraph` (`nav/laneGraph`) resolves those spans — `traffic_lane` paths, or `road` paths from the editor's street bake, split where streets cross — into junctions, crossings with a signal program or first-come right-of-way, and parking spots; `laneGraphFromDocument` reads that straight off the scene document, so editing a road moves its traffic. `createTraffic` (`nav/traffic`) then drives cars on it: IDM car following and braking, one car at a time through each intersection box (only once the exit lane has room, which is what keeps a blocked crossing from wedging the network), signals with phases, lane changes across authored `left`/`right` neighbours, parking that reserves a spot, and `setDemand(count)` for fleet size — the time-of-day curve is the caller's, not the engine's. Cars are posed, not driven: when a player or a chase needs real handling, feed intent into `physics/kinematicVehicle` and let this seam keep the ambient city.
69
69
 
70
+ ### Heat and police response
71
+
72
+ A crime only counts when a witness holds the suspect. `createPoliceResponse` (`ai/policeResponse`) reads witness facts from `sensor/perception` memory — `memoryOf` per observer, keeping the sighting's `sourceId` and `sourceTick` — and adds heat on the shared `ai/heatSystem` curve, so there is no second sight path and no second decay. The caller's tier table decides what police do at each heat: patrol, investigate, pursue, roadblock, with a pursuer budget per tier. The search area sits on the suspect's last known position, grows while the trail is cold, and a roadblock tier cordons it with sites `policeRoadsFromLaneGraph` reads off the lane graph (`traffic_roadblock` markers projected onto the lane they block, else the graph's intersections). Breaking sight for `escapeSeconds` is what starts the decay; standing in view keeps the heat. Heat, curve, tiers and search shape are caller policy with defaults, `state()`/`restore()` round-trip mid-chase, and the injected generator is the only source of variety.
73
+
70
74
  ### Fallback-seam diagnostics
71
75
 
72
76
  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.
@@ -126,6 +126,29 @@
126
126
 
127
127
  - `mobBrainGraph` (const): const mobBrainGraph: DecisionGraph — Graph-shaped description of the mob brain's idle, wander, chase, engage, and evade decisions.
128
128
 
129
+ ## @jgengine/core/ai/policeResponse
130
+
131
+ - `DEFAULT_POLICE_TIERS` (const): const DEFAULT_POLICE_TIERS: readonly PoliceTier[] — The starting tier table: patrol until heat arrives, then investigate, pursue and finally cordon.
132
+ - `DEFAULT_POLICE_TUNING` (const): const DEFAULT_POLICE_TUNING: PoliceTuning — The starting policy: linear decay, a six-second escape and a search area that grows at 2 m/s.
133
+ - `PoliceLaneHit` (interface): interface PoliceLaneHit — A lane near a world point: its id, the arc of the nearest point on it, and how far away that is.
134
+ - `PoliceResponse` (interface): interface PoliceResponse — Stateful wanted/pursuit API with retuning and save/restore support.
135
+ - `PoliceResponseConfig` (interface): interface PoliceResponseConfig extends Partial<PoliceTuning> — Everything {@link createPoliceResponse} needs: the witness seam, the roster and the caller's policy.
136
+ - `PoliceResponseKind` (type): type PoliceResponseKind = "patrol" | "investigate" | "pursue" | "roadblock" — How police answer the current heat tier — watching the streets up to cordoning them.
137
+ - `PoliceResponseState` (interface): interface PoliceResponseState — Serializable response state — the heat curve, the sight timer, the anchor and the counters.
138
+ - `PoliceRoadblockSite` (interface): interface PoliceRoadblockSite — A roadblock site on a lane, resolved from authored road data.
139
+ - `PoliceRoads` (interface): interface PoliceRoads — Road data the response reads: nearby lanes for the search area, authored sites for roadblocks.
140
+ - `PoliceRoadsOptions` (interface): interface PoliceRoadsOptions — How the document reader recognizes authored road content.
141
+ - `PoliceSearchArea` (interface): interface PoliceSearchArea — Where the search is, as the last sighting places it.
142
+ - `PoliceSighting` (interface): interface PoliceSighting — One remembered sighting of the suspect, as the perception service reports it — `PerceptionMemory` from `sensor/perception` satisfies this shape.
143
+ - `PoliceStats` (interface): interface PoliceStats — Counters a devtools probe, a HUD or a test reads.
144
+ - `PoliceStep` (interface): interface PoliceStep — One tick's answer: the heat, the tier, the search area, the cordon and the escape.
145
+ - `PoliceTickContext` (interface): interface PoliceTickContext — Per-tick world facts {@link PoliceResponse.tick} needs but cannot derive itself.
146
+ - `PoliceTier` (interface): interface PoliceTier — One response tier: the heat it begins at, what it puts on the street, and how wide it searches.
147
+ - `PoliceTuning` (interface): interface PoliceTuning — The serializable policy of a response: the tier table, the heat curve and the search shape.
148
+ - `PoliceWitness` (interface): interface PoliceWitness — The witness seam: perception memory, queried by observer id. Pass `createPerception` (`sensor/perception`) straight in — a crime is witnessed when an observer still holds the suspect in view, so there is no second sight path to keep in step.
149
+ - `createPoliceResponse` (function): function createPoliceResponse(config: PoliceResponseConfig): PoliceResponse — The wanted level a crime sandbox needs, over the shared heat curve: a crime raises heat only while a witness holds the suspect in view, the heat bleeds on a stated linear curve once the suspect has been out of every witness's sight for `escapeSeconds`, the tier table decides what police do (patrol → investigate → pursue → roadblock), the search area sits where the suspect was last seen and grows while the trail is cold, and a roadblock tier cordons it with authored sites off the lane graph. Heat, tiers and curves are caller policy; witness facts come from `sensor/perception` memory (`sourceId`/`sourceTick` kept), so there is no second sight path.
150
+ - `policeRoadsFromLaneGraph` (function): function policeRoadsFromLaneGraph(graph: LaneGraph, markers: readonly EditorMarker[] = [], options: PoliceRoadsOptions = {}): PoliceRoads — Read the police road data out of an authored lane graph: `traffic_roadblock` markers become cordon sites projected onto the lane they block, and lanes are indexed so `lanesNear` answers from the local cells rather than a scan of every lane. With no roadblock markers authored, the graph's intersections are the sites, so a cordon still sits where streets meet. Built once; the index is rebuildable from the document, so it is never serialized.
151
+
129
152
  ## @jgengine/core/ai/populationDirector
130
153
 
131
154
  - `PopulationCensus` (type): type PopulationCensus = Record<string, Record<string, number>> — Live census of alive counts keyed `region -> species -> count`, used to reconcile the director to reality.
@@ -233,6 +256,13 @@
233
256
  - `WaveView` (interface): interface WaveView — A pooled, per-frame readout of the current wave — the "brain behind WAVE 3". Reused across {@link WaveRunner.view} calls (never per-frame allocated), so read it and render it, don't retain it.
234
257
  - `createWaveRunner` (function): function createWaveRunner(config: WaveRunnerConfig): WaveRunner — A thin, stateful, observable wrapper around the seeded {@link createSpawnDirectorState} / {@link advanceSpawnDirector} model. It OWNS a {@link SpawnDirectorState}, ticks it from `update(dt, ctx?)`, keeps the new state, and forwards each emitted {@link SpawnRequest} to an optional `onSpawn` sink so the model never instantiates entities. `view()` returns a pooled per-frame readout — the "brain behind WAVE 3": the 1-based wave number, wave progress `0..1`, banked budget, per-wave and total spawn counts, alert, and done flag. `forceNextWave` / `raiseAlert` drive it, `subscribe` observes it, and `snapshot`/`restore` round-trip the plain serializable state object through a save. Nothing here is genre-specific: spawn-entry `kind`s (the director's `entryId`s) stay free strings the runner never interprets.
235
258
 
259
+ ## @jgengine/core/anim/actionLayer
260
+
261
+ - `ACTION_REST_STATE` (const): const ACTION_REST_STATE: "rest" — State id of an action layer's resting state.
262
+ - `ActionLayerInput` (interface): interface ActionLayerInput — A masked, optionally additive overlay layer: a resting clip plus one one-shot state per action.
263
+ - `actionLayer` (function): function actionLayer(input: ActionLayerInput): AnimLayer — One overlay layer from plain data: a resting state plus a one-shot state per action, each joined to the rest by a consumed-trigger transition and returning on `exitTime`. Aim, reload, wave and hit reactions are the same shape, so they are one builder instead of a hand-written state machine per game. The layer is masked by bone prefix, and `additive: true` makes it a recoil/breathing overlay on top of the base pose rather than a replacement.
264
+ - `withActionLayers` (function): function withActionLayers(graph: AnimGraph, layers: readonly ActionLayerInput[]): AnimGraph — A graph plus overlay layers: the base layers keep their ids and order, and each overlay is appended. Duplicate layer ids are rejected rather than silently shadowing, because the shell keys its mixer actions by layer id.
265
+
236
266
  ## @jgengine/core/anim/animGraph
237
267
 
238
268
  - `ANIM_PARAMS_KEY` (const): const ANIM_PARAMS_KEY: "anim.params" — Entity blackboard key the shell reads extra graph parameters from: `ctx.scene.entity.blackboard.set(id, ANIM_PARAMS_KEY, { aiming: true })`.
@@ -292,19 +322,61 @@
292
322
  - `TwoBoneOutput` (interface): interface TwoBoneOutput — Reused output buffers filled by {@link solveTwoBone}.
293
323
  - `Vec3` (type): type Vec3 = readonly [number, number, number] — A three-dimensional point or direction represented without a renderer dependency.
294
324
  - `lookAt` (function): function lookAt(input: LookAtInput): Quat — Returns a quaternion that points a +Z bone from `from` toward `target`, with optional yaw and pitch limits.
325
+ - `multiplyQuaternions` (function): function multiplyQuaternions(a: Quat, b: Quat): Quat — The product of two quaternions (`a` applied after `b`), both as `[x, y, z, w]`.
326
+ - `quaternionFromUnitVectors` (function): function quaternionFromUnitVectors(from: Vec3, to: Vec3): Quat — The shortest rotation taking one unit direction to another, as a quaternion. Parallel directions give the identity; opposite directions give a half turn about a perpendicular axis. Returns the identity for a zero-length input.
295
327
  - `solveFabrik` (function): function solveFabrik(chain: readonly Vec3[], target: Vec3, options: FabrikOptions = {}): MutableVec3[] — Solves a chain with the FABRIK forward-and-backward reaching algorithm. Returned tuples are new values; the input chain is not modified.
296
328
  - `solveTwoBone` (function): function solveTwoBone(input: TwoBoneInput, out: TwoBoneOutput): TwoBoneOutput — Solves a two-segment chain analytically, placing the middle and tip joints toward `target` and `pole`. The root remains fixed, and unreachable targets are clamped to the chain's maximum reach.
297
329
 
298
330
  ## @jgengine/core/anim/locomotionGraph
299
331
 
332
+ - `LOCOMOTION_BACKWARD_STATE` (const): const LOCOMOTION_BACKWARD_STATE: "backward" — State id of the backwards blend.
300
333
  - `LOCOMOTION_CROUCHED_PARAM` (const): const LOCOMOTION_CROUCHED_PARAM: "crouched" — Physically resolved crouch stance from the shared player motor.
334
+ - `LOCOMOTION_DIRECTION_PARAM` (const): const LOCOMOTION_DIRECTION_PARAM: "direction" — Signed lateral share of the character's motion: `-1` fully to its left, `+1` fully to its right, `0` straight ahead or straight back. A custom mover feeds it under `ANIM_PARAMS_KEY`.
335
+ - `LOCOMOTION_FACING_PARAM` (const): const LOCOMOTION_FACING_PARAM: "facing" — Dot of the character's facing with its motion direction: `1` running forward, `-1` running backwards. A custom mover feeds it under `ANIM_PARAMS_KEY`.
301
336
  - `LOCOMOTION_GROUNDED_PARAM` (const): const LOCOMOTION_GROUNDED_PARAM: "grounded" — Ground contact reported by the shared player motor; absent for custom movers without authored parameters.
302
337
  - `LOCOMOTION_LAYER` (const): const LOCOMOTION_LAYER: "base" — Layer id the locomotion graph uses; query `runtime.stateOf(LOCOMOTION_LAYER)`.
303
338
  - `LOCOMOTION_SPEED_PARAM` (const): const LOCOMOTION_SPEED_PARAM: "speed" — The parameter name the shell feeds with the entity's smoothed ground speed.
339
+ - `LOCOMOTION_STATE` (const): const LOCOMOTION_STATE: "locomotion" — State id of the forward idle/walk/run blend.
340
+ - `LOCOMOTION_STRAFE_STATE` (const): const LOCOMOTION_STRAFE_STATE: "strafe" — State id of the lateral strafe blend.
341
+ - `LOCOMOTION_TURN_RATE_PARAM` (const): const LOCOMOTION_TURN_RATE_PARAM: "turnRate" — Signed yaw rate in radians/sec; a custom mover feeds it under `ANIM_PARAMS_KEY`.
342
+ - `LOCOMOTION_TURN_STATE` (const): const LOCOMOTION_TURN_STATE: "turn" — State id of the on-the-spot turn blend.
304
343
  - `LOCOMOTION_VERTICAL_SPEED_PARAM` (const): const LOCOMOTION_VERTICAL_SPEED_PARAM: "verticalSpeed" — Resolved vertical velocity from the shared player motor, in world units/sec.
305
- - `LocomotionGraphInput` (interface): interface LocomotionGraphInput — Inputs for {@link locomotionGraph}: the idle/walk/run clip names and the one-shot table a rig config already carries.
344
+ - `LocomotionGraphInput` (interface): interface LocomotionGraphInput — Inputs for {@link locomotionGraph}: the locomotion clip names and the one-shot table a rig config already carries.
306
345
  - `animGraphFromConfig` (function): function animGraphFromConfig(config: ModelAnimationConfig): AnimGraph | undefined — The graph a model animation config plays: its `graph`, or the {@link locomotionGraph} its `states` and `oneShots` describe, retaining every one-shot variant. `undefined` for a single-clip config or incomplete locomotion roles. Idle must name a clip; an absent or blank walk role holds that idle clip at walking speeds until configured. The shell plays this and the editor inspects it, so both see the same graph.
307
- - `locomotionGraph` (function): function locomotionGraph(input: LocomotionGraphInput): AnimGraph — The engine's default locomotion as an authored graph: a speed-driven blend between idle, walk, and run, plus a state per one-shot that plays once and returns (or clamps for `death`). What `useModelAnimation` used to hardcode.
346
+ - `locomotionGraph` (function): function locomotionGraph(input: LocomotionGraphInput): AnimGraph — The engine's default locomotion as an authored graph: a speed-driven blend between idle, walk and run, plus a state per one-shot that plays once and returns (or clamps for `death`). What `useModelAnimation` used to hardcode.
347
+
348
+ ## @jgengine/core/anim/ragdollRig
349
+
350
+ - `HUMANOID_RAGDOLL_BODIES` (const): const HUMANOID_RAGDOLL_BODIES: Readonly<Partial<Record<SkeletonSlot, RagdollBodyShape>>> — Humanoid body shapes for the standard slots, sized for a roughly 1.8 m character standing on the ground. A rig of another scale multiplies them (see {@link RagdollRigOptions.scale}).
351
+ - `RagdollBodyShape` (interface): interface RagdollBodyShape — Half-extents and mass of one standard slot's physics body, in the rig's own world units.
352
+ - `RagdollPose` (interface): interface RagdollPose — One standard slot's world pose, sampled at the moment a character is killed. The orientation is the reference the ragdoll's aim rotates from, so every bone keeps the roll it died with.
353
+ - `RagdollRigOptions` (interface): interface RagdollRigOptions — Tuning for {@link ragdollConfigFromPoses}.
354
+ - `ragdollBoneOrientations` (function): function ragdollBoneOrientations(rest: readonly RagdollPose[], positions: Readonly<Partial<Record<SkeletonSlot, readonly [number, number, number]>>>): Partial<Record<SkeletonSlot, [number, number, number, number]>> — World orientations for a ragdoll's bones from its bodies' current positions: each bone rotates from its rest direction-to-child onto the current one, so the aim follows the joints while every bone keeps the roll it had in the death pose. A bone with no posed child keeps its rest orientation. The physics bodies carry translation only, so this is what gives the ragdoll a skeleton instead of a pile of boxes.
355
+ - `ragdollConfigFromPoses` (function): function ragdollConfigFromPoses(map: SkeletonMap, poses: readonly RagdollPose[], options: RagdollRigOptions = {}): RagdollConfig — A jointed ragdoll for a mapped rig from its bones' world poses at the moment of death: one box body per standard slot that has a shape, sized to the character, and one distance link per slot that has its parent present, holding the bones' own separation. The bodies start where the character actually was, so the collapse continues the death pose instead of snapping to a preset.
356
+
357
+ ## @jgengine/core/anim/skeletonStandard
358
+
359
+ - `KAYKIT_SCHEME` (const): const KAYKIT_SCHEME: RigScheme — The standard itself, as a scheme: a KayKit rig already is the standard, so its own names are the candidates. Both authored (`upperleg.l`) and runtime (`upperlegl`) spellings are accepted.
360
+ - `LOWER_BODY_MASK` (const): const LOWER_BODY_MASK: readonly string[] — Layer mask for a lower-body layer on a standard rig.
361
+ - `LOWER_BODY_SLOTS` (const): const LOWER_BODY_SLOTS: readonly SkeletonSlot[] — Standard slots a lower-body layer drives: hips, both legs and both feet.
362
+ - `MIXAMO_SCHEME` (const): const MIXAMO_SCHEME: RigScheme — Mixamo exports (`mixamorig*` names, or the same names without the prefix). Mixamo has a neck and a shoulder per side, which the standard folds into `chest` and `upperArm`; its toe is `ToeBase`, which is the standard's `toes`. Mixamo's topmost bone is `Hips`, so `root` and `hips` both resolve to it — a rig on this scheme has no bone above the pelvis.
363
+ - `QUATERNIUS_ALIEN_SCHEME` (const): const QUATERNIUS_ALIEN_SCHEME: RigScheme — Quaternius sci-fi aliens: a `Root` plus a `Spine_1..Spine_6` chain, a `Neck` and a `Head`. The pack ships no arms and no legs, so those slots stay empty and the alien can only be driven on its spine and head. That is the honest mapping: the pack's own clip set is `Alien_Idle` alone.
364
+ - `RIG_SCHEMES` (const): const RIG_SCHEMES: Readonly<Record<string, RigScheme>> — Every scheme the engine ships a mapping for, keyed by {@link RigScheme.id}.
365
+ - `RigScheme` (interface): interface RigScheme — A source naming scheme and the bone names on it that fill each standard slot. A slot lists every accepted spelling; matching is case- and punctuation-insensitive, so `mixamorigLeftUpLeg` and `LeftUpLeg` are the same candidate.
366
+ - `SKELETON_PARENT` (const): const SKELETON_PARENT: Readonly<Partial<Record<SkeletonSlot, SkeletonSlot>>> — The parent slot of each slot that has one; `root` has none.
367
+ - `SKELETON_SLOTS` (const): const SKELETON_SLOTS: readonly SkeletonSlot[] — Every standard slot, in parent-before-child order.
368
+ - `SKELETON_STANDARD` (const): const SKELETON_STANDARD: SkeletonStandard — The one humanoid skeleton standard. Bone names are the authored KayKit names; the loaded hierarchy spells them without `.` and `resolveRigNode` accepts either.
369
+ - `SkeletonMap` (interface): interface SkeletonMap — The result of mapping a rig's bones onto {@link SKELETON_STANDARD}.
370
+ - `SkeletonSlot` (type): type SkeletonSlot = | "root" | "hips" | "spine" | "chest" | "head" | "upperArm.l" | "upperArm.r" | "lowerArm.l" | "lowerArm.r" | "hand.l" | "hand.r" | "handSlot.l" | "handSlot.r" | "upperLeg.l" | "upperLeg.r" | "lowerLeg.l" | "lowerLeg.r" | "foot.l" | "foot.r" | "toes.l" | "toes.r" — The engine's one humanoid skeleton standard, and the path any other CC0 rig takes onto it.
371
+ - `SkeletonStandard` (interface): interface SkeletonStandard — A named skeleton standard: its id and the authored bone name for each slot it defines.
372
+ - `SlotLength` (interface): interface SlotLength — A standard slot and the rest-pose length of the bone chain that reaches it.
373
+ - `UPPER_BODY_MASK` (const): const UPPER_BODY_MASK: readonly string[] — Layer mask for an upper-body action layer (aim, reload, hit reaction) on a standard rig.
374
+ - `UPPER_BODY_SLOTS` (const): const UPPER_BODY_SLOTS: readonly SkeletonSlot[] — Standard slots an upper-body action layer drives: spine, chest, head and both arms.
375
+ - `mapRigToStandard` (function): function mapRigToStandard(bones: readonly string[], scheme: RigScheme | string = KAYKIT_SCHEME): SkeletonMap — Maps a rig's bone names onto {@link SKELETON_STANDARD} through one {@link RigScheme}. Each slot takes the first candidate that resolves; a candidate list is tried in order, so a scheme can name a preferred bone and fall back. Bones that fill no slot come back in `unmapped`, and slots the rig cannot fill in `missing` — the caller decides whether a partial map is usable.
376
+ - `retargetBoneScales` (function): function retargetBoneScales(source: readonly SlotLength[], target: readonly SlotLength[]): Partial<Record<SkeletonSlot, number>> — Per-slot length ratios that stretch a source rig's bone chain onto the standard's proportions, so a retargeted clip moves both rigs the same distance rather than the same angle. Slots with no counterpart on either side are omitted; a source length of zero is skipped instead of dividing.
377
+ - `retargetTracks` (function): function retargetTracks(tracks: readonly string[], map: SkeletonMap): (string | null)[] — Renames a clip's tracks from a rig's own bone names onto the standard's, so one authored clip plays on a mapped rig. The emitted node is the **runtime** bone name (see {@link runtimeBoneName}), which is what both the loaded scene graph and the loaded clips spell. A track whose node fills no slot is dropped (`null` in the result) rather than left pointing at a bone the rig does not have; a track whose property is not one of the four three writes is dropped too, since renaming it would not make it resolvable.
378
+ - `runtimeBoneName` (function): function runtimeBoneName(authored: string): string — The runtime name GLTFLoader gives an authored bone name: three's `PropertyBinding.sanitizeNodeName` strips `[ ] . : /`, so the authored `upperleg.l` loads as `upperlegl`.
379
+ - `standardBoneMask` (function): function standardBoneMask(slots: readonly SkeletonSlot[]): string[] — Bone-name prefixes for a set of standard slots, for an {@link AnimLayer.mask}. A slot on one side contributes its side-less prefix (`upperLeg.l` → `upperleg`), which matches both sides' tracks; an unsided slot contributes its own runtime name. Masking by slot means a rig mapped onto the standard is masked without hand-typing its bone names.
308
380
 
309
381
  ## @jgengine/core/area/areaEffectField
310
382
 
@@ -1324,7 +1396,7 @@
1324
1396
  - `Ragdoll` (class): class Ragdoll — A jointed multi-body character — floppy by default, or active-ragdoll when a balance motor is configured. Bones are `PhysicsWorld` bodies; links are joints on the joint API. `balance(dt, mx, mz)` drives the root toward its target height (staying upright) and pushes it by the move vector.
1325
1397
  - `RagdollBalanceConfig` (interface): interface RagdollBalanceConfig { root: string; targetHeight: number; strength?: number; moveForce?: number } — ⚠ undocumented
1326
1398
  - `RagdollBoneSpec` (interface): interface RagdollBoneSpec { name: string; position: readonly [number, number, number]; halfExtents: readonly [number, number, number]; mass?: number } — ⚠ undocumented
1327
- - `RagdollConfig` (interface): interface RagdollConfig { bones: RagdollBoneSpec[]; links: RagdollLinkSpec[]; balance?: RagdollBalanceConfig } — ⚠ undocumented
1399
+ - `RagdollConfig` (interface): interface RagdollConfig { bones: RagdollBoneSpec[]; links: RagdollLinkSpec[]; balance?: RagdollBalanceConfig } — ⚠ undocumented · used by `ragdollConfigFromPoses` (@jgengine/core/anim/ragdollRig): A jointed ragdoll for a mapped rig from its bones' world poses at the moment of death: one box body per standard slot that has a shape, size…
1328
1400
  - `RagdollLinkSpec` (interface): interface RagdollLinkSpec { a: string; b: string; kind?: "hinge" | "distance"; anchor?: readonly [number, number, number] } — ⚠ undocumented
1329
1401
  - `createRagdoll` (function): function createRagdoll(world: PhysicsWorld, config: RagdollConfig): Ragdoll — ⚠ undocumented
1330
1402
 
@@ -1754,7 +1826,7 @@
1754
1826
 
1755
1827
  - `SceneEntityQuerySource` (interface): interface SceneEntityQuerySource { list(): readonly {id: string; position: EntityPosition; rotationY: number; name?: string}[]; collidersOf?(instanceId: string): EntityColliderSet | null | undefined; inRadius?(center: EntityPosition, radius: number): readonly string[]; get… — ⚠ undocumented
1756
1828
  - `SceneObjectQuerySource` (interface): interface SceneObjectQuerySource { list(): readonly SceneObject[]; inBox?(min: EntityPosition, max: EntityPosition): readonly SceneObject[]; collidersOf?(instanceId: string): EntityColliderSet | null | undefined; halfExtentsOf?(catalogId: string): EntityPosition | null | u… — ⚠ undocumented
1757
- - `SceneRaycastApi` (interface): interface SceneRaycastApi { raycast(input: SceneRaycastInput): SceneRaycastHit | null; raycastAll(input: SceneRaycastInput): SceneRaycastHit[] } — ⚠ undocumented
1829
+ - `SceneRaycastApi` (interface): interface SceneRaycastApi { raycast(input: SceneRaycastInput): SceneRaycastHit | null; raycastAll(input: SceneRaycastInput): SceneRaycastHit[] } — ⚠ undocumented · used by `sceneCameraQuery` (@jgengine/shell/camera): `CameraCollisionQuery` over the scene's existing collision query (`ctx.scene.raycast`), which already reads world solids, scene-object colli…
1758
1830
  - `SceneRaycastDeps` (interface): interface SceneRaycastDeps { entities?: SceneEntityQuerySource; objects?: SceneObjectQuerySource; terrain?: TerrainRaycastSource; walls?: readonly WallSegment[]; solids?: Pick<WorldSolids, "inBox" | "inRay"> } — ⚠ undocumented
1759
1831
  - `SceneRaycastFilter` (interface): interface SceneRaycastFilter { entities?: boolean; objects?: boolean; terrain?: boolean; walls?: boolean } — ⚠ undocumented
1760
1832
  - `SceneRaycastHit` (interface): interface SceneRaycastHit { targetKind: SceneRaycastTargetKind; instanceId: string; catalogId?: string; colliderName: string; purpose: ColliderPurpose; damageEligible: boolean; blocks: boolean; distance: number; point: EntityPosition; normal: EntityPosition } — ⚠ undocumented · used by `firstImpact`: First impact: nearest hit that blocks, or nearest hit if none block.
@@ -2026,6 +2098,20 @@
2026
2098
  - `HitInput` (interface): interface HitInput — A single incoming hit to register on a {@link DamageDirectionTracker}. The angle is relative to the player's facing (renderer-agnostic): `0` points at the front/top of the reticle and increases clockwise, so a game passes the bearing from the player toward the attacker without knowing anything about the screen.
2027
2099
  - `createDamageDirectionTracker` (function): function createDamageDirectionTracker(options: DamageDirectionOptions = {}): DamageDirectionTracker — Create a damage-direction tracker: the classic "hit-from" feedback brain. A game calls `registerHit({ angle, intensity, kind })` with the bearing from the player toward the attacker (radians, `0` = front) and the tracker owns the fade timers and eased strength so the renderer just draws an arc per `active()` entry. It is renderer-free and genre-agnostic (the `kind` tag is never interpreted here), allocation-aware (a fixed pool, no per-frame garbage), and fully serializable via `snapshot`/`restore`. Optional angle merging collapses a burst from one direction into a single strong arc.
2028
2100
 
2101
+ ## @jgengine/core/vfx/effectLibrary
2102
+
2103
+ - `DEFAULT_EFFECT` (const): const DEFAULT_EFFECT: "sparks/weld" — The fallback effect used when a play call names an unknown effect — a typo still shows a spark.
2104
+ - `EFFECT_LIBRARY` (const): const EFFECT_LIBRARY: readonly ParticleEffect[] — The shared library of named particle effects: fire, smoke, sparks, dust, splashes, muzzle flash, explosions, magic and weather. Each entry is authored parameters only — rate, lifetime, size, colour and the sprite texture — so a game calls `ctx.particles.play("explosion/core", at)` and the editor authors the same name instead of hand-tuning an `EmitterConfig`. Names are the contract; a config retune here changes every consumer deliberately.
2105
+ - `EFFECT_PRIORITY` (const): const EFFECT_PRIORITY: { readonly weather: 20; readonly dust: 40; readonly smoke: 60; readonly magic: 70; readonly sparks: 70; readonly fire: 80; readonly splash: 80; readonly muzzle: 90; readonly explosion: 90; } — Ambient dressing drops first, then gameplay-loud effects last.
2106
+ - `EffectGroup` (type): type EffectGroup = | "fire" | "smoke" | "sparks" | "dust" | "splash" | "muzzle" | "explosion" | "magic" | "weather" — The family an effect belongs to; groups the library browser and the contact sheet.
2107
+ - `EffectOverride` (interface): interface EffectOverride — Per-call overrides merged over a library effect; each field wins over the authored default.
2108
+ - `EffectTexture` (type): type EffectTexture = "soft" | "spark" | "flake" | "flame" | "smoke" | "ribbon" | "ring" — The procedural sprite family a particle draws with on the GPU path. Each family maps to a {@link ParticleRenderHint} shape: `soft` puffs, velocity-stretched `spark` streaks, `flake` crystals, `flame` tongues, `smoke` puffs, connected `ribbon` trails and flat `ring` ripples. There is no image file to load or credit — the field is the authored name for the sprite.
2109
+ - `ParticleEffect` (interface): interface ParticleEffect — One named effect a game or the editor plays by name. Every field is data: the `config` is the same `EmitterConfig` a hand-authored emitter uses, so a library effect serializes, retunes and renders on the existing pooled GPU path without a second implementation.
2110
+ - `ResolvedEffect` (interface): interface ResolvedEffect — A library effect resolved into the exact data the director and renderer consume.
2111
+ - `effectNames` (function): function effectNames(): readonly string[] — Every library effect name, for editor pickers and runtime discovery.
2112
+ - `findEffect` (function): function findEffect(name: string): ParticleEffect | undefined — The named effect, or `undefined` when the name is not in the library.
2113
+ - `resolveEffect` (function): function resolveEffect(name: string, override?: EffectOverride): ResolvedEffect — Resolve a named effect into concrete emitter data, with any `override` winning field by field. An unknown name resolves to {@link DEFAULT_EFFECT} rather than throwing, so a typo shows *something*; call {@link findEffect} when the caller must reject an unknown name.
2114
+
2029
2115
  ## @jgengine/core/vfx/feedbackMixer
2030
2116
 
2031
2117
  - `FeedbackCurve` (type): type FeedbackCurve = readonly (readonly [number, number])[] — Piecewise-linear map from a signal value to an output value; points in ascending input order, ends clamp.
@@ -2037,8 +2123,16 @@
2037
2123
  - `createFeedbackMixer` (function): function createFeedbackMixer<TSignal extends string, TTarget extends string>(initial: FeedbackMixerConfig<TSignal, TTarget>): FeedbackMixer<TSignal, TTarget> — Creates a {@link FeedbackMixer}: the one place a game declares how sim telemetry drives presentation — rpm to engine pitch, tire saturation to squeal and rumble, speed to FOV, landing speed to a thud — instead of hand-writing that glue per vehicle, aircraft or weapon. Deterministic, allocation-free per update.
2038
2124
  - `sampleFeedbackCurve` (function): function sampleFeedbackCurve(curve: FeedbackCurve | undefined, x: number): number — Sample a {@link FeedbackCurve}; an empty curve is the identity.
2039
2125
 
2126
+ ## @jgengine/core/vfx/particleBudget
2127
+
2128
+ - `ParticleBudgetEntry` (interface): interface ParticleBudgetEntry — The graphics-tier particle budget. A tier caps the live particle count across every effect on screen; when the requested effects do not fit, whole effects are dropped in a defined order (lowest {@link ParticleBudgetEntry.priority} first) rather than every effect thinning out.
2129
+ - `ParticleBudgetGrant` (interface): interface ParticleBudgetGrant — An admitted effect and the particles it may hold.
2130
+ - `ParticleBudgetPlan` (interface): interface ParticleBudgetPlan — What the tier cap admits, and what it drops.
2131
+ - `planParticleBudget` (function): function planParticleBudget(entries: readonly ParticleBudgetEntry[], cap: number): ParticleBudgetPlan — Allocate a tier's particle cap across the effects that want it.
2132
+
2040
2133
  ## @jgengine/core/vfx/particleDirector
2041
2134
 
2135
+ - `EffectRequest` (interface): interface EffectRequest extends EffectOverride — A library effect played by name: where it plays, plus per-call overrides.
2042
2136
  - `ParticleAttachOptions` (interface): interface ParticleAttachOptions — Options for {@link ParticleDirector.attach}.
2043
2137
  - `ParticleBlendHint` (type): type ParticleBlendHint = "additive" | "normal" — How a particle effect composites on screen: `additive` for sparks/fire/glow, `normal` for smoke/dust.
2044
2138
  - `ParticleBurst` (interface): interface ParticleBurst — A one-shot burst request — consumed once by the renderer, never serialized.
@@ -2324,6 +2418,8 @@
2324
2418
  - `DEFAULT_LANDMARK_SHARE` (const): const DEFAULT_LANDMARK_SHARE: 0.04 — Default grand-plot share dial — a handful of block-scale parcels per default city.
2325
2419
  - `DEFAULT_MARKER_KINDS` (const): const DEFAULT_MARKER_KINDS: Record<string, MarkerKindStyle> — ⚠ undocumented
2326
2420
  - `DEFAULT_MINIMAP_PALETTE` (const): const DEFAULT_MINIMAP_PALETTE: MinimapBakePalette — The built-in height→color ramp and water color a bake uses when no palette override is given.
2421
+ - `DEFAULT_POLICE_TIERS` (const): const DEFAULT_POLICE_TIERS: readonly PoliceTier[] — The starting tier table: patrol until heat arrives, then investigate, pursue and finally cordon.
2422
+ - `DEFAULT_POLICE_TUNING` (const): const DEFAULT_POLICE_TUNING: PoliceTuning — The starting policy: linear decay, a six-second escape and a search area that grows at 2 m/s.
2327
2423
  - `DEFAULT_REPUTATION_TIERS` (const): const DEFAULT_REPUTATION_TIERS: readonly ReputationTier[] — ⚠ undocumented
2328
2424
  - `DamageDirectionOptions` (interface): interface DamageDirectionOptions — Options for {@link createDamageDirectionTracker}.
2329
2425
  - `DamageDirectionSnapshot` (interface): interface DamageDirectionSnapshot — Serializable state for save/restore — the clock reading plus every live indicator.
@@ -2401,6 +2497,7 @@
2401
2497
  - `HeatConfig` (interface): interface HeatConfig — Tuning for {@link createHeatState}/{@link advanceHeat} — levels, decay, and pursuit-spawn ring.
2402
2498
  - `HeatGain` (interface): interface HeatGain — One crime tick's contribution — only `witnessed` gains raise heat (unseen crimes are free, GTA-style).
2403
2499
  - `HeatLevelDef` (interface): interface HeatLevelDef — One escalation tier — the heat threshold it begins at and the pursuer count it wants active.
2500
+ - `HeatPoint` (type): type HeatPoint = readonly [number, number] — A world-space `[x, z]` used for the heat system's spawn ring and witness proximity checks.
2404
2501
  - `HeatSource` (interface): interface HeatSource — A localized warmth source — campfire, forge, geothermal vent.
2405
2502
  - `HeatState` (interface): interface HeatState — Serializable heat-system state — round-trips through `createHeatState`/`advanceHeat` each tick.
2406
2503
  - `HeightSampler` (type): type HeightSampler = (x: number, z: number) => number — A height sampler over the ground: world elevation at any `x`/`z`.
@@ -2579,6 +2676,22 @@
2579
2676
  - `PlatformCarry` (class): class PlatformCarry — Carries bodies standing on a moving platform by composing their transform with the platform's per-`step` delta — moving/rotating lifts and conveyor floors (Fall Guys, Gang Beasts). The platform is a body the game repositions each frame; riders are detected by overlap on its top face.
2580
2677
  - `Point3` (type): type Point3 = readonly [number, number, number] — Deterministic spatial sampling — draw one or many positions from a geometric region under explicit constraints, with an injected RNG, bounded attempts, and a structured pass/fail result.
2581
2678
  - `PointGravityOptions` (interface): interface PointGravityOptions — Center, strength, and falloff for radial gravity.
2679
+ - `PoliceLaneHit` (interface): interface PoliceLaneHit — A lane near a world point: its id, the arc of the nearest point on it, and how far away that is.
2680
+ - `PoliceResponse` (interface): interface PoliceResponse — Stateful wanted/pursuit API with retuning and save/restore support.
2681
+ - `PoliceResponseConfig` (interface): interface PoliceResponseConfig extends Partial<PoliceTuning> — Everything {@link createPoliceResponse} needs: the witness seam, the roster and the caller's policy.
2682
+ - `PoliceResponseKind` (type): type PoliceResponseKind = "patrol" | "investigate" | "pursue" | "roadblock" — How police answer the current heat tier — watching the streets up to cordoning them.
2683
+ - `PoliceResponseState` (interface): interface PoliceResponseState — Serializable response state — the heat curve, the sight timer, the anchor and the counters.
2684
+ - `PoliceRoadblockSite` (interface): interface PoliceRoadblockSite — A roadblock site on a lane, resolved from authored road data.
2685
+ - `PoliceRoads` (interface): interface PoliceRoads — Road data the response reads: nearby lanes for the search area, authored sites for roadblocks.
2686
+ - `PoliceRoadsOptions` (interface): interface PoliceRoadsOptions — How the document reader recognizes authored road content.
2687
+ - `PoliceSearchArea` (interface): interface PoliceSearchArea — Where the search is, as the last sighting places it.
2688
+ - `PoliceSighting` (interface): interface PoliceSighting — One remembered sighting of the suspect, as the perception service reports it — `PerceptionMemory` from `sensor/perception` satisfies this shape.
2689
+ - `PoliceStats` (interface): interface PoliceStats — Counters a devtools probe, a HUD or a test reads.
2690
+ - `PoliceStep` (interface): interface PoliceStep — One tick's answer: the heat, the tier, the search area, the cordon and the escape.
2691
+ - `PoliceTickContext` (interface): interface PoliceTickContext — Per-tick world facts {@link PoliceResponse.tick} needs but cannot derive itself.
2692
+ - `PoliceTier` (interface): interface PoliceTier — One response tier: the heat it begins at, what it puts on the street, and how wide it searches.
2693
+ - `PoliceTuning` (interface): interface PoliceTuning — The serializable policy of a response: the tier table, the heat curve and the search shape.
2694
+ - `PoliceWitness` (interface): interface PoliceWitness — The witness seam: perception memory, queried by observer id. Pass `createPerception` (`sensor/perception`) straight in — a crime is witnessed when an observer still holds the suspect in view, so there is no second sight path to keep in step.
2582
2695
  - `PositionedPrompt` (interface): interface PositionedPrompt { id: string; position: PromptPoint; priority?: number; prompt: ProximityPrompt } — ⚠ undocumented · used by `resolveActivePrompt`: Nearest prompt strictly within its radius wins; a higher-priority prompt in range beats any lower-priority one regardless of distance; equal…
2583
2696
  - `PositionedPromptPatch` (type): type PositionedPromptPatch = Partial<Omit<PositionedPrompt, "id">> — A partial edit applied to an already-registered {@link PositionedPrompt} by {@link PromptRegistry.update}. Any subset of the mutable fields — everything but the stable `id` — may be supplied; omitted fields keep their prior value.
2584
2697
  - `PromptRegistry` (interface): interface PromptRegistry — A live, observable set of interaction prompts that OWNS the registered {@link PositionedPrompt}s and tracks which one is active for a player. It is a thin stateful layer over {@link resolveActivePrompt}: registration/order lives here, the nearest-within-radius / higher-priority selection stays in the pure resolver. {@link resolve} is the per-frame hot path — call it as the player moves; subscribers fire only when the active prompt actually CHANGES.
@@ -2651,7 +2764,7 @@
2651
2764
  - `SceneKindResolveContext` (interface): interface SceneKindResolveContext — Ground sampler + options a resolver may read (terrain height/normal snap).
2652
2765
  - `SceneObject` (interface): interface SceneObject { instanceId: string; catalogId: string; position: EntityPosition; rotationY: number; parentSpace?: string; visual?: ObjectVisual; animation?: ModelAnimationConfig | "auto" | "none"; state?: Record<string, unknown>; slots?: InventoryState } — ⚠ undocumented · used by `fromRuntimeObjectRow` (@jgengine/core/runtime/objectRows): Inverse of {@link toRuntimeObjectRow}: rebuild the live placed object a host persisted.
2653
2766
  - `SceneObjectSlots` (interface): interface SceneObjectSlots — Per-instance container contents for placed objects whose catalog entry declares `slotInventory` — the engine side of "install a GPU into a rack", "put an item in a chest". Contents live on `SceneObject.slots`, so they replicate and save with the object rather than needing a game-owned table keyed by `instanceId`, and every mutation is validated against the declared `accepts` and stack limits.
2654
- - `SceneRaycastApi` (interface): interface SceneRaycastApi { raycast(input: SceneRaycastInput): SceneRaycastHit | null; raycastAll(input: SceneRaycastInput): SceneRaycastHit[] } — ⚠ undocumented
2767
+ - `SceneRaycastApi` (interface): interface SceneRaycastApi { raycast(input: SceneRaycastInput): SceneRaycastHit | null; raycastAll(input: SceneRaycastInput): SceneRaycastHit[] } — ⚠ undocumented · used by `sceneCameraQuery` (@jgengine/shell/camera): `CameraCollisionQuery` over the scene's existing collision query (`ctx.scene.raycast`), which already reads world solids, scene-object colli…
2655
2768
  - `SceneRaycastHit` (interface): interface SceneRaycastHit { targetKind: SceneRaycastTargetKind; instanceId: string; catalogId?: string; colliderName: string; purpose: ColliderPurpose; damageEligible: boolean; blocks: boolean; distance: number; point: EntityPosition; normal: EntityPosition } — ⚠ undocumented · used by `firstImpact`: First impact: nearest hit that blocks, or nearest hit if none block.
2656
2769
  - `ScreenEffect` (interface): interface ScreenEffect — A live effect in the current composite: its label, color, region, and the eased opacity `0..1` to draw *right now*. These objects are pooled and reused across {@link ScreenEffectsController.composite} calls — read them, don't retain.
2657
2770
  - `ScreenEffectEasing` (type): type ScreenEffectEasing = "linear" | "easeIn" | "easeOut" | "easeInOut" — How a transient effect's intensity curves from peak to zero over its life.
@@ -2929,6 +3042,7 @@
2929
3042
  - `createPlacedStructureStore` (function): function createPlacedStructureStore(): PlacedStructureStore — ⚠ undocumented
2930
3043
  - `createPlacementController` (function): function createPlacementController(config: PlacementControllerConfig): PlacementController — Headless placement ghost: hover → valid/invalid preview, rotate, grid/free/surface snap, commit. Pair with `@jgengine/shell/structures` `PlacementGhost` and {@link placeAssetFromCommit}.
2931
3044
  - `createPlotPermissions` (function): function createPlotPermissions(config: PlotPermissionConfig): PlotPermissions — ⚠ undocumented
3045
+ - `createPoliceResponse` (function): function createPoliceResponse(config: PoliceResponseConfig): PoliceResponse — The wanted level a crime sandbox needs, over the shared heat curve: a crime raises heat only while a witness holds the suspect in view, the heat bleeds on a stated linear curve once the suspect has been out of every witness's sight for `escapeSeconds`, the tier table decides what police do (patrol → investigate → pursue → roadblock), the search area sits where the suspect was last seen and grows while the trail is cold, and a roadblock tier cordons it with authored sites off the lane graph. Heat, tiers and curves are caller policy; witness facts come from `sensor/perception` memory (`sourceId`/`sourceTick` kept), so there is no second sight path.
2932
3046
  - `createPoseState` (function): function createPoseState(resolveAllowed: (instanceId: string) => PoseAllowedStates | null | undefined): PoseState — Stance/pose transitions — stand, crouch, prone — that change the hitbox and movement.
2933
3047
  - `createPromptRegistry` (function): function createPromptRegistry(): PromptRegistry — Create a live, observable interaction-prompt registry over the pure {@link resolveActivePrompt} resolver. Register positioned "press E" prompts, `resolve(playerPosition)` each frame as the hero moves, and read `active()` — the nearest strictly-in-range prompt (higher `priority` beats closer). The set is owned here (register/update/unregister/clear/all); selection stays pure. Subscribers are notified only when the active prompt *changes*, so a HUD does not thrash per frame, and `snapshot`/`restore` round-trip the prompts plus the active id through a save (display payloads are free strings/ids the game owns — the registry never branches on their meaning). Allocation-aware: `all()` and `resolve()` reuse a cached array instead of rebuilding per call.
2934
3048
  - `createRagdoll` (function): function createRagdoll(world: PhysicsWorld, config: RagdollConfig): Ragdoll — ⚠ undocumented
@@ -3058,6 +3172,7 @@
3058
3172
  - `pointInPolygon` (function): function pointInPolygon(point: Vec2, polygon: readonly Vec2[]): boolean — Ray-casting point-in-polygon test on the XZ plane.
3059
3173
  - `pointInVolume` (function): function pointInVolume(volume: SceneVolumeLike, point: { x: number; y: number; z: number }): boolean — True when `point` is inside an editor volume (sphere / cylinder / box). Cylinder height defaults to diameter when omitted; sphere ignores y for the common ground-plane case only when the volume radius covers the full vertical span — here y is tested for sphere and box too.
3060
3174
  - `pointSetRegion` (function): function pointSetRegion(points: readonly Vec2[], options: { weights?: readonly number[] } = {}): SampleRegion<Vec2> — A fixed set of candidate points sampled by (optionally weighted) selection — the "spawn point table" region. `contains` is exact membership. Empty when the set is empty.
3175
+ - `policeRoadsFromLaneGraph` (function): function policeRoadsFromLaneGraph(graph: LaneGraph, markers: readonly EditorMarker[] = [], options: PoliceRoadsOptions = {}): PoliceRoads — Read the police road data out of an authored lane graph: `traffic_roadblock` markers become cordon sites projected onto the lane they block, and lanes are indexed so `lanesNear` answers from the local cells rather than a scan of every lane. With no roadblock markers authored, the graph's intersections are the sites, so a cordon still sits where streets meet. Built once; the index is rebuildable from the document, so it is never serialized.
3061
3176
  - `polygonArea` (function): function polygonArea(polygon: readonly Vec2[]): number — Shoelace area of a polygon (always non-negative), in square meters.
3062
3177
  - `polygonBounds` (function): function polygonBounds(polygon: readonly Vec2[]): Aabb | null — Axis-aligned bounds of a polygon, or null if it has no points.
3063
3178
  - `polygonRegion` (function): function polygonRegion(polygon: readonly Vec2[]): SampleRegion<Vec2> — An arbitrary closed polygon on the XZ plane. Candidates are drawn uniformly from the polygon's bounding box and gated by point-in-polygon `contains`, so a run's draw count stays fixed (the sampler's attempt budget bounds the rejection, not a hidden inner loop).
@@ -3363,6 +3478,13 @@
3363
3478
  - `resolveBuildingProbabilities` (function): function resolveBuildingProbabilities(style: BuildingStyle = DEFAULT_BUILDING_STYLE, overrides?: Partial<BuildingProbabilities>): Partial<BuildingProbabilities> — Style facade probabilities with explicit overrides applied on top.
3364
3479
  - `resolveBuildingSurface` (function): function resolveBuildingSurface(surface: BuildingSurface): BuildingSurfaceMaterial — Normalises a surface to its material form so a renderer reads one shape.
3365
3480
 
3481
+ ## @jgengine/core/world/captureCamera
3482
+
3483
+ - `CaptureCameraOverride` (interface): interface CaptureCameraOverride — Per-session capture-camera override — the harness counterpart of `?cam=`/`?look=`, and the same install-style seam as `world/spawnOverride`: the runner installs one, the shell reads it, nothing changes until one is installed.
3484
+ - `clearCaptureCamera` (function): function clearCaptureCamera(): void — Clear any installed capture camera.
3485
+ - `installCaptureCamera` (function): function installCaptureCamera(override: CaptureCameraOverride | null): void — Install (or, with `null`, clear) the capture camera. Idempotent; last write wins. It outranks the game's static camera config and any runtime `ctx.camera` rig selection, and it is never saved — it is a property of this capture session, not of the game.
3486
+ - `readCaptureCamera` (function): function readCaptureCamera(): CaptureCameraOverride | null — The installed capture camera, or `null` when the game's own camera should render.
3487
+
3366
3488
  ## @jgengine/core/world/carve
3367
3489
 
3368
3490
  - `CarvableField` (class): class CarvableField implements TerrainField — A `TerrainField` you can write craters and mounds into at runtime — the height-field side of destructible terrain (Helldivers 2 explosion craters, engineer-deposited berms). Wraps a base field and layers smooth radial deformations on top, so `sampleHeight` (and therefore ground-snap, collision, and the shell's terrain mesh) all read the deformed surface. `carve` digs a bowl, `deposit` raises a mound.
@@ -3631,7 +3753,7 @@
3631
3753
  - `TerrainIslandConfig` (interface): interface TerrainIslandConfig extends TerrainEnvironmentConfig — A bounded terrain patch floating at its own altitude — sky islands, arena platforms, split landmasses with void between.
3632
3754
  - `TerrainIslandDescriptor` (type): type TerrainIslandDescriptor = Omit<TerrainEnvironmentDescriptor, "kind"> & { kind: "island"; origin: EnvironmentVec2; } — ⚠ undocumented
3633
3755
  - `TerrainMaterial` (type): type TerrainMaterial = "grass" | "sand" | "snow" | "rock" | "ash" | "highland" | "slate" — ⚠ undocumented · used by `TERRAIN_MATERIALS` (@jgengine/core/editor): The default terrain paint palette (surface id → color) shared by the panel and the mesh.
3634
- - `TerrainMaterialMaps` (interface): interface TerrainMaterialMaps — PBR map URLs for a real ground texture — the same shape `buildMaterialCatalog({ basePath }).resolve(id)!.maps` from `@jgengine/assets` returns. Kept dependency-free here so `core` never imports the assets package; any URLs (pulled maps, a CDN, a data URI) satisfy it. Omitted roughness keeps the terrain scalar; omitted AO skips occlusion sampling.
3756
+ - `TerrainMaterialMaps` (interface): interface TerrainMaterialMaps — PBR map URLs for a real ground texture — the same shape `libraryTerrainMaterial(name).maps` from `@jgengine/assets` returns. Kept dependency-free here so `core` never imports the assets package; any URLs (pulled maps, a CDN, a data URI) satisfy it. Omitted roughness keeps the terrain scalar; omitted AO skips occlusion sampling.
3635
3757
  - `TerrainMaterialRegion` (type): type TerrainMaterialRegion = TerrainCircleRegion | TerrainPolylineRegion | TerrainRectRegion — A palette zone painted over the base terrain palette. Circle (the default when no `shape` is given), `polyline` ribbons for roads/rivers, and rotatable `rect` districts all paint fully inside their core and blend back across `falloff`; later regions in the list win overlaps.
3636
3758
  - `TerrainPolylineRegion` (interface): interface TerrainPolylineRegion extends TerrainRegionStyle — A ribbon palette zone following a centerline — roads and rivers, instead of chaining overlapping circles.
3637
3759
  - `TerrainRectRegion` (interface): interface TerrainRectRegion extends TerrainRegionStyle — A rectangular palette zone, optionally rotated about the world y axis — plazas, fields, districts.
@@ -34,15 +34,21 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
34
34
  - `parseAnimGraph` (function) · `import { parseAnimGraph } from "@jgengine/core/anim/animGraph"`
35
35
  - `validateAnimGraph` (function) · `import { validateAnimGraph } from "@jgengine/core/anim/animGraph"`
36
36
 
37
- ## animation — find thigh, shin and foot bones on a humanoid rig for foot IK
37
+ ## animation — build a masked one-shot overlay layer for upper-body actions and hit reactions
38
38
 
39
+ - `actionLayer` (function) · `import { actionLayer } from "@jgengine/core/anim/actionLayer"`
39
40
  - `inferLegChains` (function) · `import { inferLegChains } from "@jgengine/core/anim/footPlacement"`
40
41
  - `lookAt` (function) · `import { lookAt } from "@jgengine/core/anim/ikSolver"`
42
+ - `multiplyQuaternions` (function) · `import { multiplyQuaternions } from "@jgengine/core/anim/ikSolver"`
41
43
  - `placeFeet` (function) · `import { placeFeet } from "@jgengine/core/anim/footPlacement"`
42
44
  - `planBoneTexture` (function) · `import { planBoneTexture } from "@jgengine/core/anim/boneTexture"`
45
+ - `quaternionFromUnitVectors` (function) · `import { quaternionFromUnitVectors } from "@jgengine/core/anim/ikSolver"`
46
+ - `ragdollBoneOrientations` (function) · `import { ragdollBoneOrientations } from "@jgengine/core/anim/ragdollRig"`
47
+ - `ragdollConfigFromPoses` (function) · `import { ragdollConfigFromPoses } from "@jgengine/core/anim/ragdollRig"`
43
48
  - `sampleBoneTexture` (function) · `import { sampleBoneTexture } from "@jgengine/core/anim/boneTexture"`
44
49
  - `solveFabrik` (function) · `import { solveFabrik } from "@jgengine/core/anim/ikSolver"`
45
50
  - `solveTwoBone` (function) · `import { solveTwoBone } from "@jgengine/core/anim/ikSolver"`
51
+ - `withActionLayers` (function) · `import { withActionLayers } from "@jgengine/core/anim/actionLayer"`
46
52
 
47
53
  ## area-effect-field — source-following area membership with enter/refresh/leave edges, stacking, and cleanup
48
54
 
@@ -218,6 +224,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
218
224
 
219
225
  - `createCameraShake` (function) · `import { createCameraShake } from "@jgengine/core/world"`
220
226
 
227
+ ## capture — force a framed capture camera over a game that owns its camera
228
+
229
+ - `installCaptureCamera` (function) · `import { installCaptureCamera } from "@jgengine/core/world/captureCamera"`
230
+
221
231
  ## character-controller — capsule walk over the physics backend — slide, step-up, slopes, crouch, platforms
222
232
 
223
233
  - `createCharacterController` (function) · `import { createCharacterController } from "@jgengine/core/movement/characterController"`
@@ -312,6 +322,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
312
322
 
313
323
  - `tickDrivableVehicle` (function) · `import { tickDrivableVehicle } from "@jgengine/core/world"`
314
324
 
325
+ ## effect-resolve — turn a named library effect into emitter config, GPU sprite texture, blending and budget priority with per-call overrides
326
+
327
+ - `resolveEffect` (function) · `import { resolveEffect } from "@jgengine/core/vfx/effectLibrary"`
328
+
315
329
  ## elevation-readout — cursor height and delta-from-reference feedback
316
330
 
317
331
  - `sampleElevation` (function) · `import { sampleElevation } from "@jgengine/core/world"`
@@ -340,6 +354,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
340
354
 
341
355
  - `createFeedbackMixer` (function) · `import { createFeedbackMixer } from "@jgengine/core/vfx/feedbackMixer"`
342
356
 
357
+ ## find-effect — look up one named particle effect in the shared library, or undefined when unknown
358
+
359
+ - `findEffect` (function) · `import { findEffect } from "@jgengine/core/vfx/effectLibrary"`
360
+
343
361
  ## 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
344
362
 
345
363
  - `ActionCodesMap` (type) · `import { ActionCodesMap } from "@jgengine/core/input/actionBindings"`
@@ -670,10 +688,22 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
670
688
  - `parseRotationPolicy` (function) · `import { parseRotationPolicy } from "@jgengine/core/world"`
671
689
  - `snapHeading` (function) · `import { snapHeading } from "@jgengine/core/world"`
672
690
 
691
+ ## plan-particle-budget — cap live particles to a graphics tier and drop whole effects lowest-priority-first when over budget
692
+
693
+ - `planParticleBudget` (function) · `import { planParticleBudget } from "@jgengine/core/vfx/particleBudget"`
694
+
673
695
  ## pole-line — editor-authorable poles + sagging cables along a path
674
696
 
675
697
  - `POLE_LINE_KIND` (const) · `import { POLE_LINE_KIND } from "@jgengine/core/world/poleLineKind"`
676
698
 
699
+ ## police-response — witness-gated heat, tiered police response, last-sighting search areas and lane-graph roadblocks
700
+
701
+ - `createPoliceResponse` (function) · `import { createPoliceResponse } from "@jgengine/core/world"`
702
+
703
+ ## police-roads — lane-graph roadblock sites and an indexed nearby-lane query for search areas
704
+
705
+ - `policeRoadsFromLaneGraph` (function) · `import { policeRoadsFromLaneGraph } from "@jgengine/core/world"`
706
+
677
707
  ## population-director — ambient per-region creature population manager with timed weighted respawns and caps
678
708
 
679
709
  - `createPopulationDirector` (function) · `import { createPopulationDirector } from "@jgengine/core/ai/populationDirector"`
@@ -755,6 +785,16 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
755
785
 
756
786
  - `createSharedAnnotations` (function) · `import { createSharedAnnotations } from "@jgengine/core/world"`
757
787
 
788
+ ## skeleton-standard — map a rig on the standard scheme onto the standard's slots
789
+
790
+ - `KAYKIT_SCHEME` (const) · `import { KAYKIT_SCHEME } from "@jgengine/core/anim/skeletonStandard"`
791
+ - `SKELETON_STANDARD` (const) · `import { SKELETON_STANDARD } from "@jgengine/core/anim/skeletonStandard"`
792
+ - `mapRigToStandard` (function) · `import { mapRigToStandard } from "@jgengine/core/anim/skeletonStandard"`
793
+ - `retargetBoneScales` (function) · `import { retargetBoneScales } from "@jgengine/core/anim/skeletonStandard"`
794
+ - `retargetTracks` (function) · `import { retargetTracks } from "@jgengine/core/anim/skeletonStandard"`
795
+ - `runtimeBoneName` (function) · `import { runtimeBoneName } from "@jgengine/core/anim/skeletonStandard"`
796
+ - `standardBoneMask` (function) · `import { standardBoneMask } from "@jgengine/core/anim/skeletonStandard"`
797
+
758
798
  ## skill-check — a timing-bar skill check where you release inside a zone
759
799
 
760
800
  - `skillCheckZoneAt` (function) · `import { skillCheckZoneAt } from "@jgengine/core/world"`