@jgengine/react 0.31.0 → 0.32.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
CHANGED
|
@@ -26,6 +26,25 @@ At publish, rename this heading to the new version and mirror the entries into
|
|
|
26
26
|
`packages/core/src/meta/changelog.ts` (the typed `CHANGELOG` export).
|
|
27
27
|
-->
|
|
28
28
|
|
|
29
|
+
## 0.32.0
|
|
30
|
+
|
|
31
|
+
### Migrate
|
|
32
|
+
|
|
33
|
+
- **Bump lockstep SDK packages to `^0.32.0`:** `@jgengine/{core,rapier,react,ws,node,sql,convex,shell,editor,assets,navbake}`. CLI `jgengine` is `0.29.0`; `@jgengine/github` is `0.19.0`.
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
|
|
37
|
+
- Audio mixing grew a mute that composes with gain: `AudioEngine.setBusMute` / `setMasterMute` keep the player's volume while silencing a bus, `masterLevel()` / `busLevel()` report the composed value, and Settings carries a mute row per bus plus "Mute all" (`sound.muted`, `sound.bus.<id>.muted`). `AudioEngine.play` and `playOneShot` hold cues in a bounded 16-slot queue while the browser keeps the context suspended and play them on the first gesture instead of dropping them; `installGestureResume` wires that gesture for both presentation paths.
|
|
38
|
+
- `AudioEngine.measure()` reads master RMS and peak from a lazily built analyser, and `installAudioProbe` publishes `window.__jgengineAudio` so a headless capture can prove sound with a measured level rather than a screenshot.
|
|
39
|
+
- `@jgengine/core/audio/feedbackCues` ships seven procedural reactions - `hit`, `hurt`, `kill`, `pickup`, `objective`, `wave`, `deny` - on an `sfx` bus, merged into every game's catalog as `feel.<cue>`. A game sound with that id replaces one; `audio.feedbackCues` replaces or silences them.
|
|
40
|
+
- `@jgengine/shell/audio/eventFeedback` maps the events the engine already announces to those reactions: `combat.damage` (local player reads as hurt, anything else as a hit), `entity.died`, `worldItem.picked_up`, `quest.completed`, `stat.levelUp` and `command.refused`. Each row carries a sound, a gamepad pulse, camera trauma and a caption; the mapping and reactions are data (`DEFAULT_FEEDBACK_BINDINGS`, `DEFAULT_FEEDBACK_REACTIONS`) a game can replace, cooldowns fold a burst into one impact, reduced motion keeps sound and rumble but drops the camera kick, and the resolved reaction is pooled so handling an event allocates nothing per frame. `attachWaveFeedback(runner, ctx.game.feedback)` announces each new wave.
|
|
41
|
+
- `ctx.game.feedback.cue(cue, caption?)` announces a moment no engine event covers and emits `feedback.cue`.
|
|
42
|
+
- `@jgengine/shell/audio/captions` and `CaptionOverlay` render a bounded, deduping, self-expiring caption line under the accessibility captions setting; `FeedbackHost` mounts the whole layer - feedback mapping, gesture resume, audio probe and captions - for both the 3D and HUD presentation paths.
|
|
43
|
+
|
|
44
|
+
### Changed
|
|
45
|
+
|
|
46
|
+
- The CI gate runs nine test shards instead of ten, and a `types` job whose stages overlap, so a docs-only pull request fits the account's 20-job runner cap with room for another run beside it. `scripts/test-shard.ts` owns which files each shard runs and what it prepares first; `bun scripts/type-shard.ts` runs the `check-types` plan two stages at a time and then the tsc fan-out alone.
|
|
47
|
+
|
|
29
48
|
## 0.31.0
|
|
30
49
|
|
|
31
50
|
### Migrate
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jgengine/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.32.0",
|
|
4
4
|
"description": "React UI layer for JGengine: GameProvider, hooks, and headless primitives over @jgengine/core.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"styles": "bun scripts/styles.ts"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@jgengine/core": "^0.
|
|
38
|
+
"@jgengine/core": "^0.32.0"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"react": "^19.0.0",
|
package/skills/jgengine/api.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
- `CHANGELOG` (const): const CHANGELOG: Record<string, ChangelogEntry> — Per-version engine changelog keyed by semver string (e.g. `"0.10.0"`).
|
|
8
8
|
- `ChangelogEntry` (interface): interface ChangelogEntry — One release's migrate steps plus added/changed/removed notes (typed mirror of CHANGELOG.md).
|
|
9
|
-
- `VERSION` (const): const VERSION: "0.
|
|
9
|
+
- `VERSION` (const): const VERSION: "0.32.0" — Installed `@jgengine/core` semver — compare against {@link CHANGELOG} keys when migrating.
|
|
10
10
|
|
|
11
11
|
## @jgengine/core/authoring
|
|
12
12
|
|
|
@@ -285,7 +285,7 @@
|
|
|
285
285
|
|
|
286
286
|
- `CHANGELOG` (const): const CHANGELOG: Record<string, ChangelogEntry> — Per-version engine changelog keyed by semver string (e.g. `"0.10.0"`).
|
|
287
287
|
- `ChangelogEntry` (interface): interface ChangelogEntry — One release's migrate steps plus added/changed/removed notes (typed mirror of CHANGELOG.md).
|
|
288
|
-
- `VERSION` (const): const VERSION: "0.
|
|
288
|
+
- `VERSION` (const): const VERSION: "0.32.0" — Installed `@jgengine/core` semver — compare against {@link CHANGELOG} keys when migrating.
|
|
289
289
|
|
|
290
290
|
## @jgengine/core/runtime/adapter
|
|
291
291
|
|
|
@@ -410,6 +410,7 @@
|
|
|
410
410
|
- `GameContextRace` (interface): interface GameContextRace { state(id: string, config?: RaceStateConfig): RaceState } — ⚠ undocumented
|
|
411
411
|
- `GameContextTurn` (interface): interface GameContextTurn { loop(id: string, config?: TurnLoopConfig): TurnLoop } — ⚠ undocumented
|
|
412
412
|
- `GameContextWorld` (interface): interface GameContextWorld { ground: TerrainField; groundHeightAt(x: number, z: number): number; solids: WorldSolids } — ⚠ undocumented
|
|
413
|
+
- `GameFeedback` (interface): interface GameFeedback — The game-announced feel seam on `ctx.game`: name a moment the engine has no event for and the shell plays its cue's sound, rumble, camera kick and caption. The reaction table lives in the shell, so a game never encodes what a hit or a wave should sound like.
|
|
413
414
|
- `HitReactionInput` (interface): interface HitReactionInput { from: string; to: string; config: HitReactionConfig | ImpactPresetName; power?: number } — ⚠ undocumented · used by `resolveHitReaction` (@jgengine/core/combat): Resolves hit feel (hitstop, knockback impulse, camera shake) from either a named `impactPresets` event (`resolveHitReaction("explosion", inp…
|
|
414
415
|
- `MoveTowardCommitOptions` (interface): interface MoveTowardCommitOptions extends MoveTowardOptions — Options for {@link SceneEntityContext.moveTowardCommit}: {@link MoveTowardOptions} plus an optional facing turn.
|
|
415
416
|
- `SceneEntityContext` (interface): interface SceneEntityContext { spawn(name: string, options?: SpawnOptions): string; despawn(instanceId: string): boolean; update: EntityStore["update"]; setPose(instanceId: string, pose: EntityPose): boolean; setVelocity: EntityStore["setVelocity"]; setPoseConstraint: … — ⚠ undocumented
|
|
@@ -46,6 +46,7 @@ Existing React games keep their entity store and use the focused
|
|
|
46
46
|
|
|
47
47
|
- **Refusals and notices are engine feedback, not per-game toasts.** Return `{ reason: "insufficient-gold", message: "Needs 40 more gold" }` from a command's `validate`: the shell shows `message` in the shared notice feed with the deny cue and stacks repeats. Push other player messages with `ctx.game.notices.push({ text, tone })` (info/success/warn/deny) from game logic instead of a hand-built toast list. The stack renders inside the HUD theme root, so it takes the game's `hudTheme` colours and font; a top or bottom stack moves clear of `HudCanvas` panels and touch controls in its column and sits above `ModalHost` (z 85). Tune or hide it with `notices: { placement, offset, avoidHud, zIndex, cues, hidden }` (`hidden` keeps cues and lets the game render `useNoticeList(ctx.game.notices)` itself); `push({ text, silent: true })` skips one notice's sound.
|
|
48
48
|
- **UI sounds come from `useUiCue()`.** Engine primitives already sound through the shell; for game-owned markup call `cue("click" | "hover" | "open" | "close" | "confirm" | "deny" | "notify")` from buttons and panels; replace a default by declaring `audio.sounds["ui.click"]`, or silence with `audio.uiCues: { hover: false }` / `false`.
|
|
49
|
+
- **A hit, kill, pickup, objective, wave and refusal react without any sound authored.** The shell maps the events the engine already announces (`combat.damage`, `entity.died`, `worldItem.picked_up`, `quest.completed`, `stat.levelUp`, `command.refused`) to a `feel.<cue>` reaction - sound, gamepad pulse, camera kick and caption - and merges procedural defaults on an `sfx` bus. Replace a sound by declaring `audio.sounds["feel.hit"]`, tune or silence with `audio.feedbackCues`, and announce a wave through `attachWaveFeedback(runner, ctx.game.feedback)` or any custom moment with `ctx.game.feedback.cue("wave", "WAVE 3")`. Captions follow the accessibility setting; sound follows the `sfx` bus volume and mute rows in Settings.
|
|
49
50
|
- Every rejected verb displays its reason on the triggering surface. Use `useServerSession(transport, gameId)` for join lifecycle. Join failure blocks play with `JoinGate` and retry; returning `{ ok: false }` without feedback is incomplete.
|
|
50
51
|
- Placement previews display claim cost and affordability inline with `TerritoryOverlay`.
|
|
51
52
|
- `ChatPanel` owns collapsed/expanded state, own-message styling, Enter focus and Escape blur; standalone hosts pass `messages`, `userId`, and `onSend` (typed `{ ok, reason }`), while context-backed games use channel props. Use native inputs so shell typing-target suspension applies.
|
|
@@ -2139,15 +2139,64 @@
|
|
|
2139
2139
|
- `EntityAudioEmitters` (function): function EntityAudioEmitters({ engine, entitySounds, }: { engine: AudioEngine; entitySounds: Record<string, string> | undefined; }): null — ⚠ undocumented
|
|
2140
2140
|
- `ObjectAudioEmitters` (function): function ObjectAudioEmitters({ engine, objectSounds, }: { engine: AudioEngine; objectSounds: Record<string, string> | undefined; }): null — ⚠ undocumented
|
|
2141
2141
|
|
|
2142
|
+
## @jgengine/shell/audio/CaptionOverlay
|
|
2143
|
+
|
|
2144
|
+
- `CaptionOverlay` (function): function CaptionOverlay({ feed, max = 2, zIndex = 84, }: { feed: CaptionFeed; /** How many lines to show at once. Default 2. */ max?: number; zIndex?: number; }): ReactNode — The captions line: a bounded, bottom-centred stack that renders only while the accessibility captions setting is on. Reads HudTheme tokens, so a game's theme reskins it, and lives in the same overlay layer as the notice stack.
|
|
2145
|
+
|
|
2146
|
+
## @jgengine/shell/audio/FeedbackHost
|
|
2147
|
+
|
|
2148
|
+
- `FeedbackHost` (function): function FeedbackHost({ ctx, engine }: { ctx: GameContext; engine: AudioEngine }): ReactNode — Mounts the engine's feel layer for one game: announced events become a sound, a gamepad pulse, a camera kick and a caption line; the audio context unlocks on the first gesture; and the caption stack follows the accessibility setting. Games supply sounds, never the rules.
|
|
2149
|
+
|
|
2142
2150
|
## @jgengine/shell/audio/audioEngine
|
|
2143
2151
|
|
|
2144
2152
|
- `AudioEmitterHandle` (interface): interface AudioEmitterHandle { isPlaying?(): boolean; setPosition(position: Vec3): void; setRate(rate: number): void; setGain(gain: number): void; setLowpass(hz: number): void; setHighpass(hz: number): void; setVelocity(velocity: Vec3): void; stop(): void } — ⚠ undocumented
|
|
2145
|
-
- `AudioEngine` (interface): interface AudioEngine { setListenerPose(pose: ListenerPose | Vec3): void; setVoiceLimit(maxTotal: number, overflow?: VoiceAllocatorConfig["overflow"]): void;
|
|
2153
|
+
- `AudioEngine` (interface): interface AudioEngine { setListenerPose(pose: ListenerPose | Vec3): void; setVoiceLimit(maxTotal: number, overflow?: VoiceAllocatorConfig["overflow"]): void; play(soundId: string, position?: Vec3): void; playOneShot(soundId: string, position?: Vec3): void; playL… — ⚠ undocumented · used by `createAudioEngine`: Game-owned sound catalog playback with bounded SFX reservations, spatial panning, and shared buses.
|
|
2154
|
+
- `AudioLevel` (interface): interface AudioLevel — Master-output level, `0..1`. Pooled by {@link AudioEngine.measure} - read it, don't retain it.
|
|
2146
2155
|
- `AudioSceneConfig` (interface): interface AudioSceneConfig { sounds?: Record<string, SoundDef>; maxVoices?: number; voiceOverflow?: VoiceAllocatorConfig["overflow"]; voiceAllocator?: VoiceAllocator; buses?: Record<string, AudioBusDef>; music?: Record<string, MusicTheme>; musicBus?: string } — ⚠ undocumented · used by `createAudioEngine`: Game-owned sound catalog playback with bounded SFX reservations, spatial panning, and shared buses.
|
|
2147
2156
|
- `ListenerPose` (interface): interface ListenerPose — Position and orientation for a spatial-audio listener.
|
|
2157
|
+
- `MAX_PENDING_CUES` (const): const MAX_PENDING_CUES: 16 — Cues held while the browser keeps the audio context suspended, before any user gesture.
|
|
2148
2158
|
- `Vec3` (interface): interface Vec3 { x: number; y: number; z: number } — ⚠ undocumented · used by `coverPoints` (@jgengine/core/ai/tacticalQueries): Returns boundary locations that are hidden from a threat.
|
|
2149
2159
|
- `createAudioEngine` (function): function createAudioEngine(config: AudioSceneConfig = {}): AudioEngine — Game-owned sound catalog playback with bounded SFX reservations, spatial panning, and shared buses.
|
|
2150
2160
|
|
|
2161
|
+
## @jgengine/shell/audio/audioProbe
|
|
2162
|
+
|
|
2163
|
+
- `AudioProbe` (interface): interface AudioProbe — The read-only handle a capture script or devtools uses to prove the mix is producing sound.
|
|
2164
|
+
- `installAudioProbe` (function): function installAudioProbe(engine: AudioEngine): () => void — Publish the audio engine's own level readout on `window.__jgengineAudio`, so a headless capture can state a measured RMS instead of guessing from a screenshot. Reads only; it never plays or changes a bus.
|
|
2165
|
+
|
|
2166
|
+
## @jgengine/shell/audio/busMix
|
|
2167
|
+
|
|
2168
|
+
- `MASTER_MUTE_SETTING_ID` (const): const MASTER_MUTE_SETTING_ID: "sound.muted" — Persisted mute for the whole mix, next to `sound.master`.
|
|
2169
|
+
- `busMuteSettingId` (function): function busMuteSettingId(busId: string): string — Persisted mute for one bus, next to its `busVolumeSettingId(busId)` volume.
|
|
2170
|
+
|
|
2171
|
+
## @jgengine/shell/audio/captions
|
|
2172
|
+
|
|
2173
|
+
- `Caption` (interface): interface Caption — One caption line as the overlay renders it.
|
|
2174
|
+
- `CaptionFeed` (interface): interface CaptionFeed — A capped, self-expiring list of caption lines that the accessibility captions setting turns on.
|
|
2175
|
+
- `CaptionFeedConfig` (interface): interface CaptionFeedConfig — Tunables for {@link createCaptionFeed}; all retunable at runtime.
|
|
2176
|
+
- `createCaptionFeed` (function): function createCaptionFeed(config: CaptionFeedConfig = {}): CaptionFeed — The caption list the feedback seam writes to and the HUD overlay reads: bounded, self-expiring, and deduped so three hits read as "Hit x3" rather than three copies. Pure state with an injected clock, so it runs headless.
|
|
2177
|
+
|
|
2178
|
+
## @jgengine/shell/audio/eventFeedback
|
|
2179
|
+
|
|
2180
|
+
- `AnyFeedbackBinding` (type): type AnyFeedbackBinding = { [Name in FeedbackEventName]: FeedbackBinding<Name> }[FeedbackEventName] — Any row of the event mapping, whichever event it binds.
|
|
2181
|
+
- `DEFAULT_FEEDBACK_BINDINGS` (const): const DEFAULT_FEEDBACK_BINDINGS: readonly AnyFeedbackBinding[] — The default mapping: each engine-announced gameplay event to the cue it raises. Damage to the local player reads as hurt, damage anywhere else as a hit; a wave start has no event of its own, so a game announces it through `notify("wave")` or `attachWaveFeedback`.
|
|
2182
|
+
- `DEFAULT_FEEDBACK_REACTIONS` (const): const DEFAULT_FEEDBACK_REACTIONS: Readonly<Record<FeedbackCue, FeedbackReaction>> — The default reactions. They are data: a game overrides one through `audio.feedbackCues` (sound) or `createEventFeedback({ reactions })` (haptics, shake, caption), and replaces the mapping through `bindings` without touching the shell.
|
|
2183
|
+
- `EventFeedback` (interface): interface EventFeedback — The event-to-reaction seam. One instance per running world.
|
|
2184
|
+
- `EventFeedbackOptions` (interface): interface EventFeedbackOptions — Options a game or host may override.
|
|
2185
|
+
- `FeedbackAnnouncer` (interface): interface FeedbackAnnouncer — The slice of `ctx.game.feedback` a wave runner needs, so this runs headless in a test.
|
|
2186
|
+
- `FeedbackBinding` (type): type FeedbackBinding<Name extends FeedbackEventName = FeedbackEventName> = { event: Name; cue: FeedbackCue; /** Default `"any"`. `"local"` only fires for the local player's own target/user/refusal. */ scope?: FeedbackScope; /** Overrides the cue's caption for this event, e.g. "You died". */ caption?… — One row of the event mapping: `event` announces, `cue` reacts.
|
|
2187
|
+
- `FeedbackDeps` (interface): interface FeedbackDeps — Everything the feedback seam reaches outside itself. Injected so it runs headless.
|
|
2188
|
+
- `FeedbackEventName` (type): type FeedbackEventName = | "combat.damage" | "entity.died" | "worldItem.picked_up" | "quest.completed" | "stat.levelUp" | "command.refused" — Event names the default mapping listens to; a game may add its own through `bindings`.
|
|
2189
|
+
- `FeedbackReaction` (interface): interface FeedbackReaction — One cue's reaction: what plays, what the hand feels, what the screen does, and what a caption says.
|
|
2190
|
+
- `FeedbackScope` (type): type FeedbackScope = "local" | "remote" | "any" — Which side of an event a binding reacts to: the local player's, someone else's, or either.
|
|
2191
|
+
- `ResolvedFeedback` (interface): interface ResolvedFeedback — A resolved trigger: pooled per binding, so a busy event stream allocates nothing to decide.
|
|
2192
|
+
- `attachEventFeedback` (function): function attachEventFeedback(events: Pick<GameEvents, "on">, feedback: EventFeedback, eventsToBind: readonly FeedbackEventName[] = Object.keys( DEFAULT_FEEDBACK_BINDINGS.reduce<Record<string, true>>((names, binding) => { names[binding.event] = true; return names; }, {}), ) as FeedbackEventName[]): (… — Subscribe the feedback seam to a game's event bus, including `feedback.cue` for moments the game announces itself. Returns the detach function; the mapping and reactions stay override-able through the seam's `retune`.
|
|
2193
|
+
- `attachWaveFeedback` (function): function attachWaveFeedback(runner: Pick<WaveRunner, "state" | "subscribe">, announce: FeedbackAnnouncer): () => void — Announce each new wave a {@link WaveRunner} starts as the `wave` cue, once per boundary. A wave has no event of its own, so this is the engine-side hookup for tower defense and wave survival games: `attachWaveFeedback(runner, ctx.game.feedback)`. Nothing is polled, the runner's own notification drives it, and one `update` that crosses two waves still fires once - the cue has a cooldown and the next boundary is announced by the next crossing.
|
|
2194
|
+
- `createEventFeedback` (function): function createEventFeedback(deps: FeedbackDeps, options: EventFeedbackOptions = {}): EventFeedback — Build the event-to-sound/haptic/shake/caption seam. The mapping is data ({@link DEFAULT_FEEDBACK_BINDINGS}), reactions are data ({@link DEFAULT_FEEDBACK_REACTIONS}), and the resolved trigger for each binding is allocated once - handling an event allocates only the caption string it actually shows, never per frame.
|
|
2195
|
+
|
|
2196
|
+
## @jgengine/shell/audio/gestureResume
|
|
2197
|
+
|
|
2198
|
+
- `installGestureResume` (function): function installGestureResume(engine: Pick<AudioEngine, "resume">, target: Pick<Document, "addEventListener" | "removeEventListener"> | null = typeof document === "undefined" ? null : document): () => void — Resume the audio context on the first real user gesture. Chrome keeps a freshly created `AudioContext` suspended until the page sees a trusted input, so pressing a key, tapping or clicking is the only moment sound may start; the engine then flushes any cue it held. One listener set per game, removed on unmount.
|
|
2199
|
+
|
|
2151
2200
|
## @jgengine/shell/audio/loopParams
|
|
2152
2201
|
|
|
2153
2202
|
- `MAX_LOOP_CUTOFF` (const): const MAX_LOOP_CUTOFF: 22050 — Highest live filter cutoff, Hz; the engine also caps it at the context's Nyquist frequency.
|
|
@@ -64,6 +64,18 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
64
64
|
|
|
65
65
|
- `readModelAnimationParams` (function) · `import { readModelAnimationParams } from "@jgengine/shell/render/useModelAnimation"`
|
|
66
66
|
|
|
67
|
+
## attach-event-feedback — one call wires every announced combat/loot/objective/refusal event to its reaction
|
|
68
|
+
|
|
69
|
+
- `attachEventFeedback` (function) · `import { attachEventFeedback } from "@jgengine/shell/audio/eventFeedback"`
|
|
70
|
+
|
|
71
|
+
## attach-wave-feedback — fire the wave cue once whenever a wave runner crosses into a new wave
|
|
72
|
+
|
|
73
|
+
- `attachWaveFeedback` (function) · `import { attachWaveFeedback } from "@jgengine/shell/audio/eventFeedback"`
|
|
74
|
+
|
|
75
|
+
## audio-probe — read the live master RMS/peak and bus gains from a running page for capture and diagnostics
|
|
76
|
+
|
|
77
|
+
- `installAudioProbe` (function) · `import { installAudioProbe } from "@jgengine/shell/audio/audioProbe"`
|
|
78
|
+
|
|
67
79
|
## authored-flocks — render authored habitat agents through caller-selected animated species models with velocity orientation and bounded model lifetime
|
|
68
80
|
|
|
69
81
|
- `AuthoredFlocks` (function) · `import { AuthoredFlocks } from "@jgengine/shell/world/AuthoredFlocks"`
|
|
@@ -114,6 +126,14 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
114
126
|
|
|
115
127
|
- `isCameraOccluderTransparent` (function) · `import { isCameraOccluderTransparent } from "@jgengine/shell/camera"`
|
|
116
128
|
|
|
129
|
+
## caption-feed — bounded, self-expiring, deduped caption lines for the accessibility captions overlay
|
|
130
|
+
|
|
131
|
+
- `createCaptionFeed` (function) · `import { createCaptionFeed } from "@jgengine/shell/audio/captions"`
|
|
132
|
+
|
|
133
|
+
## caption-overlay — render the caption feed as a themed, screen-reader-announced line stack gated by the captions accessibility setting
|
|
134
|
+
|
|
135
|
+
- `CaptionOverlay` (function) · `import { CaptionOverlay } from "@jgengine/shell/audio/CaptionOverlay"`
|
|
136
|
+
|
|
117
137
|
## capture-canvas — read the live R3F frame to a PNG data URL (needs preserveDrawingBuffer)
|
|
118
138
|
|
|
119
139
|
- `captureCanvas` (function) · `import { captureCanvas } from "@jgengine/shell/render/sceneCapture"`
|
|
@@ -285,6 +305,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
285
305
|
|
|
286
306
|
- `EntitySummary` (function) · `import { EntitySummary } from "@jgengine/react"`
|
|
287
307
|
|
|
308
|
+
## event-feedback — map announced gameplay events (damage, kill, pickup, objective, wave, refusal) to a sound, gamepad pulse, camera shake and caption through one data mapping
|
|
309
|
+
|
|
310
|
+
- `createEventFeedback` (function) · `import { createEventFeedback } from "@jgengine/shell/audio/eventFeedback"`
|
|
311
|
+
|
|
288
312
|
## event-meter-hud — render a core event/heat meter's live value, fraction, tier, and ready state in a HUD gauge
|
|
289
313
|
|
|
290
314
|
- `useEventMeter` (function) · `import { useEventMeter } from "@jgengine/react"`
|
|
@@ -293,6 +317,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
293
317
|
|
|
294
318
|
- `FastTravelMenu` (function) · `import { FastTravelMenu } from "@jgengine/react"`
|
|
295
319
|
|
|
320
|
+
## feedback-host — mount event feedback, gesture-gated audio and captions for a running game in one component
|
|
321
|
+
|
|
322
|
+
- `FeedbackHost` (function) · `import { FeedbackHost } from "@jgengine/shell/audio/FeedbackHost"`
|
|
323
|
+
|
|
296
324
|
## floating-text — overlay that renders on-screen floating combat text with per-kind color, alpha, and pop-in scale
|
|
297
325
|
|
|
298
326
|
- `FloatingText` (function) · `import { FloatingText } from "@jgengine/react"`
|
|
@@ -309,6 +337,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
309
337
|
|
|
310
338
|
- `GameContextBridge` (function) · `import { GameContextBridge } from "@jgengine/react"`
|
|
311
339
|
|
|
340
|
+
## gesture-resume — unlock the audio context on the first pointer/key/touch gesture, so pre-gesture cues play instead of vanishing
|
|
341
|
+
|
|
342
|
+
- `installGestureResume` (function) · `import { installGestureResume } from "@jgengine/shell/audio/gestureResume"`
|
|
343
|
+
|
|
312
344
|
## ground-edge-fade — fade merged ground layers into the terrain at their outer edge, keeping inner seams crisp
|
|
313
345
|
|
|
314
346
|
- `applyGroundEdgeFade` (function) · `import { applyGroundEdgeFade } from "@jgengine/shell/terrain"`
|
|
@@ -351,7 +351,7 @@ Canyon Chase is the worked example: `movement: { axis: "horizontal" }` steering,
|
|
|
351
351
|
**`presentation: "hud"` games get 3D parity.** A pure-HUD game (no camera rig) now reaches the same input/audio seams as a 3D game:
|
|
352
352
|
- **Touch gestures** — the shell mounts a headless `TouchPlaySurface` in the hud branch too, so `touch.gestures` (swipe/tap) reach actions without the game hand-wiring pointer events on its own canvas. The visible dock stays game-authored per the rule above.
|
|
353
353
|
- **No phantom reservations** — camera action names (`turnLeft`/`turnRight`/`interact`/…) are reserved *only* when a camera rig is active, so a hud game may bind them directly instead of renaming to `steer*`.
|
|
354
|
-
- **Audio actually plays**
|
|
354
|
+
- **Audio actually plays** - audio resumes on the first pointer gesture in hud games (not just 3D), and `playOneShot` self-resumes the suspended context. Cues played before any gesture wait in a bounded queue and play on the first real input instead of vanishing. Trigger sound from anywhere holding `ctx` via `ctx.game.audio.play(soundId, at?)` / `ctx.game.audio.resume()` - the reachable seam over the shell's audio engine. Bus gain and mute compose (`engine.setBusMute`), so a muted bus keeps the player's chosen volume for unmute, and Settings carries per-bus and master mute. `ctx.game.feedback.cue("wave", "WAVE 3")` raises the engine's own reaction for a moment no event covers.
|
|
355
355
|
|
|
356
356
|
## 10. Progressive instruction
|
|
357
357
|
|