@jgengine/core 0.24.0 → 0.26.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 (98) hide show
  1. package/CHANGELOG.md +92 -0
  2. package/dist/ai/rvo.d.ts +106 -0
  3. package/dist/ai/rvo.js +581 -0
  4. package/dist/ai/utilityBrain.d.ts +173 -0
  5. package/dist/ai/utilityBrain.js +277 -0
  6. package/dist/combat/cover.d.ts +169 -0
  7. package/dist/combat/cover.js +264 -0
  8. package/dist/combat/damageRuntime.d.ts +150 -0
  9. package/dist/combat/damageRuntime.js +208 -0
  10. package/dist/combat/damageTable.d.ts +116 -0
  11. package/dist/combat/damageTable.js +165 -0
  12. package/dist/combat/hitZones.d.ts +70 -0
  13. package/dist/combat/hitZones.js +77 -0
  14. package/dist/combat/magazine.d.ts +8 -0
  15. package/dist/combat/magazine.js +20 -2
  16. package/dist/combat/squadCombat.d.ts +167 -0
  17. package/dist/combat/squadCombat.js +557 -0
  18. package/dist/combat/squadTactics.d.ts +73 -0
  19. package/dist/combat/squadTactics.js +49 -0
  20. package/dist/combat/statusRuntime.d.ts +142 -0
  21. package/dist/combat/statusRuntime.js +236 -0
  22. package/dist/combat/weaponDefinition.d.ts +106 -0
  23. package/dist/combat/weaponDefinition.js +108 -0
  24. package/dist/combat/weaponSystem.d.ts +189 -0
  25. package/dist/combat/weaponSystem.js +469 -0
  26. package/dist/combat.d.ts +9 -0
  27. package/dist/combat.js +9 -0
  28. package/dist/devtools/devtools.d.ts +4 -2
  29. package/dist/devtools/devtools.js +2 -1
  30. package/dist/devtools/discover.d.ts +2 -1
  31. package/dist/devtools/discover.js +38 -0
  32. package/dist/devtools/frame.d.ts +0 -2
  33. package/dist/devtools/frame.js +1 -2
  34. package/dist/devtools/logBus.d.ts +58 -0
  35. package/dist/devtools/logBus.js +79 -0
  36. package/dist/devtools/logs.d.ts +6 -3
  37. package/dist/devtools/logs.js +17 -13
  38. package/dist/devtools/report.d.ts +120 -0
  39. package/dist/devtools/report.js +49 -0
  40. package/dist/devtools/reportEndpoint.d.ts +17 -0
  41. package/dist/devtools/reportEndpoint.js +7 -0
  42. package/dist/devtools/types.d.ts +48 -11
  43. package/dist/editor/document.js +210 -0
  44. package/dist/editor/environment.d.ts +48 -1
  45. package/dist/editor/environment.js +161 -0
  46. package/dist/editor/index.d.ts +6 -2
  47. package/dist/editor/index.js +1 -1
  48. package/dist/editor/types.d.ts +54 -0
  49. package/dist/game/notices.d.ts +4 -0
  50. package/dist/game/notices.js +4 -3
  51. package/dist/game/playableGame.d.ts +9 -1
  52. package/dist/gameplay.d.ts +4 -0
  53. package/dist/gameplay.js +4 -0
  54. package/dist/meta/changelog.d.ts +1 -1
  55. package/dist/meta/changelog.js +78 -1
  56. package/dist/nav/navCrowd.d.ts +138 -0
  57. package/dist/nav/navCrowd.js +412 -0
  58. package/dist/nav/navRuntime.d.ts +108 -0
  59. package/dist/nav/navRuntime.js +256 -0
  60. package/dist/render/colorPipeline.d.ts +53 -0
  61. package/dist/render/colorPipeline.js +79 -0
  62. package/dist/render/lightProbes.d.ts +97 -0
  63. package/dist/render/lightProbes.js +221 -0
  64. package/dist/render/lookPreset.js +4 -4
  65. package/dist/render/physicalSky.d.ts +39 -0
  66. package/dist/render/physicalSky.js +89 -0
  67. package/dist/render/postProcessing.d.ts +40 -8
  68. package/dist/render/postProcessing.js +1 -1
  69. package/dist/render/skyOwnedLighting.d.ts +31 -0
  70. package/dist/render/skyOwnedLighting.js +46 -0
  71. package/dist/sensor/perception.d.ts +66 -5
  72. package/dist/sensor/perception.js +167 -42
  73. package/dist/sim/nearAgents.d.ts +68 -0
  74. package/dist/sim/nearAgents.js +152 -0
  75. package/dist/sim/needs.d.ts +60 -0
  76. package/dist/sim/needs.js +108 -0
  77. package/dist/sim/offScreen.d.ts +181 -0
  78. package/dist/sim/offScreen.js +450 -0
  79. package/dist/sim/routine.d.ts +79 -0
  80. package/dist/sim/routine.js +137 -0
  81. package/dist/visibility/visibilitySystem.js +21 -0
  82. package/dist/world/features.d.ts +8 -0
  83. package/dist/world.d.ts +5 -0
  84. package/dist/world.js +5 -0
  85. package/package.json +1 -1
  86. package/skills/jgengine/api.md +59 -6
  87. package/skills/jgengine/capabilities.md +4 -0
  88. package/skills/jgengine-combat/SKILL.md +17 -0
  89. package/skills/jgengine-combat/api.md +246 -0
  90. package/skills/jgengine-combat/capabilities.md +112 -0
  91. package/skills/jgengine-combat/reference.md +32 -0
  92. package/skills/jgengine-gameplay/SKILL.md +8 -0
  93. package/skills/jgengine-gameplay/api.md +71 -1
  94. package/skills/jgengine-gameplay/capabilities.md +16 -0
  95. package/skills/jgengine-world/SKILL.md +3 -1
  96. package/skills/jgengine-world/api.md +107 -3
  97. package/skills/jgengine-world/capabilities.md +22 -2
  98. package/skills/jgengine-world/recipes/npc-senses.md +8 -2
@@ -6,6 +6,10 @@ import { createSpatialIndex } from "./spatialIndex.js";
6
6
  import { DEFAULT_CULLING_SETTINGS, mergeCullingSettings, resolveOverrides } from "./settings.js";
7
7
  import { noOcclusion } from "./occlusion.js";
8
8
  import { createVisibilityStats, resetCullingStats, nowMs } from "./diagnostics.js";
9
+ import { devtools } from "../devtools/devtools.js";
10
+ function round2(value) {
11
+ return Math.round(value * 100) / 100;
12
+ }
9
13
  export function createVisibilitySystem(options) {
10
14
  let settings = mergeCullingSettings(DEFAULT_CULLING_SETTINGS, options.settings ?? {});
11
15
  const occlusion = options.occlusion ?? noOcclusion;
@@ -25,6 +29,22 @@ export function createVisibilitySystem(options) {
25
29
  const preloadFrustum = createFrustum();
26
30
  const hysteresisFrustum = createFrustum();
27
31
  let updateCounter = 0;
32
+ const disposeProbe = devtools.probes.registerSystem("render/culling", {
33
+ counters: () => ({
34
+ totalObjects: stats.totalObjects,
35
+ visible: stats.visible,
36
+ preloaded: stats.preloaded,
37
+ culled: stats.drawCallsAvoided,
38
+ rejectedByFrustum: stats.rejectedByFrustum,
39
+ rejectedByDistance: stats.rejectedByDistance,
40
+ rejectedByOcclusion: stats.rejectedByOcclusion,
41
+ }),
42
+ timers: () => ({
43
+ spatialQueryMs: round2(stats.spatialQueryMs),
44
+ cullingMs: round2(stats.cullingMs),
45
+ }),
46
+ state: () => `${stats.visible}/${stats.totalObjects} visible · ${stats.drawCallsAvoided} culled · ${round2(stats.cullingMs)}ms`,
47
+ });
28
48
  function specOf(r, resolved) {
29
49
  return resolved.bounds ?? r.bounds ?? DEFAULT_BOUNDS;
30
50
  }
@@ -245,6 +265,7 @@ export function createVisibilitySystem(options) {
245
265
  return { cameras, bounds: boundsOut, partitions: index.cells(), culled, stats };
246
266
  },
247
267
  clear() {
268
+ disposeProbe();
248
269
  index.clear();
249
270
  bounds.clear();
250
271
  records.clear();
@@ -330,6 +330,14 @@ export interface SkyEnvironmentConfig {
330
330
  zenithColor?: string;
331
331
  sunIntensity?: number;
332
332
  ambientIntensity?: number;
333
+ /** Authored noon sun colour; the atmosphere reddens it toward the horizon. Default warm white. */
334
+ sunColor?: string;
335
+ /** Authored noon fog colour. Dawn, dusk and night cross-fade to their own haze so fog matches the sky. */
336
+ fogColor?: string;
337
+ /** Peak moonlight as a fraction of `sunIntensity`. Default the engine's readable night value. */
338
+ moonIntensity?: number;
339
+ /** Moonlight colour. Default cool moonlight blue. */
340
+ moonColor?: string;
333
341
  /**
334
342
  * Sun placement for the dome glow and the sky-owned sun light. With a fixed `preset` this is the
335
343
  * sun's exact direction; under `timeOfDay` it is where the sun stands at noon and the day arc
package/dist/world.d.ts CHANGED
@@ -1,9 +1,11 @@
1
1
  export { type HeatConfig, type HeatGain, type HeatLevelDef, type HeatState } from "./ai/heatSystem.js";
2
2
  export { advanceInterestGate, createInterestCensus, createInterestGateState, interestPhase, type InterestCensus, type InterestCensusAccumulator, type InterestGateInput, type InterestGateState, type InterestGateStep, type InterestSchedulerConfig, type InterestState, type InterestTier, } from "./ai/interestScheduler.js";
3
3
  export { type Job, type JobDef, type JobReport } from "./ai/jobBoard.js";
4
+ export { createRvoCrowd, type RvoAgentSpec, type RvoAgentView, type RvoCrowd, type RvoCrowdOptions, type RvoSnapshot, type RvoTunables, } from "./ai/rvo.js";
4
5
  export { advanceSpawnDirector, advanceWave, createSpawnDirectorState, pickSpawnPoint, raiseAlert, type SpawnDirectorConfig, type SpawnDirectorState, type SpawnEntry, type SpawnRequest, type WaveManifest, } from "./ai/spawnDirector.js";
5
6
  export { acquireTarget, createTargetAcquirer, type AcquisitionEnvelope, type AcquisitionPolicy, type AcquisitionResult, type AcquisitionRetention, type TargetAcquirer, } from "./ai/targetAcquisition.js";
6
7
  export { createThreatTable, type ThreatTable } from "./ai/threat.js";
8
+ export { createUtilityBrain, sampleResponseCurve, type ResponseCurve, type UtilityBrain, type UtilityBrainConfig, type UtilityBrainSnapshot, type UtilityCandidate, type UtilityDebugSnapshot, type UtilityDecision, type UtilityInterrupt, type UtilityOption, type UtilityScore, type UtilitySwitchReason, type UtilityTerm, type UtilityTunables, } from "./ai/utilityBrain.js";
7
9
  export { createWaveRunner, type WaveRunner, type WaveRunnerConfig, type WaveSpawnSink, type WaveView, } from "./ai/waveRunner.js";
8
10
  export { createAreaEffectField, type AreaEffectEvent, type AreaEffectField, type AreaEventKind, type AreaFieldState, type AreaLeaveReason, type AreaMembership, type AreaShape, type AreaSourceSpec, type AreaStepInput, } from "./area/areaEffectField.js";
9
11
  export { cappedStacks, extremumStack, independentStacks, sumMagnitude, uniqueByStackKey, type AreaStackPolicy, type MagnitudeOf, } from "./area/stackPolicy.js";
@@ -30,9 +32,11 @@ export { resolvePlayerMovementTuning, stepPlayerMovement } from "./movement/play
30
32
  export { POSE_HITBOX, createPoseState, type MovementPose } from "./movement/poseState.js";
31
33
  export { steerYaw } from "./movement/steering.js";
32
34
  export { constrainToNavGrid } from "./nav/navConstrain.js";
35
+ export { createNavCrowd, type NavCrowd, type NavCrowdAgentSpec, type NavCrowdAgentView, type NavCrowdOptions, type NavCrowdRevalidate, type NavCrowdSnapshot, type NavCrowdStats, } from "./nav/navCrowd.js";
33
36
  export { populateNavGridFromEnvironment } from "./nav/navFromEnvironment.js";
34
37
  export { createNavGrid, findPath, slopeStepCost, type NavGrid, type NavPoint } from "./nav/navGrid.js";
35
38
  export { buildNavAdjacency, closestPoint, createNavMeshQuery, raycastNav, type NavMeshAdjacency, type NavMeshData, type NavMeshLink, type NavMeshPath, type NavMeshQuery, type NavMeshQueryOptions, type NavMeshQuerySnapshot, } from "./nav/navMesh.js";
39
+ export { createNavRuntime, navMeshFromBake, type AuthoredNavLink, type NavLinkKind, type NavRebuildStats, type NavRegion, type NavRuntime, type NavRuntimeOptions, type NavRuntimeSnapshot, type SceneBake, } from "./nav/navRuntime.js";
36
40
  export { advancePathFollow, createPathFollow, pathFollowProgress, pathFollowSeek, pathFromNav, pathLength, type PathFollowConfig, type PathFollowProgress, type PathFollowState, type PathProgress, type Waypoint, } from "./nav/pathFollow.js";
37
41
  export { defineAttackMoveOrder, defineHoldOrder, defineMoveOrder, definePatrolOrder, defineStopOrder, defineTargetedOrder, type AttackMoveOrderPayload, type EmptyOrderPayload, type EngagementKindConfig, type EngagementOrderState, type MoveOrderPayload, type OrderKindConfig, type OrderMoveResult, type OrderMover, type OrderTargeting, type PatrolOrderPayload, type PatrolOrderState, type TargetedOrderPayload, } from "./orders/orderKinds.js";
38
42
  export { createOrderQueue, createOrderRegistry, type Order, type OrderCancelReason, type OrderEvent, type OrderIssueResult, type OrderKind, type OrderOutcome, type OrderPhase, type OrderProgress, type OrderQueue, type OrderQueueOptions, type OrderQueuePolicy, type OrderQueueState, type OrderRegistry, type OrderRejection, type OrderRequest, type OrderStartResult, type OrderTickReport, type OrderVec3, } from "./orders/orderQueue.js";
@@ -82,6 +86,7 @@ export { type ConcealmentSensor } from "./sensor/concealment.js";
82
86
  export { type FreezeMonitor, type FreezeViolation } from "./sensor/freezeMonitor.js";
83
87
  export { type FramingConfig, type FrustumProjection, type FrustumSample, type FrustumSensor, type FrustumTarget, } from "./sensor/frustumSensor.js";
84
88
  export { type HiddenStateSource, type SensorProbeOptions, type SensorReading } from "./sensor/hiddenStateProbe.js";
89
+ export { createPerception, type PerceptionCandidate, type PerceptionConfig, type PerceptionMemory, type PerceptionObserver, type PerceptionPosition, type PerceptionService, type PerceptionSnapshot, type PerceptionStats, type PerceptionStimulus, type PerceptionStimulusKind, } from "./sensor/perception.js";
85
90
  export { type RecordingBuffer, type RecordingBufferOptions } from "./sensor/recordingBuffer.js";
86
91
  export { type RevealHit, type RevealQuery } from "./sensor/revealQuery.js";
87
92
  export { createDayNightCycle, type DayNightCycle, type DayNightCycleOptions, type DayNightKeyframe, type DayNightSample, type DayNightSnapshot, } from "./time/dayNightCycle.js";
package/dist/world.js CHANGED
@@ -1,9 +1,11 @@
1
1
  export {} from "./ai/heatSystem.js";
2
2
  export { advanceInterestGate, createInterestCensus, createInterestGateState, interestPhase, } from "./ai/interestScheduler.js";
3
3
  export {} from "./ai/jobBoard.js";
4
+ export { createRvoCrowd, } from "./ai/rvo.js";
4
5
  export { advanceSpawnDirector, advanceWave, createSpawnDirectorState, pickSpawnPoint, raiseAlert, } from "./ai/spawnDirector.js";
5
6
  export { acquireTarget, createTargetAcquirer, } from "./ai/targetAcquisition.js";
6
7
  export { createThreatTable } from "./ai/threat.js";
8
+ export { createUtilityBrain, sampleResponseCurve, } from "./ai/utilityBrain.js";
7
9
  export { createWaveRunner, } from "./ai/waveRunner.js";
8
10
  export { createAreaEffectField, } from "./area/areaEffectField.js";
9
11
  export { cappedStacks, extremumStack, independentStacks, sumMagnitude, uniqueByStackKey, } from "./area/stackPolicy.js";
@@ -30,9 +32,11 @@ export { resolvePlayerMovementTuning, stepPlayerMovement } from "./movement/play
30
32
  export { POSE_HITBOX, createPoseState } from "./movement/poseState.js";
31
33
  export { steerYaw } from "./movement/steering.js";
32
34
  export { constrainToNavGrid } from "./nav/navConstrain.js";
35
+ export { createNavCrowd, } from "./nav/navCrowd.js";
33
36
  export { populateNavGridFromEnvironment } from "./nav/navFromEnvironment.js";
34
37
  export { createNavGrid, findPath, slopeStepCost } from "./nav/navGrid.js";
35
38
  export { buildNavAdjacency, closestPoint, createNavMeshQuery, raycastNav, } from "./nav/navMesh.js";
39
+ export { createNavRuntime, navMeshFromBake, } from "./nav/navRuntime.js";
36
40
  export { advancePathFollow, createPathFollow, pathFollowProgress, pathFollowSeek, pathFromNav, pathLength, } from "./nav/pathFollow.js";
37
41
  export { defineAttackMoveOrder, defineHoldOrder, defineMoveOrder, definePatrolOrder, defineStopOrder, defineTargetedOrder, } from "./orders/orderKinds.js";
38
42
  export { createOrderQueue, createOrderRegistry, } from "./orders/orderQueue.js";
@@ -82,6 +86,7 @@ export {} from "./sensor/concealment.js";
82
86
  export {} from "./sensor/freezeMonitor.js";
83
87
  export {} from "./sensor/frustumSensor.js";
84
88
  export {} from "./sensor/hiddenStateProbe.js";
89
+ export { createPerception, } from "./sensor/perception.js";
85
90
  export {} from "./sensor/recordingBuffer.js";
86
91
  export {} from "./sensor/revealQuery.js";
87
92
  export { createDayNightCycle, } from "./time/dayNightCycle.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jgengine/core",
3
- "version": "0.24.0",
3
+ "version": "0.26.0",
4
4
  "description": "jgengine core — pure-TypeScript game framework SDK (npm @jgengine/core). Entity stores, commands, combat, inventory, multiplayer seams. Zero dependencies. Not an ECS. Agent skills ship in the tarball. https://jgengine.com",
5
5
  "keywords": [
6
6
  "jgengine",
@@ -6,7 +6,7 @@
6
6
 
7
7
  - `CHANGELOG` (const): const CHANGELOG: Record<string, ChangelogEntry> — Per-version engine changelog keyed by semver string (e.g. `"0.10.0"`).
8
8
  - `ChangelogEntry` (interface): interface ChangelogEntry — One release's migrate steps plus added/changed/removed notes (typed mirror of CHANGELOG.md).
9
- - `VERSION` (const): const VERSION: "0.24.0" — Installed `@jgengine/core` semver — compare against {@link CHANGELOG} keys when migrating.
9
+ - `VERSION` (const): const VERSION: "0.26.0" — Installed `@jgengine/core` semver — compare against {@link CHANGELOG} keys when migrating.
10
10
 
11
11
  ## @jgengine/core/authoring
12
12
 
@@ -82,25 +82,42 @@
82
82
  - `Devtools` (interface): interface Devtools — Top-level devtools facade aggregating frame, profile, render, logs, latency, controls, discover, overrides, and probes.
83
83
  - `DevtoolsControl` (interface): interface DevtoolsControl — Registered tunable control with its resolved schema metadata and read/write/reset accessors.
84
84
  - `DevtoolsControlKind` (type): type DevtoolsControlKind = | "slider" | "toggle" | "color" | "select" | "text" | "vec2" | "vec3" | "vec4" | "interval" | "angle" | "enum" — ⚠ undocumented
85
- - `DevtoolsLogEntry` (interface): interface DevtoolsLogEntry — A single captured log line with timestamp, level, and formatted message.
85
+ - `DevtoolsLogEntry` (interface): interface DevtoolsLogEntry — A single captured log line with timestamp, level, owning system tag, and formatted message.
86
86
  - `DevtoolsLogLevel` (type): type DevtoolsLogLevel = "log" | "info" | "warn" | "error" — Severity level for a captured devtools log entry.
87
+ - `DevtoolsLogs` (interface): interface DevtoolsLogs — Bounded log ring with per-system tags, a warning/error ring, and lifetime counts.
87
88
  - `DevtoolsOverrides` (interface): interface DevtoolsOverrides { version: number; enabled: string[]; values: Record<string, unknown>; schemas?: Record<string, {kind: DevtoolsControlKind; schemaVersion?: number}> } — ⚠ undocumented
88
89
  - `DevtoolsSnapshot` (interface): interface DevtoolsSnapshot — Serializable point-in-time snapshot of devtools state: frame, render, latency, logs, controls, and discovered fields.
89
90
  - `DiscoveredEntry` (interface): interface DiscoveredEntry — A control auto-discovered by scanning an object/table, with its binding metadata and current reader.
90
91
  - `DiscoverySkip` (interface): interface DiscoverySkip { readonly path: string; readonly reason: string } — ⚠ undocumented
91
92
  - `FrameRecordSample` (interface): interface FrameRecordSample — Per-frame timing input recorded by callers: total frame time, sim time, and optional named phase durations.
92
93
  - `FrameStats` (interface): interface FrameStats — Aggregated frame-timing statistics over the recent sampling window, including per-phase breakdown.
93
- - `LONG_FRAME_MS` (const): const LONG_FRAME_MS: 33.4 — Frame duration in milliseconds above which a frame is recorded as a long-frame spike (~30fps budget).
94
+ - `FrameWindow` (interface): interface FrameWindow — Frame timings over the measured window, as the player sees them.
95
+ - `GlReport` (interface): interface GlReport — GPU-object creation counts. `total` is since the page loaded, the named fields since warmup.
96
+ - `LONG_FRAME_MS` (const): const LONG_FRAME_MS: 33.4 — The one perf record shape. `bun run perf` (CDP, out of process) and the in-page telemetry report (`window.__JG.report()`, the dev-server route, `bun run debug`) both produce exactly this object, so a budget check and a diff read either one.
94
97
  - `LatencyStats` (interface): interface LatencyStats — Aggregated latency statistics (last/avg/min/max) over recorded latency samples.
98
+ - `LogBusCounts` (interface): interface LogBusCounts — Lifetime totals per level, plus how many lines the ring dropped.
99
+ - `LogBusEntry` (interface): interface LogBusEntry — One captured line: when, how bad, which system said it, and what it said.
100
+ - `LogBusSnapshot` (interface): interface LogBusSnapshot — Read-out of the log bus: newest-window entries, the warning/error ring, totals, and evictions.
95
101
  - `LongFrameEvent` (interface): interface LongFrameEvent — Recorded spike event for a frame that exceeded the long-frame threshold, with culprit attribution.
96
102
  - `NormalizedColor` (interface): interface NormalizedColor { readonly hex: string; readonly rgb: string; readonly alpha: number; readonly hasAlpha: boolean } — ⚠ undocumented
97
103
  - `OVERRIDES_FORMAT_VERSION` (const): const OVERRIDES_FORMAT_VERSION: 1 — ⚠ undocumented
98
104
  - `OverrideApplyDiagnostic` (interface): interface OverrideApplyDiagnostic { readonly id: string; readonly reason: string } — ⚠ undocumented
99
105
  - `OverrideApplyResult` (interface): interface OverrideApplyResult — Outcome of applying a devtools overrides payload: counts of applied and skipped entries plus diagnostics.
106
+ - `PerfQuality` (type): type PerfQuality = "full" | "draft" — Graphics tier the record was measured at.
107
+ - `PerfReport` (interface): interface PerfReport — One measured window of one game: the record `bun run perf`, `window.__JG.report()` and `bun run debug` print.
100
108
  - `PhaseStats` (interface): interface PhaseStats — Timing summary for a single named profiling phase across the sampling window.
109
+ - `ProfileEntry` (interface): interface ProfileEntry — One sampled stack frame reduced to a readable key and its self time.
110
+ - `ProfileReport` (interface): interface ProfileReport — CPU profile summary; only the CDP harness can produce one.
111
+ - `RenderReport` (interface): interface RenderReport — Renderer-side counts for the measured window; `null` where the source could not read them.
101
112
  - `RenderSample` (interface): interface RenderSample — Snapshot of renderer resource counts (draw calls, triangles, geometries, textures) for a frame.
113
+ - `ReportLogLine` (interface): interface ReportLogLine — One log line as it appears in the report.
114
+ - `ReportLogs` (interface): interface ReportLogs — Bounded log read-out carried by the report: warn/error lines survive even a flood of info logs.
115
+ - `ReportTelemetry` (interface): interface ReportTelemetry — Who produced the report, and how much producing it cost.
102
116
  - `ScanFieldMeta` (interface): interface ScanFieldMeta extends TunableOptions { kind?: DevtoolsControlKind } — ⚠ undocumented
103
117
  - `ScanMeta` (type): type ScanMeta = Readonly<Record<string, ScanFieldMeta>> — ⚠ undocumented
118
+ - `SceneNodeSource` (type): type SceneNodeSource = "scene-graph" | "heap-object3d" | "unavailable" — Where the scene-node count came from: the live scene graph, a heap walk, or nowhere.
119
+ - `SystemProbe` (interface): interface SystemProbe — What one system reports about itself: totals it keeps anyway, named timings, and one state line.
120
+ - `SystemProbeReading` (interface): interface SystemProbeReading — One system's probe read out on demand; missing sections stay empty rather than absent.
104
121
  - `Tunable` (interface): interface Tunable<T> — Typed handle to a registered control, exposing its live value and subscribe/set/reset operations.
105
122
  - `TunableAccessor` (interface): interface TunableAccessor — Get/set accessor pair plus initial value used to bind a discovered field to a control.
106
123
  - `TunableChoice` (interface): interface TunableChoice<T = unknown> { readonly value: T; readonly label?: string } — ⚠ undocumented
@@ -139,9 +156,20 @@
139
156
  ## @jgengine/core/devtools/frame
140
157
 
141
158
  - `FrameModule` (interface): interface FrameModule — Frame subsystem exposing the frame-timing, profiling, and render-sample facades.
142
- - `LONG_FRAME_MS` (const): const LONG_FRAME_MS: 33.4 — Frame duration in milliseconds above which a frame is recorded as a long-frame spike (~30fps budget).
143
159
  - `createFrameModule` (const): const createFrameModule: (deps: { readProbes: () => Record<string, unknown>; }) => FrameModule — Create the frame subsystem that records frame/sim/phase timings, computes stats, and captures long-frame events.
144
160
 
161
+ ## @jgengine/core/devtools/logBus
162
+
163
+ - `LOG_BUS_CAPACITY` (const): const LOG_BUS_CAPACITY: 200 — Default measured window for the log ring; older lines are evicted, never grown.
164
+ - `LOG_BUS_MESSAGE_MAX` (const): const LOG_BUS_MESSAGE_MAX: 400 — Longest kept message; a longer one is cut once, at write time.
165
+ - `LOG_BUS_SEVERE_CAPACITY` (const): const LOG_BUS_SEVERE_CAPACITY: 64 — Warnings and errors also land in their own ring, so an error is never pushed out by info traffic.
166
+ - `LogBus` (interface): interface LogBus — A fixed-capacity ring of log lines tagged by owning system. `write` reuses a preallocated slot — no array growth, no splice, no per-write object — so a hot path can call it and only the message string (already built by the caller) costs an allocation.
167
+ - `LogBusCounts` (interface): interface LogBusCounts — Lifetime totals per level, plus how many lines the ring dropped.
168
+ - `LogBusEntry` (interface): interface LogBusEntry — One captured line: when, how bad, which system said it, and what it said.
169
+ - `LogBusOptions` (interface): interface LogBusOptions — Tunables for the log ring: capacity, the severity ring's capacity, and the clock.
170
+ - `LogBusSnapshot` (interface): interface LogBusSnapshot — Read-out of the log bus: newest-window entries, the warning/error ring, totals, and evictions.
171
+ - `createLogBus` (function): function createLogBus(options: LogBusOptions = {}): LogBus — Create the bounded log bus the devtools log facade writes through.
172
+
145
173
  ## @jgengine/core/devtools/logs
146
174
 
147
175
  - `LogsModule` (interface): interface LogsModule — Logs subsystem exposing the log-capture and latency-tracking facades.
@@ -153,6 +181,27 @@
153
181
  - `OverridesModule` (interface): interface OverridesModule — Overrides subsystem exposing the export/apply facade for persisting and restoring control values.
154
182
  - `createOverridesModule` (const): const createOverridesModule: (deps: { signal: ChangeSignal; controlRecords: Map<string, ControlRecord>; writeControl: (record: ControlRecord, raw: unknown) => boolean; discoveredRecords: Map<string, DiscoveredRecord>; enableDiscovered: (id: string) => void; }) => OverridesModule — Create the overrides subsystem that serializes changed control values and re-applies a saved overrides payload.
155
183
 
184
+ ## @jgengine/core/devtools/report
185
+
186
+ - `FrameWindow` (interface): interface FrameWindow — Frame timings over the measured window, as the player sees them.
187
+ - `GlReport` (interface): interface GlReport — GPU-object creation counts. `total` is since the page loaded, the named fields since warmup.
188
+ - `LONG_FRAME_MS` (const): const LONG_FRAME_MS: 33.4 — The one perf record shape. `bun run perf` (CDP, out of process) and the in-page telemetry report (`window.__JG.report()`, the dev-server route, `bun run debug`) both produce exactly this object, so a budget check and a diff read either one.
189
+ - `PerfQuality` (type): type PerfQuality = "full" | "draft" — Graphics tier the record was measured at.
190
+ - `PerfReport` (interface): interface PerfReport — One measured window of one game: the record `bun run perf`, `window.__JG.report()` and `bun run debug` print.
191
+ - `ProfileEntry` (interface): interface ProfileEntry — One sampled stack frame reduced to a readable key and its self time.
192
+ - `ProfileReport` (interface): interface ProfileReport — CPU profile summary; only the CDP harness can produce one.
193
+ - `RenderReport` (interface): interface RenderReport — Renderer-side counts for the measured window; `null` where the source could not read them.
194
+ - `ReportLogLine` (interface): interface ReportLogLine — One log line as it appears in the report.
195
+ - `ReportLogs` (interface): interface ReportLogs — Bounded log read-out carried by the report: warn/error lines survive even a flood of info logs.
196
+ - `ReportTelemetry` (interface): interface ReportTelemetry — Who produced the report, and how much producing it cost.
197
+ - `SceneNodeSource` (type): type SceneNodeSource = "scene-graph" | "heap-object3d" | "unavailable" — Where the scene-node count came from: the live scene graph, a heap walk, or nowhere.
198
+
199
+ ## @jgengine/core/devtools/reportEndpoint
200
+
201
+ - `REPORT_ENDPOINT_PATH` (const): const REPORT_ENDPOINT_PATH: "/__jgengine/report" — The dev-only route a running game pushes its in-page report to, and that `bun run debug <game>` reads back. One process: the page gathers, the dev server holds the newest record per game, the CLI fetches it. Nothing here ships in a production build — the Vite plugin mounts it under `apply: "serve"`.
202
+ - `ReportPushBody` (interface): interface ReportPushBody — Body the page POSTs: the game it measured and the record itself.
203
+ - `ReportRouteError` (interface): interface ReportRouteError — Body sent when there is nothing to return yet.
204
+
156
205
  ## @jgengine/core/devtools/rewriteTunables
157
206
 
158
207
  - `TunableDelta` (interface): interface TunableDelta — One changed tunable to write back into game source: which table, key path, and new value.
@@ -203,8 +252,10 @@
203
252
 
204
253
  - `Devtools` (interface): interface Devtools — Top-level devtools facade aggregating frame, profile, render, logs, latency, controls, discover, overrides, and probes.
205
254
  - `DevtoolsControl` (interface): interface DevtoolsControl — Registered tunable control with its resolved schema metadata and read/write/reset accessors.
206
- - `DevtoolsLogEntry` (interface): interface DevtoolsLogEntry — A single captured log line with timestamp, level, and formatted message.
255
+ - `DevtoolsLatency` (interface): interface DevtoolsLatency — Latency samples recorded by instrumented transport methods.
256
+ - `DevtoolsLogEntry` (interface): interface DevtoolsLogEntry — A single captured log line with timestamp, level, owning system tag, and formatted message.
207
257
  - `DevtoolsLogLevel` (type): type DevtoolsLogLevel = "log" | "info" | "warn" | "error" — Severity level for a captured devtools log entry.
258
+ - `DevtoolsLogs` (interface): interface DevtoolsLogs — Bounded log ring with per-system tags, a warning/error ring, and lifetime counts.
208
259
  - `DevtoolsSnapshot` (interface): interface DevtoolsSnapshot — Serializable point-in-time snapshot of devtools state: frame, render, latency, logs, controls, and discovered fields.
209
260
  - `DiscoveredEntry` (interface): interface DiscoveredEntry — A control auto-discovered by scanning an object/table, with its binding metadata and current reader.
210
261
  - `FrameRecordSample` (interface): interface FrameRecordSample — Per-frame timing input recorded by callers: total frame time, sim time, and optional named phase durations.
@@ -214,6 +265,8 @@
214
265
  - `OverrideApplyResult` (interface): interface OverrideApplyResult — Outcome of applying a devtools overrides payload: counts of applied and skipped entries plus diagnostics.
215
266
  - `PhaseStats` (interface): interface PhaseStats — Timing summary for a single named profiling phase across the sampling window.
216
267
  - `RenderSample` (interface): interface RenderSample — Snapshot of renderer resource counts (draw calls, triangles, geometries, textures) for a frame.
268
+ - `SystemProbe` (interface): interface SystemProbe — What one system reports about itself: totals it keeps anyway, named timings, and one state line.
269
+ - `SystemProbeReading` (interface): interface SystemProbeReading — One system's probe read out on demand; missing sections stay empty rather than absent.
217
270
  - `Tunable` (interface): interface Tunable<T> — Typed handle to a registered control, exposing its live value and subscribe/set/reset operations.
218
271
  - `TunableAccessor` (interface): interface TunableAccessor — Get/set accessor pair plus initial value used to bind a discovered field to a control.
219
272
 
@@ -232,7 +285,7 @@
232
285
 
233
286
  - `CHANGELOG` (const): const CHANGELOG: Record<string, ChangelogEntry> — Per-version engine changelog keyed by semver string (e.g. `"0.10.0"`).
234
287
  - `ChangelogEntry` (interface): interface ChangelogEntry — One release's migrate steps plus added/changed/removed notes (typed mirror of CHANGELOG.md).
235
- - `VERSION` (const): const VERSION: "0.24.0" — Installed `@jgengine/core` semver — compare against {@link CHANGELOG} keys when migrating.
288
+ - `VERSION` (const): const VERSION: "0.26.0" — Installed `@jgengine/core` semver — compare against {@link CHANGELOG} keys when migrating.
236
289
 
237
290
  ## @jgengine/core/runtime/adapter
238
291
 
@@ -48,6 +48,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
48
48
 
49
49
  - `defineSystem` (function) · `import { defineSystem } from "@jgengine/shell/gameKit"`
50
50
 
51
+ ## devtools-log-bus — a bounded, per-system-tagged log ring a system writes to and a report reads
52
+
53
+ - `LogBus` (interface) · `import { LogBus } from "@jgengine/core/devtools/logBus"`
54
+
51
55
  ## editor-catalogs — Export typed gameplay catalogs for in-editor tuning.
52
56
 
53
57
  - `EditorCatalogDefinition` (interface) · `import { EditorCatalogDefinition } from "@jgengine/shell/gameKit"`
@@ -19,6 +19,13 @@ a parallel entity store. To bolt firing onto an existing game, compose
19
19
  `resolveDamageHit`) per the
20
20
  [portable weapon-plumbing recipe](recipes/portable-weapon-plumbing.md); never add
21
21
  a default gun or FPS kit.
22
+ For a weapon that is plain data — fire mode, rate, delivery, magazine, recoil and
23
+ spread, damage — hand the definition to `createWeaponSystem`
24
+ (`combat/weaponSystem`): it runs single/auto/burst, hitscan or ballistic
25
+ projectiles, interrupted reloads and a readable ammo economy, and resolves both
26
+ deliveries through `createDamageRuntime`. Generated guns (rolled parts) are the
27
+ same `WeaponDefinition`; see
28
+ [reference.md](reference.md#weapons-as-data).
22
29
  For scene-backed `createProjectileSystem`, inject authoritative `rng`; spread
23
30
  samples independent pellet rays and prediction stays on the center ray. The
24
31
  weapon-plumbing recipe covers the bounded cone and cover contracts.
@@ -30,9 +37,17 @@ Damage commitment accepts the same structural pool adapter while the matchup,
30
37
  receiver, interceptor, and status stages remain pure. Follow the
31
38
  [portable damage/effects recipe](recipes/portable-damage-effects.md) to compose
32
39
  them over an existing store, clock, RNG, spatial index, and death flow.
40
+ Damage types × resistance, the armor curve, hit-zone crits and carried statuses
41
+ compose through `createDamageRuntime` (`combat/damageRuntime`): a readable table,
42
+ time-to-kill quotes, and status ticks that follow the declared stack rule. It
43
+ snapshots/restores as plain data; see
44
+ [reference.md](reference.md#damage-runtime-armor-crits-and-statuses).
33
45
 
34
46
  Closure-backed magazines expose snapshot/restore and `retune` for capacity/reload
35
47
  changes without reconstructing ammo state; capacity overflow is explicit.
48
+ `pauseReload`/`resumeReload` suspend a reload and keep its progress, so a
49
+ sprint or a weapon swap can interrupt one and continue it; a paused reload is
50
+ not `isReloading()` and does not advance in `tick`.
36
51
  Stat modifiers expose snapshot/restore; ability
37
52
  kits, shields, and meters expose state/restore for caller-owned saves,
38
53
  rollback, replay, and workers. Follow the
@@ -48,6 +63,8 @@ keeps its clock, reserve state, schema, and authority.
48
63
  5. Apply damage/healing/status transitions and emit provenance-rich events.
49
64
  6. Resolve death/downed and rewards separately from the hit itself.
50
65
 
66
+ Enemy squad AI is a consumer of that pipeline, not a parallel one: `createSquadCombat` (`combat/squadCombat`) picks cover from authored geometry by remembered threat, flanks by role, holds fire discipline, pins under suppression and falls back when hurt, while `engage` hands each paced shot to your damage resolution and `underFire` feeds the hit back in. Cover, roles and threat all live in [reference.md](reference.md#enemy-squad-ai).
67
+
51
68
  Compose stages through registration/policy seams; adding a damage type, effect, target policy, or reward strategy must not add a central engine branch.
52
69
 
53
70
  Weapon feel sits beside the pipeline, not inside it. `createWeaponHandling` (`combat/weaponHandling`) owns recoil pattern, camera kick versus aim kick, recovery, spread bloom, stance and ADS multipliers, and ADS time, all as serializable state. `weaponFire`/`magazine` own cadence and ammo, and call `fire()` on each shot. Sample the shot cone from `fire().spread`, add `tick().aimPitch/aimYaw` to the aim, and send `cameraPitch/cameraYaw` to the view. Two weapons differ by numbers in radians and seconds; assert them with `measureWeapon` (first and tenth shot spread, climb, reset time, ADS time, time-to-kill). To present it, return the frame from `camera.weapon` with a `createWeaponPresentation` tuning (`combat/weaponPresentation`); the first-person and shoulder rigs pose the viewmodel and camera from that one frame, and third-person code reads `aimPitch`/`aimYaw` from the same pose.