@jgengine/core 0.25.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 (95) hide show
  1. package/CHANGELOG.md +65 -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/gameplay.d.ts +4 -0
  50. package/dist/gameplay.js +4 -0
  51. package/dist/meta/changelog.d.ts +1 -1
  52. package/dist/meta/changelog.js +56 -1
  53. package/dist/nav/navCrowd.d.ts +138 -0
  54. package/dist/nav/navCrowd.js +412 -0
  55. package/dist/nav/navRuntime.d.ts +108 -0
  56. package/dist/nav/navRuntime.js +256 -0
  57. package/dist/render/colorPipeline.d.ts +53 -0
  58. package/dist/render/colorPipeline.js +79 -0
  59. package/dist/render/lightProbes.d.ts +97 -0
  60. package/dist/render/lightProbes.js +221 -0
  61. package/dist/render/lookPreset.js +4 -4
  62. package/dist/render/physicalSky.d.ts +39 -0
  63. package/dist/render/physicalSky.js +89 -0
  64. package/dist/render/postProcessing.d.ts +40 -8
  65. package/dist/render/postProcessing.js +1 -1
  66. package/dist/render/skyOwnedLighting.d.ts +31 -0
  67. package/dist/render/skyOwnedLighting.js +46 -0
  68. package/dist/sensor/perception.d.ts +66 -5
  69. package/dist/sensor/perception.js +167 -42
  70. package/dist/sim/nearAgents.d.ts +68 -0
  71. package/dist/sim/nearAgents.js +152 -0
  72. package/dist/sim/needs.d.ts +60 -0
  73. package/dist/sim/needs.js +108 -0
  74. package/dist/sim/offScreen.d.ts +181 -0
  75. package/dist/sim/offScreen.js +450 -0
  76. package/dist/sim/routine.d.ts +79 -0
  77. package/dist/sim/routine.js +137 -0
  78. package/dist/visibility/visibilitySystem.js +21 -0
  79. package/dist/world/features.d.ts +8 -0
  80. package/dist/world.d.ts +5 -0
  81. package/dist/world.js +5 -0
  82. package/package.json +1 -1
  83. package/skills/jgengine/api.md +59 -6
  84. package/skills/jgengine/capabilities.md +4 -0
  85. package/skills/jgengine-combat/SKILL.md +17 -0
  86. package/skills/jgengine-combat/api.md +246 -0
  87. package/skills/jgengine-combat/capabilities.md +112 -0
  88. package/skills/jgengine-combat/reference.md +32 -0
  89. package/skills/jgengine-gameplay/SKILL.md +8 -0
  90. package/skills/jgengine-gameplay/api.md +71 -1
  91. package/skills/jgengine-gameplay/capabilities.md +16 -0
  92. package/skills/jgengine-world/SKILL.md +3 -1
  93. package/skills/jgengine-world/api.md +107 -3
  94. package/skills/jgengine-world/capabilities.md +22 -2
  95. package/skills/jgengine-world/recipes/npc-senses.md +8 -2
@@ -12,6 +12,7 @@
12
12
  - `AnimationClip` (interface): interface AnimationClip { id: string; frames: number; fps: number; ranges: readonly FrameRange[]; loop?: boolean } — ⚠ undocumented · used by `createGraphPose` (@jgengine/shell/render/useModelAnimation): Binds a graph's clips to a rig exactly as `useModelAnimation` does (masked layers get filtered clips, additive layers additive ones) so a ho…
13
13
  - `AntiOneShotConfig` (interface): interface AntiOneShotConfig — Caller data for the anti-one-shot composition: "cannot cross lethal while above X, leave Y, then recover for Z".
14
14
  - `AntiOneShotPolicy` (interface): interface AntiOneShotPolicy — The anti-one-shot policy: an interceptor plus its serializable recovery-immunity state.
15
+ - `ArmorConfig` (interface): interface ArmorConfig — The armor curve: `mitigation = armor / (armor + halfValue)`. Naming the half value instead of a percentage per point is what makes a generated gun's armor number readable — armor equal to `halfValue` halves the hit, and mitigation approaches the cap without ever reaching it.
15
16
  - `BUILTIN_COMPLETION_PREDICATES` (const): const BUILTIN_COMPLETION_PREDICATES: Readonly<Record<string, CompletionPredicate>> — Built-in completion predicates covering the common encounter gates: `immediate` (leaf marker), `manual`/`flag` (external event via a caller flag), `timer` (phase seconds), `cleared` (tagged entity count drained), `metric` (threshold on a live value), `event` (cumulative event count), and `quorum` (at least N of nested predicates met). Merge your own over these through {@link EncounterConfig.predicates}.
16
17
  - `BUILTIN_SPAWN_PROVIDERS` (const): const BUILTIN_SPAWN_PROVIDERS: Readonly<Record<string, SpawnProvider>> — Built-in spawn providers. `points` reads a named authored scene point list from context and cycles it to emit `count` requests, keeping coordinates in the scene rather than the sequence. `list` echoes literal requests authored in `params.requests`. Merge your own catalog- or director-backed providers through {@link EncounterConfig.spawnProviders}.
17
18
  - `BuildupProc` (interface): interface BuildupProc { status: string; durationMs: number } — ⚠ undocumented
@@ -26,14 +27,29 @@
26
27
  - `ComboStep` (interface): interface ComboStep { id: string; clip: string; cancelInto?: readonly string[]; cancelPhases?: readonly FramePhase[]; stance?: string } — ⚠ undocumented
27
28
  - `ComboTier` (interface): interface ComboTier — One tier threshold in a {@link ComboMeter}. Tiers gate on the integer combo `count` (not a filled fraction), and `id` is a free string the game owns and styles ("good", "great", "savage", …) — the model never interprets it.
28
29
  - `CompletionPredicate` (type): type CompletionPredicate = ( params: Readonly<Record<string, unknown>>, ctx: PredicateContext, ) => boolean — A completion predicate: pure and deterministic, returns true when the node it gates is finished. Registered under a `kind` and reused across phases with different `params`.
30
+ - `CoverChoice` (interface): interface CoverChoice — A scored cover point, with every term visible so a probe or test can see why it won.
31
+ - `CoverField` (interface): interface CoverField extends CoverSource — Cover derived from authored geometry, rebuilt when the world is edited.
32
+ - `CoverFieldOptions` (interface): interface CoverFieldOptions extends CoverPointOptions — Options for {@link createCoverField}: the authored boxes, the dials, and a probe name.
33
+ - `CoverFieldSnapshot` (interface): interface CoverFieldSnapshot — Serializable cover field state: the boxes and the dials, not the derived points.
34
+ - `CoverPoint` (interface): interface CoverPoint — One stand position beside authored geometry, with the face direction it hides against.
35
+ - `CoverPointOptions` (interface): interface CoverPointOptions — How cover points sit relative to the geometry that generates them.
36
+ - `CoverQuery` (interface): interface CoverQuery — Everything one cover decision reads: where the member is, what it believes, and its role's policy.
37
+ - `CoverSolid` (interface): interface CoverSolid — A box of authored world geometry a body can hide behind. Structurally a `WorldSolid`, so `ctx.world.solids.all()` drops in directly; a game with its own collision can pass any boxes.
38
+ - `CoverSource` (interface): interface CoverSource — Bounded source of cover near a position: generated from authored geometry, or the game's own spatial query. Called for one member at a time, never for the whole squad.
39
+ - `CoverThreat` (interface): interface CoverThreat — A remembered threat: where the member last saw it and how strongly it still believes it.
40
+ - `CoverWeights` (interface): interface CoverWeights — What each term is worth when choosing cover. A role owns these numbers.
29
41
  - `CrossingDirection` (type): type CrossingDirection = "falling" | "rising" — The direction a crossing moved: `falling` = value decreased past the mark, `rising` = increased past it.
30
42
  - `CrossingPolicy` (type): type CrossingPolicy = "once" | "repeat" — Whether a threshold may fire repeatedly (`repeat`) or at most once per direction (`once`).
31
43
  - `CrossingTrigger` (type): type CrossingTrigger = "falling" | "rising" | "both" — Which directions should emit crossings.
44
+ - `DEFAULT_ARMOR_HALF_VALUE` (const): const DEFAULT_ARMOR_HALF_VALUE: 100 — Default armor points at which a hit is halved.
45
+ - `DEFAULT_CRIT_MULTIPLIER` (const): const DEFAULT_CRIT_MULTIPLIER: 2 — Default crit multiplier for a zone that does not name one.
32
46
  - `DEFAULT_EYE_HEIGHT` (const): const DEFAULT_EYE_HEIGHT: number — Shot-origin and first-person camera eye height above an entity's position: 90% of the default 1.8m hitbox top.
33
47
  - `DEFAULT_FIRE_PULSE_SECONDS` (const): const DEFAULT_FIRE_PULSE_SECONDS: 0.12 — Default `RenderCueTuning.firePulseSeconds`.
34
48
  - `DEFAULT_HIT_PULSE_SECONDS` (const): const DEFAULT_HIT_PULSE_SECONDS: 0.2 — Default `RenderCueTuning.hitPulseSeconds`.
49
+ - `DEFAULT_MAX_MITIGATION` (const): const DEFAULT_MAX_MITIGATION: 0.9 — Default ceiling on armor mitigation, so armor never fully negates a hit.
35
50
  - `DEFAULT_RENDER_CUES` (const): const DEFAULT_RENDER_CUES: Readonly<EntityRenderCues> — Neutral starting cue set: idle, unarmed, undamaged.
36
51
  - `DEFAULT_VFX_PRESET` (const): const DEFAULT_VFX_PRESET: VfxPresetName — The fallback flavor used when a `vfx()` call names an unknown preset — so a typo still shows a spark instead of nothing.
52
+ - `DEFAULT_WEAPON_PELLETS_IN_FLIGHT` (const): const DEFAULT_WEAPON_PELLETS_IN_FLIGHT: 32 — Default live-pellet cap for a weapon that does not name one.
37
53
  - `DamageChannelId` (type): type DamageChannelId = string — Caller-owned id for a damage channel — `"fire"`, `"ballistic"`, `"physical"`, `"corrosive"`. The engine never branches on the value; a channel is whatever a game's data declares.
38
54
  - `DamageClampConfig` (interface): interface DamageClampConfig — Caller data for a flat per-hit damage cap.
39
55
  - `DamageContext` (interface): interface DamageContext — The facts a received-damage modifier predicates over: the incoming `channel`, who dealt it, the statuses the target currently carries, and any caller tags. All caller-owned strings; the engine never assigns meaning to a channel, source, status, or tag.
@@ -44,8 +60,18 @@
44
60
  - `DamagePipeline` (interface): interface DamagePipeline — A mutable ordered interceptor chain that transitions can install into and remove from at runtime.
45
61
  - `DamagePredicate` (interface): interface DamagePredicate — A typed predicate over a {@link DamageContext}. Every listed facet must match (AND); a facet matches when the context value is in the list (channels/sources) or shares any member (targetStatuses/tags). An empty predicate matches every hit.
46
62
  - `DamageResolution` (interface): interface DamageResolution — The outcome of running a pending application through the pipeline: what to apply, what was deferred, and why each value changed.
63
+ - `DamageRuntimeConfig` (interface): interface DamageRuntimeConfig — Everything the model reads: the table, the zones, the status definitions, and the RNG.
64
+ - `DamageRuntimeHitInput` (interface): interface DamageRuntimeHitInput — One incoming hit: what struck, how hard, where it landed, and what statuses it carries.
65
+ - `DamageRuntimeResolution` (interface): interface DamageRuntimeResolution — Every stage of one resolved hit, so a UI or a test can read why the final number is what it is.
66
+ - `DamageRuntimeRetune` (interface): interface DamageRuntimeRetune — In-play dials, all retunable without discarding registered targets or carried statuses.
67
+ - `DamageRuntimeState` (interface): interface DamageRuntimeState — Serialized model state: registered targets, carried statuses, and the probe counters.
68
+ - `DamageStatusRequest` (interface): interface DamageStatusRequest — One status a hit tries to apply, scaled against that status's own base chance and values.
69
+ - `DamageTableDef` (interface): interface DamageTableDef — A serializable damage table: damage types scored against resistance classes, plus the armor curve those rows are read with. It is data a generated gun (B3.1) and a weapon runtime (B2.3) pass in; the engine holds no built-in elemental chart.
70
+ - `DamageTableRow` (interface): interface DamageTableRow — One readable row: a type against a resistance class at a given armor value.
71
+ - `DamageTypeDef` (interface): interface DamageTypeDef — One damage type's row in a damage table: a caller-owned id (`"kinetic"`, `"fire"`) and how it fares against each resistance class a target can present (`"flesh"`, `"shield"`, `"armor"`). Values below `1` are resisted, `1` is neutral, above `1` is a weakness, and `0` is immune.
47
72
  - `DeathReason` (type): type DeathReason = | { kind: "player_kill"; killerUserId: string; via?: { item?: string } } | { kind: "environment"; source: string } | { kind: "self"; source: string } — Why an entity died — who or what gets credit, for drop/command rules and the `entity.died` event.
48
73
  - `DefenseResolution` (interface): interface DefenseResolution — Result of resolving an incoming hit against a defensive window: `outcome` is the graded defense (`"parry"`/`"block"`/`"iframe"`/`"hit"`) and `perfect` is true when the input landed inside the tight just-frame at the window's start rather than its wider active span.
74
+ - `DuplicateDamageTypeError` (class): class DuplicateDamageTypeError extends Error — Raised when a table lists the same damage type twice, which would make the winner depend on order.
49
75
  - `EncounterConfig` (interface): interface EncounterConfig<TData = unknown> — Non-serializable configuration for an encounter: the authored phase tree plus predicate and spawn-provider registries. Re-supplied alongside a loaded {@link EncounterState}, the way `advanceSpawnDirector` pairs config with state, so the state itself stays pure data.
50
76
  - `EncounterContext` (interface): interface EncounterContext — Context passed to {@link startEncounter} / {@link updateEncounter}: the caller signals predicates evaluate against, plus named `spawnPoints` catalogs (authored in the scene, referenced by key) that spawn providers place against. Never holds coordinates the sequence itself owns.
51
77
  - `EncounterEvent` (type): type EncounterEvent = | { type: "enter"; phaseId: string; iteration: number } | { type: "spawn"; phaseId: string; provider: string; requests: readonly EncounterSpawnRequest[] } | { type: "complete"; phaseId: string; iteration: number; reason: string } | { type: "done" } — A lifecycle event emitted while stepping an encounter, carrying provenance.
@@ -59,13 +85,20 @@
59
85
  - `EventMeterFeedResult` (interface): interface EventMeterFeedResult { tag: string; reset: boolean; amount: number; value: number; fraction: number; fired: boolean; ready: boolean; overflow: number; tier: string | null; tierChanged: boolean } — ⚠ undocumented
60
86
  - `FireCadence` (interface): interface FireCadence — A minimal minimum-interval rate gate for a repeated action — weapon fire, ability spam, any "no faster than N per second" rule. Deterministic and serializable: its whole state is the elapsed time since the last action, exposed via {@link FireCadence.elapsedMs}.
61
87
  - `FireCadenceConfig` (interface): interface FireCadenceConfig — Tuning for {@link createFireCadence}: the minimum interval between actions and whether it starts ready.
88
+ - `FireDiscipline` (interface): interface FireDiscipline — Fire discipline: how long a member stays down between shots, how long it leans out, and how fast it fires while it has a target. A role that peeks for longer holds an angle better and spends more time in the open — the trade is the caller's to tune.
62
89
  - `FrameRange` (interface): interface FrameRange { phase: FramePhase; from: number; to: number } — ⚠ undocumented
63
90
  - `HitReactionConfig` (interface): interface HitReactionConfig { hitstopMs: number; knockback: number; vertical?: number; shake?: CameraShake; trauma?: number; timescale?: number } — ⚠ undocumented · used by `resolveHitReaction`: Resolves hit feel (hitstop, knockback impulse, camera shake) from either a named `impactPresets` event (`resolveHitReaction("explosion", inp…
91
+ - `HitZoneDef` (interface): interface HitZoneDef — One hit zone: what a hit there multiplies, and how much likelier it is to crit.
92
+ - `HitZoneResolution` (interface): interface HitZoneResolution — One resolved zone: where the hit landed, whether it crit, and the single multiplier to apply.
93
+ - `HitZoneTable` (interface): interface HitZoneTable — A serializable hit-zone table: where a hit landed, what that multiplies, and the table's base crit chance and multiplier. A generated gun or a weapon runtime passes its own zones in; the engine has no built-in headshot.
64
94
  - `ImmunityWindow` (interface): interface ImmunityWindow — A per-target timed immunity policy: an interceptor plus grant/query/serialize controls, toggled by transitions.
65
95
  - `InterceptContext` (interface): interface InterceptContext — Read-only context an interceptor may consult — deterministic clock and optional health lookup for HP-relative policies.
66
96
  - `InterceptDecision` (type): type InterceptDecision = | { kind: "pass" } | { kind: "transform"; amount: number; note?: string } | { kind: "clamp"; max: number; note?: string } | { kind: "redirect"; target: string; note?: string } | { kind: "split"; parts: readonly PendingDamage[]; note?: string } | { kind: "defer"; note?: strin… — What an interceptor decides to do with the pending application it was handed. `split` parts continue at the NEXT interceptor (never re-entering the splitter), which is what keeps the pipeline bounded and recursion-free.
67
97
  - `InterceptRecord` (interface): interface InterceptRecord — One row of provenance: which interceptor acted, what it decided, and the amount before/after it acted.
68
98
  - `MAX_INTERCEPT_STEPS` (const): const MAX_INTERCEPT_STEPS: 256 — Upper bound on interceptor evaluations for a single resolution; guards against runaway `split`/`redirect` loops.
99
+ - `MAX_WEAPON_BURST_COUNT` (const): const MAX_WEAPON_BURST_COUNT: 64 — Cap on `WeaponBurstDef.count`.
100
+ - `MAX_WEAPON_PELLETS_IN_FLIGHT` (const): const MAX_WEAPON_PELLETS_IN_FLIGHT: 256 — Cap on `WeaponDefinition.maxInFlight`; bounds per-tick projectile work.
101
+ - `MAX_WEAPON_PELLETS_PER_SHOT` (const): const MAX_WEAPON_PELLETS_PER_SHOT: 64 — Cap on `WeaponDefinition.pellets`; bounds rays and damage resolutions per shot.
69
102
  - `Magazine` (interface): interface Magazine — A per-weapon magazine: discrete loaded rounds, a timed reload that refills from a reserve pool, and the reserve-pool interaction itself — the primitive that replaces hand-rolling mag size, reload delay, and reserve bookkeeping per game (#536.2).
70
103
  - `MagazineConfig` (interface): interface MagazineConfig — Tuning for `createMagazine`: mag capacity, reload delay, and where reserve ammo is drawn from.
71
104
  - `MagazineReserve` (interface): interface MagazineReserve — Draws ammo for a `Magazine`'s reload from wherever the reserve pool actually lives.
@@ -100,6 +133,16 @@
100
133
  - `ShotOriginPolicy` (type): type ShotOriginPolicy = | { kind: "converge"; muzzle?: EntityPosition; height?: number } | { kind: "eye"; height?: number } | { kind: "legacy" } | { kind: "entity" } | { kind: "entityOffset"; offset: EntityPosition } | { kind: "muzzle"; offset?: EntityPosition } | { kind: "camera"; origin: EntityPos… — How a shot's world-space origin (and optional direction) is resolved before prediction/settlement. - `converge` — the shot leaves the gun `muzzle` yet still passes through whatever the shooter's sightline (crosshair) covers: origin is the muzzle offset, direction is bent from the muzzle to the eye ray's aim point. The projectile system's default for a free `{ yaw, pitch }` aim, so a bullet visibly comes from the barrel without missing the reticle. Needs a scene raycast to find the aim point (`convergeShot`); a bare `resolveShot` degrades to a straight muzzle ray. Passes an explicit `{ origin, direction }` aim through untouched. - `eye` — `aim.origin` when present, else the shooter's entity position raised to eye height; the shot traces the shooter's sightline, so what the crosshair covers is what gets hit. - `legacy` — `aim.origin` when present, else the shooter's raw entity position (feet). - `entity` — always the shooter's entity position. - `entityOffset` / `muzzle` — entity-local offset rotated by the shooter's yaw (muzzle on a weapon model). - `camera` — explicit camera/reticle world origin (and optional direction override). - `world` — absolute world origin.
101
134
  - `SpawnProvider` (type): type SpawnProvider = ( params: Readonly<Record<string, unknown>>, ctx: SpawnProviderContext, ) => readonly EncounterSpawnRequest[] — A spawn provider: given the phase's `params` and context, returns the spawn requests to emit on enter. Deterministic; reads authored scene points from `ctx.spawnPoints` rather than embedding coordinates.
102
135
  - `SpawnProviderContext` (interface): interface SpawnProviderContext — Evaluation context a spawn provider receives when its phase is entered.
136
+ - `SquadCombat` (interface): interface SquadCombat — Drive a squad: cover by threat, flanking, fire discipline, suppression and retreat.
137
+ - `SquadCombatOptions` (interface): interface SquadCombatOptions extends SquadCombatTunables — Everything the squad AI reads from the game, plus its tunables.
138
+ - `SquadCombatSnapshot` (interface): interface SquadCombatSnapshot — Serializable squad state: member decisions, the crowd, and the tuning they ran under.
139
+ - `SquadCombatStats` (interface): interface SquadCombatStats — Counters a probe or a test reads.
140
+ - `SquadCombatTunables` (interface): interface SquadCombatTunables — In-play dials, every one retunable.
141
+ - `SquadComposition` (interface): interface SquadComposition — A squad's roles in assignment order.
142
+ - `SquadMemberSpec` (interface): interface SquadMemberSpec — One combatant the runtime drives: whose side it is on, where it stands, and how hurt it is.
143
+ - `SquadMemberView` (interface): interface SquadMemberView — One member as a HUD, a probe or a test reads it. Allocates.
144
+ - `SquadMode` (type): type SquadMode = "advance" | "cover" | "peek" | "retreat" | "down" — What a member is doing this tick: moving to cover, holding it, leaning out, falling back, or down.
145
+ - `SquadRole` (interface): interface SquadRole — One caller-defined squad role. The engine ships the machinery and no role list: a game names its own roles and dials them, so `"assault"`, `"breacher"` and `"sniper"` are content, not engine concepts.
103
146
  - `StatPool` (interface): interface StatPool — Serializable state for any bounded named resource. The caller defines what the resource means.
104
147
  - `StatPoolAccess` (interface): interface StatPoolAccess<TOwnerId extends string = string, TStatId extends string = string> — Structural bridge to caller-owned resource state. Implement it over an ECS, Redux/Zustand store, plain object, database-backed model, or JGengine's native entity stats without moving ownership into the SDK.
105
148
  - `StatPoolAccessResult` (type): type StatPoolAccessResult = | ({ status: "ok" } & StatPoolChange) | { status: "rejected"; reason: string } — Outcome of changing a pool through {@link StatPoolAccess}.
@@ -110,25 +153,66 @@
110
153
  - `StatsSnapshot` (interface): interface StatsSnapshot<TStat extends string> — Complete plain-data state for a {@link Stats} runtime.
111
154
  - `StatusApplicationOutcome` (interface): interface StatusApplicationOutcome — The result of one status application: the outcome, the resulting instance (if any), and the roll.
112
155
  - `StatusApplicationSpec` (interface): interface StatusApplicationSpec — A serializable, effect-agnostic description of a status a hit tries to apply: an opaque `status` id the game routes to its own effect/DoT pipeline, a base landing `chance`, and the duration/ticks/magnitude/attribution/stacking data the resolver needs. The engine never interprets `status` — it is a reference, not a built-in condition.
156
+ - `StatusApplyOptions` (interface): interface StatusApplyOptions — Options for one application; scales compose with the definition's own base values.
157
+ - `StatusDamageTick` (interface): interface StatusDamageTick — A damage tick, already routed through the caller's `applyDamage` when one was supplied.
158
+ - `StatusDef` (interface): interface StatusDef — One status the runtime can apply: how a fresh application folds into a live one ({@link StatusStackPolicy}), how long it lives, how often and how hard it ticks, and what a tick does. The stack rule is the *only* thing that changes stacks — per-tick magnitude is `magnitude × stacks`, never `magnitude ^ applications`.
159
+ - `StatusEffect` (type): type StatusEffect = | { kind: "damage"; channel: string } | { kind: "stat"; stat: string } — What a status does on each tick. Damage ticks are reported with the channel the caller's damage pipeline resolves, so a burn still meets resistances and armor; stat ticks are a per-stack delta summed by {@link StatusRuntime.statDelta} (`slow` → move speed, `corrode` → armor).
113
160
  - `StatusEffectView` (interface): interface StatusEffectView — A serializable, presentation-ready snapshot of one active status for a timeline HUD row: a stable list `id`, the free-string `kind` the game styles (color / label / icon), the countdown-ring inputs `remainingMs` / `durationMs`, the `stacks` badge count, and the optional `magnitude` in force. Derived from a live {@link StatusInstance} via {@link toStatusEffectView}. The engine never interprets `kind` — it is a reference the game skins, not a built-in condition.
114
161
  - `StatusEffectViewOptions` (interface): interface StatusEffectViewOptions — Options for {@link toStatusEffectView} / {@link toStatusEffectViews}.
162
+ - `StatusExpiredTick` (interface): interface StatusExpiredTick — A status ended: remove its HUD row and any stat delta it was contributing.
115
163
  - `StatusImmunity` (interface): interface StatusImmunity — Statuses a target cannot receive at all — the immunity half of application policy.
116
164
  - `StatusInstance` (interface): interface StatusInstance — A live status carried by a target — the serializable, replicable state a status application mutates.
165
+ - `StatusRuntime` (interface): interface StatusRuntime — A pool of per-target statuses that applies stack rules, ticks effects, and hands its state out.
166
+ - `StatusRuntimeEntry` (interface): interface StatusRuntimeEntry — One live status plus its tick timer, as saved and restored.
167
+ - `StatusRuntimeOptions` (interface): interface StatusRuntimeOptions — Tunables a game can adjust at runtime without discarding carried statuses.
168
+ - `StatusRuntimeState` (interface): interface StatusRuntimeState — Serializable runtime state: carried statuses, tick timers, and the counters a probe reports.
117
169
  - `StatusStackPolicy` (type): type StatusStackPolicy = | { kind: "refresh" } | { kind: "stack"; max?: number; add?: number } | { kind: "replace" } | { kind: "ignore" } — How a fresh application behaves when the target already carries the status. - `refresh` — reset duration, hold the existing stack count. - `stack` — add `add` stacks (default 1) up to `max`, and refresh duration. - `replace` — overwrite duration and magnitude, stacks reset to 1. - `ignore` — leave the existing instance untouched (first application wins).
170
+ - `StatusStatTick` (interface): interface StatusStatTick — A stat tick: a per-stack delta the caller reads while the status lives.
171
+ - `StatusTickEvent` (type): type StatusTickEvent = StatusDamageTick | StatusStatTick | StatusExpiredTick — Everything one `tick` call produced, in target-then-status order.
172
+ - `TargetProfile` (interface): interface TargetProfile — What a target presents to the table: one resistance class and its armor points.
118
173
  - `TargetTraitId` (type): type TargetTraitId = string — Caller-owned id for a target trait — `"armored"`, `"fleshy"`, `"wet"`, `"boss"`. Traits are the receiver-side facets a matchup scores a channel against.
119
174
  - `TelegraphConfig` (interface): interface TelegraphConfig { shape: TelegraphShape; at: EntityPosition; dir?: number; windupMs?: number; turns?: number; kind?: string } — ⚠ undocumented
120
175
  - `TelegraphShape` (type): type TelegraphShape = | { kind: "circle"; radius: number } | { kind: "ring"; radius: number; innerRadius: number } | { kind: "cone"; radius: number; angle: number } | { kind: "line"; length: number; width: number } — ⚠ undocumented
176
+ - `ThreatReadOptions` (interface): interface ThreatReadOptions — Bounds for {@link threatsFromMemory}.
177
+ - `ThreatReading` (interface): interface ThreatReading extends CoverThreat — One remembered enemy with its age, so a role can treat a stale sighting differently.
121
178
  - `ThresholdCrossing` (interface): interface ThresholdCrossing — One emitted crossing: which threshold, which way, and the value span that produced it.
122
179
  - `ThresholdCrossingConfig` (interface): interface ThresholdCrossingConfig — Caller configuration for a threshold tracker.
123
180
  - `ThresholdMark` (interface): interface ThresholdMark — Generic numeric threshold-crossing tracker (#931).
124
181
  - `ThresholdTracker` (interface): interface ThresholdTracker — A stateful crossing tracker over one numeric source.
125
182
  - `ThresholdTrackerSnapshot` (interface): interface ThresholdTrackerSnapshot — Serializable tracker state for save/load.
183
+ - `TimeToKill` (interface): interface TimeToKill — A quoted time to kill for one fixed hit against one health pool.
184
+ - `UnknownDamageTypeError` (class): class UnknownDamageTypeError extends Error — Raised when a hit names a damage type the table does not define, rather than silently dealing 1×.
185
+ - `UnknownHitZoneError` (class): class UnknownHitZoneError extends Error — Raised when neither the named zone nor the table's `defaultZone` exists — a data error, not a 1× hit.
186
+ - `UnknownStatusError` (class): class UnknownStatusError extends Error — Raised when a caller names a status id the runtime has no definition for.
187
+ - `UnknownTargetProfileError` (class): class UnknownTargetProfileError extends Error — Raised when a hit names a target that was never registered — a wiring error, not a 0-damage hit.
126
188
  - `VfxPreset` (interface): interface VfxPreset — A named visual flavor for a one-shot `combat.vfx` burst: the archetype (`kind`), a `0xRRGGBB` tint, and optional default `durationMs`/`radius`. Purely presentation — a preset never touches damage, range, or any authoritative value; it just answers "what does this attack look like" so a game does not hand-roll colors and archetypes.
127
189
  - `VfxPresetName` (type): type VfxPresetName = keyof typeof vfxPresets — A named key into {@link vfxPresets} (`"arrow" | "fireball" | "lightning" | "web" | "slash" | "shield" | "heal" | ...`).
190
+ - `WeaponAim` (interface): interface WeaponAim — Current aim with recoil applied.
191
+ - `WeaponAmmo` (interface): interface WeaponAmmo — Ammo and cadence as a HUD or a test reads them.
192
+ - `WeaponBurstDef` (interface): interface WeaponBurstDef — Shots one trigger pull commits to. A burst finishes even if the trigger is released.
193
+ - `WeaponContact` (interface): interface WeaponContact — Where one pellet met the caller's world.
194
+ - `WeaponDamageDef` (interface): interface WeaponDamageDef — What one pellet does when it reaches a registered damage target.
195
+ - `WeaponDefinition` (interface): interface WeaponDefinition — One weapon as plain data: cadence, fire mode, delivery, magazine, feel and damage. Nothing here is a function, so a generator can roll it, a save can store it, and two weapons differ by their fields.
196
+ - `WeaponFalloffDef` (interface): interface WeaponFalloffDef — Linear damage falloff between two distances.
197
+ - `WeaponFireMode` (type): type WeaponFireMode = "single" | "auto" | "burst" — How a held or tapped trigger turns into shots.
128
198
  - `WeaponFireResult` (interface): interface WeaponFireResult<THit> — The result of a {@link WeaponRuntime.fire} attempt. On `"fired"`, `hits` is whatever the caller's raycast returned and `resolutions` holds the portable damage provenance for each hit that produced a {@link DamageHitInput}; on any blocked status both are empty and no round was spent.
129
199
  - `WeaponFireStatus` (type): type WeaponFireStatus = "fired" | "cooling" | "empty" | "reloading" — Why a {@link WeaponRuntime.fire} attempt resolved the way it did.
200
+ - `WeaponHitscanDef` (interface): interface WeaponHitscanDef — A ray resolved in the caller's world on the same tick it is fired.
201
+ - `WeaponImpact` (interface): interface WeaponImpact — One resolved hit, with the damage runtime's full provenance.
202
+ - `WeaponImpactContext` (interface): interface WeaponImpactContext — Read-only facts about the hit handed to `damageFor`.
203
+ - `WeaponMagazineDef` (interface): interface WeaponMagazineDef — Loaded rounds, reload time, and where reloads draw from.
204
+ - `WeaponProjectileDef` (interface): interface WeaponProjectileDef — A travelling pellet simulated by the runtime, resolved when it meets the caller's world.
205
+ - `WeaponProjectileView` (interface): interface WeaponProjectileView — A pellet alive in flight, for presentation or prediction.
206
+ - `WeaponReloadState` (type): type WeaponReloadState = "idle" | "reloading" | "interrupted" — `idle`, `reloading`, or `interrupted` after `interruptReload()` kept the progress.
130
207
  - `WeaponRuntime` (interface): interface WeaponRuntime<TAim, THit> — A composed weapon fire controller: a {@link FireCadence} gate, an optional {@link Magazine}, the caller's raycast, and portable {@link resolveDamageHit} damage resolution wired together. The caller retains ownership of models, animations, muzzle flash, audio, input, camera, aiming, and the raycaster itself.
131
208
  - `WeaponRuntimeConfig` (interface): interface WeaponRuntimeConfig<TAim, THit> — Configuration for {@link createWeaponRuntime}. Everything time/ammo-related is plain data; the two callbacks are where the caller keeps ownership of aiming, its own raycaster/projectile query, and how a hit maps to damage. There is deliberately no default weapon, projectile, or damage table here — those are game content.
209
+ - `WeaponShotRecord` (interface): interface WeaponShotRecord — One fired shot: the cone it fired into, the kick it took, and where each pellet went.
210
+ - `WeaponStats` (interface): interface WeaponStats — Lifetime ammo economy and resolution counters.
211
+ - `WeaponSystem` (interface): interface WeaponSystem — A weapon as the game drives it: base aim in, shots and resolved impacts out. Fire mode, cadence, handling, delivery, magazine and damage all come from one {@link WeaponDefinition}; the game owns input, the muzzle, and the world query.
212
+ - `WeaponSystemDeps` (interface): interface WeaponSystemDeps — Everything the runtime is allowed to reach: RNG, the damage model, the muzzle, and the world query.
213
+ - `WeaponSystemState` (interface): interface WeaponSystemState — Serializable runtime state: aim, handling, ammo, trigger, pellets and counters.
214
+ - `WeaponTickResult` (interface): interface WeaponTickResult — What one {@link WeaponSystem.tick} produced. Empty arrays on a quiet tick.
215
+ - `WeaponVec3` (type): type WeaponVec3 = readonly [number, number, number] — A world-space point or direction in metres.
132
216
  - `activePhaseId` (function): function activePhaseId<TData>(state: EncounterState<TData>): string | null — The deepest active phase id — the completion cursor a HUD reads to label the current wave/phase — or null before start and once done.
133
217
  - `advanceCombo` (function): function advanceCombo(input: AdvanceComboInput): AdvanceComboResult — ⚠ undocumented
134
218
  - `advanceMotionCues` (function): function advanceMotionCues(cues: EntityRenderCues, speed: number, dt: number, tuning?: RenderCueTuning): EntityRenderCues — Advances `bobPhase` and decays `recoil` from a live `speed` sample (e.g. `groundSpeed(entity)`); leaves event-driven fields untouched.
@@ -137,10 +221,17 @@
137
221
  - `applyRenderDeathEvent` (function): function applyRenderDeathEvent(cues: EntityRenderCues): EntityRenderCues — Marks the cue set dead after `entity.died`; sticky for the lifetime of the render component.
138
222
  - `applyRenderHitEvent` (function): function applyRenderHitEvent(cues: EntityRenderCues): EntityRenderCues — Marks a `combat.hitReaction` pulse; the caller clears `hit` again after its own pulse window.
139
223
  - `applyStatPoolDelta` (function): function applyStatPoolDelta<TOwnerId extends string, TStatId extends string>(access: StatPoolAccess<TOwnerId, TStatId>, ownerId: TOwnerId, statId: TStatId, amount: number): StatPoolAccessResult — Read, transition, and write a caller-owned pool through the structural adapter. Positive amounts increase the resource; negative amounts decrease it. The adapter receives a complete replacement value, never a hidden mutation.
224
+ - `armorMitigation` (function): function armorMitigation(armor: number, config?: ArmorConfig): number — Fraction of a hit that armor removes at `armor` points; `0` at zero armor, capped by config.
225
+ - `assertDamageTable` (function): function assertDamageTable(table: DamageTableDef): void — Validate a table once at construction: duplicate ids and non-finite multipliers fail loudly.
226
+ - `assertHitZoneTable` (function): function assertHitZoneTable(table: HitZoneTable): void — Validate a zone table once at construction: duplicate ids and a missing default fail loudly.
227
+ - `assertWeaponDefinition` (function): function assertWeaponDefinition(definition: WeaponDefinition): void — Check a definition before it reaches the runtime, so a rolled weapon fails loudly at construction instead of firing nothing. Throws `RangeError` naming the field.
228
+ - `assignSquadRoles` (function): function assignSquadRoles(members: readonly string[], composition: SquadComposition): Map<string, SquadRole> — Deterministic role per member: declared order, `count` copies of each role, and the list repeats when the squad is larger than the composition. Same members and composition give the same answer on every host and after a reload.
140
229
  - `attackMeta` (function): function attackMeta(tags: readonly AttackTag[], extra?: Omit<AttackMeta, "tags">): AttackMeta — ⚠ undocumented
141
230
  - `changeStatPool` (function): function changeStatPool(pool: StatPool, amount: number): StatPoolChange — Pure bounded delta transition with the exact applied amount and boundary flags.
142
231
  - `convergeShot` (function): function convergeShot(deps: ShotOriginDeps, from: string, aim: Aim, range: number, sightHit: (origin: EntityPosition, direction: EntityPosition) => EntityPosition | null, muzzleOffset?: EntityPosition): ResolvedShot | null — Resolves a `converge` shot with scene knowledge: fires from the gun muzzle but bends the direction so the shot passes through the aim point the shooter's eye ray covers. `sightHit` casts the eye ray and returns where it lands (first impact), or `null` to fall back to a point `range` metres down the sightline. A `{ origin, direction }` aim is passed through unchanged (nothing to converge).
143
232
  - `counters` (function): function counters(meta: AttackMeta, move: CounterMove): boolean — ⚠ undocumented
233
+ - `coverPointsFromSolids` (function): function coverPointsFromSolids(solids: readonly CoverSolid[], options: CoverPointOptions = {}): CoverPoint[] — Stand points on every vertical face of the supplied boxes: one row per face, spaced along it, `standOff` out from the surface. Geometry in, cover out — no authored coordinate list, so moving a wall in the editor moves its cover.
234
+ - `coverPointsNear` (function): function coverPointsNear(points: readonly CoverPoint[], position: Vec3, radius: number, limit = Number.POSITIVE_INFINITY): CoverPoint[] — The `limit` nearest cover points within `radius`, nearest first, ties broken by id. A linear scan of the points the field holds; the field caps how many points exist.
144
235
  - `createAbilityKit` (function): function createAbilityKit(configs: readonly AbilitySlotConfig[], options: AbilityKitOptions = {}): AbilityKit — A bar of cooldown-gated abilities the player fires by slot, tracking readiness and cooldown per ability.
145
236
  - `createAccumulatorMeter` (function): function createAccumulatorMeter(config: AccumulatorMeterConfig): AccumulatorMeter — A raw accumulating gauge that crosses named tier thresholds as a value builds, with optional decay — the primitive under charge, rage, and combo meters.
146
237
  - `createAntiOneShotPolicy` (function): function createAntiOneShotPolicy(config: AntiOneShotConfig): AntiOneShotPolicy — Compose a reusable anti-one-shot / chip-guard clamp from caller data: while above `guardAboveFraction` a hit cannot drop the target below `leaveFraction`, and after such a save the target gets `recoverMs` of immunity. Below the guard fraction, lethal hits pass unchanged.
@@ -149,8 +240,10 @@
149
240
  - `createComboMeter` (function): function createComboMeter(config: ComboMeterConfig): ComboMeter — A count-based combo / multiplier meter: an integer hit chain that climbs with every `hit()` and drops when a decay window elapses, crossing free-string tier thresholds ("good" → "great" → "savage") that derive a score multiplier, while tracking the peak reached. Time is injected (`now`, default `Date.now`) and/or driven by `update(dt)`; `view()` returns a pooled draw-state and `snapshot`/`restore` round-trips the chain through a save.
150
241
  - `createComboPoints` (function): function createComboPoints(config: ComboPointsConfig): ComboPoints — Build and spend finisher points — the combo-point economy behind rogue-style builders and spenders.
151
242
  - `createComboRunner` (function): function createComboRunner(combo: ComboString, anim: AnimationState): ComboRunner — Advance a chained melee string from timed inputs, tracking the current step and its cancel/continue windows.
243
+ - `createCoverField` (function): function createCoverField(options: CoverFieldOptions): CoverField — Cover over authored boxes: derive the stand points once, re-derive them after an edit, and answer bounded nearest-cover queries. `walkable` is injected, so the seam checks the game's own ground test (`nav.findPolygon(point) !== null`) instead of assuming a flat floor.
152
244
  - `createDamageClamp` (function): function createDamageClamp(config: DamageClampConfig): DamageInterceptor — A stateless interceptor that caps any single application at `maxPerHit`.
153
245
  - `createDamagePipeline` (function): function createDamagePipeline(initial?: readonly DamageInterceptor[]): DamagePipeline — Create a mutable damage interception chain that state transitions install into and remove from — e.g. add an invulnerability interceptor on phase enter and drop it on phase exit. Evaluation order is install order.
246
+ - `createDamageRuntime` (function): function createDamageRuntime(config: DamageRuntimeConfig): DamageRuntime — Build a damage model from data. The number a hit lands is `base × zone multiplier × type multiplier × (1 − armor mitigation)`; statuses tick through the same table and armor, so a burn on an armored target is mitigated like a bullet.
154
247
  - `createDeathSystem` (function): function createDeathSystem(deps: DeathSystemDeps): DeathSystem — Resolve entity death and the on-death consequences — drops, respawn eligibility, kill credit.
155
248
  - `createDefensiveWindow` (function): function createDefensiveWindow(config: DefensiveWindowConfig): DefensiveWindow — Open a timed defensive window — block, parry, or i-frames — and test incoming hits against it.
156
249
  - `createDotField` (function): function createDotField(): DotField — Builds an empty {@link DotField}; `apply` DoTs onto it and drain damage each frame with `tick`.
@@ -164,15 +257,20 @@
164
257
  - `createProjectileSystem` (function): function createProjectileSystem(deps: ProjectileSystemDeps): ProjectileSystem — Spawn and advance projectiles each frame, resolving travel, lifetime, and hits.
165
258
  - `createRegenShield` (function): function createRegenShield(config: RegenShieldConfig): RegenShield — Builds a {@link RegenShield} that suppresses regen for `regenDelayMs` after each hit.
166
259
  - `createResourcePool` (function): function createResourcePool(config: ResourcePoolConfig): ResourcePool — A regenerating resource pool — mana, stamina, energy — that actions spend from and that refills over time.
260
+ - `createSquadCombat` (function): function createSquadCombat(options: SquadCombatOptions): SquadCombat — One squad's combat behavior per tick, over the perception memory a game already keeps and the nav crowd it already moves. Each member picks a stand point that hides it from the threats it remembers, at the distance and angle its role wants; a flanker goes around the side, a support holds and fires, an assault closes. Incoming fire raises suppression, which pins the member to its cover and stops its shooting until the meter decays; low health turns it toward the point furthest from the threat.
167
261
  - `createStaggerMeter` (function): function createStaggerMeter(config: StaggerMeterConfig): StaggerMeter — Build a stagger/poise gauge from landed hits toward a break threshold that staggers the target.
168
262
  - `createStatPool` (function): function createStatPool(input: StatPoolInput): StatPool — Normalize caller data into a complete plain-data pool.
169
263
  - `createStats` (function): function createStats<TStat extends string>(base: Record<TStat, number>, options?: CreateStatsOptions): Stats<TStat> — A stat block whose base values take stacking, timed buffs and debuffs, resolving the modified value on read.
264
+ - `createStatusRuntime` (function): function createStatusRuntime(options: StatusRuntimeOptions): StatusRuntime — Build a status runtime over serializable definitions. Applications reuse {@link resolveStatusApplication} for chance and stacking, so a refresh/stack-to-N/ignore rule has exactly one implementation; `tick` then applies each status's declared effect per stack.
170
265
  - `createThresholdTracker` (function): function createThresholdTracker(config: ThresholdCrossingConfig): ThresholdTracker — Track a numeric source against named thresholds and emit ordered crossing events with direction, hysteresis, once/repeat policy, and skipped-threshold handling. Deterministic and serializable; drives phase transitions, armor breaks, resource warnings, and any other threshold consumer.
171
266
  - `createWeaponRuntime` (function): function createWeaponRuntime<TAim, THit>(config: WeaponRuntimeConfig<TAim, THit>): WeaponRuntime<TAim, THit> — Compose a portable weapon fire controller from the existing generic primitives — a {@link FireCadence} gate, an optional {@link Magazine}, a caller-provided raycast, and portable {@link resolveDamageHit} damage resolution. No `GameContext`, entity store, renderer, or default weapon is involved; the caller owns aiming, its raycaster, and all presentation, and the result carries full damage provenance for authority to apply and replicate.
267
+ - `createWeaponSystem` (function): function createWeaponSystem(initial: WeaponDefinition, deps: WeaponSystemDeps): WeaponSystem — Build a weapon from one {@link WeaponDefinition}. The game supplies aim, a muzzle, one world query, the damage model and an RNG; the runtime owns fire modes, cadence, recoil, bloom, reload states, ammo accounting, projectile ballistics and the damage call. State snapshots as plain data.
172
268
  - `encounterProgress` (function): function encounterProgress<TData>(state: EncounterState<TData>): { completed: number; total: number; done: boolean; } — Progress across the whole tree: how many phases have completed out of the total node count, plus whether the encounter has finished. Counts injected phases.
173
269
  - `eyeHeightFromColliders` (function): function eyeHeightFromColliders(set: EntityColliderSet | null | undefined): number — Eye height derived from a collider set: 90% of the tallest hitbox top, or the humanoid default when unknown.
174
270
  - `filterOutcomeFor` (function): function filterOutcomeFor(outcome: AllocationOutcome, viewerId: string): AllocationOutcome — Project an allocation outcome for one viewer, enforcing replication authoritatively: private grants belonging to other recipients are dropped, and pools the viewer cannot claim (not eligible, or reserved for someone else) are hidden — so private rewards never leak across the wire while provenance on the viewer's own grants is preserved.
175
271
  - `forceCompletePhase` (function): function forceCompletePhase<TData>(config: EncounterConfig<TData>, state: EncounterState<TData>, ctx: EncounterContext = {}, phaseId?: string): EncounterStep<TData> — Force the deepest active phase (or a named active phase) to complete now, regardless of its predicate — the escape hatch for external/scripted completion and failure/retry flows. Advances into the next phase and emits the same lifecycle events as a natural completion. Returns no events if the phase is not currently active. Mutates and returns `state`.
272
+ - `formatDamageTable` (function): function formatDamageTable(rows: readonly DamageTableRow[]): string[] — Render rows as fixed-width lines for a devtools probe, a CLI dump, or a test's diff.
273
+ - `hitZoneMultiplier` (function): function hitZoneMultiplier(table: HitZoneTable, zone: string | undefined, crit: boolean): { zone: string; multiplier: number; fallback: boolean } — The multiplier a zone earns for a known crit outcome, without rolling. `resolveHitZone` and a time-to-kill quote both read through this, so a quoted crit uses the same number combat does.
176
274
  - `impactPresets` (const): const impactPresets: { readonly pickup: { readonly hitstopMs: 0; readonly trauma: 0.15; }; readonly jumpLand: { readonly hitstopMs: 0; readonly trauma: 0.2; }; readonly enemyKilled: { readonly hitstopMs: 40; readonly trauma: 0.3; }; readonly playerHit: { readonly hitstopMs: 70; readonly trauma: 0.4;… — Calibrated per-event impact feel — hitstop and trauma numbers harvested from a shipped game-feel reference, not hand-invented per game. `explosion` and `playerHit` are "heavy hit" events (60–90ms hitstop @ 0.05 timescale); `pickup`/`jumpLand` are light events with no hitstop. Trauma is later clamped to 1.0 by `resolveHitReaction`.
177
275
  - `injectPhase` (function): function injectPhase<TData>(state: EncounterState<TData>, phase: EncounterPhase<TData>, at: PhaseInjectAt): EncounterState<TData> — Insert a phase into the working tree at runtime — before/after a sibling, or as a child of a group — without disturbing already-completed or active nodes. The injected subtree becomes part of the serializable state (so a mid-encounter save preserves it) and is reached in normal order if it sits ahead of the cursor. Ids must stay tree-unique. Mutates and returns `state`.
178
276
  - `isBlockable` (function): function isBlockable(meta: AttackMeta): boolean — ⚠ undocumented
@@ -180,26 +278,37 @@
180
278
  - `isParryable` (function): function isParryable(meta: AttackMeta): boolean — ⚠ undocumented
181
279
  - `patchStatPool` (function): function patchStatPool(pool: StatPool, patch: StatPoolPatch): StatPool — Apply new bounds/current to a pool, clamping the result and leaving the input untouched.
182
280
  - `phaseStatus` (function): function phaseStatus<TData>(state: EncounterState<TData>, id: string): PhaseStatus | null — The runtime status of a phase by id (pending/active/complete), or null if the id is unknown — for driving per-phase HUD ticks and objective checklists.
281
+ - `readDamageTable` (function): function readDamageTable(table: DamageTableDef, read?: { base?: number; armor?: number | Readonly<Record<string, number>>; types?: readonly string[]; resistances?: readonly string[]; }): DamageTableRow[] — Read the whole table as rows — every listed type against every listed resistance class, each with the armor it was read at and the damage a base would land. This is the table a designer reads and a test asserts, so a resistance change is visible without running combat.
183
282
  - `resistanceScale` (function): function resistanceScale<TCategory extends string = string, TProperty extends string = string>(matrix: ResistanceMatrix<TCategory, TProperty>, category: TCategory | string, targetProperties: readonly (TProperty | string)[]): number — ⚠ undocumented
184
283
  - `resolveClaim` (function): function resolveClaim(pool: ClaimablePool, claimantId: string, options?: { nowMs?: number; private?: boolean }): ClaimOutcome — Resolve an authoritative claim against a pool. Idempotent: re-claiming an already-owned pool returns the same grants without re-awarding, a losing claimant gets no grants, and eligibility, reservation, and expiry are all enforced here — the authority, not UI hiding, decides ownership. Returns the updated (serializable) pool plus the grants owed.
185
284
  - `resolveDamage` (function): function resolveDamage(interceptors: readonly DamageInterceptor[], pending: PendingDamage, ctx: InterceptContext): DamageResolution — Run one pending damage application through an ordered interceptor list before lethality resolves, returning the final applications, any deferred ones, and full provenance. Pure and deterministic; bounded by `MAX_INTERCEPT_STEPS`.
186
285
  - `resolveDamageHit` (function): function resolveDamageHit(input: DamageHitInput): DamageHitResolution — Resolve a full damage hit through the data-defined pipeline: score the channel against the target's traits ({@link resolveMatchup}), scale base impact by the matchup's `impact` axis, run receiver-side modifiers ({@link resolveReceivedDamage}), then roll status application scaled by the matchup's `applyChance`/`magnitude`/`duration` axes ({@link resolveStatusApplication}). Pure and deterministic given a seeded `rng`; returns full provenance and never mutates state, so it is safe to run on the authority and replicate the results.
187
286
  - `resolveDefense` (function): function resolveDefense(input: ResolveDefenseInput): DefenseResolution — ⚠ undocumented
188
287
  - `resolveHitReaction` (function): function resolveHitReaction(config: HitReactionConfig | ImpactPresetName, input: HitReactionInput): HitReaction — Resolves hit feel (hitstop, knockback impulse, camera shake) from either a named `impactPresets` event (`resolveHitReaction("explosion", input)`) or a raw `HitReactionConfig` override.
288
+ - `resolveHitZone` (function): function resolveHitZone(table: HitZoneTable, zone: string | undefined, rng?: () => number): HitZoneResolution — Resolve where a hit landed into one multiplier: roll the crit chance for that zone, and apply the crit multiplier when it lands, otherwise the zone's own multiplier. The injected `rng` keeps the roll reproducible; a zero chance draws nothing, so a deterministic test can force either outcome.
189
289
  - `resolveMatchup` (function): function resolveMatchup<TChannel extends string = DamageChannelId, TTrait extends string = TargetTraitId>(matchup: DamageMatchup<TChannel, TTrait>, channel: TChannel | string, targetTraits: readonly (TTrait | string)[]): ResolvedMatchup — Resolve a channel against a target's traits into independent, multiplied outputs. Pure and order-independent: every trait the channel has an entry for contributes, its axes multiplying into the running product; unmatched traits are skipped, and when nothing matches the table's `default` (or identity) is used. Bounded by the trait count — no world scan.
190
290
  - `resolveReceivedDamage` (function): function resolveReceivedDamage(input: ReceivedDamageInput): ReceivedDamageResult — Run receiver-side modifiers over an incoming hit, in ascending `order`, recording every step. Amplification, reduction, caps, conversion, redirection, and immunity are all data — the engine applies whichever policies the target's modifiers declare and never branches on channel names. Pure over serializable data with bounded work (one pass over the modifier list); an `immune` policy short-circuits the remaining modifiers.
191
291
  - `resolveResistance` (function): function resolveResistance<TCategory extends string = string, TProperty extends string = string>(matrix: ResistanceMatrix<TCategory, TProperty>, category: TCategory | string, targetProperties: readonly (TProperty | string)[]): ResistanceResult — ⚠ undocumented
192
292
  - `resolveShot` (function): function resolveShot(deps: ShotOriginDeps, from: string, aim: Aim, policy: ShotOriginPolicy = { kind: "eye" }): ResolvedShot | null — Resolves a shot's world origin and direction from the shooter's eye or muzzle and an {@link Aim}, ready for `ctx.scene.raycast` — the aim half of a hitscan weapon.
193
293
  - `resolveStatusApplication` (function): function resolveStatusApplication(input: StatusApplicationInput): StatusApplicationOutcome — Resolve one status application: reject on immunity, roll landing chance against injected RNG, scale magnitude/duration by the matchup, then fold into any existing instance per the stacking policy. Deterministic given a seeded `rng`; pure over serializable data, so authority can run it and replicate only the returned `instance`.
294
+ - `resolveTableDamage` (function): function resolveTableDamage(table: DamageTableDef, input: { type: string; resistance: string; armor?: number; base?: number }): DamageTableRow — Resolve one hit through the table: type multiplier, then armor mitigation, and the base scaled by both. Formula order matters only for the numbers a UI shows — the product is the same.
194
295
  - `resolveVfxPreset` (function): function resolveVfxPreset(name: string | undefined, overrides?: Partial<VfxPreset>): VfxPreset — Resolve a named visual flavor into a concrete `{ kind, color, durationMs?, radius? }`, with any provided `overrides` winning field by field. An unknown name resolves to {@link DEFAULT_VFX_PRESET} rather than throwing, so a flavor typo degrades to a visible spark. Used by `ctx.scene.entity.vfx({ preset })`; call it directly when you need the raw numbers (e.g. to seed a retained `vfxInstance` from the same vocabulary).
195
296
  - `rollCheck` (function): function rollCheck(input: CheckInput, rng?: () => number): CheckResult — Resolve a tabletop-style pass/fail roll against a target number with modifiers and crit/fumble bands.
297
+ - `scoreCoverPoint` (function): function scoreCoverPoint(point: CoverPoint, query: CoverQuery): CoverChoice | null — Score one stand point against the threats the member remembers. `hidden` is the confidence-weighted share of remembered threats that cannot see the point, so a point that hides from the shooter that matters scores over one that hides from a fading one. Returns `null` when nothing it can hide from is worth considering.
298
+ - `selectCover` (function): function selectCover(points: readonly CoverPoint[], query: CoverQuery): CoverChoice | null — The best of `points` under one {@link CoverQuery}, ties broken by point id. Pass the candidate set the injected query already bounded; this scores what it is given.
196
299
  - `startEncounter` (function): function startEncounter<TData>(config: EncounterConfig<TData>, state: EncounterState<TData>, ctx: EncounterContext = {}): EncounterStep<TData> — Start a created encounter: enter the first root phase, descending to its first leaf, and return the initial enter/spawn events. A no-op (with no events) on an already-started or empty encounter. Mutates and returns `state`.
197
300
  - `statusEffectRemainingFraction` (function): function statusEffectRemainingFraction(view: StatusEffectView): number — Fraction of the countdown still remaining, clamped to `[0,1]` — the value a countdown ring fills to. Returns `0` when the duration is non-positive. Pure; safe to call every frame.
301
+ - `threatsFromMemory` (function): function threatsFromMemory(perception: PerceptionService, observerId: string, nowMs: number, options: ThreatReadOptions = {}): ThreatReading[] — Read one member's threat list out of its perception memory — the only source of threat. Sorted by confidence, then recency, then id, and bounded by `max`, so a crowded memory cannot grow the tick.
198
302
  - `tierAt` (function): function tierAt(value: number, tiers: readonly MeterTier[]): string | null — The highest tier id whose `at` threshold `value` has reached, or `null` below every tier — the pure lookup `createAccumulatorMeter`/`createEventMeter` call on every `add`/`tick`.
199
303
  - `toStatusEffectView` (function): function toStatusEffectView(instance: StatusInstance, options?: StatusEffectViewOptions): StatusEffectView — Map one live {@link StatusInstance} to a serializable {@link StatusEffectView} for a HUD row. Pure: no clock, no allocation beyond the returned view. The `durationMs` option supplies the ring denominator the instance does not carry; without it the ring reads full.
200
304
  - `toStatusEffectViews` (function): function toStatusEffectViews(instances: readonly StatusInstance[], options?: StatusEffectViewOptions): StatusEffectView[] — Map a list of live statuses (a target's carried {@link StatusInstance}s) to ordered HUD views. Pure and allocation-bounded — one pass, one view per input — so a status bar can render straight off the existing status model's snapshot without re-deriving ring math or timers.
305
+ - `typeBypassesArmor` (function): function typeBypassesArmor(table: DamageTableDef, type: string): boolean — Whether armor points reduce this damage type.
306
+ - `typeMultiplier` (function): function typeMultiplier(table: DamageTableDef, type: string, resistance: string): number — The type × resistance multiplier for one class, before armor. An unknown type throws; an unknown resistance class uses the table's `defaultMultiplier`, since target classes may be narrower than the table's coverage.
201
307
  - `updateEncounter` (function): function updateEncounter<TData>(config: EncounterConfig<TData>, state: EncounterState<TData>, dt: number, ctx: EncounterContext = {}): EncounterStep<TData> — Advance an encounter by `dt` seconds against the caller `ctx`. Accumulates time, then repeatedly: evaluates the cursor node's completion predicate; on completion, either re-runs its subtree (`repeat`) or marks it complete and advances to the next sibling — bubbling up to parent groups whose children are exhausted — entering the next chain and emitting enter/spawn/complete events. Emits a final `done` event when the last phase completes. Bounded per call by node count; mutates and returns `state`.
202
308
  - `vfxPresets` (const): const vfxPresets: { readonly arrow: { readonly kind: "projectile"; readonly color: 14207392; }; readonly bolt: { readonly kind: "projectile"; readonly color: 16777215; }; readonly fireball: { readonly kind: "projectile"; readonly color: 16738842; readonly durationMs: 460; }; readonly firebolt: { rea… — Ready-made visual flavors for the things players actually shoot, swing, and cast — so `ctx.scene.entity.vfx({ preset: "arrow", from, to })` renders a visible bolt with zero tuning, and `"lightning"`, `"web"`, `"slash"`, `"shield"`, `"heal"`, `"explosion"` likewise just work. Each entry only picks a `kind` (`projectile | beam | nova | glow | spark`), a color, and a lifetime; it is visual vocabulary (like named CSS colors), not a gameplay archetype — combat numbers stay in the caller's own effect/ability data. Any field passed to `vfx()` overrides the preset, and unknown names fall back to `spark` so a flavor typo still shows *something* rather than nothing.
309
+ - `weaponDamageAt` (function): function weaponDamageAt(definition: WeaponDefinition, distance: number): number — Damage multiplier at a distance: `1` before `falloff.start`, linearly down to `endScale` at `end`.
310
+ - `weaponIntervalMs` (function): function weaponIntervalMs(definition: WeaponDefinition): number — Milliseconds between shots at full cadence: `60_000 / roundsPerMinute`.
311
+ - `weaponMaxInFlight` (function): function weaponMaxInFlight(definition: WeaponDefinition): number — Live pellets one definition may keep, clamped to a bounded range.
203
312
 
204
313
  ## @jgengine/core/combat/abilityKit
205
314
 
@@ -286,6 +395,25 @@
286
395
  - `createComboRunner` (function): function createComboRunner(combo: ComboString, anim: AnimationState): ComboRunner — Advance a chained melee string from timed inputs, tracking the current step and its cancel/continue windows.
287
396
  - `stepById` (function): function stepById(combo: ComboString, stepId: string): ComboStep | null — ⚠ undocumented
288
397
 
398
+ ## @jgengine/core/combat/cover
399
+
400
+ - `CoverChoice` (interface): interface CoverChoice — A scored cover point, with every term visible so a probe or test can see why it won.
401
+ - `CoverField` (interface): interface CoverField extends CoverSource — Cover derived from authored geometry, rebuilt when the world is edited.
402
+ - `CoverFieldOptions` (interface): interface CoverFieldOptions extends CoverPointOptions — Options for {@link createCoverField}: the authored boxes, the dials, and a probe name.
403
+ - `CoverFieldSnapshot` (interface): interface CoverFieldSnapshot — Serializable cover field state: the boxes and the dials, not the derived points.
404
+ - `CoverPoint` (interface): interface CoverPoint — One stand position beside authored geometry, with the face direction it hides against.
405
+ - `CoverPointOptions` (interface): interface CoverPointOptions — How cover points sit relative to the geometry that generates them.
406
+ - `CoverQuery` (interface): interface CoverQuery — Everything one cover decision reads: where the member is, what it believes, and its role's policy.
407
+ - `CoverSolid` (interface): interface CoverSolid — A box of authored world geometry a body can hide behind. Structurally a `WorldSolid`, so `ctx.world.solids.all()` drops in directly; a game with its own collision can pass any boxes.
408
+ - `CoverSource` (interface): interface CoverSource — Bounded source of cover near a position: generated from authored geometry, or the game's own spatial query. Called for one member at a time, never for the whole squad.
409
+ - `CoverThreat` (interface): interface CoverThreat — A remembered threat: where the member last saw it and how strongly it still believes it.
410
+ - `CoverWeights` (interface): interface CoverWeights — What each term is worth when choosing cover. A role owns these numbers.
411
+ - `coverPointsFromSolids` (function): function coverPointsFromSolids(solids: readonly CoverSolid[], options: CoverPointOptions = {}): CoverPoint[] — Stand points on every vertical face of the supplied boxes: one row per face, spaced along it, `standOff` out from the surface. Geometry in, cover out — no authored coordinate list, so moving a wall in the editor moves its cover.
412
+ - `coverPointsNear` (function): function coverPointsNear(points: readonly CoverPoint[], position: Vec3, radius: number, limit = Number.POSITIVE_INFINITY): CoverPoint[] — The `limit` nearest cover points within `radius`, nearest first, ties broken by id. A linear scan of the points the field holds; the field caps how many points exist.
413
+ - `createCoverField` (function): function createCoverField(options: CoverFieldOptions): CoverField — Cover over authored boxes: derive the stand points once, re-derive them after an edit, and answer bounded nearest-cover queries. `walkable` is injected, so the seam checks the game's own ground test (`nav.findPolygon(point) !== null`) instead of assuming a flat floor.
414
+ - `scoreCoverPoint` (function): function scoreCoverPoint(point: CoverPoint, query: CoverQuery): CoverChoice | null — Score one stand point against the threats the member remembers. `hidden` is the confidence-weighted share of remembered threats that cannot see the point, so a point that hides from the shooter that matters scores over one that hides from a fading one. Returns `null` when nothing it can hide from is worth considering.
415
+ - `selectCover` (function): function selectCover(points: readonly CoverPoint[], query: CoverQuery): CoverChoice | null — The best of `points` under one {@link CoverQuery}, ties broken by point id. Pass the candidate set the injected query already bounded; this scores what it is given.
416
+
289
417
  ## @jgengine/core/combat/damageInterceptors
290
418
 
291
419
  - `AntiOneShotConfig` (interface): interface AntiOneShotConfig — Caller data for the anti-one-shot composition: "cannot cross lethal while above X, leave Y, then recover for Z".
@@ -322,6 +450,38 @@
322
450
  - `DamageHitResolution` (interface): interface DamageHitResolution — The full, provenance-rich result of resolving a hit — every stage kept for UI and debugging. `impact` is the final number to drain; `matchup`, `received`, and `status` expose exactly how it got there. Authority applies `impact` (plus `received.redirects`/`conversions`) and the returned status `instance`; nothing here mutates game state.
323
451
  - `resolveDamageHit` (function): function resolveDamageHit(input: DamageHitInput): DamageHitResolution — Resolve a full damage hit through the data-defined pipeline: score the channel against the target's traits ({@link resolveMatchup}), scale base impact by the matchup's `impact` axis, run receiver-side modifiers ({@link resolveReceivedDamage}), then roll status application scaled by the matchup's `applyChance`/`magnitude`/`duration` axes ({@link resolveStatusApplication}). Pure and deterministic given a seeded `rng`; returns full provenance and never mutates state, so it is safe to run on the authority and replicate the results.
324
452
 
453
+ ## @jgengine/core/combat/damageRuntime
454
+
455
+ - `DamageRuntime` (interface): interface DamageRuntime — A damage model: registered targets, the table/zones/statuses they are hit with, and carried status state. `hit` resolves a blow, `tick` runs status effects, `timeToKill` quotes a fight, and `table` reads the whole chart. State is plain data, so a save, a replay, or a worker can move it.
456
+ - `DamageRuntimeConfig` (interface): interface DamageRuntimeConfig — Everything the model reads: the table, the zones, the status definitions, and the RNG.
457
+ - `DamageRuntimeHitInput` (interface): interface DamageRuntimeHitInput — One incoming hit: what struck, how hard, where it landed, and what statuses it carries.
458
+ - `DamageRuntimeResolution` (interface): interface DamageRuntimeResolution — Every stage of one resolved hit, so a UI or a test can read why the final number is what it is.
459
+ - `DamageRuntimeRetune` (interface): interface DamageRuntimeRetune — In-play dials, all retunable without discarding registered targets or carried statuses.
460
+ - `DamageRuntimeState` (interface): interface DamageRuntimeState — Serialized model state: registered targets, carried statuses, and the probe counters.
461
+ - `DamageStatusRequest` (interface): interface DamageStatusRequest — One status a hit tries to apply, scaled against that status's own base chance and values.
462
+ - `TargetProfile` (interface): interface TargetProfile — What a target presents to the table: one resistance class and its armor points.
463
+ - `TimeToKill` (interface): interface TimeToKill — A quoted time to kill for one fixed hit against one health pool.
464
+ - `UnknownTargetProfileError` (class): class UnknownTargetProfileError extends Error — Raised when a hit names a target that was never registered — a wiring error, not a 0-damage hit.
465
+ - `createDamageRuntime` (function): function createDamageRuntime(config: DamageRuntimeConfig): DamageRuntime — Build a damage model from data. The number a hit lands is `base × zone multiplier × type multiplier × (1 − armor mitigation)`; statuses tick through the same table and armor, so a burn on an armored target is mitigated like a bullet.
466
+
467
+ ## @jgengine/core/combat/damageTable
468
+
469
+ - `ArmorConfig` (interface): interface ArmorConfig — The armor curve: `mitigation = armor / (armor + halfValue)`. Naming the half value instead of a percentage per point is what makes a generated gun's armor number readable — armor equal to `halfValue` halves the hit, and mitigation approaches the cap without ever reaching it.
470
+ - `DEFAULT_ARMOR_HALF_VALUE` (const): const DEFAULT_ARMOR_HALF_VALUE: 100 — Default armor points at which a hit is halved.
471
+ - `DEFAULT_MAX_MITIGATION` (const): const DEFAULT_MAX_MITIGATION: 0.9 — Default ceiling on armor mitigation, so armor never fully negates a hit.
472
+ - `DamageTableDef` (interface): interface DamageTableDef — A serializable damage table: damage types scored against resistance classes, plus the armor curve those rows are read with. It is data a generated gun (B3.1) and a weapon runtime (B2.3) pass in; the engine holds no built-in elemental chart.
473
+ - `DamageTableRow` (interface): interface DamageTableRow — One readable row: a type against a resistance class at a given armor value.
474
+ - `DamageTypeDef` (interface): interface DamageTypeDef — One damage type's row in a damage table: a caller-owned id (`"kinetic"`, `"fire"`) and how it fares against each resistance class a target can present (`"flesh"`, `"shield"`, `"armor"`). Values below `1` are resisted, `1` is neutral, above `1` is a weakness, and `0` is immune.
475
+ - `DuplicateDamageTypeError` (class): class DuplicateDamageTypeError extends Error — Raised when a table lists the same damage type twice, which would make the winner depend on order.
476
+ - `UnknownDamageTypeError` (class): class UnknownDamageTypeError extends Error — Raised when a hit names a damage type the table does not define, rather than silently dealing 1×.
477
+ - `armorMitigation` (function): function armorMitigation(armor: number, config?: ArmorConfig): number — Fraction of a hit that armor removes at `armor` points; `0` at zero armor, capped by config.
478
+ - `assertDamageTable` (function): function assertDamageTable(table: DamageTableDef): void — Validate a table once at construction: duplicate ids and non-finite multipliers fail loudly.
479
+ - `formatDamageTable` (function): function formatDamageTable(rows: readonly DamageTableRow[]): string[] — Render rows as fixed-width lines for a devtools probe, a CLI dump, or a test's diff.
480
+ - `readDamageTable` (function): function readDamageTable(table: DamageTableDef, read?: { base?: number; armor?: number | Readonly<Record<string, number>>; types?: readonly string[]; resistances?: readonly string[]; }): DamageTableRow[] — Read the whole table as rows — every listed type against every listed resistance class, each with the armor it was read at and the damage a base would land. This is the table a designer reads and a test asserts, so a resistance change is visible without running combat.
481
+ - `resolveTableDamage` (function): function resolveTableDamage(table: DamageTableDef, input: { type: string; resistance: string; armor?: number; base?: number }): DamageTableRow — Resolve one hit through the table: type multiplier, then armor mitigation, and the base scaled by both. Formula order matters only for the numbers a UI shows — the product is the same.
482
+ - `typeBypassesArmor` (function): function typeBypassesArmor(table: DamageTableDef, type: string): boolean — Whether armor points reduce this damage type.
483
+ - `typeMultiplier` (function): function typeMultiplier(table: DamageTableDef, type: string, resistance: string): number — The type × resistance multiplier for one class, before armor. An unknown type throws; an unknown resistance class uses the table's `defaultMultiplier`, since target classes may be narrower than the table's coverage.
484
+
325
485
  ## @jgengine/core/combat/death
326
486
 
327
487
  - `DeathIdentity` (interface): interface DeathIdentity { catalogId: string; userId?: string; displayName?: string; position: [number, number, number] } — ⚠ undocumented
@@ -436,6 +596,17 @@
436
596
  - `impactPresets` (const): const impactPresets: { readonly pickup: { readonly hitstopMs: 0; readonly trauma: 0.15; }; readonly jumpLand: { readonly hitstopMs: 0; readonly trauma: 0.2; }; readonly enemyKilled: { readonly hitstopMs: 40; readonly trauma: 0.3; }; readonly playerHit: { readonly hitstopMs: 70; readonly trauma: 0.4;… — Calibrated per-event impact feel — hitstop and trauma numbers harvested from a shipped game-feel reference, not hand-invented per game. `explosion` and `playerHit` are "heavy hit" events (60–90ms hitstop @ 0.05 timescale); `pickup`/`jumpLand` are light events with no hitstop. Trauma is later clamped to 1.0 by `resolveHitReaction`.
437
597
  - `resolveHitReaction` (function): function resolveHitReaction(config: HitReactionConfig | ImpactPresetName, input: HitReactionInput): HitReaction — Resolves hit feel (hitstop, knockback impulse, camera shake) from either a named `impactPresets` event (`resolveHitReaction("explosion", input)`) or a raw `HitReactionConfig` override.
438
598
 
599
+ ## @jgengine/core/combat/hitZones
600
+
601
+ - `DEFAULT_CRIT_MULTIPLIER` (const): const DEFAULT_CRIT_MULTIPLIER: 2 — Default crit multiplier for a zone that does not name one.
602
+ - `HitZoneDef` (interface): interface HitZoneDef — One hit zone: what a hit there multiplies, and how much likelier it is to crit.
603
+ - `HitZoneResolution` (interface): interface HitZoneResolution — One resolved zone: where the hit landed, whether it crit, and the single multiplier to apply.
604
+ - `HitZoneTable` (interface): interface HitZoneTable — A serializable hit-zone table: where a hit landed, what that multiplies, and the table's base crit chance and multiplier. A generated gun or a weapon runtime passes its own zones in; the engine has no built-in headshot.
605
+ - `UnknownHitZoneError` (class): class UnknownHitZoneError extends Error — Raised when neither the named zone nor the table's `defaultZone` exists — a data error, not a 1× hit.
606
+ - `assertHitZoneTable` (function): function assertHitZoneTable(table: HitZoneTable): void — Validate a zone table once at construction: duplicate ids and a missing default fail loudly.
607
+ - `hitZoneMultiplier` (function): function hitZoneMultiplier(table: HitZoneTable, zone: string | undefined, crit: boolean): { zone: string; multiplier: number; fallback: boolean } — The multiplier a zone earns for a known crit outcome, without rolling. `resolveHitZone` and a time-to-kill quote both read through this, so a quoted crit uses the same number combat does.
608
+ - `resolveHitZone` (function): function resolveHitZone(table: HitZoneTable, zone: string | undefined, rng?: () => number): HitZoneResolution — Resolve where a hit landed into one multiplier: roll the crit chance for that zone, and apply the crit multiplier when it lands, otherwise the zone's own multiplier. The injected `rng` keeps the roll reproducible; a zero chance draws nothing, so a deterministic test can force either outcome.
609
+
439
610
  ## @jgengine/core/combat/magazine
440
611
 
441
612
  - `Magazine` (interface): interface Magazine — A per-weapon magazine: discrete loaded rounds, a timed reload that refills from a reserve pool, and the reserve-pool interaction itself — the primitive that replaces hand-rolling mag size, reload delay, and reserve bookkeeping per game (#536.2).
@@ -549,6 +720,28 @@
549
720
  - `eyeHeightFromColliders` (function): function eyeHeightFromColliders(set: EntityColliderSet | null | undefined): number — Eye height derived from a collider set: 90% of the tallest hitbox top, or the humanoid default when unknown.
550
721
  - `resolveShot` (function): function resolveShot(deps: ShotOriginDeps, from: string, aim: Aim, policy: ShotOriginPolicy = { kind: "eye" }): ResolvedShot | null — Resolves a shot's world origin and direction from the shooter's eye or muzzle and an {@link Aim}, ready for `ctx.scene.raycast` — the aim half of a hitscan weapon.
551
722
 
723
+ ## @jgengine/core/combat/squadCombat
724
+
725
+ - `SquadCombat` (interface): interface SquadCombat — Drive a squad: cover by threat, flanking, fire discipline, suppression and retreat.
726
+ - `SquadCombatOptions` (interface): interface SquadCombatOptions extends SquadCombatTunables — Everything the squad AI reads from the game, plus its tunables.
727
+ - `SquadCombatSnapshot` (interface): interface SquadCombatSnapshot — Serializable squad state: member decisions, the crowd, and the tuning they ran under.
728
+ - `SquadCombatStats` (interface): interface SquadCombatStats — Counters a probe or a test reads.
729
+ - `SquadCombatTunables` (interface): interface SquadCombatTunables — In-play dials, every one retunable.
730
+ - `SquadMemberSpec` (interface): interface SquadMemberSpec — One combatant the runtime drives: whose side it is on, where it stands, and how hurt it is.
731
+ - `SquadMemberView` (interface): interface SquadMemberView — One member as a HUD, a probe or a test reads it. Allocates.
732
+ - `SquadMode` (type): type SquadMode = "advance" | "cover" | "peek" | "retreat" | "down" — What a member is doing this tick: moving to cover, holding it, leaning out, falling back, or down.
733
+ - `createSquadCombat` (function): function createSquadCombat(options: SquadCombatOptions): SquadCombat — One squad's combat behavior per tick, over the perception memory a game already keeps and the nav crowd it already moves. Each member picks a stand point that hides it from the threats it remembers, at the distance and angle its role wants; a flanker goes around the side, a support holds and fires, an assault closes. Incoming fire raises suppression, which pins the member to its cover and stops its shooting until the meter decays; low health turns it toward the point furthest from the threat.
734
+
735
+ ## @jgengine/core/combat/squadTactics
736
+
737
+ - `FireDiscipline` (interface): interface FireDiscipline — Fire discipline: how long a member stays down between shots, how long it leans out, and how fast it fires while it has a target. A role that peeks for longer holds an angle better and spends more time in the open — the trade is the caller's to tune.
738
+ - `SquadComposition` (interface): interface SquadComposition — A squad's roles in assignment order.
739
+ - `SquadRole` (interface): interface SquadRole — One caller-defined squad role. The engine ships the machinery and no role list: a game names its own roles and dials them, so `"assault"`, `"breacher"` and `"sniper"` are content, not engine concepts.
740
+ - `ThreatReadOptions` (interface): interface ThreatReadOptions — Bounds for {@link threatsFromMemory}.
741
+ - `ThreatReading` (interface): interface ThreatReading extends CoverThreat — One remembered enemy with its age, so a role can treat a stale sighting differently.
742
+ - `assignSquadRoles` (function): function assignSquadRoles(members: readonly string[], composition: SquadComposition): Map<string, SquadRole> — Deterministic role per member: declared order, `count` copies of each role, and the list repeats when the squad is larger than the composition. Same members and composition give the same answer on every host and after a reload.
743
+ - `threatsFromMemory` (function): function threatsFromMemory(perception: PerceptionService, observerId: string, nowMs: number, options: ThreatReadOptions = {}): ThreatReading[] — Read one member's threat list out of its perception memory — the only source of threat. Sorted by confidence, then recency, then id, and bounded by `max`, so a crowded memory cannot grow the tick.
744
+
552
745
  ## @jgengine/core/combat/statusApplication
553
746
 
554
747
  - `StatusApplicationInput` (interface): interface StatusApplicationInput — Everything `resolveStatusApplication` needs; all data plus an injected RNG for reproducibility.
@@ -569,6 +762,22 @@
569
762
  - `toStatusEffectView` (function): function toStatusEffectView(instance: StatusInstance, options?: StatusEffectViewOptions): StatusEffectView — Map one live {@link StatusInstance} to a serializable {@link StatusEffectView} for a HUD row. Pure: no clock, no allocation beyond the returned view. The `durationMs` option supplies the ring denominator the instance does not carry; without it the ring reads full.
570
763
  - `toStatusEffectViews` (function): function toStatusEffectViews(instances: readonly StatusInstance[], options?: StatusEffectViewOptions): StatusEffectView[] — Map a list of live statuses (a target's carried {@link StatusInstance}s) to ordered HUD views. Pure and allocation-bounded — one pass, one view per input — so a status bar can render straight off the existing status model's snapshot without re-deriving ring math or timers.
571
764
 
765
+ ## @jgengine/core/combat/statusRuntime
766
+
767
+ - `StatusApplyOptions` (interface): interface StatusApplyOptions — Options for one application; scales compose with the definition's own base values.
768
+ - `StatusDamageTick` (interface): interface StatusDamageTick — A damage tick, already routed through the caller's `applyDamage` when one was supplied.
769
+ - `StatusDef` (interface): interface StatusDef — One status the runtime can apply: how a fresh application folds into a live one ({@link StatusStackPolicy}), how long it lives, how often and how hard it ticks, and what a tick does. The stack rule is the *only* thing that changes stacks — per-tick magnitude is `magnitude × stacks`, never `magnitude ^ applications`.
770
+ - `StatusEffect` (type): type StatusEffect = | { kind: "damage"; channel: string } | { kind: "stat"; stat: string } — What a status does on each tick. Damage ticks are reported with the channel the caller's damage pipeline resolves, so a burn still meets resistances and armor; stat ticks are a per-stack delta summed by {@link StatusRuntime.statDelta} (`slow` → move speed, `corrode` → armor).
771
+ - `StatusExpiredTick` (interface): interface StatusExpiredTick — A status ended: remove its HUD row and any stat delta it was contributing.
772
+ - `StatusRuntime` (interface): interface StatusRuntime — A pool of per-target statuses that applies stack rules, ticks effects, and hands its state out.
773
+ - `StatusRuntimeEntry` (interface): interface StatusRuntimeEntry — One live status plus its tick timer, as saved and restored.
774
+ - `StatusRuntimeOptions` (interface): interface StatusRuntimeOptions — Tunables a game can adjust at runtime without discarding carried statuses.
775
+ - `StatusRuntimeState` (interface): interface StatusRuntimeState — Serializable runtime state: carried statuses, tick timers, and the counters a probe reports.
776
+ - `StatusStatTick` (interface): interface StatusStatTick — A stat tick: a per-stack delta the caller reads while the status lives.
777
+ - `StatusTickEvent` (type): type StatusTickEvent = StatusDamageTick | StatusStatTick | StatusExpiredTick — Everything one `tick` call produced, in target-then-status order.
778
+ - `UnknownStatusError` (class): class UnknownStatusError extends Error — Raised when a caller names a status id the runtime has no definition for.
779
+ - `createStatusRuntime` (function): function createStatusRuntime(options: StatusRuntimeOptions): StatusRuntime — Build a status runtime over serializable definitions. Applications reuse {@link resolveStatusApplication} for chance and stacking, so a refresh/stack-to-N/ignore rule has exactly one implementation; `tick` then applies each status's declared effect per stack.
780
+
572
781
  ## @jgengine/core/combat/telegraph
573
782
 
574
783
  - `HazardCycleConfig` (interface): interface HazardCycleConfig { windupMs: number; activeMs: number; cooldownMs?: number; offsetMs?: number } — ⚠ undocumented
@@ -597,6 +806,26 @@
597
806
  - `resolveVfxPreset` (function): function resolveVfxPreset(name: string | undefined, overrides?: Partial<VfxPreset>): VfxPreset — Resolve a named visual flavor into a concrete `{ kind, color, durationMs?, radius? }`, with any provided `overrides` winning field by field. An unknown name resolves to {@link DEFAULT_VFX_PRESET} rather than throwing, so a flavor typo degrades to a visible spark. Used by `ctx.scene.entity.vfx({ preset })`; call it directly when you need the raw numbers (e.g. to seed a retained `vfxInstance` from the same vocabulary).
598
807
  - `vfxPresets` (const): const vfxPresets: { readonly arrow: { readonly kind: "projectile"; readonly color: 14207392; }; readonly bolt: { readonly kind: "projectile"; readonly color: 16777215; }; readonly fireball: { readonly kind: "projectile"; readonly color: 16738842; readonly durationMs: 460; }; readonly firebolt: { rea… — Ready-made visual flavors for the things players actually shoot, swing, and cast — so `ctx.scene.entity.vfx({ preset: "arrow", from, to })` renders a visible bolt with zero tuning, and `"lightning"`, `"web"`, `"slash"`, `"shield"`, `"heal"`, `"explosion"` likewise just work. Each entry only picks a `kind` (`projectile | beam | nova | glow | spark`), a color, and a lifetime; it is visual vocabulary (like named CSS colors), not a gameplay archetype — combat numbers stay in the caller's own effect/ability data. Any field passed to `vfx()` overrides the preset, and unknown names fall back to `spark` so a flavor typo still shows *something* rather than nothing.
599
808
 
809
+ ## @jgengine/core/combat/weaponDefinition
810
+
811
+ - `DEFAULT_WEAPON_PELLETS_IN_FLIGHT` (const): const DEFAULT_WEAPON_PELLETS_IN_FLIGHT: 32 — Default live-pellet cap for a weapon that does not name one.
812
+ - `MAX_WEAPON_BURST_COUNT` (const): const MAX_WEAPON_BURST_COUNT: 64 — Cap on `WeaponBurstDef.count`.
813
+ - `MAX_WEAPON_PELLETS_IN_FLIGHT` (const): const MAX_WEAPON_PELLETS_IN_FLIGHT: 256 — Cap on `WeaponDefinition.maxInFlight`; bounds per-tick projectile work.
814
+ - `MAX_WEAPON_PELLETS_PER_SHOT` (const): const MAX_WEAPON_PELLETS_PER_SHOT: 64 — Cap on `WeaponDefinition.pellets`; bounds rays and damage resolutions per shot.
815
+ - `WeaponBurstDef` (interface): interface WeaponBurstDef — Shots one trigger pull commits to. A burst finishes even if the trigger is released.
816
+ - `WeaponDamageDef` (interface): interface WeaponDamageDef — What one pellet does when it reaches a registered damage target.
817
+ - `WeaponDefinition` (interface): interface WeaponDefinition — One weapon as plain data: cadence, fire mode, delivery, magazine, feel and damage. Nothing here is a function, so a generator can roll it, a save can store it, and two weapons differ by their fields.
818
+ - `WeaponFalloffDef` (interface): interface WeaponFalloffDef — Linear damage falloff between two distances.
819
+ - `WeaponFireMode` (type): type WeaponFireMode = "single" | "auto" | "burst" — How a held or tapped trigger turns into shots.
820
+ - `WeaponHitscanDef` (interface): interface WeaponHitscanDef — A ray resolved in the caller's world on the same tick it is fired.
821
+ - `WeaponMagazineDef` (interface): interface WeaponMagazineDef — Loaded rounds, reload time, and where reloads draw from.
822
+ - `WeaponProjectileDef` (interface): interface WeaponProjectileDef — A travelling pellet simulated by the runtime, resolved when it meets the caller's world.
823
+ - `WeaponVec3` (type): type WeaponVec3 = readonly [number, number, number] — A world-space point or direction in metres.
824
+ - `assertWeaponDefinition` (function): function assertWeaponDefinition(definition: WeaponDefinition): void — Check a definition before it reaches the runtime, so a rolled weapon fails loudly at construction instead of firing nothing. Throws `RangeError` naming the field.
825
+ - `weaponDamageAt` (function): function weaponDamageAt(definition: WeaponDefinition, distance: number): number — Damage multiplier at a distance: `1` before `falloff.start`, linearly down to `endScale` at `end`.
826
+ - `weaponIntervalMs` (function): function weaponIntervalMs(definition: WeaponDefinition): number — Milliseconds between shots at full cadence: `60_000 / roundsPerMinute`.
827
+ - `weaponMaxInFlight` (function): function weaponMaxInFlight(definition: WeaponDefinition): number — Live pellets one definition may keep, clamped to a bounded range.
828
+
600
829
  ## @jgengine/core/combat/weaponFeedback
601
830
 
602
831
  - `WeaponFeedbackSignal` (type): type WeaponFeedbackSignal = "shot" | "kick" | "impact" | "recoil" | "cameraKick" | "spread" | "ads" — Signals {@link createWeaponFeedbackSignals} reports each tick, named for `createFeedbackMixer` routes. `shot`, `kick` and `impact` are pulses that read non-zero only on the tick they happened; the rest follow the handling frame.
@@ -643,6 +872,23 @@
643
872
  - `createWeaponPresentation` (function): function createWeaponPresentation(initial: WeaponPresentationTuning = {}): WeaponPresentation — Creates a {@link WeaponPresentation}: per-weapon viewmodel offset, ADS pose and viewmodel FOV, sway and inertia from look speed, bob from the movement probe, and a recoil kick, all driven by the same handling frame third-person and over-the-shoulder rigs read. Deterministic and allocation-free per update.
644
873
  - `viewmodelFovScale` (function): function viewmodelFovScale(worldFov: number, viewmodelFov: number): number — Scale for a viewmodel drawn at `viewmodelFov` through a camera at `worldFov` (both degrees): multiply the viewmodel's camera-space x and y by this and it projects as if rendered with its own FOV, without a second camera or render pass.
645
874
 
875
+ ## @jgengine/core/combat/weaponSystem
876
+
877
+ - `WeaponAim` (interface): interface WeaponAim — Current aim with recoil applied.
878
+ - `WeaponAmmo` (interface): interface WeaponAmmo — Ammo and cadence as a HUD or a test reads them.
879
+ - `WeaponContact` (interface): interface WeaponContact — Where one pellet met the caller's world.
880
+ - `WeaponImpact` (interface): interface WeaponImpact — One resolved hit, with the damage runtime's full provenance.
881
+ - `WeaponImpactContext` (interface): interface WeaponImpactContext — Read-only facts about the hit handed to `damageFor`.
882
+ - `WeaponProjectileView` (interface): interface WeaponProjectileView — A pellet alive in flight, for presentation or prediction.
883
+ - `WeaponReloadState` (type): type WeaponReloadState = "idle" | "reloading" | "interrupted" — `idle`, `reloading`, or `interrupted` after `interruptReload()` kept the progress.
884
+ - `WeaponShotRecord` (interface): interface WeaponShotRecord — One fired shot: the cone it fired into, the kick it took, and where each pellet went.
885
+ - `WeaponStats` (interface): interface WeaponStats — Lifetime ammo economy and resolution counters.
886
+ - `WeaponSystem` (interface): interface WeaponSystem — A weapon as the game drives it: base aim in, shots and resolved impacts out. Fire mode, cadence, handling, delivery, magazine and damage all come from one {@link WeaponDefinition}; the game owns input, the muzzle, and the world query.
887
+ - `WeaponSystemDeps` (interface): interface WeaponSystemDeps — Everything the runtime is allowed to reach: RNG, the damage model, the muzzle, and the world query.
888
+ - `WeaponSystemState` (interface): interface WeaponSystemState — Serializable runtime state: aim, handling, ammo, trigger, pellets and counters.
889
+ - `WeaponTickResult` (interface): interface WeaponTickResult — What one {@link WeaponSystem.tick} produced. Empty arrays on a quiet tick.
890
+ - `createWeaponSystem` (function): function createWeaponSystem(initial: WeaponDefinition, deps: WeaponSystemDeps): WeaponSystem — Build a weapon from one {@link WeaponDefinition}. The game supplies aim, a muzzle, one world query, the damage model and an RNG; the runtime owns fire modes, cadence, recoil, bloom, reload states, ammo accounting, projectile ballistics and the damage call. State snapshots as plain data.
891
+
646
892
  ## @jgengine/core/stats/accumulatorMeter
647
893
 
648
894
  - `AccumulatorMeter` (interface): interface AccumulatorMeter { state(): AccumulatorMeterState; restore(next: AccumulatorMeterState): void; value(): number; fraction(): number; tier(): string | null; broke(): boolean; add(amount: number): MeterAddResult; drain(amount: number): void; reset(): void; tic… — ⚠ undocumented · used by `createAccumulatorMeter`: A raw accumulating gauge that crosses named tier thresholds as a value builds, with optional decay — the primitive under charge, rage, and c…