@jgengine/core 0.9.0 → 0.10.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.
- package/CHANGELOG.md +21 -1
- package/dist/ai/spawnDirector.d.ts +13 -0
- package/dist/ai/spawnDirector.js +40 -16
- package/dist/ai/spawnPoint.d.ts +5 -0
- package/dist/ai/spawnPoint.js +5 -0
- package/dist/audio/audioFalloff.d.ts +5 -2
- package/dist/audio/music.d.ts +53 -0
- package/dist/audio/music.js +34 -0
- package/dist/audio/synth.d.ts +60 -0
- package/dist/audio/synth.js +8 -0
- package/dist/authoring.d.ts +14 -0
- package/dist/authoring.js +14 -0
- package/dist/cartridge/runtime.js +2 -2
- package/dist/combat/dotField.d.ts +43 -0
- package/dist/combat/dotField.js +48 -0
- package/dist/combat/projectiles.d.ts +2 -2
- package/dist/combat/projectiles.js +18 -5
- package/dist/combat/regenShield.d.ts +35 -0
- package/dist/combat/regenShield.js +45 -0
- package/dist/combat/shotOrigin.d.ts +13 -1
- package/dist/combat/shotOrigin.js +31 -2
- package/dist/editor/commands.d.ts +65 -0
- package/dist/editor/commands.js +193 -0
- package/dist/editor/document.d.ts +40 -0
- package/dist/editor/document.js +153 -0
- package/dist/editor/index.d.ts +30 -0
- package/dist/editor/index.js +6 -0
- package/dist/editor/types.d.ts +69 -0
- package/dist/editor/types.js +29 -0
- package/dist/game/connectedPlayers.d.ts +28 -0
- package/dist/game/connectedPlayers.js +21 -0
- package/dist/game/defineGame.d.ts +42 -1
- package/dist/game/defineGame.js +1 -0
- package/dist/game/events.d.ts +15 -0
- package/dist/game/keyValueStore.d.ts +25 -0
- package/dist/game/keyValueStore.js +61 -0
- package/dist/game/levelSequence.d.ts +24 -2
- package/dist/game/levelSequence.js +51 -2
- package/dist/game/lootTable.d.ts +4 -0
- package/dist/game/lootTable.js +1 -0
- package/dist/game/modelAnimation.d.ts +8 -0
- package/dist/game/modelAnimation.js +18 -0
- package/dist/game/playableGame.d.ts +68 -5
- package/dist/game/recordBook.d.ts +3 -5
- package/dist/index.d.ts +7 -0
- package/dist/index.js +7 -0
- package/dist/input/actionBindings.d.ts +2 -0
- package/dist/input/actionBindings.js +1 -0
- package/dist/input/axisInput.d.ts +7 -0
- package/dist/input/axisInput.js +16 -0
- package/dist/interaction/contextMenu.d.ts +1 -0
- package/dist/interaction/contextMenu.js +1 -0
- package/dist/interaction/proximityPrompt.d.ts +3 -1
- package/dist/interaction/proximityPrompt.js +3 -3
- package/dist/meta/changelog.d.ts +1 -1
- package/dist/meta/changelog.js +22 -1
- package/dist/movement/movementModel.d.ts +4 -0
- package/dist/movement/movementModel.js +13 -0
- package/dist/movement/playerMovement.d.ts +41 -0
- package/dist/movement/playerMovement.js +181 -0
- package/dist/random/rng.d.ts +2 -0
- package/dist/random/rng.js +2 -0
- package/dist/render/postProcessing.d.ts +56 -0
- package/dist/render/postProcessing.js +1 -0
- package/dist/runtime/adapter.d.ts +19 -0
- package/dist/runtime/adapter.js +11 -5
- package/dist/runtime/gameContext.d.ts +53 -10
- package/dist/runtime/gameContext.js +192 -54
- package/dist/runtime/hostedGameRunner.d.ts +49 -0
- package/dist/runtime/hostedGameRunner.js +62 -0
- package/dist/runtime/hostedWorldSession.d.ts +65 -0
- package/dist/runtime/hostedWorldSession.js +57 -0
- package/dist/runtime/inputSnapshot.d.ts +13 -0
- package/dist/runtime/inputSnapshot.js +2 -0
- package/dist/runtime/motionIntents.d.ts +2 -0
- package/dist/runtime/motionIntents.js +11 -0
- package/dist/runtime/worldChannel.d.ts +58 -0
- package/dist/runtime/worldChannel.js +69 -0
- package/dist/runtime/worldMirror.d.ts +24 -0
- package/dist/runtime/worldMirror.js +31 -0
- package/dist/runtime/worldReplication.d.ts +46 -0
- package/dist/runtime/worldReplication.js +185 -0
- package/dist/runtime/worldSnapshot.d.ts +17 -0
- package/dist/runtime/worldSnapshot.js +15 -0
- package/dist/scene/assetCatalog.d.ts +1 -0
- package/dist/scene/colliders.d.ts +6 -0
- package/dist/scene/colliders.js +25 -1
- package/dist/scene/entityStats.d.ts +4 -0
- package/dist/scene/entityStats.js +21 -0
- package/dist/scene/entityStore.d.ts +29 -0
- package/dist/scene/entityStore.js +60 -0
- package/dist/store/observableKeyedStore.d.ts +4 -0
- package/dist/store/observableKeyedStore.js +9 -0
- package/dist/tactics/fallingGrid.d.ts +2 -2
- package/dist/tactics/fallingGrid.js +2 -2
- package/dist/ui/gameLayout.d.ts +125 -0
- package/dist/ui/gameLayout.js +129 -0
- package/dist/ui/orientation.d.ts +39 -0
- package/dist/ui/orientation.js +49 -0
- package/dist/ui/swingTimer.d.ts +42 -0
- package/dist/ui/swingTimer.js +38 -0
- package/dist/visibility/spatialIndex.js +34 -20
- package/dist/world/environmentSummary.d.ts +11 -2
- package/dist/world/environmentSummary.js +11 -1
- package/dist/world/features.d.ts +134 -5
- package/dist/world/features.js +40 -3
- package/dist/world/roads.d.ts +47 -0
- package/dist/world/roads.js +146 -0
- package/dist/world/streets.d.ts +80 -0
- package/dist/world/streets.js +144 -0
- package/dist/world/terrain.d.ts +32 -5
- package/dist/world/terrain.js +124 -12
- package/llms.txt +485 -79
- package/package.json +1 -1
package/llms.txt
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @jgengine/core
|
|
2
2
|
> jgengine core — genre-agnostic TypeScript game engine SDK (npm @jgengine/core). Runtime, scene, combat, inventory, multiplayer primitives. Zero dependencies. https://jgengine.com
|
|
3
3
|
|
|
4
|
-
Version: 0.
|
|
4
|
+
Version: 0.10.0
|
|
5
5
|
License: AGPL-3.0-only
|
|
6
6
|
Repository: https://github.com/Noisemaker111/jgengine
|
|
7
7
|
Docs: https://jgengine.com
|
|
@@ -72,14 +72,22 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
72
72
|
- SpawnDirectorConfig (interface): interface SpawnDirectorConfig
|
|
73
73
|
- SpawnDirectorState (interface): interface SpawnDirectorState
|
|
74
74
|
- SpawnEntry (interface): interface SpawnEntry
|
|
75
|
+
- SpawnPointDistanceBias (type): type SpawnPointDistanceBias = "near" | "far" | "none" — Preference for picking a spawn point relative to `avoid` positions: closer, farther, or unweighted.
|
|
76
|
+
- SpawnPointSelectionOptions (interface): interface SpawnPointSelectionOptions — Semantic options for selecting a spawn point without exposing weighting internals.
|
|
75
77
|
- SpawnRequest (interface): interface SpawnRequest
|
|
76
78
|
- WaveManifest (interface): interface WaveManifest
|
|
77
79
|
- advanceSpawnDirector (function): function advanceSpawnDirector(config: SpawnDirectorConfig, state: SpawnDirectorState, dt: number, ctx: DirectorContext): DirectorStep
|
|
78
80
|
- advanceWave (function): function advanceWave(config: SpawnDirectorConfig, state: SpawnDirectorState): SpawnDirectorState
|
|
79
81
|
- createSpawnDirectorState (function): function createSpawnDirectorState(config: SpawnDirectorConfig): SpawnDirectorState
|
|
80
|
-
- pickSpawnPoint (function): function pickSpawnPoint(
|
|
82
|
+
- pickSpawnPoint (function): function pickSpawnPoint(options: SpawnPointSelectionOptions): NavPoint | null — Selects a candidate spawn point using a semantic distance preference and caller-supplied randomness.
|
|
81
83
|
- raiseAlert (function): function raiseAlert(state: SpawnDirectorState, amount: number): SpawnDirectorState
|
|
82
84
|
|
|
85
|
+
### @jgengine/core/ai/spawnPoint
|
|
86
|
+
|
|
87
|
+
- SpawnPointDistanceBias (type): type SpawnPointDistanceBias = "near" | "far" | "none" — Preference for picking a spawn point relative to `avoid` positions: closer, farther, or unweighted.
|
|
88
|
+
- SpawnPointSelectionOptions (interface): interface SpawnPointSelectionOptions — Semantic options for selecting a spawn point without exposing weighting internals.
|
|
89
|
+
- selectSpawnPoint (function): function selectSpawnPoint(options: SpawnPointSelectionOptions): NavPoint | null — Selects a spawn point by game intent while keeping weighting mechanics internal.
|
|
90
|
+
|
|
83
91
|
### @jgengine/core/ai/threat
|
|
84
92
|
|
|
85
93
|
- HighestThreatOptions (interface): interface HighestThreatOptions
|
|
@@ -126,6 +134,61 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
126
134
|
- distance3 (function): function distance3(a: { x: number; y: number; z: number }, b: { x: number; y: number; z: number }): number
|
|
127
135
|
- resolveEmitterGain (function): function resolveEmitterGain(distance: number, sound: Pick<SoundDef, "gain" | "positional" | "falloff">, busGain: number): number
|
|
128
136
|
|
|
137
|
+
### @jgengine/core/audio/music
|
|
138
|
+
|
|
139
|
+
- MusicInstrument (type): type MusicInstrument = | "strings" | "flute" | "harp" | "horn" | "choir" | "bell" | "timpani" | "bass" | "stacc" | "pad" | "lute" | "dulcimer" | "frameDrum" | "warDrum" | "reed" | "pipe" | "squareLead" | "woodBlock" | "tinyBell" | "piano" | "shaker" | "brassStab" | "cymSwell" | "oboe" — Named synthesised instrument. Each maps to a voice in the shell's instrument library (`@jgengine/shell/audio/musicVoices`); an unknown name falls back to a plain sine voice so a theme is never silent.
|
|
140
|
+
- MusicTheme (interface): interface MusicTheme — A through-composed, looping music track. `events` need not be sorted; the director schedules them ahead against a fixed anchor so loops are seamless.
|
|
141
|
+
- NoteEvent (interface): interface NoteEvent — One scheduled note in a theme, positioned on the loop's quarter-note grid.
|
|
142
|
+
- ScheduledNote (interface): interface ScheduledNote — A resolved note occurrence: the source note plus its absolute onset in seconds.
|
|
143
|
+
- mtof (function): function mtof(midi: number): number — Standard equal-temperament MIDI-to-frequency (A4 = 440 Hz at MIDI 69).
|
|
144
|
+
- notesInWindow (function): function notesInWindow(theme: MusicTheme, anchorSec: number, fromSec: number, toSec: number): ScheduledNote[] — Pure lookahead scheduler: every note occurrence of `theme` whose onset falls in the half-open window `(fromSec, toSec]`, given the theme's loop-zero at `anchorSec`. Handles any number of loop wraps, so a director calls it once per tick with a non-overlapping window and never double-schedules a note.
|
|
145
|
+
- themeLoopSeconds (function): function themeLoopSeconds(theme: MusicTheme): number — Loop length of a theme in seconds.
|
|
146
|
+
|
|
147
|
+
### @jgengine/core/audio/synth
|
|
148
|
+
|
|
149
|
+
- NoiseVoice (interface): interface NoiseVoice — A filtered white-noise burst — impacts, whooshes, breath, crackle. Realised from a shared 1s noise buffer at a randomised playback rate and start offset, decaying exponentially to silence at `duration * decay`.
|
|
150
|
+
- SynthFilter (type): type SynthFilter = "lowpass" | "highpass" | "bandpass" — Biquad filter shape a noise voice is coloured through.
|
|
151
|
+
- SynthPatch (interface): interface SynthPatch — A procedural sound cue: a set of voices triggered together, each with its own `delay`, summed into one one-shot. Pure serialisable data — the shell realises it on Web Audio, so the same catalog runs headless in tests with no `AudioContext`.
|
|
152
|
+
- SynthVoice (type): type SynthVoice = ToneVoice | NoiseVoice — One layered voice of a synth cue.
|
|
153
|
+
- SynthWave (type): type SynthWave = "sine" | "square" | "sawtooth" | "triangle" — Oscillator waveform available to a pitched synth voice.
|
|
154
|
+
- ToneVoice (interface): interface ToneVoice — A pitched oscillator voice: a 12ms linear attack to `gain`, then an exponential decay to silence across `duration`, with an optional exponential pitch slide from `freq` to `slideTo`.
|
|
155
|
+
- patchDuration (function): function patchDuration(patch: SynthPatch): number — Total wall-clock length of a patch in seconds — the latest voice end across all voices.
|
|
156
|
+
|
|
157
|
+
### @jgengine/core/authoring
|
|
158
|
+
|
|
159
|
+
- ActionCodesMap (type): type ActionCodesMap<TAction extends string = string, TCode extends string = string> = Record< TAction, ActionCodes<TCode> > — Maps each game action name to the input codes (hold/toggle keys, repeat rate) that trigger it.
|
|
160
|
+
- Drop (interface): interface Drop — A resolved loot outcome — one item or currency grant with its rolled count.
|
|
161
|
+
- GameDefinition (interface): interface GameDefinition<TAssetRef extends ModelAssetRef = ModelAssetRef, TMultiplayer = unknown> — Fully-resolved game description produced by {@link defineGame} — assets, scene, and opted-in subsystems.
|
|
162
|
+
- GameDefinitionConfig (type): type GameDefinitionConfig<TAssetRef extends ModelAssetRef = ModelAssetRef, TMultiplayer = unknown> = Omit<GameDefinition<TAssetRef, TMultiplayer>, "scene" | "assets"> & { assets?: AssetCatalog<TAssetRef>; } — Input to {@link defineGame} — a `GameDefinition` with `scene` derived and `assets` optional.
|
|
163
|
+
- GameLoop (interface): interface GameLoop<TContext = unknown> — Lifecycle hooks a game implements to drive init, per-tick simulation, and player join/leave.
|
|
164
|
+
- GameServerConfig (type): type GameServerConfig = "persistent" | { mode: string; [key: string]: unknown } — Hosting mode for a game's multiplayer server: `"persistent"`, or a custom mode with its own options.
|
|
165
|
+
- InventoryDeclaration (interface): interface InventoryDeclaration — Shape of one named inventory a game declares — slot count, accepted item types, HUD binding.
|
|
166
|
+
- LootEntry (interface): interface LootEntry — One possible drop in a {@link LootTableDef} — an item or currency, its count range, and its odds.
|
|
167
|
+
- LootTableDef (interface): interface LootTableDef — A named, validated loot table — its roll count, weighted-vs-independent mode, and candidate entries.
|
|
168
|
+
- PhysicsConfig (interface): interface PhysicsConfig — World gravity and jump tuning, plus scene-object collision opt-ins, for the game's physics step.
|
|
169
|
+
- SpawnPointDistanceBias (type): type SpawnPointDistanceBias = "near" | "far" | "none" — Preference for picking a spawn point relative to `avoid` positions: closer, farther, or unweighted.
|
|
170
|
+
- SpawnPointSelectionOptions (interface): interface SpawnPointSelectionOptions — Semantic options for selecting a spawn point without exposing weighting internals.
|
|
171
|
+
- WorldFeature (type): type WorldFeature = | ({ kind: "biomes" } & BiomesWorldConfig) | ({ kind: "voxel" } & VoxelWorldConfig) | ({ kind: "plots" } & PlotsWorldConfig) | ({ kind: "tilemap" } & TilemapWorldConfig) | EnvironmentWorldFeature | { kind: "flat" } — A declared world shape — biomes, voxel grid, plots, tilemap, environment, or flat — passed to `defineGame`.
|
|
172
|
+
- biomes (function): function biomes(config: BiomesWorldConfig): WorldFeature — Declares a biome-painted world — the whole-world alternative to a single `environment()` terrain.
|
|
173
|
+
- building (function): function building(config: BuildingEnvironmentConfig = {}): BuildingEnvironmentDescriptor — Declares a cluster of procedurally-massed buildings for `environment()` — count, footprint, stories, style.
|
|
174
|
+
- contextVerb (function): function contextVerb(label: string, command: string, args?: Record<string, unknown>): ContextVerb — Builds a {@link ContextVerb} for a right-click menu entry.
|
|
175
|
+
- defineGame (function): function defineGame<TAssetRef extends ModelAssetRef, TMultiplayer>(config: GameDefinitionConfig<TAssetRef, TMultiplayer>): GameDefinition<TAssetRef, TMultiplayer> — Task-first entry point for authoring a game: fills in `scene` and default `assets`, validates `name`.
|
|
176
|
+
- environment (function): function environment(config: EnvironmentWorldConfig = {}): EnvironmentWorldFeature — Composes an `environment()` world feature from terrain, sky, weather, vegetation, water, structures, roads, and pads.
|
|
177
|
+
- flat (function): function flat(): WorldFeature — Declares an empty flat world — the minimal `WorldFeature` for games with no terrain of their own.
|
|
178
|
+
- grass (function): function grass(config: GrassEnvironmentConfig = {}): GrassEnvironmentDescriptor — Declares a grass vegetation patch for `environment()` — area, blade sizing, density, and colors.
|
|
179
|
+
- lootTable (function): function lootTable(def: LootTableDef): LootTableDef — Validates a loot table definition and returns it unchanged, for use with {@link createLootRegistry}.
|
|
180
|
+
- ocean (function): function ocean(config: OceanEnvironmentConfig = {}): OceanEnvironmentDescriptor — Declares an ocean water body for `environment()` — bounds, level, and wave tuning.
|
|
181
|
+
- plots (function): function plots(config: PlotsWorldConfig = {}): WorldFeature — Declares a subdivided-plots world — farming, base-building, and other parcel-based layouts.
|
|
182
|
+
- rain (function): function rain(config: RainEnvironmentConfig = {}): RainEnvironmentDescriptor — Declares a rainfall weather effect for `environment()` — area, density, speed, and wind.
|
|
183
|
+
- road (function): function road(config: RoadEnvironmentConfig): RoadEnvironmentDescriptor — Declare a road ribbon for an `environment()` world; the shell drapes and renders it over the terrain.
|
|
184
|
+
- seededRng (function): function seededRng(seed: string | number): () => number — Deterministic pseudo-random generator seeded from a string or number — same seed, same sequence.
|
|
185
|
+
- seededStreams (function): function seededStreams(seed: string | number): (stream: string) => () => number — Derives independent, deterministic {@link seededRng} streams from one base seed, keyed by stream name.
|
|
186
|
+
- selectSpawnPoint (function): function selectSpawnPoint(options: SpawnPointSelectionOptions): NavPoint | null — Selects a spawn point by game intent while keeping weighting mechanics internal.
|
|
187
|
+
- snow (function): function snow(config: SnowEnvironmentConfig = {}): SnowEnvironmentDescriptor — Declares a snowfall weather effect for `environment()` — area, density, drift, and wind.
|
|
188
|
+
- terrain (function): function terrain(config: TerrainEnvironmentConfig = {}): TerrainEnvironmentDescriptor — Declares a heightfield terrain patch for `environment()` — bounds, noise, materials, and flatten masks.
|
|
189
|
+
- tilemap (function): function tilemap(config: TilemapWorldConfig): WorldFeature — Declares a 2D tilemap world from a map string.
|
|
190
|
+
- voxel (function): function voxel(config: VoxelWorldConfig): WorldFeature — Declares a voxel-grid world for block-based games.
|
|
191
|
+
|
|
129
192
|
### @jgengine/core/behaviour/behaviour
|
|
130
193
|
|
|
131
194
|
- Behaviour (class): class Behaviour — Subclass and override the lifecycle hooks. A behaviour only joins the per-frame update dispatch if it actually overrides `onUpdate` (prototype-identity check at each activation), so hook-only behaviours cost nothing per frame.
|
|
@@ -356,6 +419,13 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
356
419
|
- totalWindowMs (function): function totalWindowMs(config: DefensiveWindowConfig): number
|
|
357
420
|
- windowActiveAt (function): function windowActiveAt(config: DefensiveWindowConfig, elapsedMs: number): boolean
|
|
358
421
|
|
|
422
|
+
### @jgengine/core/combat/dotField
|
|
423
|
+
|
|
424
|
+
- DotField (interface): interface DotField — A pool of timed damage-over-time instances keyed by string (`bleed`, `poison`, `burn`) — the recurring-damage counterpart to `breakMeters`' one-shot buildup procs (#536.4). `tick` advances every active DoT and returns the damage due this frame, one entry per interval crossed, so the game routes each through its own effect pipeline; a pure scheduler that never applies damage itself.
|
|
425
|
+
- DotSpec (interface): interface DotSpec — A single damage-over-time instance: how hard, how often, and for how long.
|
|
426
|
+
- DotTick (interface): interface DotTick — One damage event (or expiry marker) emitted by `DotField.tick` for the game to route through its effects.
|
|
427
|
+
- createDotField (function): function createDotField(): DotField — Builds an empty {@link DotField}; `apply` DoTs onto it and drain damage each frame with `tick`.
|
|
428
|
+
|
|
359
429
|
### @jgengine/core/combat/downed
|
|
360
430
|
|
|
361
431
|
- DownedConfig (interface): interface DownedConfig
|
|
@@ -410,6 +480,12 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
410
480
|
- SettleResult (type): type SettleResult = | { status: "settled"; shotId: string; at: [number, number, number]; hits: EffectResult[]; origin?: [number, number, number]; } | { status: "rejected"; shotId: string; reason: string }
|
|
411
481
|
- createProjectileSystem (function): function createProjectileSystem(deps: ProjectileSystemDeps): ProjectileSystem
|
|
412
482
|
|
|
483
|
+
### @jgengine/core/combat/regenShield
|
|
484
|
+
|
|
485
|
+
- RegenShield (interface): interface RegenShield — A shield pool that stops regenerating for `regenDelayMs` after every hit, then refills at `regenPerSecond` — the delayed-regen primitive that replaces snapshot-comparing stat values per tick to detect "damage taken" (#536.3). `damage` resets the grace timer; `tick` counts it down and regenerates once it elapses.
|
|
486
|
+
- RegenShieldConfig (interface): interface RegenShieldConfig — Tuning for `createRegenShield`: pool size, refill rate, and the post-damage grace period.
|
|
487
|
+
- createRegenShield (function): function createRegenShield(config: RegenShieldConfig): RegenShield — Builds a {@link RegenShield} that suppresses regen for `regenDelayMs` after each hit.
|
|
488
|
+
|
|
413
489
|
### @jgengine/core/combat/resistance
|
|
414
490
|
|
|
415
491
|
- ResistVerdict (type): type ResistVerdict = "immune" | "resist" | "normal" | "vulnerable"
|
|
@@ -428,12 +504,14 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
428
504
|
|
|
429
505
|
### @jgengine/core/combat/shotOrigin
|
|
430
506
|
|
|
507
|
+
- 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.
|
|
431
508
|
- ResolvedShot (interface): interface ResolvedShot
|
|
432
509
|
- ShotOriginDeps (interface): interface ShotOriginDeps
|
|
433
|
-
- ShotOriginPolicy (type): type ShotOriginPolicy = | { kind: "legacy" } | { kind: "entity" } | { kind: "entityOffset"; offset: EntityPosition } | { kind: "muzzle"; offset?: EntityPosition } | { kind: "camera"; origin: EntityPosition; direction?: EntityPosition } | { kind: "world"; origin:
|
|
510
|
+
- ShotOriginPolicy (type): type ShotOriginPolicy = | { kind: "eye"; height?: number } | { kind: "legacy" } | { kind: "entity" } | { kind: "entityOffset"; offset: EntityPosition } | { kind: "muzzle"; offset?: EntityPosition } | { kind: "camera"; origin: EntityPosition; direction?: EntityPosition } | { kind: "world"; origin: En… — How a shot's world-space origin (and optional direction) is resolved before prediction/settlement. - `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 (the default). - `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.
|
|
434
511
|
- aimDirection (function): function aimDirection(aim: Aim): EntityPosition | null
|
|
435
512
|
- aimSpreadDeg (function): function aimSpreadDeg(aim: Aim): number
|
|
436
|
-
-
|
|
513
|
+
- 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.
|
|
514
|
+
- resolveShot (function): function resolveShot(deps: ShotOriginDeps, from: string, aim: Aim, policy: ShotOriginPolicy = { kind: "eye" }): ResolvedShot | null
|
|
437
515
|
|
|
438
516
|
### @jgengine/core/combat/telegraph
|
|
439
517
|
|
|
@@ -726,6 +804,76 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
726
804
|
- createEmptyWallet (function): function createEmptyWallet(): WalletState
|
|
727
805
|
- grant (function): function grant(state: WalletState, currency: string, amount: number): WalletState
|
|
728
806
|
|
|
807
|
+
### @jgengine/core/editor/commands
|
|
808
|
+
|
|
809
|
+
- EditorCommand (type): type EditorCommand = | { type: "select"; ids: readonly string[] } | { type: "clearSelection" } | { type: "setTransform"; id: string; position?: EditorVec3; rotationY?: number } | { type: "addMarker"; marker: EditorMarker } | { type: "remove"; id: string } | { type: "setVolume"; id: string; patch: Pa… — A single editor mutation — select, move, add, remove, undo/redo — dispatched to a session.
|
|
810
|
+
- EditorSession (interface): interface EditorSession — Stateful, undoable handle for driving scene edits from UI or an MCP agent.
|
|
811
|
+
- EditorSessionState (interface): interface EditorSessionState — The document plus current selection at a point in editor history.
|
|
812
|
+
- createEditorSession (function): function createEditorSession(initial: EditorDocument, historyLimit = 100): EditorSession — Creates an editor session with undo/redo history seeded from an initial document.
|
|
813
|
+
- summarizeEditorSession (function): function summarizeEditorSession(state: EditorSessionState): { markers: number; volumes: number; paths: number; annotations: number; selection: string[]; selectedMarker?: EditorMarker; selectedVolume?: EditorVolume; } — Compact snapshot of a session state — counts, selection, and the selected object.
|
|
814
|
+
|
|
815
|
+
### @jgengine/core/editor/document
|
|
816
|
+
|
|
817
|
+
- cloneEditorDocument (function): function cloneEditorDocument(doc: EditorDocument): EditorDocument — Deep-copies an editor document so edits never mutate the source.
|
|
818
|
+
- createEmptyEditorDocument (function): function createEmptyEditorDocument(): EditorDocument — Builds a fresh, empty editor document to start authoring a scene from scratch.
|
|
819
|
+
- editorDocumentBounds (function): function editorDocumentBounds(doc: EditorDocument): { min: { x: number; y: number; z: number }; max: { x: number; y: number; z: number }; } | null — Computes the world-space min/max bounds spanning every object in a document, or null if empty.
|
|
820
|
+
- exportEditorDocumentJson (function): function exportEditorDocumentJson(doc: EditorDocument, pretty = true): string — Serializes an editor document to JSON text for saving or export.
|
|
821
|
+
- findEditorMarker (function): function findEditorMarker(doc: EditorDocument, id: string): EditorMarker | undefined — Looks up a marker by id in an editor document.
|
|
822
|
+
- findEditorNote (function): function findEditorNote(doc: EditorDocument, id: string): EditorNote | undefined — Looks up an annotation note by id in an editor document.
|
|
823
|
+
- findEditorPath (function): function findEditorPath(doc: EditorDocument, id: string): EditorPath | undefined — Looks up a path by id in an editor document.
|
|
824
|
+
- findEditorVolume (function): function findEditorVolume(doc: EditorDocument, id: string): EditorVolume | undefined — Looks up a volume by id in an editor document.
|
|
825
|
+
- importEditorDocumentJson (function): function importEditorDocumentJson(raw: string): EditorDocument — Parses JSON text back into a normalized editor document.
|
|
826
|
+
- listEditorKinds (function): function listEditorKinds(doc: EditorDocument): { markers: string[]; volumes: string[]; paths: string[]; } — Lists the distinct marker, volume, and path kinds authored in a document.
|
|
827
|
+
- mergeEditorDocuments (function): function mergeEditorDocuments(...docs: readonly EditorDocument[]): EditorDocument — Combines multiple editor documents' markers, volumes, paths, and notes into one.
|
|
828
|
+
- normalizeEditorLayers (function): function normalizeEditorLayers(input: EditorLayersInput | undefined | null): EditorDocument — Resolves a game's `editorLayers` export — document, partial data, or factory — into a full document.
|
|
829
|
+
|
|
830
|
+
### @jgengine/core/editor/index
|
|
831
|
+
|
|
832
|
+
- EditorCommand (type): type EditorCommand = | { type: "select"; ids: readonly string[] } | { type: "clearSelection" } | { type: "setTransform"; id: string; position?: EditorVec3; rotationY?: number } | { type: "addMarker"; marker: EditorMarker } | { type: "remove"; id: string } | { type: "setVolume"; id: string; patch: Pa… — A single editor mutation — select, move, add, remove, undo/redo — dispatched to a session.
|
|
833
|
+
- EditorDocument (interface): interface EditorDocument — The full authored scene: every marker, volume, path, and note for a game.
|
|
834
|
+
- EditorKindVisibility (interface): interface EditorKindVisibility — Per-kind show/hide flags for the editor's layer panel.
|
|
835
|
+
- EditorLayersInput (type): type EditorLayersInput = | EditorDocument | Partial<Omit<EditorDocument, "version">> | (() => EditorDocument | Partial<Omit<EditorDocument, "version">>) — Accepted shape for a game's `editorLayers` export: a document, partial data, or a factory.
|
|
836
|
+
- EditorMarker (interface): interface EditorMarker — A placeable point object in the scene — spawn, mob, chest, POI, etc.
|
|
837
|
+
- EditorNote (interface): interface EditorNote — A free-text annotation pinned to a world position for designers.
|
|
838
|
+
- EditorPath (interface): interface EditorPath — A polyline of points — road, corridor, patrol route — placed in the scene.
|
|
839
|
+
- EditorSession (interface): interface EditorSession — Stateful, undoable handle for driving scene edits from UI or an MCP agent.
|
|
840
|
+
- EditorSessionState (interface): interface EditorSessionState — The document plus current selection at a point in editor history.
|
|
841
|
+
- EditorVec3 (type): type EditorVec3 = { x: number; y: number; z: number } — A world-space point used across editor markers, volumes, and paths.
|
|
842
|
+
- EditorVolume (interface): interface EditorVolume — A spatial region — zone, aggro range, capture area — placed in the scene.
|
|
843
|
+
- EditorVolumeShape (type): type EditorVolumeShape = "sphere" | "cylinder" | "box" — Collision shape a volume is rendered and tested as.
|
|
844
|
+
- WELL_KNOWN_MARKER_KINDS (const): const WELL_KNOWN_MARKER_KINDS: readonly ["player_spawn", "mob", "boss", "vendor", "chest", "travel", "npc", "poi", "prop", "goal", "branch"] — Standard marker kinds recognized with default colors and behavior.
|
|
845
|
+
- WELL_KNOWN_PATH_KINDS (const): const WELL_KNOWN_PATH_KINDS: readonly ["road", "corridor", "branch", "route"] — Standard path kinds recognized with default colors and behavior.
|
|
846
|
+
- WELL_KNOWN_VOLUME_KINDS (const): const WELL_KNOWN_VOLUME_KINDS: readonly ["zone", "flatten", "cluster", "aggro", "leash", "discover", "capture", "prompt", "poi", "respawn_skip"] — Standard volume kinds recognized with default colors and behavior.
|
|
847
|
+
- cloneEditorDocument (function): function cloneEditorDocument(doc: EditorDocument): EditorDocument — Deep-copies an editor document so edits never mutate the source.
|
|
848
|
+
- createEditorSession (function): function createEditorSession(initial: EditorDocument, historyLimit = 100): EditorSession — Creates an editor session with undo/redo history seeded from an initial document.
|
|
849
|
+
- createEmptyEditorDocument (function): function createEmptyEditorDocument(): EditorDocument — Builds a fresh, empty editor document to start authoring a scene from scratch.
|
|
850
|
+
- editorDocumentBounds (function): function editorDocumentBounds(doc: EditorDocument): { min: { x: number; y: number; z: number }; max: { x: number; y: number; z: number }; } | null — Computes the world-space min/max bounds spanning every object in a document, or null if empty.
|
|
851
|
+
- exportEditorDocumentJson (function): function exportEditorDocumentJson(doc: EditorDocument, pretty = true): string — Serializes an editor document to JSON text for saving or export.
|
|
852
|
+
- findEditorMarker (function): function findEditorMarker(doc: EditorDocument, id: string): EditorMarker | undefined — Looks up a marker by id in an editor document.
|
|
853
|
+
- findEditorNote (function): function findEditorNote(doc: EditorDocument, id: string): EditorNote | undefined — Looks up an annotation note by id in an editor document.
|
|
854
|
+
- findEditorPath (function): function findEditorPath(doc: EditorDocument, id: string): EditorPath | undefined — Looks up a path by id in an editor document.
|
|
855
|
+
- findEditorVolume (function): function findEditorVolume(doc: EditorDocument, id: string): EditorVolume | undefined — Looks up a volume by id in an editor document.
|
|
856
|
+
- importEditorDocumentJson (function): function importEditorDocumentJson(raw: string): EditorDocument — Parses JSON text back into a normalized editor document.
|
|
857
|
+
- listEditorKinds (function): function listEditorKinds(doc: EditorDocument): { markers: string[]; volumes: string[]; paths: string[]; } — Lists the distinct marker, volume, and path kinds authored in a document.
|
|
858
|
+
- mergeEditorDocuments (function): function mergeEditorDocuments(...docs: readonly EditorDocument[]): EditorDocument — Combines multiple editor documents' markers, volumes, paths, and notes into one.
|
|
859
|
+
- normalizeEditorLayers (function): function normalizeEditorLayers(input: EditorLayersInput | undefined | null): EditorDocument — Resolves a game's `editorLayers` export — document, partial data, or factory — into a full document.
|
|
860
|
+
- summarizeEditorSession (function): function summarizeEditorSession(state: EditorSessionState): { markers: number; volumes: number; paths: number; annotations: number; selection: string[]; selectedMarker?: EditorMarker; selectedVolume?: EditorVolume; } — Compact snapshot of a session state — counts, selection, and the selected object.
|
|
861
|
+
|
|
862
|
+
### @jgengine/core/editor/types
|
|
863
|
+
|
|
864
|
+
- EditorDocument (interface): interface EditorDocument — The full authored scene: every marker, volume, path, and note for a game.
|
|
865
|
+
- EditorKindVisibility (interface): interface EditorKindVisibility — Per-kind show/hide flags for the editor's layer panel.
|
|
866
|
+
- EditorLayersInput (type): type EditorLayersInput = | EditorDocument | Partial<Omit<EditorDocument, "version">> | (() => EditorDocument | Partial<Omit<EditorDocument, "version">>) — Accepted shape for a game's `editorLayers` export: a document, partial data, or a factory.
|
|
867
|
+
- EditorMarker (interface): interface EditorMarker — A placeable point object in the scene — spawn, mob, chest, POI, etc.
|
|
868
|
+
- EditorNote (interface): interface EditorNote — A free-text annotation pinned to a world position for designers.
|
|
869
|
+
- EditorPath (interface): interface EditorPath — A polyline of points — road, corridor, patrol route — placed in the scene.
|
|
870
|
+
- EditorVec3 (type): type EditorVec3 = { x: number; y: number; z: number } — A world-space point used across editor markers, volumes, and paths.
|
|
871
|
+
- EditorVolume (interface): interface EditorVolume — A spatial region — zone, aggro range, capture area — placed in the scene.
|
|
872
|
+
- EditorVolumeShape (type): type EditorVolumeShape = "sphere" | "cylinder" | "box" — Collision shape a volume is rendered and tested as.
|
|
873
|
+
- WELL_KNOWN_MARKER_KINDS (const): const WELL_KNOWN_MARKER_KINDS: readonly ["player_spawn", "mob", "boss", "vendor", "chest", "travel", "npc", "poi", "prop", "goal", "branch"] — Standard marker kinds recognized with default colors and behavior.
|
|
874
|
+
- WELL_KNOWN_PATH_KINDS (const): const WELL_KNOWN_PATH_KINDS: readonly ["road", "corridor", "branch", "route"] — Standard path kinds recognized with default colors and behavior.
|
|
875
|
+
- WELL_KNOWN_VOLUME_KINDS (const): const WELL_KNOWN_VOLUME_KINDS: readonly ["zone", "flatten", "cluster", "aggro", "leash", "discover", "capture", "prompt", "poi", "respawn_skip"] — Standard volume kinds recognized with default colors and behavior.
|
|
876
|
+
|
|
729
877
|
### @jgengine/core/faction/factions
|
|
730
878
|
|
|
731
879
|
- FactionDef (interface): interface FactionDef
|
|
@@ -776,6 +924,12 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
776
924
|
- createChatFilter (function): function createChatFilter(config: ChatFilterConfig): ChatFilter
|
|
777
925
|
- normalizeChatText (function): function normalizeChatText(text: string): string
|
|
778
926
|
|
|
927
|
+
### @jgengine/core/game/connectedPlayers
|
|
928
|
+
|
|
929
|
+
- ConnectedPlayer (interface): interface ConnectedPlayer — A player currently joined to a hosted world — the unit a shared-world loop iterates instead of `ctx.player`.
|
|
930
|
+
- ConnectedPlayers (interface): interface ConnectedPlayers — The set of players connected to one hosted world. A single-player game uses `ctx.player`; a shared-world loop reads `ctx.game.players` so `onTick` can advance every connected hero, not just the one local player. The host (`HostedGameRunner`) drives `join`/`leave`/`setInput`; game code reads `list`/`ids`/`has`/`count`/`input`.
|
|
931
|
+
- createConnectedPlayers (function): function createConnectedPlayers(): ConnectedPlayers — Build an empty {@link ConnectedPlayers} registry — the host joins/leaves players; the game loop reads them.
|
|
932
|
+
|
|
779
933
|
### @jgengine/core/game/controlGate
|
|
780
934
|
|
|
781
935
|
- PLAY_CONTROLS_STORE_KEY (const): const PLAY_CONTROLS_STORE_KEY: "jg.playControls"
|
|
@@ -793,17 +947,21 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
793
947
|
|
|
794
948
|
### @jgengine/core/game/defineGame
|
|
795
949
|
|
|
796
|
-
- GameDefinition (interface): interface GameDefinition<TAssetRef extends ModelAssetRef = ModelAssetRef, TMultiplayer = unknown>
|
|
797
|
-
- GameDefinitionConfig (type): type GameDefinitionConfig<TAssetRef extends ModelAssetRef = ModelAssetRef, TMultiplayer = unknown> = Omit<GameDefinition<TAssetRef, TMultiplayer>, "scene" | "assets"> & { assets?: AssetCatalog<TAssetRef>; }
|
|
798
|
-
-
|
|
799
|
-
-
|
|
800
|
-
-
|
|
801
|
-
-
|
|
802
|
-
-
|
|
950
|
+
- GameDefinition (interface): interface GameDefinition<TAssetRef extends ModelAssetRef = ModelAssetRef, TMultiplayer = unknown> — Fully-resolved game description produced by {@link defineGame} — assets, scene, and opted-in subsystems.
|
|
951
|
+
- GameDefinitionConfig (type): type GameDefinitionConfig<TAssetRef extends ModelAssetRef = ModelAssetRef, TMultiplayer = unknown> = Omit<GameDefinition<TAssetRef, TMultiplayer>, "scene" | "assets"> & { assets?: AssetCatalog<TAssetRef>; } — Input to {@link defineGame} — a `GameDefinition` with `scene` derived and `assets` optional.
|
|
952
|
+
- GameFeatures (interface): interface GameFeatures — Opt-in `ctx.game.*` subsystems. Absent = off: the game doesn't carry (or expose) it, and `ctx.game.<name>` is `undefined`. Present (`true`) builds it. The universal base — `commands`, `events`, `store`, `feed` — is always on and not listed here. This is what keeps core genre-agnostic: a puzzle game isn't handed an MMO's leaderboard/roster/turn plumbing it never asked for.
|
|
953
|
+
- GameLoop (interface): interface GameLoop<TContext = unknown> — Lifecycle hooks a game implements to drive init, per-tick simulation, and player join/leave.
|
|
954
|
+
- GameServerConfig (type): type GameServerConfig = "persistent" | { mode: string; [key: string]: unknown } — Hosting mode for a game's multiplayer server: `"persistent"`, or a custom mode with its own options.
|
|
955
|
+
- InventoryDeclaration (interface): interface InventoryDeclaration — Shape of one named inventory a game declares — slot count, accepted item types, HUD binding.
|
|
956
|
+
- LoopPlayer (interface): interface LoopPlayer — Identity of a player joining or leaving a hosted world — passed to the multiplayer loop hooks.
|
|
957
|
+
- PhysicsConfig (interface): interface PhysicsConfig — World gravity and jump tuning, plus scene-object collision opt-ins, for the game's physics step.
|
|
958
|
+
- defineGame (function): function defineGame<TAssetRef extends ModelAssetRef, TMultiplayer>(config: GameDefinitionConfig<TAssetRef, TMultiplayer>): GameDefinition<TAssetRef, TMultiplayer> — Task-first entry point for authoring a game: fills in `scene` and default `assets`, validates `name`.
|
|
803
959
|
|
|
804
960
|
### @jgengine/core/game/events
|
|
805
961
|
|
|
962
|
+
- AudioMusicEvent (interface): interface AudioMusicEvent — Crossfade the procedural soundtrack to `theme` (null fades out), optionally transposing the incoming theme by `transpose` semitones.
|
|
806
963
|
- AudioPlayEvent (interface): interface AudioPlayEvent
|
|
964
|
+
- AudioResumeEvent (type): type AudioResumeEvent = Record<string, never> — Request that the shell's audio engine resume its (browser-gesture-suspended) context; carries no payload.
|
|
807
965
|
- ChatMessageEvent (interface): interface ChatMessageEvent
|
|
808
966
|
- CombatHitReactionEvent (interface): interface CombatHitReactionEvent
|
|
809
967
|
- CombatTelegraphCancelledEvent (interface): interface CombatTelegraphCancelledEvent
|
|
@@ -811,6 +969,7 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
811
969
|
- CosmeticsChangedEvent (interface): interface CosmeticsChangedEvent
|
|
812
970
|
- DeathReason (type): type DeathReason = | { kind: "player_kill"; killerUserId: string; via?: { item?: string } } | { kind: "environment"; source: string } | { kind: "self"; source: string }
|
|
813
971
|
- EmotePlayedEvent (interface): interface EmotePlayedEvent
|
|
972
|
+
- EntityAnimationEvent (interface): interface EntityAnimationEvent — Request that an entity's rig play a one-shot animation clip bound to `event` in its `animation.oneShots` (e.g. an "attack" swing); the shell resolves the clip and plays it once over the locomotion state.
|
|
814
973
|
- EntityDiedEvent (interface): interface EntityDiedEvent
|
|
815
974
|
- EntityFloatTextEvent (interface): interface EntityFloatTextEvent
|
|
816
975
|
- FormChangedEvent (interface): interface FormChangedEvent
|
|
@@ -851,6 +1010,14 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
851
1010
|
- isPlaying (function): function isPlaying(ctx: GameContext): boolean
|
|
852
1011
|
- setGamePhase (function): function setGamePhase(ctx: GameContext, phase: GamePhase): void — Set the current phase. Publishes it to `ctx.game.store` (React reads it via `useGamePhase`) and gates the shell's on-screen touch controls in one call — `playing` shows them, every other phase hides them. This is the whole "main menu shouldn't show touch controls" wiring: call it once per phase transition and the dock follows.
|
|
853
1012
|
|
|
1013
|
+
### @jgengine/core/game/keyValueStore
|
|
1014
|
+
|
|
1015
|
+
- KeyValueStorage (interface): interface KeyValueStorage — Structural, DOM-free storage backend: the browser `localStorage` satisfies it, as does a test stub or `null`. The one storage seam core primitives target so persistence code never needs the DOM `Storage` lib.
|
|
1016
|
+
- KeyValueStore (interface): interface KeyValueStore<T> — A single persisted, mutable cell: read the current value, overwrite it, or read-modify-write with {@link KeyValueStore.update}. Unlike a record book it has no monotonic guard — the value goes wherever you set it.
|
|
1017
|
+
- KeyValueStoreConfig (interface): interface KeyValueStoreConfig<T> — Config for {@link createKeyValueStore}: the storage `key`, the `initial` value used before anything is saved, an optional `storage` backend (defaults to `localStorage`, pass `null` for memory-only), and optional custom `serialize`/`deserialize` (default JSON).
|
|
1018
|
+
- createKeyValueStore (function): function createKeyValueStore<T>(config: KeyValueStoreConfig<T>): KeyValueStore<T> — A lightweight mutable local save cell for single-player state (a credit bank, a settings blob, level progress) — the read-modify-write counterpart to the monotonic `recordBook`. Persists through a {@link KeyValueStorage} (browser `localStorage` by default); corrupt or unavailable storage degrades to in-memory and never throws into a game tick.
|
|
1019
|
+
- defaultKeyValueStorage (function): function defaultKeyValueStorage(): KeyValueStorage | null — The ambient `localStorage` as a {@link KeyValueStorage} when one exists (browser), otherwise `null` (server/tests) — never references the DOM `Storage` type, so it is safe in core.
|
|
1020
|
+
|
|
854
1021
|
### @jgengine/core/game/leaderboard
|
|
855
1022
|
|
|
856
1023
|
- IncrementResult (type): type IncrementResult = { status: "ok"; value: number } | { status: "rejected"; reason: "not-tracked" }
|
|
@@ -864,10 +1031,12 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
864
1031
|
|
|
865
1032
|
- CurrentLevel (interface): interface CurrentLevel<TLevelConfig>
|
|
866
1033
|
- LevelDescriptor (interface): interface LevelDescriptor<TLevelConfig>
|
|
1034
|
+
- LevelRecord (interface): interface LevelRecord — Persisted per-level outcome: whether it has ever been cleared and the best star rating achieved.
|
|
867
1035
|
- LevelSequence (interface): interface LevelSequence<TLevelConfig>
|
|
868
1036
|
- LevelSequenceConfig (interface): interface LevelSequenceConfig<TLevelConfig>
|
|
869
1037
|
- LevelSequenceProgress (interface): interface LevelSequenceProgress
|
|
870
1038
|
- LevelSequenceStatus (type): type LevelSequenceStatus = "idle" | "playing" | "cleared" | "failed" | "complete"
|
|
1039
|
+
- LevelStars (type): type LevelStars = 0 | 1 | 2 | 3 — A level's star rating, 0 (cleared, no stars) to 3.
|
|
871
1040
|
- createLevelSequence (function): function createLevelSequence<TLevelConfig>(config: LevelSequenceConfig<TLevelConfig>): LevelSequence<TLevelConfig> — A pure, deterministic level campaign: an ordered list of levels, each with its own opaque config, played through a `start` → (`clear` → `advance`)* → `complete` happy path, with `fail`/`retry` handling per-level attempts. Mirrors the reducer style of `game/race.ts` and `ai/spawnDirector.ts` — no I/O, no timers, just state transitions driven by the caller.
|
|
872
1041
|
|
|
873
1042
|
### @jgengine/core/game/loadout
|
|
@@ -890,13 +1059,18 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
890
1059
|
|
|
891
1060
|
### @jgengine/core/game/lootTable
|
|
892
1061
|
|
|
893
|
-
- Drop (interface): interface Drop
|
|
894
|
-
- LootEntry (interface): interface LootEntry
|
|
1062
|
+
- Drop (interface): interface Drop — A resolved loot outcome — one item or currency grant with its rolled count.
|
|
1063
|
+
- LootEntry (interface): interface LootEntry — One possible drop in a {@link LootTableDef} — an item or currency, its count range, and its odds.
|
|
895
1064
|
- LootRegistry (interface): interface LootRegistry
|
|
896
|
-
- LootTableDef (interface): interface LootTableDef
|
|
1065
|
+
- LootTableDef (interface): interface LootTableDef — A named, validated loot table — its roll count, weighted-vs-independent mode, and candidate entries.
|
|
897
1066
|
- createLootRegistry (function): function createLootRegistry(): LootRegistry
|
|
898
1067
|
- grantDrops (function): function grantDrops(drops: Drop[], appliers: { putItem: (itemId: string, count: number) => unknown; grantCurrency: (currencyId: string, amount: number) => unknown; }): void
|
|
899
|
-
- lootTable (function): function lootTable(def: LootTableDef): LootTableDef
|
|
1068
|
+
- lootTable (function): function lootTable(def: LootTableDef): LootTableDef — Validates a loot table definition and returns it unchanged, for use with {@link createLootRegistry}.
|
|
1069
|
+
|
|
1070
|
+
### @jgengine/core/game/modelAnimation
|
|
1071
|
+
|
|
1072
|
+
- AUTO_ONE_SHOT_EVENTS (const): const AUTO_ONE_SHOT_EVENTS: readonly ["hit", "death"] — The two one-shot events the shell fires automatically for an entity, from `combat.hitReaction` and `entity.died`.
|
|
1073
|
+
- resolveOneShotClip (function): function resolveOneShotClip(oneShots: Record<string, string | readonly string[]> | undefined, event: string, roll: number): string | null — Resolves the clip name a one-shot `event` should play from a model's `animation.oneShots` map, or `null` if the event isn't bound. A `string[]` binding picks a variant by `roll` (a value in `[0, 1)`), so combat can vary attack swings. Pure and deterministic given `roll` — the shell supplies the randomness.
|
|
900
1074
|
|
|
901
1075
|
### @jgengine/core/game/ping
|
|
902
1076
|
|
|
@@ -938,6 +1112,8 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
938
1112
|
- LightingConfig (interface): interface LightingConfig — Declarative lighting replacing the shell's hardcoded ambient/directional default (#207.5); mounts regardless of world kind, only when supplied.
|
|
939
1113
|
- LockOnCameraConfig (interface): interface LockOnCameraConfig — Lock-on / strafe rig (#26) — yaw bound to player→target, move axis becomes strafe.
|
|
940
1114
|
- ModelAnimationConfig (interface): interface ModelAnimationConfig — Rig playback for a `ModelConfig`'s GLTF animation clips — looping idles, one-shots, and held poses.
|
|
1115
|
+
- ModelAnimationStates (interface): interface ModelAnimationStates — Movement-state clip set for `ModelAnimationConfig.states`: the shell reads the entity's live speed each frame and crossfades between these clips, so a walking mob animates without any game-side driver.
|
|
1116
|
+
- ModelAttachment (interface): interface ModelAttachment — Parents a prop/weapon model to a named bone or node on the host model's rig — a sword on `handslot.r`, a spellbook offhand — following the bone's animated transform each frame.
|
|
941
1117
|
- ModelConfig (interface): interface ModelConfig
|
|
942
1118
|
- ModelMaterialOverride (interface): interface ModelMaterialOverride — Per-entity PBR material override (#151.3) applied to every `MeshStandardMaterial` in the model's cloned scene graph.
|
|
943
1119
|
- MovementCommitFrame (interface): interface MovementCommitFrame — One frame's movement resolution handed to `PlayerMovementConfig.beforeCommit`.
|
|
@@ -1011,7 +1187,7 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
1011
1187
|
- RecordBook (interface): interface RecordBook<K extends string>
|
|
1012
1188
|
- RecordBookConfig (interface): interface RecordBookConfig<K extends string>
|
|
1013
1189
|
- RecordDirection (type): type RecordDirection = "lower" | "higher"
|
|
1014
|
-
- RecordStorage (
|
|
1190
|
+
- RecordStorage (type): type RecordStorage = KeyValueStorage — The structural storage backend a record book persists through — an alias of the shared {@link KeyValueStorage} seam (browser `localStorage`, a test stub, or `null`).
|
|
1015
1191
|
- RecordSubmission (interface): interface RecordSubmission<K extends string>
|
|
1016
1192
|
- createRecordBook (function): function createRecordBook<K extends string>(config: RecordBookConfig<K>): RecordBook<K> — A personal-best record book: named numeric fields each racing toward "lower" (times) or "higher" (scores, streaks), persisted through a structural key-value storage (pass `localStorage` in a browser, a stub in tests, or `null` for in-memory only). Corrupt or unavailable storage degrades to an empty book — a record write never throws into a game tick.
|
|
1017
1193
|
|
|
@@ -1125,6 +1301,8 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
1125
1301
|
- CameraVisibilityContext (interface): interface CameraVisibilityContext — A camera's position and reach, as seen by the visibility system.
|
|
1126
1302
|
- CommandDef (type): type CommandDef<TInput = unknown> = { validate: ( snapshot: GameRuntimeSnapshot, input: TInput, actorUserId: string, ) => CommandValidationError | null; apply: ( snapshot: GameRuntimeSnapshot, input: TInput, actorUserId: string, ) => GameRuntimeSnapshot; }
|
|
1127
1303
|
- CommandValidationError (type): type CommandValidationError = { reason: string }
|
|
1304
|
+
- ConnectedPlayer (interface): interface ConnectedPlayer — A player currently joined to a hosted world — the unit a shared-world loop iterates instead of `ctx.player`.
|
|
1305
|
+
- ConnectedPlayers (interface): interface ConnectedPlayers — The set of players connected to one hosted world. A single-player game uses `ctx.player`; a shared-world loop reads `ctx.game.players` so `onTick` can advance every connected hero, not just the one local player. The host (`HostedGameRunner`) drives `join`/`leave`/`setInput`; game code reads `list`/`ids`/`has`/`count`/`input`.
|
|
1128
1306
|
- DEFAULT_VISIBILITY_SETTINGS (const): const DEFAULT_VISIBILITY_SETTINGS: Readonly<VisibilityDefaults> — Default `VisibilityDefaults` a `VisibilitySystem` falls back to when unspecified.
|
|
1129
1307
|
- FeedUnsubscribe (type): type FeedUnsubscribe = () => void
|
|
1130
1308
|
- GameBackend (type): type GameBackend<TPresenceRow = unknown, TPresenceLocation = unknown, TGameId extends string = string> = { transport: GameRuntimeTransport; feeds?: GameRuntimeFeeds; presence?: PresenceTransport<TPresenceRow, TPresenceLocation, TGameId>; }
|
|
@@ -1136,10 +1314,20 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
1136
1314
|
- GameRuntimeServerView (type): type GameRuntimeServerView = { serverId: string; gameId: string; revision: number; memberUserIds: string[]; serverState: unknown; updatedAt: number; }
|
|
1137
1315
|
- GameRuntimeSnapshot (type): type GameRuntimeSnapshot = { version: number; gameId: string; serverId: string; server: RuntimeServerRow; players: Record<string, RuntimePlayerRow>; chunks: Record<string, RuntimeChunkRow>; revision: number; dirty: { server: boolean; players: string[]; chunks: string[]; }; }
|
|
1138
1316
|
- GameRuntimeTransport (type): type GameRuntimeTransport = { joinServer: (args: { gameId: string; serverId?: string }) => Promise<JoinServerResult>; leaveServer: (args: { serverId: string }) => Promise<void>; runCommand: (args: RunCommandArgs) => Promise<TransportRunCommandResult>; }
|
|
1317
|
+
- HostedGameRunner (interface): interface HostedGameRunner — The GameContext-loop equivalent of the pure-reducer `createGameHost`: one authoritative `createGameContext` per world, driven server-side. `onInit` runs once at construction; `onNewPlayer`/`onPlayerLeave` fire per join/leave; `tick` advances `onTick` then commits a revision. Clients pull a full {@link WorldSnapshot} baseline once, then per-tick {@link WorldDiff}s from their last revision. Games ship only normal GameContext code — the runner adds no per-game surface.
|
|
1318
|
+
- HostedGameRunnerOptions (interface): interface HostedGameRunnerOptions<TAssetRef extends ModelAssetRef, TMultiplayer> — Config for {@link createHostedGameRunner}: the game definition, its content lookup, and an optional host identity.
|
|
1319
|
+
- HostedWorldRecord (interface): interface HostedWorldRecord — One hosted world's persisted authoritative state — the unit a {@link HostedWorldStore} loads and saves.
|
|
1320
|
+
- HostedWorldSession (interface): interface HostedWorldSession — The stateful host substrate a long-lived backend binds (ws server, browser P2P host): a live {@link HostedGameRunner} loaded from a {@link HostedWorldStore}, auto-persisted on tick, serving each client a baseline then diffs. The stateless Convex path doesn't use this — it reconstructs a runner from the same store per invocation and diffs with `diffSnapshots`. Either way the store seam and the game code are identical.
|
|
1321
|
+
- HostedWorldSessionOptions (interface): interface HostedWorldSessionOptions<TAssetRef extends ModelAssetRef, TMultiplayer> — Config for {@link createHostedWorldSession}: the game, its persistence store, and the auto-save cadence.
|
|
1322
|
+
- HostedWorldStore (interface): interface HostedWorldStore — Narrow persistence seam for a hosted world — the {@link HostedWorldRecord} counterpart of `HostPersistence`. Backends implement it (memory/file/sql/convex); the session never names one. A stateful host loads once and saves on a cadence; a stateless host reconstructs from `load()` each invocation.
|
|
1323
|
+
- HostedWorldSync (type): type HostedWorldSync = | { kind: "baseline"; revision: number; snapshot: WorldSnapshot } | { kind: "diff"; diff: WorldDiff } — A client replication pull: a full baseline (first sync / fell behind) or a diff since the client's cursor.
|
|
1139
1324
|
- HydrateInput (type): type HydrateInput = { gameId: string; serverId: string; serverRow: RuntimeServerRow; playersByUserId: Record<string, RuntimePlayerRow>; chunksByKey: Record<string, RuntimeChunkRow>; revision?: number; }
|
|
1325
|
+
- INPUT_COMMAND (const): const INPUT_COMMAND: "engine.input" — Reserved command name the authoritative host intercepts on the existing `runCommand` transport to route a client's {@link InputFrame} to `session.input`, so per-tick input needs no separate wire.
|
|
1326
|
+
- InputFrame (interface): interface InputFrame — One client's input for a tick — the semantic held-action set plus pointer state, the serializable, over-the-wire counterpart of {@link InputSnapshot} the host stores per connected player.
|
|
1140
1327
|
- JoinServerResult (type): type JoinServerResult = { serverId: string; isNew: boolean; }
|
|
1141
1328
|
- LiveGameBackend (type): type LiveGameBackend<TPresenceRow = unknown, TPresenceLocation = unknown, TGameId extends string = string> = GameBackend<TPresenceRow, TPresenceLocation, TGameId> & { presenceSync: PresenceSync; pushFeedEntry: (args: { serverId: string; action: string; entry: unknown }) => Promise<void>; chatSyncFor…
|
|
1142
|
-
- MultiplayerAdapterConfig (type): type MultiplayerAdapterConfig = | { kind: "convex"; topology?: MultiplayerTopology } | { kind: "ws"; topology?: MultiplayerTopology; url?: string } | { kind: "socketio"; topology?: MultiplayerTopology; url?: string
|
|
1329
|
+
- MultiplayerAdapterConfig (type): type MultiplayerAdapterConfig = | { kind: "convex"; topology?: MultiplayerTopology; authority?: MultiplayerAuthority } | { kind: "ws"; topology?: MultiplayerTopology; url?: string; authority?: MultiplayerAuthority } | { kind: "socketio"; topology?: MultiplayerTopology; url?: string; authority?: Mult…
|
|
1330
|
+
- MultiplayerAuthority (type): type MultiplayerAuthority = "server" | "client" — Where the world simulation is authoritative. `"client"` (default) keeps the historical model — each client runs its own `onTick` and syncs only presence/feeds/chat. `"server"` opts into host-authoritative replication: the host runs the loop, and the shell mirrors the server's world into the local `ctx` instead of simulating locally.
|
|
1143
1331
|
- MultiplayerSession (type): type MultiplayerSession = { gameId: string; userId: string; backend: LiveGameBackend; feedActions: string[]; }
|
|
1144
1332
|
- MultiplayerTopology (type): type MultiplayerTopology = "shared" | "lobbies" | "private"
|
|
1145
1333
|
- PresencePoseRow (type): type PresencePoseRow = { userId: string; position: { x: number; y: number; z: number }; rotationY: number; rotationPitch: number; lastSeenAt: number; }
|
|
@@ -1161,6 +1349,7 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
1161
1349
|
- SaveScope (type): type SaveScope = "player" | "chunks" | "player+chunks"
|
|
1162
1350
|
- ServerLoopHooks (type): type ServerLoopHooks = { onInit?: (ctx: RuntimeInitContext) => void; onNewPlayer?: (ctx: RuntimeLoopContext) => void; onTick?: (ctx: RuntimeWorldContext, dtSeconds: number) => void; }
|
|
1163
1351
|
- ServersPoolConfig (type): type ServersPoolConfig = { maxServers: number; slotsPerServer: number; minPlayersToStart?: number; adapter: MultiplayerAdapterConfig; }
|
|
1352
|
+
- SnapshotModule (interface): interface SnapshotModule<T = unknown> — The replication seam for host-authoritative shared worlds: the opt-in feature manifest *is* the replication schema. Each live subsystem a game opts into registers a {@link SnapshotModule} keyed by name; the host serializes exactly the registered set into a {@link WorldSnapshot} and a client hydrates the same keys back. Adding a replicated subsystem is a registration, never a new branch.
|
|
1164
1353
|
- TransportRunCommandResult (type): type TransportRunCommandResult = | { ok: true } | { ok: false; reason: string }
|
|
1165
1354
|
- VisibilityBounds (interface): interface VisibilityBounds — Sphere bounds used for visibility/streaming distance checks.
|
|
1166
1355
|
- VisibilityDecision (interface): interface VisibilityDecision — Result of evaluating one object against the active cameras this tick.
|
|
@@ -1170,29 +1359,52 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
1170
1359
|
- VisibilityOverrides (interface): interface VisibilityOverrides — Per-object escape hatches that bypass or tune the default visibility policy.
|
|
1171
1360
|
- VisibilityPoint (interface): interface VisibilityPoint — World-space point; `z` is optional for 2D adapters.
|
|
1172
1361
|
- VisibilitySystem (class): class VisibilitySystem — Engine-level visibility and asset-residency policy.
|
|
1362
|
+
- WorldClientFrame (type): type WorldClientFrame = | { t: "join"; isNew: boolean } | { t: "leave" } | { t: "command"; name: string; input: unknown } | { t: "input"; frame: InputFrame } — Client→host frame: a player's session verbs. What a transport marshals upstream.
|
|
1363
|
+
- WorldClientLink (interface): interface WorldClientLink — The client end of a {@link WorldHost} connection: applies server frames to a local `ctx`, sends session verbs upstream.
|
|
1364
|
+
- WorldDiff (interface): interface WorldDiff — A revision-stamped delta over a {@link WorldSnapshot}. The host sends one per tick to each client, carrying only what changed since that client's last acknowledged revision — entity/stat/store deltas plus whole snapshots of any other opted-in module (feed, leaderboard, chat, …) that changed. Fold it onto a prior baseline with {@link applyWorldDiff}.
|
|
1365
|
+
- WorldHost (interface): interface WorldHost — The transport-agnostic host: fans one {@link HostedWorldSession} out to many connections, each tracking its own revision cursor so a joiner gets a baseline and everyone else gets diffs. A ws server, a Convex function, or an in-process loopback all drive the same shape — decode a frame → `connection.receive`; after `session.tick` → `broadcast`. No wire format is assumed; frames are plain data a transport serializes however it likes.
|
|
1366
|
+
- WorldHostConnection (interface): interface WorldHostConnection — One client's link on the host side: routes its upstream frames into the shared session, pushes it sync frames.
|
|
1367
|
+
- WorldMirror (interface): interface WorldMirror — The client end of host-authoritative replication: folds a host's baseline + {@link WorldDiff} stream onto a local {@link GameContext}. It keeps the last full {@link WorldSnapshot}, advances it with each diff, and pushes the result through `ctx.hydrate` — so the client mirrors exactly the subsystems its own game opted into and silently ignores host modules it lacks. This is the inverse of a {@link HostedWorldSession}; the transport in between (loopback, ws, Convex) is irrelevant.
|
|
1368
|
+
- WorldReplicator (type): type WorldReplicator = ReturnType<typeof createWorldReplicator> — The stateful diff tracker returned by {@link createWorldReplicator}: `commit()`, `diff(sinceRevision)`, `revision()`.
|
|
1369
|
+
- WorldServerFrame (type): type WorldServerFrame = | { t: "baseline"; revision: number; snapshot: WorldSnapshot } | { t: "diff"; diff: WorldDiff } — Host→client frame: the full baseline a joiner needs, then per-tick diffs. What a transport marshals downstream.
|
|
1370
|
+
- WorldSnapshot (type): type WorldSnapshot = Record<string, unknown> — Full world baseline keyed by {@link SnapshotModule.key} — one entry per opted-in subsystem.
|
|
1173
1371
|
- adapterOf (function): function adapterOf(multiplayer: unknown): MultiplayerAdapterConfig | null
|
|
1372
|
+
- applyWorldDiff (function): function applyWorldDiff(baseline: WorldSnapshot, diff: WorldDiff): WorldSnapshot — Fold a {@link WorldDiff} onto a prior {@link WorldSnapshot} baseline, returning the next full snapshot — the client-side inverse of {@link createWorldReplicator}. Pure data in, pure data out: upserts changed entities, stats and store keys, drops the removed ones, and replaces changed module snapshots wholesale.
|
|
1373
|
+
- applyWorldSnapshot (function): function applyWorldSnapshot(modules: readonly SnapshotModule[], snapshot: WorldSnapshot): void — Hydrate every registered module whose key is present in `snapshot`; keys absent from it are left untouched.
|
|
1174
1374
|
- clearDirtyFlags (function): function clearDirtyFlags(snapshot: GameRuntimeSnapshot): GameRuntimeSnapshot
|
|
1175
|
-
-
|
|
1375
|
+
- composeWorldSnapshot (function): function composeWorldSnapshot(modules: readonly SnapshotModule[]): WorldSnapshot — Serialize every registered module into one keyed baseline — the host→client full-world send.
|
|
1376
|
+
- convex (function): function convex(config?: { topology?: MultiplayerTopology; authority?: MultiplayerAuthority }): MultiplayerAdapterConfig
|
|
1377
|
+
- createConnectedPlayers (function): function createConnectedPlayers(): ConnectedPlayers — Build an empty {@link ConnectedPlayers} registry — the host joins/leaves players; the game loop reads them.
|
|
1176
1378
|
- createEmptyPlayerRow (function): function createEmptyPlayerRow(userId: string): RuntimePlayerRow
|
|
1177
1379
|
- createEmptyServerRow (function): function createEmptyServerRow(): RuntimeServerRow
|
|
1178
1380
|
- createGameRuntime (function): function createGameRuntime(definition: GameRuntimeDefinition): GameRuntime
|
|
1381
|
+
- createHostedGameRunner (function): function createHostedGameRunner<TAssetRef extends ModelAssetRef, TMultiplayer>(options: HostedGameRunnerOptions<TAssetRef, TMultiplayer>): HostedGameRunner — Build a {@link HostedGameRunner} — one authoritative GameContext world driven server-side from the game's own loop.
|
|
1382
|
+
- createHostedWorldSession (function): function createHostedWorldSession<TAssetRef extends ModelAssetRef, TMultiplayer>(options: HostedWorldSessionOptions<TAssetRef, TMultiplayer>): HostedWorldSession — Build a {@link HostedWorldSession} — a live runner loaded from a {@link HostedWorldStore} and auto-persisted on tick.
|
|
1179
1383
|
- createRuntimeSnapshot (function): function createRuntimeSnapshot(args: { gameId: string; serverId: string; server?: RuntimeServerRow; players?: Record<string, RuntimePlayerRow>; chunks?: Record<string, RuntimeChunkRow>; revision?: number; }): GameRuntimeSnapshot
|
|
1180
|
-
-
|
|
1384
|
+
- createWorldClientLink (function): function createWorldClientLink(ctx: Pick<GameContext, "hydrate">, send: (frame: WorldClientFrame) => void): WorldClientLink — Build a {@link WorldClientLink} — the client end that mirrors host frames into `ctx` and sends session verbs upstream.
|
|
1385
|
+
- createWorldHost (function): function createWorldHost(session: HostedWorldSession): WorldHost — Build a {@link WorldHost} fanning one {@link HostedWorldSession} out to many cursor-tracked connections.
|
|
1386
|
+
- createWorldMirror (function): function createWorldMirror(ctx: Pick<GameContext, "hydrate">): WorldMirror — Build a {@link WorldMirror} that replicates a host's baseline + diff stream onto `ctx` via `ctx.hydrate`.
|
|
1387
|
+
- createWorldReplicator (function): function createWorldReplicator(takeSnapshot: () => WorldSnapshot): { commit: () => number; diff: (sinceRevision: number) => WorldDiff; revision: () => number; } — Turns successive full {@link WorldSnapshot}s into per-client {@link WorldDiff}s. Each `commit()` re-reads the world, stamps every item that changed with the new revision, and remembers removals; `diff(sinceRevision)` then replays exactly the items stamped after that revision. Everything the tracker holds is JSON — the same shape that rides the wire — so a diff is inherently serializable. Change-detection is a full re-serialize per commit; dirty-hint acceleration is a later optimization behind the same seam.
|
|
1388
|
+
- diffSnapshots (function): function diffSnapshots(prev: WorldSnapshot, next: WorldSnapshot, revision: number): WorldDiff — Diff two full {@link WorldSnapshot}s directly, stamping the result at `revision` — the stateless counterpart of {@link createWorldReplicator} for hosts that persist snapshots rather than keep a live tracker (Convex reconstructs per invocation). `applyWorldDiff(prev, diffSnapshots(prev, next, r))` reproduces `next`.
|
|
1389
|
+
- fly (function): function fly(config: { app: string; topology?: MultiplayerTopology; path?: string; authority?: MultiplayerAuthority }): MultiplayerAdapterConfig
|
|
1181
1390
|
- isSaveEnabled (function): function isSaveEnabled(config: SaveConfig): config is Exclude<SaveConfig, "none">
|
|
1182
|
-
-
|
|
1391
|
+
- isServerAuthoritative (function): function isServerAuthoritative(multiplayer: unknown): boolean — True when the adapter opts into host-authoritative world replication (`authority: "server"`).
|
|
1392
|
+
- lan (function): function lan(config?: { topology?: MultiplayerTopology; port?: number; path?: string; authority?: MultiplayerAuthority; }): MultiplayerAdapterConfig
|
|
1183
1393
|
- markPlayerDirty (function): function markPlayerDirty(snapshot: GameRuntimeSnapshot, userId: string): GameRuntimeSnapshot
|
|
1184
1394
|
- markServerDirty (function): function markServerDirty(snapshot: GameRuntimeSnapshot): GameRuntimeSnapshot
|
|
1395
|
+
- memoryWorldStore (function): function memoryWorldStore(seed?: HostedWorldRecord): HostedWorldStore — In-process {@link HostedWorldStore} for tests, local play, and the browser-tab P2P host.
|
|
1185
1396
|
- multiplayerAdapterKind (function): function multiplayerAdapterKind(multiplayer: unknown): string | null
|
|
1186
1397
|
- offline (function): function offline(): MultiplayerAdapterConfig
|
|
1187
|
-
- p2p (function): function p2p(config?: { topology?: MultiplayerTopology; room?: string }): MultiplayerAdapterConfig
|
|
1398
|
+
- p2p (function): function p2p(config?: { topology?: MultiplayerTopology; room?: string; authority?: MultiplayerAuthority }): MultiplayerAdapterConfig
|
|
1188
1399
|
- parseSaveAutoMs (function): function parseSaveAutoMs(auto: string): number
|
|
1400
|
+
- pullWorld (function): function pullWorld(session: HostedWorldSession, mirror: WorldMirror): void — Pull one replication step from a co-located {@link HostedWorldSession} into a {@link WorldMirror} — the no-network local path (host and client in one process). A fresh mirror pulls a baseline; thereafter it pulls a diff since its own revision. The same `sync(sinceRevision)` call is what a networked transport marshals.
|
|
1189
1401
|
- runCommand (function): function runCommand<TInput>(snapshot: GameRuntimeSnapshot, commands: Record<string, CommandDef<TInput>>, commandName: string, input: TInput, actorUserId: string): RunCommandResult
|
|
1190
1402
|
- saveScopeIncludesChunks (function): function saveScopeIncludesChunks(scope: SaveScope): boolean
|
|
1191
1403
|
- saveScopeIncludesPlayer (function): function saveScopeIncludesPlayer(scope: SaveScope): boolean
|
|
1192
1404
|
- servers (function): function servers(config: ServersPoolConfig): ServersPoolConfig
|
|
1193
|
-
- socketIo (function): function socketIo(config?: { topology?: MultiplayerTopology; url?: string }): MultiplayerAdapterConfig
|
|
1405
|
+
- socketIo (function): function socketIo(config?: { topology?: MultiplayerTopology; url?: string; authority?: MultiplayerAuthority }): MultiplayerAdapterConfig
|
|
1194
1406
|
- splitProfilePlayer (function): function splitProfilePlayer(player: RuntimePlayerRow): { persistent: RuntimePlayerRow; session: Record<string, unknown>; }
|
|
1195
|
-
- ws (function): function ws(config?: { topology?: MultiplayerTopology; url?: string }): MultiplayerAdapterConfig
|
|
1407
|
+
- ws (function): function ws(config?: { topology?: MultiplayerTopology; url?: string; authority?: MultiplayerAuthority }): MultiplayerAdapterConfig
|
|
1196
1408
|
|
|
1197
1409
|
### @jgengine/core/input/actionBindings
|
|
1198
1410
|
|
|
@@ -1201,13 +1413,13 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
1201
1413
|
- ActionBindingMap (type): type ActionBindingMap<TAction extends string, TCode extends string = string> = Record< TAction, ActionBinding<TCode> >
|
|
1202
1414
|
- ActionBindingModes (interface): interface ActionBindingModes<TCode extends string = string>
|
|
1203
1415
|
- ActionCodes (type): type ActionCodes<TCode extends string = string> = | readonly TCode[] | { hold?: readonly TCode[]; toggle?: readonly TCode[]; repeatMs?: number }
|
|
1204
|
-
- ActionCodesMap (type): type ActionCodesMap<TAction extends string = string, TCode extends string = string> = Record< TAction, ActionCodes<TCode> >
|
|
1416
|
+
- ActionCodesMap (type): type ActionCodesMap<TAction extends string = string, TCode extends string = string> = Record< TAction, ActionCodes<TCode> > — Maps each game action name to the input codes (hold/toggle keys, repeat rate) that trigger it.
|
|
1205
1417
|
- ActionStateBindingMap (type): type ActionStateBindingMap<TAction extends string, TCode extends string = string> = Record< TAction, ActionBindingConfig<TCode> >
|
|
1206
1418
|
- ActionStateTracker (interface): interface ActionStateTracker<TAction extends string>
|
|
1207
1419
|
- ShouldDispatchActionInput (interface): interface ShouldDispatchActionInput
|
|
1208
1420
|
- actionLabel (function): function actionLabel(map: ActionCodesMap, action: string): string | null — Short display label for an action's first bound code, or null when unbound.
|
|
1209
1421
|
- actionRepeatMs (function): function actionRepeatMs(codes: ActionCodes | undefined): number | undefined
|
|
1210
|
-
- bindingLabel (function): function bindingLabel(code: string): string
|
|
1422
|
+
- bindingLabel (function): function bindingLabel(code: string): string — Short display label for a raw key/button code (e.g. `"KeyW"` → `"W"`).
|
|
1211
1423
|
- bindingMatches (function): function bindingMatches<TCode extends string>(code: TCode, binding: ActionBinding<TCode>): boolean
|
|
1212
1424
|
- createActionStateTracker (function): function createActionStateTracker<TAction extends string, TCode extends string = string>(map: ActionStateBindingMap<TAction, TCode>): ActionStateTracker<TAction>
|
|
1213
1425
|
- hotbarSlotActionIndex (function): function hotbarSlotActionIndex(action: string): number | null — 0-based slot index for a hotbar-slot action name (`slot1`, `hotbarSlot1`), or null.
|
|
@@ -1236,6 +1448,7 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
1236
1448
|
- clampAxis (function): function clampAxis(value: number, range: AxisRange): number
|
|
1237
1449
|
- createAxisChannel (function): function createAxisChannel<TAxes extends string>(config: GenericAxisConfig<TAxes>): GenericAxisChannel<TAxes>
|
|
1238
1450
|
- rampToward (function): function rampToward(current: number, target: number, rate: number, dt: number): number
|
|
1451
|
+
- sampleAxisBindings (function): function sampleAxisBindings<TAxes extends string>(bindings: Record<TAxes, AxisBinding>, isDown: (code: string) => boolean, pointer?: PointerAxisState | null, ranges?: Partial<Record<TAxes, AxisRange>>): Record<TAxes, number> — Instantaneous (unsmoothed) analog sample of an axis binding map — the per-frame target an `AxisChannel` ramps toward, exposed as a pure read for `ctx.input.axis` and headless sampling. Each axis is its binding's positive-minus-negative held state (or its pointer source while a pointer is active), clamped to the axis range; axes without a listed range default to bipolar `[-1, 1]`.
|
|
1239
1452
|
|
|
1240
1453
|
### @jgengine/core/input/bindingOverrides
|
|
1241
1454
|
|
|
@@ -1316,7 +1529,7 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
1316
1529
|
- ContextTargetKind (type): type ContextTargetKind = "entity" | "object"
|
|
1317
1530
|
- ContextVerb (interface): interface ContextVerb — One right-click verb: a label plus the command it dispatches (walk-then-act supported by args).
|
|
1318
1531
|
- buildContextMenu (function): function buildContextMenu(input: BuildContextMenuInput): ContextMenu | null — Assemble a menu from a target's catalog verbs; null when the target lists none.
|
|
1319
|
-
- contextVerb (function): function contextVerb(label: string, command: string, args?: Record<string, unknown>): ContextVerb
|
|
1532
|
+
- contextVerb (function): function contextVerb(label: string, command: string, args?: Record<string, unknown>): ContextVerb — Builds a {@link ContextVerb} for a right-click menu entry.
|
|
1320
1533
|
- contextVerbInput (function): function contextVerbInput(menu: ContextMenu, verb: ContextVerb): Record<string, unknown> — Command input a chosen verb dispatches: the verb's own args, plus the target id and the world point, so a single handler can walk the actor to the target then perform it.
|
|
1321
1534
|
|
|
1322
1535
|
### @jgengine/core/interaction/proximityPrompt
|
|
@@ -1332,7 +1545,7 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
1332
1545
|
- ProximityPromptDisplay (type): type ProximityPromptDisplay = KeybindPromptDisplay | GaugePromptDisplay | LabelPromptDisplay
|
|
1333
1546
|
- command (function): function command(name: string, input?: unknown): PromptCommand
|
|
1334
1547
|
- gauge (function): function gauge(gaugeId: string): GaugePromptDisplay
|
|
1335
|
-
- keybind (function): function keybind(actionId: string): KeybindPromptDisplay
|
|
1548
|
+
- keybind (function): function keybind(actionId: string, label?: string): KeybindPromptDisplay
|
|
1336
1549
|
- label (function): function label(text: string): LabelPromptDisplay
|
|
1337
1550
|
- positionedPromptsEqual (function): function positionedPromptsEqual(a: PositionedPrompt, b: PositionedPrompt): boolean
|
|
1338
1551
|
- promptCommandsEqual (function): function promptCommandsEqual(a: PromptCommand | null, b: PromptCommand | null): boolean
|
|
@@ -1446,7 +1659,7 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
1446
1659
|
- RolledItem (interface): interface RolledItem
|
|
1447
1660
|
- RollerConfig (interface): interface RollerConfig
|
|
1448
1661
|
- createAffixRoller (function): function createAffixRoller(config: RollerConfig): AffixRoller
|
|
1449
|
-
- seededRng (function): function seededRng(seed: string | number): () => number
|
|
1662
|
+
- seededRng (function): function seededRng(seed: string | number): () => number — Deterministic pseudo-random generator seeded from a string or number — same seed, same sequence.
|
|
1450
1663
|
|
|
1451
1664
|
### @jgengine/core/item/durability
|
|
1452
1665
|
|
|
@@ -1507,7 +1720,7 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
1507
1720
|
|
|
1508
1721
|
- CHANGELOG (const): const CHANGELOG: Record<string, ChangelogEntry>
|
|
1509
1722
|
- ChangelogEntry (interface): interface ChangelogEntry
|
|
1510
|
-
- VERSION (const): const VERSION: "0.
|
|
1723
|
+
- VERSION (const): const VERSION: "0.10.0"
|
|
1511
1724
|
|
|
1512
1725
|
### @jgengine/core/movement/avatarGait
|
|
1513
1726
|
|
|
@@ -1585,10 +1798,20 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
1585
1798
|
- constrainStepToAxis (function): function constrainStepToAxis(stepX: number, stepZ: number, axis: "x" | "z"): MovementFrameStep — Zeroes the off-axis component so travel is locked to a single world axis (`PlayerMovementConfig.mode: "axis"`).
|
|
1586
1799
|
- createEmptyMovementKeys (function): function createEmptyMovementKeys(): MovementKeysState
|
|
1587
1800
|
- createPlayerMotionState (function): function createPlayerMotionState(): PlayerMotionState
|
|
1801
|
+
- nearbyObstacles (function): function nearbyObstacles(objects: readonly { position: readonly [number, number, number] }[], center: readonly [number, number, number], radius: number = OBSTACLE_GATHER_RADIUS): CollisionObstacle[] — Placed objects within `radius` (XZ) of `center`, as {@link CollisionObstacle}s to pre-filter for {@link resolveObstacleStep}.
|
|
1588
1802
|
- resolveMovementIntent (function): function resolveMovementIntent(keys: MovementKeysState, canMove: boolean): MovementIntent — Translate the set of held keys into an intent. When `canMove` is false (a menu is open, the world is paused) the avatar is fully idle so it never drifts behind an overlay.
|
|
1589
1803
|
- resolveObstacleStep (function): function resolveObstacleStep(current: readonly [number, number, number], stepX: number, stepZ: number, obstacles: readonly CollisionObstacle[], playerRadius: number = DEFAULT_OBSTACLE_PLAYER_RADIUS): MovementFrameStep — Resolve a horizontal step against nearby placed objects with classic axis-separated sliding (try X, then Z against the post-X position) so walking into a wall stops the blocked axis but keeps sliding along the other. Obstacles are treated as circle-vs-AABB in the X/Z plane, inflated by `playerRadius`; an obstacle whose vertical span misses the player's feet-to-head span is skipped entirely. Callers should pre-filter to nearby objects — this also early-exits per obstacle on horizontal distance.
|
|
1590
1804
|
- snapPositionToGrid (function): function snapPositionToGrid(x: number, z: number, cellSize: number): [number, number] — Snap a world position to its containing cell's center, matching the bounds-free form of `navGrid`'s and `tacticalGrid`'s `floor` + half-cell convention (`PlayerMovementConfig.mode: "grid"`).
|
|
1591
1805
|
|
|
1806
|
+
### @jgengine/core/movement/playerMovement
|
|
1807
|
+
|
|
1808
|
+
- PlayerMovementTuning (interface): interface PlayerMovementTuning — The resolved, per-world movement configuration {@link stepPlayerMovement} integrates against — the same inputs the shell FrameDriver used to read piecemeal, gathered into one struct so single-player and host movement run identical math.
|
|
1809
|
+
- forgetPlayerMovement (function): function forgetPlayerMovement(ctx: GameContext, userId: string): void — Drop a player's retained movement state (heading + kinematic body) — call on leave so a rejoin starts fresh instead of resuming stale velocity.
|
|
1810
|
+
- playerMovementHeading (function): function playerMovementHeading(ctx: GameContext, userId: string): number — One player's current heading (radians), integrated by {@link stepPlayerMovement} — the shell reads it back into its camera/aim yaw.
|
|
1811
|
+
- resolvePhysicsTuning (function): function resolvePhysicsTuning(physics: PhysicsConfig | undefined): MovementTuningOverrides | undefined — Maps a game's declared `physics` onto the movement controllers' tuning. `PhysicsConfig.gravity` is a signed world acceleration (negative points down), but the controllers integrate `velocityY -= gravityAcceleration * dt` and expect a positive downward magnitude — so gravity is negated here to keep down-pointing gravity pulling down.
|
|
1812
|
+
- resolvePlayerMovementTuning (function): function resolvePlayerMovementTuning(opts: { collision?: VoxelCollisionConfig; movement?: PlayerMovementConfig; physics?: PhysicsConfig; world?: WorldFeature; }): PlayerMovementTuning — Gather a game's collision/movement/physics/world config into a {@link PlayerMovementTuning} — call once per world; both the shell and a host pass the result to {@link stepPlayerMovement}.
|
|
1813
|
+
- stepPlayerMovement (function): function stepPlayerMovement(ctx: GameContext, userId: string, input: InputFrame, dt: number, tuning: PlayerMovementTuning, heading?: number): void — Integrate one player's movement for a tick from their held-input frame and commit the pose — the single genre-agnostic controller both the shell (its local player) and a host (each connected player in `onTick`) call, so single-player and server-authoritative movement are identical. Reads the player's controlled entity, terrain, scene solids, and pending motion impulses; writes the entity pose via `setPose`. Retains heading + kinematic body per `userId` on the `ctx`. Pass `heading` to override the internally-integrated yaw (the shell owns yaw for its camera); omit it and the controller turns from the frame's `turnLeft`/`turnRight` actions.
|
|
1814
|
+
|
|
1592
1815
|
### @jgengine/core/movement/poseState
|
|
1593
1816
|
|
|
1594
1817
|
- AimMode (type): type AimMode = "hip" | "ads"
|
|
@@ -1964,8 +2187,8 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
1964
2187
|
|
|
1965
2188
|
### @jgengine/core/random/rng
|
|
1966
2189
|
|
|
1967
|
-
- seededRng (function): function seededRng(seed: string | number): () => number
|
|
1968
|
-
- seededStreams (function): function seededStreams(seed: string | number): (stream: string) => () => number
|
|
2190
|
+
- seededRng (function): function seededRng(seed: string | number): () => number — Deterministic pseudo-random generator seeded from a string or number — same seed, same sequence.
|
|
2191
|
+
- seededStreams (function): function seededStreams(seed: string | number): (stream: string) => () => number — Derives independent, deterministic {@link seededRng} streams from one base seed, keyed by stream name.
|
|
1969
2192
|
|
|
1970
2193
|
### @jgengine/core/random/seedLink
|
|
1971
2194
|
|
|
@@ -1975,21 +2198,31 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
1975
2198
|
- seedFromUrl (function): function seedFromUrl(url: string, param = DEFAULT_SEED_PARAM): string | null
|
|
1976
2199
|
- withSeedParam (function): function withSeedParam(url: string, seed: string | number, param = DEFAULT_SEED_PARAM): string
|
|
1977
2200
|
|
|
2201
|
+
### @jgengine/core/render/postProcessing
|
|
2202
|
+
|
|
2203
|
+
- AoConfig (interface): interface AoConfig — Ground-truth ambient occlusion stage — darkens contact creases and cavities for depth.
|
|
2204
|
+
- BloomConfig (interface): interface BloomConfig — UnrealBloom stage — soft HDR glow around bright pixels (sun, glints, emissive).
|
|
2205
|
+
- GradeConfig (interface): interface GradeConfig — Final colour-grade stage: lift/gain/gamma, saturation, vignette, film grain — applied in display space after tone mapping.
|
|
2206
|
+
- PostProcessingConfig (interface): interface PostProcessingConfig — Declarative post-processing chain (RenderPass → AO → Bloom → tone-map output → Grade). Present on a game means the shell mounts an `EffectComposer` and owns the render; absent means the renderer draws directly (unchanged). Each stage is a config object, `false` to skip, or omitted for its default. Pure data — no three.js types leak into core.
|
|
2207
|
+
- ToneMappingMode (type): type ToneMappingMode = "aces" | "agx" | "reinhard" | "cineon" | "linear" | "none" — Renderer tone-mapping curve applied by the post chain's output stage.
|
|
2208
|
+
|
|
1978
2209
|
### @jgengine/core/runtime/adapter
|
|
1979
2210
|
|
|
1980
|
-
- MultiplayerAdapterConfig (type): type MultiplayerAdapterConfig = | { kind: "convex"; topology?: MultiplayerTopology } | { kind: "ws"; topology?: MultiplayerTopology; url?: string } | { kind: "socketio"; topology?: MultiplayerTopology; url?: string
|
|
2211
|
+
- MultiplayerAdapterConfig (type): type MultiplayerAdapterConfig = | { kind: "convex"; topology?: MultiplayerTopology; authority?: MultiplayerAuthority } | { kind: "ws"; topology?: MultiplayerTopology; url?: string; authority?: MultiplayerAuthority } | { kind: "socketio"; topology?: MultiplayerTopology; url?: string; authority?: Mult…
|
|
2212
|
+
- MultiplayerAuthority (type): type MultiplayerAuthority = "server" | "client" — Where the world simulation is authoritative. `"client"` (default) keeps the historical model — each client runs its own `onTick` and syncs only presence/feeds/chat. `"server"` opts into host-authoritative replication: the host runs the loop, and the shell mirrors the server's world into the local `ctx` instead of simulating locally.
|
|
1981
2213
|
- MultiplayerTopology (type): type MultiplayerTopology = "shared" | "lobbies" | "private"
|
|
1982
2214
|
- ServersPoolConfig (type): type ServersPoolConfig = { maxServers: number; slotsPerServer: number; minPlayersToStart?: number; adapter: MultiplayerAdapterConfig; }
|
|
1983
2215
|
- adapterOf (function): function adapterOf(multiplayer: unknown): MultiplayerAdapterConfig | null
|
|
1984
|
-
- convex (function): function convex(config?: { topology?: MultiplayerTopology }): MultiplayerAdapterConfig
|
|
1985
|
-
- fly (function): function fly(config: { app: string; topology?: MultiplayerTopology; path?: string }): MultiplayerAdapterConfig
|
|
1986
|
-
-
|
|
2216
|
+
- convex (function): function convex(config?: { topology?: MultiplayerTopology; authority?: MultiplayerAuthority }): MultiplayerAdapterConfig
|
|
2217
|
+
- fly (function): function fly(config: { app: string; topology?: MultiplayerTopology; path?: string; authority?: MultiplayerAuthority }): MultiplayerAdapterConfig
|
|
2218
|
+
- isServerAuthoritative (function): function isServerAuthoritative(multiplayer: unknown): boolean — True when the adapter opts into host-authoritative world replication (`authority: "server"`).
|
|
2219
|
+
- lan (function): function lan(config?: { topology?: MultiplayerTopology; port?: number; path?: string; authority?: MultiplayerAuthority; }): MultiplayerAdapterConfig
|
|
1987
2220
|
- multiplayerAdapterKind (function): function multiplayerAdapterKind(multiplayer: unknown): string | null
|
|
1988
2221
|
- offline (function): function offline(): MultiplayerAdapterConfig
|
|
1989
|
-
- p2p (function): function p2p(config?: { topology?: MultiplayerTopology; room?: string }): MultiplayerAdapterConfig
|
|
2222
|
+
- p2p (function): function p2p(config?: { topology?: MultiplayerTopology; room?: string; authority?: MultiplayerAuthority }): MultiplayerAdapterConfig
|
|
1990
2223
|
- servers (function): function servers(config: ServersPoolConfig): ServersPoolConfig
|
|
1991
|
-
- socketIo (function): function socketIo(config?: { topology?: MultiplayerTopology; url?: string }): MultiplayerAdapterConfig
|
|
1992
|
-
- ws (function): function ws(config?: { topology?: MultiplayerTopology; url?: string }): MultiplayerAdapterConfig
|
|
2224
|
+
- socketIo (function): function socketIo(config?: { topology?: MultiplayerTopology; url?: string; authority?: MultiplayerAuthority }): MultiplayerAdapterConfig
|
|
2225
|
+
- ws (function): function ws(config?: { topology?: MultiplayerTopology; url?: string; authority?: MultiplayerAuthority }): MultiplayerAdapterConfig
|
|
1993
2226
|
|
|
1994
2227
|
### @jgengine/core/runtime/cameraDirector
|
|
1995
2228
|
|
|
@@ -2008,6 +2241,7 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
2008
2241
|
|
|
2009
2242
|
- CatalogEntityRole (type): type CatalogEntityRole = "player" | "enemy" | "hostile" | "npc" | "vehicle"
|
|
2010
2243
|
- FloatTextInput (interface): interface FloatTextInput
|
|
2244
|
+
- GameAudio (interface): interface GameAudio — Reachable audio seam on `ctx.game`: `play`, `music`, and `resume` route through the `audio.play`/`audio.music`/`audio.resume` events the shell's audio engine listens on, so game code triggers sound without importing the shell.
|
|
2011
2245
|
- GameContext (interface): interface GameContext
|
|
2012
2246
|
- GameContextCards (interface): interface GameContextCards
|
|
2013
2247
|
- GameContextCommands (interface): interface GameContextCommands
|
|
@@ -2075,8 +2309,27 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
2075
2309
|
- topLeaderboardRows (function): function topLeaderboardRows(rows: Iterable<LeaderboardRow>, args: { gameId: string; stat: string; scope: LeaderboardScope; serverId?: string; limit?: number; }): LeaderboardEntry[]
|
|
2076
2310
|
- trimFeedEntries (function): function trimFeedEntries<T>(entries: T[], limit = FEED_RING_LIMIT): T[]
|
|
2077
2311
|
|
|
2312
|
+
### @jgengine/core/runtime/hostedGameRunner
|
|
2313
|
+
|
|
2314
|
+
- HostedGameRunner (interface): interface HostedGameRunner — The GameContext-loop equivalent of the pure-reducer `createGameHost`: one authoritative `createGameContext` per world, driven server-side. `onInit` runs once at construction; `onNewPlayer`/`onPlayerLeave` fire per join/leave; `tick` advances `onTick` then commits a revision. Clients pull a full {@link WorldSnapshot} baseline once, then per-tick {@link WorldDiff}s from their last revision. Games ship only normal GameContext code — the runner adds no per-game surface.
|
|
2315
|
+
- HostedGameRunnerOptions (interface): interface HostedGameRunnerOptions<TAssetRef extends ModelAssetRef, TMultiplayer> — Config for {@link createHostedGameRunner}: the game definition, its content lookup, and an optional host identity.
|
|
2316
|
+
- INPUT_COMMAND (const): const INPUT_COMMAND: "engine.input" — Reserved command name the authoritative host intercepts on the existing `runCommand` transport to route a client's {@link InputFrame} to `session.input`, so per-tick input needs no separate wire.
|
|
2317
|
+
- InputFrame (interface): interface InputFrame — One client's input for a tick — the semantic held-action set plus pointer state, the serializable, over-the-wire counterpart of {@link InputSnapshot} the host stores per connected player.
|
|
2318
|
+
- createHostedGameRunner (function): function createHostedGameRunner<TAssetRef extends ModelAssetRef, TMultiplayer>(options: HostedGameRunnerOptions<TAssetRef, TMultiplayer>): HostedGameRunner — Build a {@link HostedGameRunner} — one authoritative GameContext world driven server-side from the game's own loop.
|
|
2319
|
+
|
|
2320
|
+
### @jgengine/core/runtime/hostedWorldSession
|
|
2321
|
+
|
|
2322
|
+
- HostedWorldRecord (interface): interface HostedWorldRecord — One hosted world's persisted authoritative state — the unit a {@link HostedWorldStore} loads and saves.
|
|
2323
|
+
- HostedWorldSession (interface): interface HostedWorldSession — The stateful host substrate a long-lived backend binds (ws server, browser P2P host): a live {@link HostedGameRunner} loaded from a {@link HostedWorldStore}, auto-persisted on tick, serving each client a baseline then diffs. The stateless Convex path doesn't use this — it reconstructs a runner from the same store per invocation and diffs with `diffSnapshots`. Either way the store seam and the game code are identical.
|
|
2324
|
+
- HostedWorldSessionOptions (interface): interface HostedWorldSessionOptions<TAssetRef extends ModelAssetRef, TMultiplayer> — Config for {@link createHostedWorldSession}: the game, its persistence store, and the auto-save cadence.
|
|
2325
|
+
- HostedWorldStore (interface): interface HostedWorldStore — Narrow persistence seam for a hosted world — the {@link HostedWorldRecord} counterpart of `HostPersistence`. Backends implement it (memory/file/sql/convex); the session never names one. A stateful host loads once and saves on a cadence; a stateless host reconstructs from `load()` each invocation.
|
|
2326
|
+
- HostedWorldSync (type): type HostedWorldSync = | { kind: "baseline"; revision: number; snapshot: WorldSnapshot } | { kind: "diff"; diff: WorldDiff } — A client replication pull: a full baseline (first sync / fell behind) or a diff since the client's cursor.
|
|
2327
|
+
- createHostedWorldSession (function): function createHostedWorldSession<TAssetRef extends ModelAssetRef, TMultiplayer>(options: HostedWorldSessionOptions<TAssetRef, TMultiplayer>): HostedWorldSession — Build a {@link HostedWorldSession} — a live runner loaded from a {@link HostedWorldStore} and auto-persisted on tick.
|
|
2328
|
+
- memoryWorldStore (function): function memoryWorldStore(seed?: HostedWorldRecord): HostedWorldStore — In-process {@link HostedWorldStore} for tests, local play, and the browser-tab P2P host.
|
|
2329
|
+
|
|
2078
2330
|
### @jgengine/core/runtime/inputSnapshot
|
|
2079
2331
|
|
|
2332
|
+
- InputFrame (interface): interface InputFrame — One client's input for a tick — the semantic held-action set plus pointer state, the serializable, over-the-wire counterpart of {@link InputSnapshot} the host stores per connected player.
|
|
2080
2333
|
- InputSnapshot (interface): interface InputSnapshot
|
|
2081
2334
|
- createInputSnapshot (function): function createInputSnapshot(): InputSnapshot
|
|
2082
2335
|
|
|
@@ -2085,6 +2338,7 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
2085
2338
|
- MotionIntentBatch (interface): interface MotionIntentBatch
|
|
2086
2339
|
- MotionIntents (interface): interface MotionIntents — Seam for game code to reach the motion the shell's FrameDriver otherwise owns privately (#162.4). Game code calls `impulse`, `pushHorizontal`, `setVerticalVelocity`, and/or `setY` from `onTick` or commands; the shell calls `takePending()` once per frame, before integrating gravity, to drain what accumulated. `setY` wins over physics for that frame; impulses add to the velocity the driver is about to integrate; a later `setVerticalVelocity` replaces that velocity outright. Horizontal pushes compose with the walk controller (#282.4): they add to its horizontal velocity and decay naturally as it re-blends toward input — knockback, dashes, explosion shoves without raw `setPose` offsets.
|
|
2087
2340
|
- applyHorizontalImpulses (function): function applyHorizontalImpulses(velocityX: number, velocityZ: number, batch: MotionIntentBatch | null): readonly [number, number] — Fold a batch's horizontal pushes into a controller's velocity pair — shared by the walk and voxel drivers.
|
|
2341
|
+
- applyMotionImpulses (function): function applyMotionImpulses(currentVelocity: number, batch: MotionIntentBatch | null): number — Fold a batch's vertical impulses into a controller's velocity, then apply an outright `setVerticalVelocity` override — the vertical counterpart of {@link applyHorizontalImpulses}.
|
|
2088
2342
|
- createMotionIntents (function): function createMotionIntents(): MotionIntents
|
|
2089
2343
|
|
|
2090
2344
|
### @jgengine/core/runtime/persistenceScope
|
|
@@ -2159,11 +2413,42 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
2159
2413
|
- VisibilityPoint (interface): interface VisibilityPoint — World-space point; `z` is optional for 2D adapters.
|
|
2160
2414
|
- VisibilitySystem (class): class VisibilitySystem — Engine-level visibility and asset-residency policy.
|
|
2161
2415
|
|
|
2416
|
+
### @jgengine/core/runtime/worldChannel
|
|
2417
|
+
|
|
2418
|
+
- WorldClientFrame (type): type WorldClientFrame = | { t: "join"; isNew: boolean } | { t: "leave" } | { t: "command"; name: string; input: unknown } | { t: "input"; frame: InputFrame } — Client→host frame: a player's session verbs. What a transport marshals upstream.
|
|
2419
|
+
- WorldClientLink (interface): interface WorldClientLink — The client end of a {@link WorldHost} connection: applies server frames to a local `ctx`, sends session verbs upstream.
|
|
2420
|
+
- WorldHost (interface): interface WorldHost — The transport-agnostic host: fans one {@link HostedWorldSession} out to many connections, each tracking its own revision cursor so a joiner gets a baseline and everyone else gets diffs. A ws server, a Convex function, or an in-process loopback all drive the same shape — decode a frame → `connection.receive`; after `session.tick` → `broadcast`. No wire format is assumed; frames are plain data a transport serializes however it likes.
|
|
2421
|
+
- WorldHostConnection (interface): interface WorldHostConnection — One client's link on the host side: routes its upstream frames into the shared session, pushes it sync frames.
|
|
2422
|
+
- WorldServerFrame (type): type WorldServerFrame = | { t: "baseline"; revision: number; snapshot: WorldSnapshot } | { t: "diff"; diff: WorldDiff } — Host→client frame: the full baseline a joiner needs, then per-tick diffs. What a transport marshals downstream.
|
|
2423
|
+
- createWorldClientLink (function): function createWorldClientLink(ctx: Pick<GameContext, "hydrate">, send: (frame: WorldClientFrame) => void): WorldClientLink — Build a {@link WorldClientLink} — the client end that mirrors host frames into `ctx` and sends session verbs upstream.
|
|
2424
|
+
- createWorldHost (function): function createWorldHost(session: HostedWorldSession): WorldHost — Build a {@link WorldHost} fanning one {@link HostedWorldSession} out to many cursor-tracked connections.
|
|
2425
|
+
|
|
2426
|
+
### @jgengine/core/runtime/worldMirror
|
|
2427
|
+
|
|
2428
|
+
- WorldMirror (interface): interface WorldMirror — The client end of host-authoritative replication: folds a host's baseline + {@link WorldDiff} stream onto a local {@link GameContext}. It keeps the last full {@link WorldSnapshot}, advances it with each diff, and pushes the result through `ctx.hydrate` — so the client mirrors exactly the subsystems its own game opted into and silently ignores host modules it lacks. This is the inverse of a {@link HostedWorldSession}; the transport in between (loopback, ws, Convex) is irrelevant.
|
|
2429
|
+
- createWorldMirror (function): function createWorldMirror(ctx: Pick<GameContext, "hydrate">): WorldMirror — Build a {@link WorldMirror} that replicates a host's baseline + diff stream onto `ctx` via `ctx.hydrate`.
|
|
2430
|
+
- pullWorld (function): function pullWorld(session: HostedWorldSession, mirror: WorldMirror): void — Pull one replication step from a co-located {@link HostedWorldSession} into a {@link WorldMirror} — the no-network local path (host and client in one process). A fresh mirror pulls a baseline; thereafter it pulls a diff since its own revision. The same `sync(sinceRevision)` call is what a networked transport marshals.
|
|
2431
|
+
|
|
2432
|
+
### @jgengine/core/runtime/worldReplication
|
|
2433
|
+
|
|
2434
|
+
- WorldDiff (interface): interface WorldDiff — A revision-stamped delta over a {@link WorldSnapshot}. The host sends one per tick to each client, carrying only what changed since that client's last acknowledged revision — entity/stat/store deltas plus whole snapshots of any other opted-in module (feed, leaderboard, chat, …) that changed. Fold it onto a prior baseline with {@link applyWorldDiff}.
|
|
2435
|
+
- WorldReplicator (type): type WorldReplicator = ReturnType<typeof createWorldReplicator> — The stateful diff tracker returned by {@link createWorldReplicator}: `commit()`, `diff(sinceRevision)`, `revision()`.
|
|
2436
|
+
- applyWorldDiff (function): function applyWorldDiff(baseline: WorldSnapshot, diff: WorldDiff): WorldSnapshot — Fold a {@link WorldDiff} onto a prior {@link WorldSnapshot} baseline, returning the next full snapshot — the client-side inverse of {@link createWorldReplicator}. Pure data in, pure data out: upserts changed entities, stats and store keys, drops the removed ones, and replaces changed module snapshots wholesale.
|
|
2437
|
+
- createWorldReplicator (function): function createWorldReplicator(takeSnapshot: () => WorldSnapshot): { commit: () => number; diff: (sinceRevision: number) => WorldDiff; revision: () => number; } — Turns successive full {@link WorldSnapshot}s into per-client {@link WorldDiff}s. Each `commit()` re-reads the world, stamps every item that changed with the new revision, and remembers removals; `diff(sinceRevision)` then replays exactly the items stamped after that revision. Everything the tracker holds is JSON — the same shape that rides the wire — so a diff is inherently serializable. Change-detection is a full re-serialize per commit; dirty-hint acceleration is a later optimization behind the same seam.
|
|
2438
|
+
- diffSnapshots (function): function diffSnapshots(prev: WorldSnapshot, next: WorldSnapshot, revision: number): WorldDiff — Diff two full {@link WorldSnapshot}s directly, stamping the result at `revision` — the stateless counterpart of {@link createWorldReplicator} for hosts that persist snapshots rather than keep a live tracker (Convex reconstructs per invocation). `applyWorldDiff(prev, diffSnapshots(prev, next, r))` reproduces `next`.
|
|
2439
|
+
|
|
2440
|
+
### @jgengine/core/runtime/worldSnapshot
|
|
2441
|
+
|
|
2442
|
+
- SnapshotModule (interface): interface SnapshotModule<T = unknown> — The replication seam for host-authoritative shared worlds: the opt-in feature manifest *is* the replication schema. Each live subsystem a game opts into registers a {@link SnapshotModule} keyed by name; the host serializes exactly the registered set into a {@link WorldSnapshot} and a client hydrates the same keys back. Adding a replicated subsystem is a registration, never a new branch.
|
|
2443
|
+
- WorldSnapshot (type): type WorldSnapshot = Record<string, unknown> — Full world baseline keyed by {@link SnapshotModule.key} — one entry per opted-in subsystem.
|
|
2444
|
+
- applyWorldSnapshot (function): function applyWorldSnapshot(modules: readonly SnapshotModule[], snapshot: WorldSnapshot): void — Hydrate every registered module whose key is present in `snapshot`; keys absent from it are left untouched.
|
|
2445
|
+
- composeWorldSnapshot (function): function composeWorldSnapshot(modules: readonly SnapshotModule[]): WorldSnapshot — Serialize every registered module into one keyed baseline — the host→client full-world send.
|
|
2446
|
+
|
|
2162
2447
|
### @jgengine/core/scene/assetCatalog
|
|
2163
2448
|
|
|
2164
2449
|
- AssetCatalog (interface): interface AssetCatalog<TMeta extends ModelAssetRef = ModelAssetRef>
|
|
2165
2450
|
- ModelAssetRef (interface): interface ModelAssetRef
|
|
2166
|
-
- ModelDims (interface): interface ModelDims
|
|
2451
|
+
- ModelDims (interface): interface ModelDims — Measured horizontal footprint, footprint center, and lowest Y of a model in model space.
|
|
2167
2452
|
- createAssetCatalog (function): function createAssetCatalog<TMeta extends ModelAssetRef = ModelAssetRef>(): AssetCatalog<TMeta>
|
|
2168
2453
|
|
|
2169
2454
|
### @jgengine/core/scene/assetPreload
|
|
@@ -2203,6 +2488,8 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
2203
2488
|
- ColliderDef (interface): interface ColliderDef
|
|
2204
2489
|
- ColliderPurpose (type): type ColliderPurpose = "physical" | "damage"
|
|
2205
2490
|
- ColliderShape (type): type ColliderShape = | { kind: "sphere"; radius: number; offset?: EntityPosition } | { kind: "aabb"; halfExtents: EntityPosition; offset?: EntityPosition }
|
|
2491
|
+
- DEFAULT_ENTITY_BODY_HALF_EXTENTS (const): const DEFAULT_ENTITY_BODY_HALF_EXTENTS: EntityPosition — Matches the shell's fallback actor capsule (~0.7m wide, feet at y=0 to head at ~1.8m).
|
|
2492
|
+
- DEFAULT_ENTITY_BODY_OFFSET (const): const DEFAULT_ENTITY_BODY_OFFSET: EntityPosition — Entity-local center of the default body hitbox (half its height above the feet).
|
|
2206
2493
|
- DEFAULT_ENTITY_HIT_RADIUS (const): const DEFAULT_ENTITY_HIT_RADIUS: 0.5
|
|
2207
2494
|
- DEFAULT_OBJECT_HALF_EXTENTS (const): const DEFAULT_OBJECT_HALF_EXTENTS: EntityPosition
|
|
2208
2495
|
- EntityColliderSet (interface): interface EntityColliderSet
|
|
@@ -2212,6 +2499,7 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
2212
2499
|
- defaultEntityColliders (function): function defaultEntityColliders(): EntityColliderSet
|
|
2213
2500
|
- defaultObjectColliders (function): function defaultObjectColliders(halfExtents: EntityPosition = DEFAULT_OBJECT_HALF_EXTENTS): EntityColliderSet
|
|
2214
2501
|
- resolveColliders (function): function resolveColliders(set: EntityColliderSet | null | undefined): ResolvedCollider[]
|
|
2502
|
+
- scaledObjectColliders (function): function scaledObjectColliders(scale: readonly [number, number, number]): EntityColliderSet — Blocking physical body derived from an object's rendered scale: a grounded box spanning the visual (base at y=0, matching the shell's fallback mesh).
|
|
2215
2503
|
- worldOffset (function): function worldOffset(local: EntityPosition | undefined, position: EntityPosition, rotationY: number): EntityPosition
|
|
2216
2504
|
|
|
2217
2505
|
### @jgengine/core/scene/entityStats
|
|
@@ -2225,11 +2513,14 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
2225
2513
|
- applyPoolDelta (function): function applyPoolDelta(map: StatValueMap, statId: string, amount: number): PoolDeltaResult
|
|
2226
2514
|
- createEntityStatsApi (function): function createEntityStatsApi(resolve: (instanceId: string) => StatValueMap | undefined): EntityStatsApi
|
|
2227
2515
|
- getStatValue (function): function getStatValue(map: StatValueMap, statId: string): StatValue | null
|
|
2516
|
+
- hydrateEntityStats (function): function hydrateEntityStats(store: Map<string, StatValueMap>, data: Record<string, StatValueMap>): void — Replace a live stat store with deep copies of a snapshot, clearing entities absent from it.
|
|
2228
2517
|
- seedStatValues (function): function seedStatValues(catalogStats: StatCatalog): StatValueMap
|
|
2229
2518
|
- setStatValue (function): function setStatValue(map: StatValueMap, statId: string, patch: StatValuePatch): StatValueMap
|
|
2519
|
+
- snapshotEntityStats (function): function snapshotEntityStats(store: ReadonlyMap<string, StatValueMap>): Record<string, StatValueMap> — Deep-copy the per-entity stat maps into a serializable record — the transport counterpart of {@link hydrateEntityStats}.
|
|
2230
2520
|
|
|
2231
2521
|
### @jgengine/core/scene/entityStore
|
|
2232
2522
|
|
|
2523
|
+
- EntityBlackboard (interface): interface EntityBlackboard — Per-entity scratch and cooldown timers, auto-cleared on despawn (#533.8) — the home for AI state (next-shot-at, alert level) that would otherwise be smuggled through the serializable `meta` or a hand-pruned module-level map. Keys share one namespace per entity: `arm`/`ready`/`remaining` store a deadline under a key, `get`/`set` store arbitrary scratch.
|
|
2233
2524
|
- EntityMovement (interface): interface EntityMovement
|
|
2234
2525
|
- EntityPose (interface): interface EntityPose
|
|
2235
2526
|
- EntityPosition (type): type EntityPosition = readonly [number, number, number]
|
|
@@ -2630,8 +2921,8 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
2630
2921
|
- FallingGridSnapshot (interface): interface FallingGridSnapshot<TCell>
|
|
2631
2922
|
- GravityIntervalConfig (interface): interface GravityIntervalConfig
|
|
2632
2923
|
- LockState (interface): interface LockState — Elapsed grounded time for the lock-delay state machine. `null` means airborne / not timing.
|
|
2633
|
-
- createFallingGrid (function): function createFallingGrid<TCell>(config: FallingGridConfig): FallingGrid<TCell> — A game-agnostic cell grid for gravity-driven falling-block mechanics: bounded occupancy, footprint placement/collision, line-clear collapse, column-gravity settle (for match-3/sand style cascades), and a pure lock-delay helper. Extracted from
|
|
2634
|
-
- gravityIntervalMs (function): function gravityIntervalMs(level: number, config: GravityIntervalConfig = {}): number — Gravity tick interval for a given level: linear speed-up with a floor, generalizing
|
|
2924
|
+
- createFallingGrid (function): function createFallingGrid<TCell>(config: FallingGridConfig): FallingGrid<TCell> — A game-agnostic cell grid for gravity-driven falling-block mechanics: bounded occupancy, footprint placement/collision, line-clear collapse, column-gravity settle (for match-3/sand style cascades), and a pure lock-delay helper. Extracted from falling-block tetris logic so any falling-block game can adopt it instead of reimplementing the grid.
|
|
2925
|
+
- gravityIntervalMs (function): function gravityIntervalMs(level: number, config: GravityIntervalConfig = {}): number — Gravity tick interval for a given level: linear speed-up with a floor, generalizing a falling-block `gravityInterval` (`max(0.05, 0.8 - level * 0.07)` seconds) to milliseconds with configurable base/step/floor.
|
|
2635
2926
|
|
|
2636
2927
|
### @jgengine/core/tactics/predictiveQuery
|
|
2637
2928
|
|
|
@@ -2781,6 +3072,33 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
2781
3072
|
- TurnState (interface): interface TurnState
|
|
2782
3073
|
- createTurnLoop (function): function createTurnLoop<TAction = unknown>(config: TurnLoopConfig): TurnLoop<TAction>
|
|
2783
3074
|
|
|
3075
|
+
### @jgengine/core/ui/gameLayout
|
|
3076
|
+
|
|
3077
|
+
- DetectCollisionsOptions (interface): interface DetectCollisionsOptions — Options for `detectLayoutCollisions`.
|
|
3078
|
+
- GameLayoutMode (type): type GameLayoutMode = "desktop-wide" | "desktop-compact" | "mobile-landscape" | "mobile-portrait" — The explicit composition mode a game renders for — not a scaled desktop layout.
|
|
3079
|
+
- GameViewportLayout (interface): interface GameViewportLayout — The shared live geometry the engine allocates once and every UI subsystem reads.
|
|
3080
|
+
- HudPriority (type): type HudPriority = "critical" | "secondary" | "tertiary" — Gameplay-importance tier of a HUD element.
|
|
3081
|
+
- Insets (interface): interface Insets — Edge insets in CSS pixels (safe areas, reservations).
|
|
3082
|
+
- LayoutCollision (interface): interface LayoutCollision — One detected forbidden/warned overlap between two regions.
|
|
3083
|
+
- LayoutCollisionPolicy (type): type LayoutCollisionPolicy = "forbid" | "allow" | "warn" — How a region participates in collision reporting.
|
|
3084
|
+
- LayoutModeInput (interface): interface LayoutModeInput — Inputs to `resolveLayoutMode`.
|
|
3085
|
+
- LayoutRect (interface): interface LayoutRect — Axis-aligned rectangle in CSS pixels (origin top-left). Structurally compatible with a `DOMRect`'s edge fields.
|
|
3086
|
+
- LayoutRegion (interface): interface LayoutRegion — A physical rectangle a UI subsystem occupies, published to the shared registry.
|
|
3087
|
+
- LayoutRegionKind (type): type LayoutRegionKind = "hud" | "control" | "system" | "screen" — What kind of UI a registered region belongs to.
|
|
3088
|
+
- MobileHudBehavior (type): type MobileHudBehavior = | "persistent" | "compact" | "icon" | "transient" | "hidden" | "sheet" | "modal" — How a HUD element adapts on phones.
|
|
3089
|
+
- ZERO_INSETS (const): const ZERO_INSETS: Insets — All-zero insets.
|
|
3090
|
+
- computeGameplayRect (function): function computeGameplayRect(viewport: LayoutRect, safeArea: Insets, reserved: readonly LayoutRect[]): LayoutRect — The usable gameplay rectangle after reserving physical zones for touch controls and system UI. Starts from the safe-area-inset viewport and carves each reserved zone from whichever edge it hugs.
|
|
3091
|
+
- detectLayoutCollisions (function): function detectLayoutCollisions(regions: readonly LayoutRegion[], options?: DetectCollisionsOptions): LayoutCollision[] — Every forbidden or warned rectangle intersection across the registered regions. A pair is skipped when either side's policy is `allow`, when either opts into the other via `allowOverlapWith`, when they share a `collisionGroup`, or when the overlap is below `minArea`.
|
|
3092
|
+
- formatLayoutCollisions (function): function formatLayoutCollisions(collisions: readonly LayoutCollision[]): string — Human-readable diagnostic block for a set of collisions (empty string when there are none).
|
|
3093
|
+
- intersects (function): function intersects(a: LayoutRect, b: LayoutRect): boolean — Do the two rectangles share any interior area? Touching edges do not count.
|
|
3094
|
+
- isMobileMode (function): function isMobileMode(mode: GameLayoutMode): boolean — Whether a mode is one of the phone modes.
|
|
3095
|
+
- orientationOf (function): function orientationOf(width: number, height: number): LayoutOrientation — Portrait when taller than wide, else landscape.
|
|
3096
|
+
- overlapArea (function): function overlapArea(a: LayoutRect, b: LayoutRect): number — Overlapping area in px²; 0 when the rectangles don't intersect.
|
|
3097
|
+
- rectArea (function): function rectArea(rect: LayoutRect): number — Area of a rect in px².
|
|
3098
|
+
- rectHeight (function): function rectHeight(rect: LayoutRect): number — Height of a rect (clamped to ≥0).
|
|
3099
|
+
- rectWidth (function): function rectWidth(rect: LayoutRect): number — Width of a rect (clamped to ≥0).
|
|
3100
|
+
- resolveLayoutMode (function): function resolveLayoutMode(input: LayoutModeInput): GameLayoutMode — Resolve the explicit composition mode. A mobile layout is not a scaled desktop layout: a coarse-pointer device on a game that supports mobile is always a `mobile-*` mode, split by live orientation; everything else is a desktop mode split by width.
|
|
3101
|
+
|
|
2784
3102
|
### @jgengine/core/ui/hudLayout
|
|
2785
3103
|
|
|
2786
3104
|
- HUD_ANCHOR_FRACTIONS (const): const HUD_ANCHOR_FRACTIONS: Record<HudAnchor, { fx: number; fy: number }>
|
|
@@ -2815,6 +3133,27 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
2815
3133
|
- rectOverflow (function): function rectOverflow(rect: HudRect, viewport: HudSize, tolerance = 1.5): Omit<HudOverflow, "id"> | null — Non-null when any edge of `rect` falls outside `viewport` by more than `tolerance` px; each field is the overflow distance past that edge (0 when inside).
|
|
2816
3134
|
- resolveHudFit (function): function resolveHudFit(config: HudViewportConfig | undefined, mobile: boolean): Required<HudFitConfig>
|
|
2817
3135
|
|
|
3136
|
+
### @jgengine/core/ui/orientation
|
|
3137
|
+
|
|
3138
|
+
- GameOrientation (type): type GameOrientation = "landscape" | "portrait" | GameOrientationConfig — The `orientation` field of a game definition. Legacy shorthand `"landscape"` / `"portrait"` stays advisory (a dismissible rotate hint, never a gate). The object form `{ mobile: "landscape-required" }` is the strict contract that blocks gameplay behind the rotate screen until the device is turned.
|
|
3139
|
+
- GameOrientationConfig (interface): interface GameOrientationConfig — Object form of the `orientation` field: the strict per-platform contract.
|
|
3140
|
+
- LayoutOrientation (type): type LayoutOrientation = "portrait" | "landscape" — A concrete device orientation.
|
|
3141
|
+
- MobileOrientationRule (type): type MobileOrientationRule = | "any" | "portrait" | "landscape" | "portrait-required" | "landscape-required" | "unsupported" — The game-level orientation contract. A game declares how it supports phone orientations; the shell resolves that against the live orientation to decide whether to show a dismissible rotate hint (advisory) or a full-screen gate that blocks gameplay until the device is turned (required).
|
|
3142
|
+
- OrientationRequirement (interface): interface OrientationRequirement — The resolved orientation contract for a platform: whether it's supported, gated, or merely preferred.
|
|
3143
|
+
- orientationGateActive (function): function orientationGateActive(requirement: OrientationRequirement, liveOrientation: LayoutOrientation): boolean — The rotate gate blocks gameplay: a hard requirement (or `unsupported`) the live orientation doesn't satisfy.
|
|
3144
|
+
- orientationHintActive (function): function orientationHintActive(requirement: OrientationRequirement, liveOrientation: LayoutOrientation): boolean — An advisory rotate hint applies: a preference (not a hard gate) the live orientation doesn't satisfy.
|
|
3145
|
+
- resolveMobileOrientationRule (function): function resolveMobileOrientationRule(orientation: GameOrientation | undefined): MobileOrientationRule — Normalize the `orientation` field (legacy string or object) to a single mobile rule.
|
|
3146
|
+
- resolveOrientationRequirement (function): function resolveOrientationRequirement(orientation: GameOrientation | undefined, platform: "mobile" | "desktop"): OrientationRequirement — Resolve the game's orientation declaration into a concrete requirement for a platform. Desktop is always unconstrained.
|
|
3147
|
+
|
|
3148
|
+
### @jgengine/core/ui/swingTimer
|
|
3149
|
+
|
|
3150
|
+
- SWING_EDGE_EPSILON (const): const SWING_EDGE_EPSILON: 0.0001 — Threshold above which a timer jump is treated as a fresh swing (period reset).
|
|
3151
|
+
- SwingLabelKind (type): type SwingLabelKind = "ready" | "seconds" — Whether the bar shows a "ready" label or the seconds-remaining countdown.
|
|
3152
|
+
- SwingPlayerInput (interface): interface SwingPlayerInput — The local player's auto-attack state for one frame. `swingTimer` counts DOWN in seconds to 0 (= a swing lands).
|
|
3153
|
+
- SwingTargetInput (interface): interface SwingTargetInput — The current target, or the fields the bar needs from it.
|
|
3154
|
+
- SwingTimerState (interface): interface SwingTimerState — One frame of swing-bar state plus the cursor (`nextPeriod`/`nextTimer`) the caller carries back into the next call.
|
|
3155
|
+
- swingTimerState (function): function swingTimerState(player: SwingPlayerInput, target: SwingTargetInput | null, prevPeriod: number, prevTimer: number): SwingTimerState — Pure swing-timer bar state — no hidden state, no clock, no DOM. The caller threads `prevPeriod`/`prevTimer` back each frame. The period is recovered on the reset edge (when `swingTimer` jumps up = a new swing began) as `max(swingTimer, weapon.speed)`, so the fill is correct even without knowing the weapon's exact cadence. Hidden unless auto-attacking a live, non-object target.
|
|
3156
|
+
|
|
2818
3157
|
### @jgengine/core/visibility/assetStreaming
|
|
2819
3158
|
|
|
2820
3159
|
- AssetLoadResult (interface): interface AssetLoadResult
|
|
@@ -3020,6 +3359,7 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
3020
3359
|
- EnvironmentCounts (interface): interface EnvironmentCounts
|
|
3021
3360
|
- EnvironmentSummary (interface): interface EnvironmentSummary
|
|
3022
3361
|
- IslandSummary (interface): interface IslandSummary extends TerrainSummary
|
|
3362
|
+
- RoadSummary (interface): interface RoadSummary — One road descriptor's resolved footprint: vertex count, width, and total centerline length.
|
|
3023
3363
|
- StructureSummary (interface): interface StructureSummary
|
|
3024
3364
|
- TerrainHeightStats (interface): interface TerrainHeightStats
|
|
3025
3365
|
- TerrainSummary (interface): interface TerrainSummary
|
|
@@ -3031,6 +3371,7 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
3031
3371
|
|
|
3032
3372
|
### @jgengine/core/world/features
|
|
3033
3373
|
|
|
3374
|
+
- BiomeBand (interface): interface BiomeBand — A z-ordered ground palette zone — the linear-boundary counterpart to the radial `materialRegions`. Adjacent bands cross-fade into each other across a `fade`-wide window centered on the midpoint z between their centers, so a multi-biome world (vale → marsh → peaks along z) blends its ground color instead of hard-switching. Order the list by ascending `z`.
|
|
3034
3375
|
- BiomesWorldConfig (interface): interface BiomesWorldConfig extends WorldGridConfig
|
|
3035
3376
|
- BuildingEnvironmentConfig (interface): interface BuildingEnvironmentConfig
|
|
3036
3377
|
- BuildingEnvironmentDescriptor (type): type BuildingEnvironmentDescriptor = { kind: "building" } & Required< Pick<BuildingEnvironmentConfig, "count" | "footprint" | "stories" | "storyHeight" | "spacing" | "style"> > & Pick<BuildingEnvironmentConfig, "seed" | "position" | "palette">
|
|
@@ -3049,44 +3390,52 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
3049
3390
|
- PlotsWorldConfig (interface): interface PlotsWorldConfig extends WorldGridConfig
|
|
3050
3391
|
- RainEnvironmentConfig (interface): interface RainEnvironmentConfig
|
|
3051
3392
|
- RainEnvironmentDescriptor (type): type RainEnvironmentDescriptor = { kind: "rain" } & Required< Pick<RainEnvironmentConfig, "area" | "density" | "speed" | "dropLength" | "wind" | "color"> >
|
|
3393
|
+
- RoadEnvironmentConfig (interface): interface RoadEnvironmentConfig — Config for {@link road}: a flat asphalt ribbon draped over the terrain along a centerline.
|
|
3394
|
+
- RoadEnvironmentDescriptor (type): type RoadEnvironmentDescriptor = { kind: "road" } & Required< Pick<RoadEnvironmentConfig, "path" | "width" | "color" | "markings" | "markingColor" | "elevation"> > & { /** Resolved sidewalk band, or `false` when the road has none. */ sidewalk: { width: number; color: string } | false; } — Resolved road descriptor produced by {@link road} and rendered by the shell environment scene.
|
|
3052
3395
|
- SkyEnvironmentConfig (interface): interface SkyEnvironmentConfig
|
|
3053
3396
|
- SkyEnvironmentDescriptor (type): type SkyEnvironmentDescriptor = { kind: "sky" } & Required< Pick<SkyEnvironmentConfig, "preset" | "timeOfDay"> > & Omit<SkyEnvironmentConfig, "preset" | "timeOfDay">
|
|
3054
3397
|
- SnowEnvironmentConfig (interface): interface SnowEnvironmentConfig
|
|
3055
3398
|
- SnowEnvironmentDescriptor (type): type SnowEnvironmentDescriptor = { kind: "snow" } & Required< Pick<SnowEnvironmentConfig, "area" | "density" | "speed" | "flakeSize" | "drift" | "wind" | "color"> >
|
|
3056
3399
|
- StructureEnvironmentDescriptor (type): type StructureEnvironmentDescriptor = BuildingEnvironmentDescriptor
|
|
3400
|
+
- TerrainCircleRegion (interface): interface TerrainCircleRegion extends TerrainRegionStyle — A circular palette zone painted over the base terrain palette — snow caps, ash wastes, spawn circles.
|
|
3057
3401
|
- TerrainColors (interface): interface TerrainColors
|
|
3402
|
+
- TerrainDetailConfig (interface): interface TerrainDetailConfig — Procedural detail-surface layer for terrain: a noise-driven shader that keeps the biome-tinted base ground (from `colors`/`biomeBands`) and blends distinct rock, sand, and snow over it by slope, height, and waterline — turning a flat vertex-colour surface into varied, textured-reading ground with no image assets.
|
|
3058
3403
|
- TerrainEnvironmentConfig (interface): interface TerrainEnvironmentConfig
|
|
3059
3404
|
- TerrainEnvironmentDescriptor (type): type TerrainEnvironmentDescriptor = { kind: "terrain" } & Required< Pick<TerrainEnvironmentConfig, "bounds" | "height"> > & Omit<TerrainEnvironmentConfig, "bounds" | "height">
|
|
3060
3405
|
- TerrainFlattenMask (interface): interface TerrainFlattenMask
|
|
3061
3406
|
- TerrainIslandConfig (interface): interface TerrainIslandConfig extends TerrainEnvironmentConfig — A bounded terrain patch floating at its own altitude — sky islands, arena platforms, split landmasses with void between.
|
|
3062
3407
|
- TerrainIslandDescriptor (type): type TerrainIslandDescriptor = Omit<TerrainEnvironmentDescriptor, "kind"> & { kind: "island"; origin: EnvironmentVec2; }
|
|
3063
3408
|
- TerrainMaterial (type): type TerrainMaterial = "grass" | "sand" | "snow" | "rock" | "ash" | "highland" | "slate"
|
|
3064
|
-
- TerrainMaterialRegion (
|
|
3409
|
+
- TerrainMaterialRegion (type): type TerrainMaterialRegion = TerrainCircleRegion | TerrainPolylineRegion | TerrainRectRegion — A palette zone painted over the base terrain palette. Circle (the default when no `shape` is given), `polyline` ribbons for roads/rivers, and rotatable `rect` districts all paint fully inside their core and blend back across `falloff`; later regions in the list win overlaps.
|
|
3410
|
+
- TerrainPolylineRegion (interface): interface TerrainPolylineRegion extends TerrainRegionStyle — A ribbon palette zone following a centerline — roads and rivers, instead of chaining overlapping circles.
|
|
3411
|
+
- TerrainRectRegion (interface): interface TerrainRectRegion extends TerrainRegionStyle — A rectangular palette zone, optionally rotated about the world y axis — plazas, fields, districts.
|
|
3412
|
+
- TerrainRegionStyle (interface): interface TerrainRegionStyle — Palette and blend fields shared by every `TerrainMaterialRegion` shape.
|
|
3065
3413
|
- TilemapWorldConfig (interface): interface TilemapWorldConfig extends WorldGridConfig
|
|
3066
3414
|
- VegetationEnvironmentDescriptor (type): type VegetationEnvironmentDescriptor = GrassEnvironmentDescriptor
|
|
3067
3415
|
- VoxelWorldConfig (interface): interface VoxelWorldConfig extends WorldGridConfig
|
|
3068
3416
|
- WaterEnvironmentDescriptor (type): type WaterEnvironmentDescriptor = OceanEnvironmentDescriptor
|
|
3069
3417
|
- WeatherEnvironmentDescriptor (type): type WeatherEnvironmentDescriptor = RainEnvironmentDescriptor | SnowEnvironmentDescriptor
|
|
3070
3418
|
- WorldBounds (interface): interface WorldBounds
|
|
3071
|
-
- WorldFeature (type): type WorldFeature = | ({ kind: "biomes" } & BiomesWorldConfig) | ({ kind: "voxel" } & VoxelWorldConfig) | ({ kind: "plots" } & PlotsWorldConfig) | ({ kind: "tilemap" } & TilemapWorldConfig) | EnvironmentWorldFeature | { kind: "flat" }
|
|
3419
|
+
- WorldFeature (type): type WorldFeature = | ({ kind: "biomes" } & BiomesWorldConfig) | ({ kind: "voxel" } & VoxelWorldConfig) | ({ kind: "plots" } & PlotsWorldConfig) | ({ kind: "tilemap" } & TilemapWorldConfig) | EnvironmentWorldFeature | { kind: "flat" } — A declared world shape — biomes, voxel grid, plots, tilemap, environment, or flat — passed to `defineGame`.
|
|
3072
3420
|
- WorldGridCell (interface): interface WorldGridCell
|
|
3073
3421
|
- WorldGridConfig (interface): interface WorldGridConfig — Shared by `biomes()`/`voxel()`/`plots()`/`tilemap()` so the shell can render their declared content as instanced boxes without a hand-written renderer.
|
|
3074
|
-
- biomes (function): function biomes(config: BiomesWorldConfig): WorldFeature
|
|
3075
|
-
- building (function): function building(config: BuildingEnvironmentConfig = {}): BuildingEnvironmentDescriptor
|
|
3076
|
-
- environment (function): function environment(config: EnvironmentWorldConfig = {}): EnvironmentWorldFeature
|
|
3077
|
-
- flat (function): function flat(): WorldFeature
|
|
3078
|
-
- grass (function): function grass(config: GrassEnvironmentConfig = {}): GrassEnvironmentDescriptor
|
|
3422
|
+
- biomes (function): function biomes(config: BiomesWorldConfig): WorldFeature — Declares a biome-painted world — the whole-world alternative to a single `environment()` terrain.
|
|
3423
|
+
- building (function): function building(config: BuildingEnvironmentConfig = {}): BuildingEnvironmentDescriptor — Declares a cluster of procedurally-massed buildings for `environment()` — count, footprint, stories, style.
|
|
3424
|
+
- environment (function): function environment(config: EnvironmentWorldConfig = {}): EnvironmentWorldFeature — Composes an `environment()` world feature from terrain, sky, weather, vegetation, water, structures, roads, and pads.
|
|
3425
|
+
- flat (function): function flat(): WorldFeature — Declares an empty flat world — the minimal `WorldFeature` for games with no terrain of their own.
|
|
3426
|
+
- grass (function): function grass(config: GrassEnvironmentConfig = {}): GrassEnvironmentDescriptor — Declares a grass vegetation patch for `environment()` — area, blade sizing, density, and colors.
|
|
3079
3427
|
- island (function): function island(config: TerrainIslandConfig): TerrainIslandDescriptor
|
|
3080
|
-
- ocean (function): function ocean(config: OceanEnvironmentConfig = {}): OceanEnvironmentDescriptor
|
|
3428
|
+
- ocean (function): function ocean(config: OceanEnvironmentConfig = {}): OceanEnvironmentDescriptor — Declares an ocean water body for `environment()` — bounds, level, and wave tuning.
|
|
3081
3429
|
- pad (function): function pad(config: PadEnvironmentConfig): PadEnvironmentDescriptor
|
|
3082
3430
|
- padFlattenMasks (function): function padFlattenMasks(pads: readonly PadEnvironmentDescriptor[]): readonly TerrainFlattenMask[] — Derives implicit `TerrainFlattenMask`s carving each pad's footprint into the terrain beneath it. Elevated pads (absolute `elevation`) float free and carve nothing.
|
|
3083
|
-
- plots (function): function plots(config: PlotsWorldConfig = {}): WorldFeature
|
|
3084
|
-
- rain (function): function rain(config: RainEnvironmentConfig = {}): RainEnvironmentDescriptor
|
|
3431
|
+
- plots (function): function plots(config: PlotsWorldConfig = {}): WorldFeature — Declares a subdivided-plots world — farming, base-building, and other parcel-based layouts.
|
|
3432
|
+
- rain (function): function rain(config: RainEnvironmentConfig = {}): RainEnvironmentDescriptor — Declares a rainfall weather effect for `environment()` — area, density, speed, and wind.
|
|
3433
|
+
- road (function): function road(config: RoadEnvironmentConfig): RoadEnvironmentDescriptor — Declare a road ribbon for an `environment()` world; the shell drapes and renders it over the terrain.
|
|
3085
3434
|
- sky (function): function sky(config: SkyEnvironmentConfig = {}): SkyEnvironmentDescriptor
|
|
3086
|
-
- snow (function): function snow(config: SnowEnvironmentConfig = {}): SnowEnvironmentDescriptor
|
|
3087
|
-
- terrain (function): function terrain(config: TerrainEnvironmentConfig = {}): TerrainEnvironmentDescriptor
|
|
3088
|
-
- tilemap (function): function tilemap(config: TilemapWorldConfig): WorldFeature
|
|
3089
|
-
- voxel (function): function voxel(config: VoxelWorldConfig): WorldFeature
|
|
3435
|
+
- snow (function): function snow(config: SnowEnvironmentConfig = {}): SnowEnvironmentDescriptor — Declares a snowfall weather effect for `environment()` — area, density, drift, and wind.
|
|
3436
|
+
- terrain (function): function terrain(config: TerrainEnvironmentConfig = {}): TerrainEnvironmentDescriptor — Declares a heightfield terrain patch for `environment()` — bounds, noise, materials, and flatten masks.
|
|
3437
|
+
- tilemap (function): function tilemap(config: TilemapWorldConfig): WorldFeature — Declares a 2D tilemap world from a map string.
|
|
3438
|
+
- voxel (function): function voxel(config: VoxelWorldConfig): WorldFeature — Declares a voxel-grid world for block-based games.
|
|
3090
3439
|
|
|
3091
3440
|
### @jgengine/core/world/fog
|
|
3092
3441
|
|
|
@@ -3241,6 +3590,18 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
3241
3590
|
- createRegionField (function): function createRegionField<T = unknown>(config: RegionFieldConfig<T>): RegionField<T>
|
|
3242
3591
|
- isRegionField (function): function isRegionField(field: TerrainField): field is RegionField
|
|
3243
3592
|
|
|
3593
|
+
### @jgengine/core/world/roads
|
|
3594
|
+
|
|
3595
|
+
- RoadPoint (type): type RoadPoint = readonly [number, number] — A road centerline vertex in world XZ.
|
|
3596
|
+
- RoadRibbon (interface): interface RoadRibbon — Renderer-ready triangle ribbon: flat position triples plus triangle indices.
|
|
3597
|
+
- RoadRibbonOptions (interface): interface RoadRibbonOptions — Options for {@link buildRoadRibbon}.
|
|
3598
|
+
- RoadSample (interface): interface RoadSample — Result of {@link nearestOnPath}: closest point on the centerline plus distance and tangent.
|
|
3599
|
+
- buildRoadRibbon (function): function buildRoadRibbon(path: readonly RoadPoint[], width: number, sampleHeight: (x: number, z: number) => number, options: RoadRibbonOptions = {}): RoadRibbon — Triangulate a road centerline into a ground-draped ribbon mesh: the polyline is subdivided, each vertex is offset half a `width` along the local perpendicular, and every vertex sits at `sampleHeight(x, z) + elevation`. Pure geometry — the shell (or any renderer) turns the result into a mesh, and tests can assert on it directly.
|
|
3600
|
+
- dashSegments (function): function dashSegments(path: readonly RoadPoint[], dashLength = 3, gapLength = 3): readonly (readonly RoadPoint[])[] — Split a centerline into dash sub-polylines for lane markings: `dashLength` of painted line, `gapLength` of asphalt, repeated along the path's arc length. Feed each returned sub-path back through {@link buildRoadRibbon} with a thin width to mesh the dashes.
|
|
3601
|
+
- isOnRoad (function): function isOnRoad(path: readonly RoadPoint[], width: number, x: number, z: number): boolean — True when the query point lies within half the road `width` of the centerline.
|
|
3602
|
+
- nearestOnPath (function): function nearestOnPath(path: readonly RoadPoint[], x: number, z: number): RoadSample | null — Closest-point query against a road centerline — the seam traffic AI, spawn placement, and "am I on the road" checks share. Returns null for a degenerate path.
|
|
3603
|
+
- pathLength (function): function pathLength(path: readonly RoadPoint[]): number — Total arc length of a centerline in world units.
|
|
3604
|
+
|
|
3244
3605
|
### @jgengine/core/world/scatter
|
|
3245
3606
|
|
|
3246
3607
|
- ScatterArea (interface): interface ScatterArea
|
|
@@ -3256,6 +3617,21 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
3256
3617
|
- pickWeighted (function): function pickWeighted<T>(entries: readonly { value: T; weight: number }[], roll: number): T | null — Weighted pick from opaque entries; `roll` in [0, 1). Returns null when empty.
|
|
3257
3618
|
- scatterItems (function): function scatterItems<T>(field: RegionField<T>, area: Aabb, layersFor: (sample: RegionSample<T>) => readonly ScatterLayer[], options: { cell?: number; max?: number; saltKey?: number } = {}): ScatterInstance[] — Deterministically place opaque items across `area`, grounded on a region field. For each grid cell it asks `layersFor` which items may appear in that region and rolls one against their densities. The engine never interprets `item` — a game maps it to a mesh or entity. Content scatter (region-driven density) as opposed to `scatter` in `./scatter`, which is renderer-free geometric point distribution.
|
|
3258
3619
|
|
|
3620
|
+
### @jgengine/core/world/streets
|
|
3621
|
+
|
|
3622
|
+
- FurnitureSpot (interface): interface FurnitureSpot — A placement anchor on the curb line: position, outward-facing heading, and which side it sits on.
|
|
3623
|
+
- FurnitureSpotOptions (interface): interface FurnitureSpotOptions — Options for {@link furnitureSpots}.
|
|
3624
|
+
- ParkingSpot (interface): interface ParkingSpot — A curbside parking anchor: position at the road edge and a heading parallel to the street.
|
|
3625
|
+
- ParkingSpotOptions (interface): interface ParkingSpotOptions — Options for {@link parkingSpots}.
|
|
3626
|
+
- StreetLane (interface): interface StreetLane — A directed lane derived from a road: an offset centerline plus its direction of travel.
|
|
3627
|
+
- furnitureSpots (function): function furnitureSpots(road: RoadEnvironmentDescriptor, options: FurnitureSpotOptions = {}): readonly FurnitureSpot[] — Evenly spaced street-furniture anchors along a road's curb lines — streetlights, palms, signs, hydrants, benches. Each spot sits just outside the asphalt (plus `outset`), faces away from the street, and alternates sides by default so lights stagger like a real avenue. This is the answer to "where do I put it": furniture is an asset of the street, never a hand-typed coordinate.
|
|
3628
|
+
- laneCenters (function): function laneCenters(road: RoadEnvironmentDescriptor): readonly [StreetLane, StreetLane] — Two right-hand-traffic lane centerlines for a road — each offset a quarter of the drivable width from the center and ordered in its direction of travel. Feed a lane's `path` straight into `nav/pathFollow` for traffic AI, or use its endpoints as directed car spawn points.
|
|
3629
|
+
- offsetPath (function): function offsetPath(path: readonly RoadPoint[], offset: number): readonly RoadPoint[] — Offset a centerline sideways by a signed distance along its local perpendicular — the building block for lanes, curb lines, and sidewalk paths. Positive offsets fall on the left of the direction of travel, negative on the right.
|
|
3630
|
+
- parkingSpots (function): function parkingSpots(road: RoadEnvironmentDescriptor, options: ParkingSpotOptions = {}): readonly ParkingSpot[] — Curbside parking anchors along a road: hugging the edge of the asphalt, headed parallel to the street in that side's direction of travel. Spawn parked vehicles here instead of eyeballing coordinates in the middle of the carriageway.
|
|
3631
|
+
- sidewalkPaths (function): function sidewalkPaths(road: RoadEnvironmentDescriptor): readonly (readonly RoadPoint[])[] — The two sidewalk walking paths of a road — offset polylines running down the middle of each sidewalk band. Pedestrians spawn and route along these instead of the asphalt.
|
|
3632
|
+
- sidewalkPoint (function): function sidewalkPoint(road: RoadEnvironmentDescriptor, side: "left" | "right", fraction: number): RoadPoint | null — A deterministic point on one of a road's sidewalks at a normalized position — `side` picks the band, `fraction` (0..1) picks how far along. The canonical pedestrian spawn helper.
|
|
3633
|
+
- sidewalkWidthOf (function): function sidewalkWidthOf(road: RoadEnvironmentDescriptor): number — Resolved sidewalk band widths for a road; zero when the road declares no sidewalk.
|
|
3634
|
+
|
|
3259
3635
|
### @jgengine/core/world/support
|
|
3260
3636
|
|
|
3261
3637
|
- DebrisOptions (interface): interface DebrisOptions
|
|
@@ -3287,29 +3663,33 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
3287
3663
|
- DEFAULT_MAX_WALK_SLOPE (const): const DEFAULT_MAX_WALK_SLOPE: 0.6
|
|
3288
3664
|
- DEFAULT_TERRAIN_MATERIAL (const): const DEFAULT_TERRAIN_MATERIAL: TerrainMaterial
|
|
3289
3665
|
- FLAT_FIELD (const): const FLAT_FIELD: TerrainField
|
|
3290
|
-
- FractalNoiseConfig (interface): interface FractalNoiseConfig
|
|
3666
|
+
- FractalNoiseConfig (interface): interface FractalNoiseConfig — Octave settings for {@link fractalNoise}: frequency, layering, and optional ridged shaping.
|
|
3291
3667
|
- GroundSnapEntityStore (interface): interface GroundSnapEntityStore
|
|
3292
3668
|
- GroundSnapTarget (interface): interface GroundSnapTarget
|
|
3293
3669
|
- HeightMapFieldConfig (interface): interface HeightMapFieldConfig
|
|
3294
3670
|
- ISLAND_VOID_HEIGHT (const): const ISLAND_VOID_HEIGHT: -256 — Ground height between islands when no base terrain exists — deep enough to read as a fall into the void, finite so physics stays sane.
|
|
3295
|
-
- NoiseFieldConfig (interface): interface NoiseFieldConfig
|
|
3671
|
+
- NoiseFieldConfig (interface): interface NoiseFieldConfig — Configuration for {@link noiseField}: seed, amplitude, and fractal noise shaping.
|
|
3672
|
+
- ResolvedTerrainDetail (type): type ResolvedTerrainDetail = Required<Omit<TerrainDetailConfig, "waterLevel">> & { waterLevel: number } — A {@link TerrainDetailConfig} with every field resolved to a concrete value — the shape the shell's detail material consumes.
|
|
3296
3673
|
- RollingFieldConfig (interface): interface RollingFieldConfig
|
|
3297
3674
|
- TERRAIN_MATERIAL_PALETTES (const): const TERRAIN_MATERIAL_PALETTES: Record<TerrainMaterial, TerrainPalette>
|
|
3298
|
-
- TerrainField (interface): interface TerrainField
|
|
3299
|
-
- TerrainNormal (type): type TerrainNormal = readonly [number, number, number]
|
|
3675
|
+
- TerrainField (interface): interface TerrainField — A sampleable ground surface: height and normal at any x/z, with optional bounds and water level.
|
|
3676
|
+
- TerrainNormal (type): type TerrainNormal = readonly [number, number, number] — A surface normal vector at a terrain sample point.
|
|
3300
3677
|
- TerrainPalette (interface): interface TerrainPalette
|
|
3301
3678
|
- TerrainSlopeSample (interface): interface TerrainSlopeSample
|
|
3302
|
-
- arenaField (function): function arenaField(config: ArenaFieldConfig = {}): TerrainField
|
|
3679
|
+
- arenaField (function): function arenaField(config: ArenaFieldConfig = {}): TerrainField — Builds a `TerrainField` with a flat spawn plateau, rolling hills, and a basin, for combat arenas.
|
|
3303
3680
|
- composeIslandFields (function): function composeIslandFields(base: TerrainField | null, islands: readonly TerrainIslandDescriptor[], voidHeight = ISLAND_VOID_HEIGHT): TerrainField — Composes a base terrain and any number of bounded islands into one world field: inside an island's rect the island's own field (sampled in island-local coordinates) wins, elsewhere the base terrain answers, and with no base the gap is `ISLAND_VOID_HEIGHT` void. Later islands win overlaps.
|
|
3304
|
-
-
|
|
3305
|
-
-
|
|
3306
|
-
-
|
|
3681
|
+
- createBiomeBandSampler (function): function createBiomeBandSampler(bands: readonly BiomeBand[], fallback: TerrainPalette): (z: number) => TerrainPalette — Per-z palette sampler over a descriptor's `biomeBands` — ordered zones that cross-fade their ground palette across a `fade`-wide window centered on the midpoint z between adjacent centers. Below the first / above the last band clamps to that band's palette. Returns `fallback` when no bands are declared. Pure math, unit-testable independent of rendering.
|
|
3682
|
+
- createTerrainPaletteSampler (function): function createTerrainPaletteSampler(descriptor: Pick<TerrainEnvironmentConfig, "material" | "colors" | "materialRegions" | "biomeBands">): (x: number, z: number) => TerrainPalette — Per-position palette sampler over the descriptor's base `material`/`colors` plus its z-ordered `biomeBands` (painted first) and radial `materialRegions` (painted over) — the multi-biome coloring seam. Regions paint fully inside `radius` and blend back across `falloff`; later regions win overlaps.
|
|
3683
|
+
- flatField (function): function flatField(): TerrainField — A flat, zero-height `TerrainField` for arenas with no elevation.
|
|
3684
|
+
- fractalNoise (function): function fractalNoise(x: number, z: number, config: FractalNoiseConfig): number — Layers `valueNoise` octaves per `config` into a single normalized noise sample.
|
|
3307
3685
|
- groundFieldFor (function): function groundFieldFor(world?: WorldFeature): TerrainField
|
|
3686
|
+
- hasEnvironmentTerrain (function): function hasEnvironmentTerrain(world: WorldFeature | undefined): boolean — Whether a world declares real terrain (base heightfield or islands) rather than a flat plane — gates terrain-floor sampling in the movement controllers.
|
|
3308
3687
|
- heightMapField (function): function heightMapField(config: HeightMapFieldConfig): TerrainField
|
|
3309
|
-
- noiseField (function): function noiseField(config: NoiseFieldConfig = {}): TerrainField
|
|
3688
|
+
- noiseField (function): function noiseField(config: NoiseFieldConfig = {}): TerrainField — Builds a `TerrainField` whose height is fractal noise shaped by `config`.
|
|
3310
3689
|
- resolveEnvironmentField (function): function resolveEnvironmentField(feature: EnvironmentWorldFeature): TerrainField — The full ground field for an environment world: base `terrain` composed with any `islands`.
|
|
3311
|
-
- resolveGroundStep (function): function resolveGroundStep(field: TerrainField, x: number, z: number, stepX: number, stepZ: number, maxSlope = DEFAULT_MAX_WALK_SLOPE): { stepX: number; stepZ: number }
|
|
3312
|
-
-
|
|
3690
|
+
- resolveGroundStep (function): function resolveGroundStep(field: TerrainField, x: number, z: number, stepX: number, stepZ: number, maxSlope = DEFAULT_MAX_WALK_SLOPE): { stepX: number; stepZ: number } — Zeroes out a movement step's x or z component where it would climb steeper than `maxSlope`.
|
|
3691
|
+
- resolveTerrainDetail (function): function resolveTerrainDetail(config: TerrainDetailConfig, terrainWaterLevel = 0): ResolvedTerrainDetail — Fill a `TerrainDetailConfig` with defaults; `waterLevel` falls back to the terrain's own water level.
|
|
3692
|
+
- resolveTerrainField (function): function resolveTerrainField(descriptor?: TerrainEnvironmentDescriptor): TerrainField — Resolves a `TerrainEnvironmentDescriptor` into a concrete `TerrainField`, applying flatten masks.
|
|
3313
3693
|
- resolveTerrainPalette (function): function resolveTerrainPalette(descriptor: Pick<TerrainEnvironmentConfig, "material" | "colors"> = {}): TerrainPalette
|
|
3314
3694
|
- rollingField (function): function rollingField(config: RollingFieldConfig = {}): TerrainField
|
|
3315
3695
|
- sampleSlope (function): function sampleSlope(field: TerrainField, x: number, z: number): TerrainSlopeSample — Local slope from the field's normal — the input for gravity-roll, ski acceleration, and slide checks (#284.6).
|
|
@@ -3317,8 +3697,8 @@ Imports use deep paths: `@jgengine/core/<path>`.
|
|
|
3317
3697
|
- slopeForce (function): function slopeForce(field: TerrainField, x: number, z: number, scale = 9.8): readonly [number, number] — Downhill force/acceleration from the local slope: direction × sin(slope angle) × `scale`. Add it to any integrator's XZ velocity each tick (`scale` ≈ gravity for a free-rolling body).
|
|
3318
3698
|
- snapEntityToGround (function): function snapEntityToGround(entities: GroundSnapEntityStore, id: string, field: TerrainField, offset = 0): boolean — Ground-snaps an already-spawned entity in place; returns false when `id` is unknown.
|
|
3319
3699
|
- snapToGround (function): function snapToGround(field: TerrainField, position: readonly [number, number, number], offset = 0): [number, number, number] — Returns `position` with `y` replaced by the field's ground height (plus `offset`) at its `x`/`z`.
|
|
3320
|
-
- valueNoise (function): function valueNoise(x: number, z: number, seed: number): number
|
|
3321
|
-
- withNormal (function): function withNormal(sampleHeight: (x: number, z: number) => number): TerrainField["sampleNormal"]
|
|
3700
|
+
- valueNoise (function): function valueNoise(x: number, z: number, seed: number): number — Smoothly interpolated 2D value noise in `[-1, 1]` for the given seed.
|
|
3701
|
+
- withNormal (function): function withNormal(sampleHeight: (x: number, z: number) => number): TerrainField["sampleNormal"] — Derives a `TerrainField.sampleNormal` from a height sampler via finite-difference gradients.
|
|
3322
3702
|
|
|
3323
3703
|
### @jgengine/core/world/voxelField
|
|
3324
3704
|
|
|
@@ -3497,7 +3877,9 @@ Exact import paths and export names — **do not invent paths**; every row below
|
|
|
3497
3877
|
| Loadout | `game/loadout` | `LoadoutDef`, `LoadoutItemEntry`, `Loadouts` |
|
|
3498
3878
|
| Cosmetic loadout | `game/cosmetics` | `createCosmetics`, `Cosmetics`, `CosmeticLoadoutDef` |
|
|
3499
3879
|
| Quest | `game/quest` | `QuestDef`, `QuestRewards`, `QuestObjective`, `QuestJournal` |
|
|
3500
|
-
| World features | `world/features` | `WorldFeature`, `biomes`, `voxel`, `plots`, `tilemap`, `flat`, `environment`, `terrain`, `rain`, `snow`, `grass`, `ocean`, `building` |
|
|
3880
|
+
| World features | `world/features` | `WorldFeature`, `biomes`, `voxel`, `plots`, `tilemap`, `flat`, `environment`, `terrain`, `rain`, `snow`, `grass`, `ocean`, `building`, `road` |
|
|
3881
|
+
| Street layout | `world/streets` | `laneCenters`, `sidewalkPaths`, `furnitureSpots`, `parkingSpots`, `sidewalkPoint`, `offsetPath`, `sidewalkWidthOf`, `StreetLane`, `FurnitureSpot`, `ParkingSpot` — where things belong on a `road()`: lanes for traffic, sidewalks for peds, curb anchors for furniture |
|
|
3882
|
+
| Road ribbons | `world/roads` | `buildRoadRibbon`, `dashSegments`, `nearestOnPath`, `isOnRoad`, `pathLength`, `RoadRibbon`, `RoadSample`, `RoadPoint` — geometry + queries behind the `road()` environment feature |
|
|
3501
3883
|
| Voxel field | `world/voxelField` | `createVoxelField`, `VoxelField`, `VoxelCell`, `VoxelHit`, `VoxelBounds`, `VoxelFieldSummary`, `VoxelFace`, `VOXEL_FACES`, `VOXEL_FACE_NORMALS` — a chunked block lattice, distinct from the `voxel()` `WorldFeature` descriptor |
|
|
3502
3884
|
| Terrain field | `world/terrain` | `TerrainField`, `noiseField`, `resolveTerrainField`, `rollingField`, `fractalNoise`, `valueNoise`, `withNormal`, `arenaField`, `flatField`, `resolveGroundStep`, `snapToGround`, `snapEntityToGround`, `resolveTerrainPalette`, `TERRAIN_MATERIAL_PALETTES` |
|
|
3503
3885
|
| Seeded RNG | `random/rng` | `seededRng`, `seededStreams` |
|
|
@@ -3789,7 +4171,7 @@ A voxel block is an object. A rack is an object with a slot inventory. A GPU is
|
|
|
3789
4171
|
|
|
3790
4172
|
## Game repo layout
|
|
3791
4173
|
|
|
3792
|
-
Every game is one shape, enforced by `check-game-shape` (part of `check-types`): the top of `src/` holds only the skeleton, and every game-specific module, UI component, and test lives under `src/game/`. Dense files — one `catalog.ts` per domain, never one file per entry.
|
|
4174
|
+
Every game is one shape, enforced by `check-game-shape` (part of `check-types`): the top of `src/` holds only the skeleton, and every game-specific module, UI component, and test lives under `src/game/`. In this repo the gate also requires a root `package.json` script `"games:<id>": "bun run --cwd Games/<id> dev"` for every `Games/*` directory — add it with the scaffold, or the very first `check-types` fails before the compiler even runs. Dense files — one `catalog.ts` per domain, never one file per entry.
|
|
3793
4175
|
|
|
3794
4176
|
```
|
|
3795
4177
|
src/
|
|
@@ -3820,6 +4202,8 @@ src/
|
|
|
3820
4202
|
|
|
3821
4203
|
**Smart defaults** — omit any of these and the call still resolves: `multiplayer` → `offline()`; `assets` → an empty asset catalog; `loop` hooks (`onInit`/`onNewPlayer`/`onTick`) → no-ops; `content` → `{}`; `GameUI` → an empty component; `camera` → third-person orbit; `feed` → 20-entry ring buffers per action; a `world` of kind `environment()` auto-renders as the backdrop with no `environment` component supplied — a non-`environment()` world (`flat()`, `voxel()`, …) still needs the game to hand it one.
|
|
3822
4204
|
|
|
4205
|
+
**Opt-in `ctx.game.*` subsystems (`features`)** — core is genre-agnostic: the always-on base is `commands` / `events` / `store` / `feed` (plus `audio`), and genre subsystems are opt-in via `defineGame({ features: { roster, cards, turn, race, leaderboard, social, chat } })`. Omit one and `ctx.game.<name>` is `undefined` — a puzzle game isn't handed a card pile, race state, or party/chat it never asked for. Declare only what the game uses (`chat` implies `social`). (The content cluster — economy/quest/loot/trade — joins this manifest in a later slim-core phase.)
|
|
4206
|
+
|
|
3823
4207
|
```ts
|
|
3824
4208
|
// game.config.ts — imports only, nothing inline
|
|
3825
4209
|
import { defineGame } from "@jgengine/shell/defineGame";
|
|
@@ -3881,7 +4265,7 @@ export const physics: PhysicsConfig = { gravity: -32 };
|
|
|
3881
4265
|
|
|
3882
4266
|
- `PhysicsConfig.gravity`/`jumpVelocity` tune the shell's built-in walk controller's fall/jump feel (defaults ~`-24`/`7.1`) — the only two levers on gravity and jump height; everything else about movement (speed, poses) stays catalog `movement` fields.
|
|
3883
4267
|
- Input bindings are string arrays (hold semantics) or `{ hold, toggle, repeatMs? }` for the same verb. `repeatMs` turns a held action into an auto-repeat fire (build-mode place-on-drag, rapid-fire without a separate `wasPressed`/interval combo in game code) — the shell refires the command every `repeatMs` while the binding stays down, on top of its normal press-edge fire.
|
|
3884
|
-
- **Keybind → command convention.** The shell fires a command for any bound action that isn't reserved: pressing an action runs a command of the **same name** if one is defined, else a `ui.<action>` fallback (so `openBackpack` → `ui.openBackpack`). Just declare the binding and a matching command — no per-game `keydown` listener. Reserved actions the shell consumes natively and never routes to a command: `moveForward/moveBack/moveLeft/moveRight`, `turnLeft/turnRight`, `sprint`, `jump`, `tabTarget`, `clearTarget`, `useAbility`, `interact`, and any `hotbarSlotN`/`slotN`. `tabTarget`/`clearTarget` run `target.cycle`/`target.clear` (native `cycleTarget`/`setTarget` fallback).
|
|
4268
|
+
- **Keybind → command convention.** The shell fires a command for any bound action that isn't reserved: pressing an action runs a command of the **same name** if one is defined, else a `ui.<action>` fallback (so `openBackpack` → `ui.openBackpack`). Just declare the binding and a matching command — no per-game `keydown` listener. Reserved actions the shell consumes natively and never routes to a command: `moveForward/moveBack/moveLeft/moveRight`, `turnLeft/turnRight`, `sprint`, `jump`, `tabTarget`, `clearTarget`, `useAbility`, `interact`, and any `hotbarSlotN`/`slotN`. `tabTarget`/`clearTarget` run `target.cycle`/`target.clear` (native `cycleTarget`/`setTarget` fallback). Because `hotbarSlotN` never reaches a command, a game that drives selection from game code binds its own `selectSlot1..N` actions, defines matching commands that write a store key, and reads it back through `hotbarSelection: () => ...` on `defineGame` (see `loot-shooter`).
|
|
3885
4269
|
- **`interact`** is special: pressing it resolves the active proximity prompt from the `prompts` field of `defineGame({...})` and runs that prompt's `invoke` command. A prompt with `invoke: null` is display-only and does nothing on the key.
|
|
3886
4270
|
- UI keybind badges derive from `keybinds` via `actionLabel(keybinds, "openBackpack")` — `bindingLabel` maps codes to short labels (`Digit1`→`1`, `KeyB`→`B`, `mouse0`→`LMB`, `Escape`→`Esc`). Never hardcode label strings; they drift the moment a binding changes.
|
|
3887
4271
|
- `server.mode` is a string your loop/commands interpret — the engine ships no gamemode presets.
|
|
@@ -3966,7 +4350,7 @@ Catalog-first, no per-game audio glue. The `audio` field of `defineGame({...})`
|
|
|
3966
4350
|
The shell ships a **rig library**; a game picks and tunes one through `camera` config, never by writing camera positions from `onTick`. Select with `camera.rig` (or the `perspective: "third" | "first"` shorthand) — or by config block alone (#207.8): supplying `camera.<rig>` selects that rig with no redundant `rig` field, checked in the table's order; an explicit `rig` wins when several blocks are present:
|
|
3967
4351
|
| `rig` | For | Key config (`camera.<rig>`) |
|
|
3968
4352
|
|-------|-----|------------------------------|
|
|
3969
|
-
| `orbit` (default) | Third-person chase | `initialDistance`, `targetHeight`, `min/maxPolarAngle`, drag/zoom |
|
|
4353
|
+
| `orbit` (default) | Third-person chase | Top-level fields on `camera` itself — `initialDistance`, `minDistance`/`maxDistance`, `targetHeight`, `min/maxPolarAngle`, drag/zoom. There is **no `camera.orbit` block**; orbit is the one rig tuned at the top level |
|
|
3970
4354
|
| `first` | FPS mouse-look | `firstPerson: { eyeHeight, sensitivity, maxPitch, reticle, viewmodel }` |
|
|
3971
4355
|
| `topDown` | ARPG iso / top-down (Diablo IV, Hades II) | `topDown: { height, pitch, yaw, followSmoothing, zoom }` — decoupled follow; `pitch` is camera elevation (PI/2 = straight down, near 0 = grazing and boom-distance blows up past `frustum.far`) |
|
|
3972
4356
|
| `rts` | Free-pan / edge-scroll (The Sims, Manor Lords) | `rts: { panSpeed, edgeScroll, rotateSpeed, bounds, start, pan }` — `pan: false` turns it into a static backdrop camera: no WASD/arrow pan, no edge-scroll, no Q/E rotate, no wheel zoom, still re-centers on `followEntityId` if one resolves |
|
|
@@ -4025,7 +4409,7 @@ ctx.time advance, now, calendar, snapshot; pause, play, toggle,
|
|
|
4025
4409
|
ctx.subscribe / ctx.version change signal — UI layers bind via useSyncExternalStore
|
|
4026
4410
|
```
|
|
4027
4411
|
|
|
4028
|
-
`content.itemById(id)` supplies `{ use?, weapon?, trade? }
|
|
4412
|
+
`content.itemById(id)` supplies `{ use?, weapon?, trade? }` — exact shapes: `use` is the handler **name string** (not an object), `weapon` is a flat `Record<string, number | Record<string, number>>` built from your catalog stats (`damage`, `projectile.{...}`, `explosion.{...}`), and every lookup returns **`null`** (not `undefined`) for an unknown id; `content.entityById(id)` supplies `{ stats?, receive?, onDeath?, movement?, role? }`; `content.objectById(id)` supplies `GameContextObjectEntry` `{ proximityPrompt?, breakable?, slotInventory? }`. Build all three from your catalogs in `content.ts`. Call-shape gotchas that cost typecheck loops: `ctx.item.use.use(input)` takes **one** argument on the ctx surface (the two-arg `use(state, input)` is the raw factory shape); `ctx.scene.entity.moveToward(id, target, { speed, dt, stopDistance? })` — the third argument is an options object, never a bare `dt`; `nav/pathFollow`'s `createPathFollow(config)` returns the initial `PathFollowState` directly and `advancePathFollow(config, state, dt)` returns the **next state** (with `position: [x,y,z]`, `heading`, `done`) — there is no wrapper object, and `Waypoint` is a `[x, y, z]` tuple. A placed object resolves its catalog entry via `ctx.scene.object.catalog(instanceId)`; `ctx.scene.object.at(x, y, z, tolerance?)` finds placed objects near a point (grid interaction, click resolution beyond the pointer service). `ctx.scene.entity.update(id, patch)` writes a shallow patch onto a spawned entity's mutable fields (e.g. `movement.walkSpeed`) without a full respawn — `scene/movementSpeed`'s `applyStatDrivenSpeed(deps, id, { baseSpeed, multiplierStat?, flatBonusStat? })` is the catalog-driven helper that recomputes and writes `movement.walkSpeed` from a stat-modifier pair each time a buff changes.
|
|
4029
4413
|
|
|
4030
4414
|
### Two tiers: `ctx` runtime vs pure factories
|
|
4031
4415
|
|
|
@@ -4183,7 +4567,7 @@ description: Gameplay systems: items, quests, economy, crafting, turns, objectiv
|
|
|
4183
4567
|
| `snap` | `"grid"` \| `"free"` \| `"wall"` |
|
|
4184
4568
|
| `solid` | Blocks movement |
|
|
4185
4569
|
| `breakable` | `false` or `{ baseBreakTime, harvest, drops, dropsWhenUnmet }` |
|
|
4186
|
-
| `proximityPrompt` | Float UI + optional command invoke |
|
|
4570
|
+
| `proximityPrompt` | Float UI + optional command invoke; `keybind(action, label?)` shows what the interaction does beside the key (`[E] Open Chest`) |
|
|
4187
4571
|
| `slotInventory` | Attached container `{ slots, accepts }` created at place time (`object:<instanceId>`) |
|
|
4188
4572
|
|
|
4189
4573
|
Break resolution: `duration = baseBreakTime / (tool?.breakSpeed ?? 1)`; drops per `when` (`always` / `harvestMet` / `silkTouch` / `playerKill`); then `inventory.put` + `object.remove`.
|
|
@@ -4285,6 +4669,10 @@ Banned in the engine: `weapon.fire`, `consumable.use`, `game.combat.*`, per-weap
|
|
|
4285
4669
|
|
|
4286
4670
|
A capture item's `item.use` handler composes the primitives instead of forking them: read the wild target's hp via `ctx.scene.entity.stats.get(target, "health")`, roll `rollCapture({ hpFraction, catchPower })`, and on success call `ctx.scene.entity.despawn(target)` + `ctx.game.roster.capture(ownerId, catalogId)` — the wild scene entity is removed and re-parented into the owner's persisted roster; the react `CaptureOdds({ chance })` component shows the live odds meter the UI quality bar requires.
|
|
4287
4671
|
|
|
4672
|
+
### Local saves — mutable single-player state
|
|
4673
|
+
|
|
4674
|
+
`@jgengine/core/game/keyValueStore` — `createKeyValueStore({ key, initial, storage? })` is a single persisted mutable cell (`get` / `set` / `update` / `clear`) for single-player state a `recordBook` can't hold: a credit bank, a settings blob, level progress. `recordBook` is monotonic — it keeps only improved values; reach for the KV store when the value moves both ways. It targets the DOM-free `KeyValueStorage` seam (defaults to `localStorage`, pass a stub in tests or `null` for memory-only); corrupt or unavailable storage degrades to in-memory and never throws into a tick. Author any core-side persistence against `KeyValueStorage`, never the DOM `Storage` type — core has no DOM lib, so copying shell's `fovPreference.ts` shape into core fails the build.
|
|
4675
|
+
|
|
4288
4676
|
## Combat — effects, projectiles, death, feel, abilities
|
|
4289
4677
|
## Card, board & shaped-inventory primitives
|
|
4290
4678
|
Pure, renderer-free structures for card, board, and deckbuilder games — they sit **beside** the slot inventory, not in place of it. All are immutable-reducer + thin-controller pairs, mirroring the two-tier ctx/factory model: use the `create*` controller in game code, reach for the exported pure functions (`draw`, `moveCards`, `tickTimeline`, `laneAggregate`, `runPipeline`, `placeShaped`) for unit tests and headless servers.
|
|
@@ -4518,6 +4906,8 @@ description: Combat API: effects, projectiles, damage, abilities, loot, weapons.
|
|
|
4518
4906
|
|
|
4519
4907
|
Combat primitives — effects & projectiles, death handling, melee/defense/telegraph feel, and abilities/resources/auto-target/resistance/run drafts. Full surface: **[reference.md](https://github.com/Noisemaker111/jgengine/blob/main/.claude/skills/jgengine-combat/reference.md)**.
|
|
4520
4908
|
|
|
4909
|
+
**One-shot rig animations.** A model's `animation.oneShots` (in `entityModels`) maps event names → clip name(s), played once over the locomotion `states` then released. `hit` and `death` auto-fire from the entity's own `combat.hitReaction` / `entity.died` (no wiring); trigger any other (e.g. `attack`) with `ctx.game.playEntityAnimation(instanceId, event)` when the swing lands. A `string[]` binding picks a random variant per trigger; `death` clamps on its final frame. Pairs with `animation.states` — the shell fades the loco state out for the clip and back in when it finishes (death holds the pose). Clip-selection logic is the pure `resolveOneShotClip` in `@jgengine/core/game/modelAnimation`.
|
|
4910
|
+
|
|
4521
4911
|
## Loot
|
|
4522
4912
|
|
|
4523
4913
|
```ts
|
|
@@ -4557,7 +4947,7 @@ fireProjectile({ from, via, aim, effect }) // → shotId (pending)
|
|
|
4557
4947
|
settleProjectile(shotId) // authoritative → { at, hits } | rejection
|
|
4558
4948
|
```
|
|
4559
4949
|
|
|
4560
|
-
`Aim = { origin, direction } | { yaw, pitch, spread? }`. Hitscan settles into per-hit effects; ballistic shots (`weapon.projectile` with `fuseTime`/`settleOn`) settle to a landing point — the handler then calls `effect({ at: settle.at, radius })`. Settling twice rejects. Prediction is never authority.
|
|
4950
|
+
`Aim = { origin, direction } | { yaw, pitch, spread? }`. Shot origin resolves via `ShotOriginPolicy` (default `eye`: `aim.origin` when given, else shooter position raised to eye height — 90% of the shooter's own hitbox top when colliders are known, ~1.6m otherwise — so shots trace the sightline and the crosshair is truth; `muzzle`/`camera`/`world` for custom rigs, `legacy` = raw entity position). Default entity hitbox is a body-covering AABB (feet to ~1.8m). Objects with a `visual.scale` get a matching grounded physical body automatically; explicit catalog `colliders`/`halfExtents` override. Hitscan settles into per-hit effects; ballistic shots (`weapon.projectile` with `fuseTime`/`settleOn`) settle to a landing point — the handler then calls `effect({ at: settle.at, radius })`. Settling twice rejects. Prediction is never authority.
|
|
4561
4951
|
|
|
4562
4952
|
Raycasts are **object-aware**: the default raycast checks placed scene objects as well as entities, discriminated by `RaycastHit.kind` (`"entity" | "object"` — an `ObjectRaycastHit` also carries `catalogId`). A crate or wall between shooter and target blocks or absorbs the shot instead of every projectile passing through scenery; supply `ProjectileSystemDeps.objects` (`{ list(), halfExtents?(catalogId) }`, matching `ObjectStore.list()` structurally) to opt in, with a `[0.5, 0.5, 0.5]` half-extent default per object.
|
|
4563
4953
|
|
|
@@ -4581,6 +4971,8 @@ Layered on top of effects/projectiles/death — none of it replaces them, i
|
|
|
4581
4971
|
- **Defensive window** (`combat/defensiveWindow`) — a parry/block/dodge with `{ startupMs, activeMs, recoveryMs, iframes }`. `resolveDefense({ config, elapsedMs, attack })` is the pure overlap of the defender's window against the moment the attacker's `active` frames land → `parry | block | iframe | hit`. `createDefensiveWindow(config)` tracks the open time (`open(now)` / `evaluate(now, attack)` / `isInvulnerable(now)`).
|
|
4582
4972
|
- **Combo strings** (`combat/comboString`) — `ComboStep`s with `cancelInto` + `cancelPhases` + optional `stance`, over the anim SM. `advanceCombo(...)` (pure) accepts the next attack only inside the current step's cancel window and matching stance; `createComboRunner(combo, anim)` drives the SM.
|
|
4583
4973
|
- **Meters share one accumulator** (`stats/accumulatorMeter`) — `createAccumulatorMeter({ max, mode: "hold" | "reset", decayPerSecond, decayDelayMs, tiers })`: fill via `add(n)` → `MeterAddResult { fired, overflow, tier, tierChanged }`, `tick(dt)` decays after an idle grace. `combat/breakMeters` builds two on it: `createStaggerMeter` (mode `hold` — fills from hits, `broke()` stays true until `recover()` after a riposte/deathblow) and `createBuildupMeter` (mode `reset` — `add(n)` returns a `BuildupProc { status, durationMs }` at threshold for bleed/frost/rot, then decays). The same base backs G6's ult/streak meters.
|
|
4974
|
+
- **Delayed-regen shield** (`combat/regenShield`) — `createRegenShield(config)` is a shield pool that pauses regen for `regenDelayMs` after every hit, then refills at `regenPerSecond`; `damage`/`tick` replace snapshot-comparing a stat to detect "took damage."
|
|
4975
|
+
- **DoT field** (`combat/dotField`) — `createDotField()` schedules recurring damage-over-time instances (bleed/poison/burn) keyed by id; `tick` drains due damage per interval. Pairs with `combat/breakMeters`' `BuildupProc` — a fired buildup applies a DoT instead of one-shot damage.
|
|
4584
4976
|
- **Dash / dodge** (`movement/dash`) — `createDashState({ distance, durationMs, iframes, staminaCost, staminaMax, staminaRegenPerSecond, cooldownMs })`: `tryDash(dir, now)` → burst or `{ reason: "no-stamina" | "cooldown" | "dashing" }`, `isInvulnerable(now)` for the i-frame window, `offset(now)` for the burst displacement, `tick(dt, now)` regens stamina.
|
|
4585
4977
|
- **Hit reaction** (`combat/hitReaction`) — `resolveHitReaction(config, { attackerPos, targetPos, power })` (pure) → `{ hitstopMs, impulse, shake }`. Wired on `ctx`: `ctx.scene.entity.hitReaction({ from, to, config, power? })` knocks the target back and emits `combat.hitReaction` (the shell reads `shake` for a trauma channel — feeds a G2 camera-shake rig when present, else the shell's own kick).
|
|
4586
4978
|
- **Telegraph** (`combat/telegraph`) — a `TelegraphShape` (`circle | ring | cone | line`) + `windupMs`. `ctx.scene.entity.telegraph({ from, shape, at, dir?, windupMs, kind?, effect? })` emits `combat.telegraph` for the shell to draw a ground decal that fills over the windup, and — if `effect` is bound — applies that effect to everyone inside the shape (`pointInTelegraph`) at activation. Returns a cancel handle. **Turn-scoped variant**, for tactics/turn-based enemy intents instead of a real-time windup: set `TelegraphConfig.turns` (a count of turns instead of `windupMs`), then read `telegraphTurnProgress(config, startedTurn, currentTurn)` (0..1 fill fraction), `telegraphFiredAtTurn(config, startedTurn, currentTurn)` (has it resolved), and `telegraphTurnsRemaining(config, startedTurn, currentTurn)` — the same "enemy will hit this tile" decal, advanced by `turnLoop.round()`/`active()` instead of a clock.
|
|
@@ -4721,6 +5113,18 @@ Shell wiring: `@jgengine/shell/vision/RevealVision` (`RevealHighlights` — dept
|
|
|
4721
5113
|
|
|
4722
5114
|
Renderer-free world surface — query primitives, environment fields + weather + realm composition, survival meters/moodles, interactive building & terraform, the optional headless physics world, vehicles/mounts/racing, and spawn placement. Full surface: **[reference.md](https://github.com/Noisemaker111/jgengine/blob/main/.claude/skills/jgengine-world/reference.md)**.
|
|
4723
5115
|
|
|
5116
|
+
## Level design — places, not noise
|
|
5117
|
+
|
|
5118
|
+
Procedural terrain is a canvas, never the level. A world with only `terrain()` + spawn clusters reads as an empty sandbox no matter how big its bounds. Every open-world game composes **places** on top of the canvas, and the checklist below is the floor, not the ceiling:
|
|
5119
|
+
|
|
5120
|
+
- **Every site is a set-piece.** A camp is a wall ring with a deliberate gate opening facing the approach, cover inside, a tower or banner as its silhouette — never a bare radius of spawns. Compose from placed objects (`ctx.scene.object.place` with stable instance ids) driven by pure data functions (ring/scatter/route generators) so bun tests can assert the composition without rendering.
|
|
5121
|
+
- **Roads make the space legible.** Physically connect sites: trace waypoint routes between them and feed the points into the terrain descriptor's `flatten` masks so the route is genuinely walkable, then dress it — markers, wrecks, signposts — every ~60 units so the player can read the path at a glance. A test should walk the route and assert the step height stays small.
|
|
5122
|
+
- **One landmark per region.** A water tower, crashed vehicle, gate, or monument visible from the approach gives orientation without a map. Distinct silhouette + distinct color.
|
|
5123
|
+
- **Anchor → approach → arena → reward.** Each combat site needs a visible anchor drawing the player in, a readable approach (the road), an arena with cover the AI and player both use, and a visible reward (chest, vendor, quest turn-in) at the back so clearing it feels earned.
|
|
5124
|
+
- **People, not just spawns.** Hubs hold named NPCs (talkable entities with prompts), vendors, and light props (lamps, crates, stalls). A hub with only vending-machine objects is a menu, not a town.
|
|
5125
|
+
- **Density budget.** Inside a site's flatten radius aim for roughly one placed object per 6–10 units of radius; along roads one prop per ~60 units; open wilderness stays sparse so sites contrast. Frustum/distance culling is automatic — err toward more dressing, not less.
|
|
5126
|
+
- **Guided openness, never invisible walls.** Total freedom reads as emptiness: herd the player physically. Raise terrain amplitude so off-road relief is genuinely unclimbable, gate slope in `movement.beforeCommit` (block a step whose ground rises past a climb limit, with per-axis slide so it feels like a wall of rock, not a script), and carve the intended route as the one low path. Then buy the openness back deliberately: side pockets (a den, a cache, a wreck field) hang off the main road on short spur routes, each with its own reward, so exploration branches from the path instead of dissolving it. Test the herding the same way you test the road: assert off-road samples rise above the climb limit while every route and spur stays under it.
|
|
5127
|
+
|
|
4724
5128
|
## Turn-based & tactics (renderer-free)
|
|
4725
5129
|
|
|
4726
5130
|
# jgengine domain API — World features
|
|
@@ -4738,11 +5142,13 @@ Descriptors from `@jgengine/core/world/features` — config data the runner
|
|
|
4738
5142
|
| `plots(config)` | Shared city + instanced interiors |
|
|
4739
5143
|
| `tilemap({ map })` | 2D/2.5D levels |
|
|
4740
5144
|
| `flat()` | Plain arena |
|
|
4741
|
-
| `environment({ terrain, sky, weather, vegetation, water, structures, pads })` | Composable outdoor scene — terrain + sky/time-of-day + rain/snow + grass + ocean + buildings + ground pads. Each field takes the matching descriptor: `terrain()`, `sky()`, `rain()`/`snow()`, `grass()`, `ocean()`, `building()`, `pad()`. `building()` and `
|
|
5145
|
+
| `environment({ terrain, sky, weather, vegetation, water, structures, pads })` | Composable outdoor scene — terrain + sky/time-of-day + rain/snow + grass + ocean + buildings + ground pads. Each field takes the matching descriptor: `terrain()`, `sky()`, `rain()`/`snow()`, `grass()`, `ocean()`, `building()`, `pad()`. `building()`, `ocean()`, `rain()`/`snow()`, and `grass()` take `position: [x, z]` to site a cluster/water body/weather band/vegetation patch away from the origin (several settlements, an offset lake, per-biome rain, a distant meadow) — the weather/grass `position` is sugar for `area.position`, so a biome-banded world places each band by zone; building clusters ground-snap to the terrain field per building; each `pad()` (a flat platform/paved patch — `{ center, size: [w,d] | { radius }, height?, color?, rotationY? }`) implicitly flattens the terrain beneath it via a `TerrainFlattenMask`, so a building pad or spawn circle never fights the noise field underneath |
|
|
4742
5146
|
|
|
4743
5147
|
`biomes`/`voxel`/`plots`/`tilemap` share a `WorldGridConfig` (`cells?: WorldGridCell[]`, `cellSize?`, `baseHeight?`, `defaultColor?`) — a `WorldGridCell` is `{ x, z, height?, color? }`, one extruded box per cell. `resolveGridInstances(config)` (`@jgengine/core/world/gridInstances`) is the pure cell→instance math (position, scale, color per cell); the shell renders the result as a single `THREE.InstancedMesh` **automatically whenever `PlayableGame.environment` is unset and `game.world` is one of these four grid kinds** — no manual render wiring for a cell-based world, same auto-render convention as `environment()` worlds.
|
|
4744
5148
|
|
|
4745
|
-
`terrain()`'s `material` (a named palette — `"grass" | "sand" | "snow" | "rock" | "ash"`, resolved via `resolveTerrainPalette`/`TERRAIN_MATERIAL_PALETTES` in `world/terrain`) sets the default low/high/waterline colors; `colors: { low?, high?, waterline? }` overrides any of them field-by-field, and `segments` tunes the render mesh's subdivision. `flatten: TerrainFlattenMask[]` (`{ center, radius, height?, falloff? }`) carves explicit flat circles into the noise field independent of pads — building foundations, spawn circles, roads — blending back to the noise height over `falloff` (default `radius * 0.5`). `sky({ preset?, timeOfDay?, horizonColor?, zenithColor?, sunIntensity?, ambientIntensity?, fog? })` — `preset: "day" | "dusk" | "night"` (default `"day"`) is the static look; `timeOfDay: true` instead drives sun position, sky colors, and fog from the world clock's `calendar().dayFraction` every frame (`@jgengine/shell`'s `TimeOfDayDaylight` mounts this automatically for an `environment()` world with `sky` set — no per-game render wiring). **Gotcha:** `sunIntensity` / `ambientIntensity` are honored only on the **day** keyframe (`daylightCycle` builds dusk/night/dawn from fixed constants). Raising intensities under `preset: "dusk"` or `"night"` does nothing — use `preset: "day"` plus warm `horizonColor`/`zenithColor` when the first screenshot must be bright and readable (see `jgengine`'s first-shot art recipe).
|
|
5149
|
+
`terrain()`'s `material` (a named palette — `"grass" | "sand" | "snow" | "rock" | "ash"`, resolved via `resolveTerrainPalette`/`TERRAIN_MATERIAL_PALETTES` in `world/terrain`) sets the default low/high/waterline colors; `colors: { low?, high?, waterline? }` overrides any of them field-by-field, and `segments` tunes the render mesh's subdivision. `biomeBands: BiomeBand[]` (`{ z, fade?, material?, colors? }`) cross-fades the **ground palette along the world's z axis** — ordered zones (vale → marsh → peaks) that blend across a `fade`-wide window (default 64) centered on each boundary; it's the linear counterpart to the radial `materialRegions` (which paint on top; each region is a `TerrainCircleRegion` (default), `TerrainPolylineRegion` (roads/rivers), or `TerrainRectRegion` (rotatable districts), sharing a `TerrainRegionStyle`). Pure/testable via `createTerrainPaletteSampler`/`createBiomeBandSampler` in `world/terrain`; the shell renders it through the same per-vertex `paletteAt` seam as regions, no extra wiring. (Per-zone **fog/sky** cross-fade isn't wired yet — three.js fog is scene-global — so bands drive ground color today.) `flatten: TerrainFlattenMask[]` (`{ center, radius, height?, falloff? }`) carves explicit flat circles into the noise field independent of pads — building foundations, spawn circles, roads — blending back to the noise height over `falloff` (default `radius * 0.5`). `detail: TerrainDetailConfig` (`{ rockColor?, sandColor?, snowColor?, rockSlopeStart?, snowHeight?, waterLevel?, detailScale?, macroScale?, roughness?, strength? }`) swaps the flat vertex-colour ground for a procedural noise shader (`@jgengine/shell`'s `createTerrainDetailMaterial`, defaults via `resolveTerrainDetail`): it keeps the biome-tinted base and blends noise-broken **rock by slope**, **sand by waterline**, and **snow by height** over it — textured-reading terrain with no image assets, full PBR (lit/shadowed/fogged), composing with the post-processing chain. Omit for the flat look. `sky({ preset?, timeOfDay?, horizonColor?, zenithColor?, sunIntensity?, ambientIntensity?, fog? })` — `preset: "day" | "dusk" | "night"` (default `"day"`) is the static look; `timeOfDay: true` instead drives sun position, sky colors, and fog from the world clock's `calendar().dayFraction` every frame (`@jgengine/shell`'s `TimeOfDayDaylight` mounts this automatically for an `environment()` world with `sky` set — no per-game render wiring). The sky dome renders a bright HDR sun disc + warm glow (aligned to the sun light, so it blooms through a `postProcessing` chain), a mid-sky procedural cloud band, and a horizon haze — no HDRI assets; richer than a flat gradient with zero config. **Gotcha:** `sunIntensity` / `ambientIntensity` are honored only on the **day** keyframe (`daylightCycle` builds dusk/night/dawn from fixed constants). Raising intensities under `preset: "dusk"` or `"night"` does nothing — use `preset: "day"` plus warm `horizonColor`/`zenithColor` when the first screenshot must be bright and readable (see `jgengine`'s first-shot art recipe).
|
|
5150
|
+
|
|
5151
|
+
**Flatten-mask composition semantics** (`withFlattenMasks` — easy to fight if guessed): masks apply **sequentially and the last matching mask wins outright**; there is no mask-to-mask blending. Inside `radius` the height becomes that mask's `target` exactly; in the `radius..radius+falloff` annulus it lerps from `target` back to the **raw noise height**, never to another mask's output. `target` defaults to the raw noise height at the mask's own center (`height` overrides). Consequences to know before composing roads or multi-site layouts: (1) a chain of default-height circles stair-steps, because each snaps to its own center's noise — give road masks explicit `height` values ramped between the endpoint site targets; (2) a later mask's falloff annulus punches raw-noise bumps through an earlier flat area — order masks so the surface that must win comes last, keep road `falloff` small (≤ radius), and skip road circles inside an already-flat site radius; (3) raw noise rarely herds — to guarantee canyon walls, emit masks with `height` *above* the surroundings flanking the corridor. A working road-ramp recipe (overlapping circles every ~12 units, height ramped between site targets, ramp clamped flat inside each site radius, wall masks alongside) lives in `Games/borderlands2/src/game/world/level.ts`.
|
|
4746
5152
|
|
|
4747
5153
|
`parentSpace` positions are local to that space — convert at seams only.
|
|
4748
5154
|
|
|
@@ -4815,7 +5221,7 @@ Turn data-only placement into the build tooling of Valheim/Enshrouded/The Sims/F
|
|
|
4815
5221
|
**Structural destruction (`physics/structure`).** `StructureGraph` models a building as nodes (pieces) + load-bearing edges with some nodes `anchor`ed (foundations). `damage(id, n)`/`damageEdge(a,b,n)`/`severEdge(a,b)` wear pieces and connections; when one breaks, the graph recomputes reachability to an anchor and returns a single `CollapseEvent { fell }` — every piece the loss disconnected. `toDebris(world, event)` sinks the fallen pieces into a `PhysicsWorld` as rigid bodies (The Finals, Rainbow Six). It is coarse by design: **replicate the collapse event (the `fell` id list), not each fragment's physics** — game clients re-derive the debris locally. Piece integrity and edge strength default from a `StructureMaterial` table (DATA).
|
|
4816
5222
|
### Vehicles, mounts, crash damage & racing
|
|
4817
5223
|
Five primitives layer a driving/racing game over the physics sim and `world/water`. All are **data-first** (spec the chassis/wheels/grip curve, damage thresholds, and checkpoint layout as catalog data) and pure `@jgengine/core`; renderers live in the game/shell. Each `update(dt, …)` runs **before** the shared `world.step(dt)`.
|
|
4818
|
-
- **Analog input — `input/axisInput`.** `AxisInput { throttle, brake, steer, handbrake }` is a continuous channel, **distinct from the digital action bindings**. `new AxisChannel({ bindings, smoothing })` ramps held keys into pedal-like analog values (`sample(dt, isDown)`), or `setAnalog(axis, value)` drives it straight from a gamepad axis. `DRIVE_AXIS_BINDINGS` is a ready WASD/arrow map.
|
|
5224
|
+
- **Analog input — `input/axisInput`.** `AxisInput { throttle, brake, steer, handbrake }` is a continuous channel, **distinct from the digital action bindings**. `new AxisChannel({ bindings, smoothing })` ramps held keys into pedal-like analog values (`sample(dt, isDown)`), or `setAnalog(axis, value)` drives it straight from a gamepad axis. `DRIVE_AXIS_BINDINGS` is a ready WASD/arrow map. `sampleAxisBindings` is the instantaneous, unsmoothed read the channel ramps toward each frame — exposed headlessly as `ctx.input.axis(bindings, ranges?)`, bound to **action names** rather than raw key codes (#533.7).
|
|
4819
5225
|
- **`physics/vehicleBody`.** `createVehicleBody(world, config)` is an arcade car: a chassis box body with per-wheel suspension held by G3's `springJoint` against the sampled `groundHeight`, drive/brake along the heading, and a `GripCurve` (`sampleGripCurve`) that bleeds lateral velocity for cornering — and, under `handbrake`, drift. `update(dt, axisInput)` then `world.step`. Because the chassis is a real body it still collides, which feeds crash damage. Rocket League, Trackmania, Wreckfest.
|
|
4820
5226
|
- **`physics/buoyancy`.** `createBuoyantBody(world, { body, water, … })` floats a body on a CPU `WaterSurface` (Archimedes per hull point + water drag) so it settles at the waterline and rides the Gerstner waves; pass an `AxisInput` to `update(dt, time, input?)` and it drives as a boat (thrust + yaw + keel). Sea of Thieves, BOTW rafts.
|
|
4821
5227
|
- **`scene/mount`.** `createMountController()` transfers control to a driven entity: `register({ id, kit, seats })`, `mount(riderId, mountId, seatId?)`, `dismount`. Read `cameraTarget(riderId)` to point the follow camera and `driveTarget(riderId)` to route that rider's `AxisInput` at the mount — the control seat drives, passenger seats ride (multi-seat shared vehicles), and an un-mounted rider drives themselves. `driver(mountId)`/`occupants(mountId)`/`kitOf`. Palworld mounts, V Rising horse, a crewed ship.
|
|
@@ -4980,4 +5386,4 @@ import { bootCartridge, cartridgeSmokeTest, tickCartridge } from "@jgengine/core
|
|
|
4980
5386
|
cartridgeSmokeTest(config); // validate + world summary + headless run/spawn/kill/gem smoke
|
|
4981
5387
|
```
|
|
4982
5388
|
|
|
4983
|
-
`bootCartridge`/`tickCartridge` build a headless `GameContext` and drive the loop (auto-choosing drafts) for custom assertions — see
|
|
5389
|
+
`bootCartridge`/`tickCartridge` build a headless `GameContext` and drive the loop (auto-choosing drafts) for custom assertions — see `@jgengine/core/cartridge/testkit`. The testkit imports `bun:test`; import it from test files only.
|