@jgengine/shell 0.31.0 → 0.33.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 +67 -0
- package/dist/GamePlayerShell.js +7 -0
- package/dist/Shell3dPresentation.js +6 -4
- package/dist/ShellChrome.d.ts +3 -1
- package/dist/ShellChrome.js +3 -2
- package/dist/ShellHudPresentation.js +1 -1
- package/dist/audio/AudioComponents.js +61 -51
- package/dist/audio/CaptionOverlay.d.ts +15 -0
- package/dist/audio/CaptionOverlay.js +50 -0
- package/dist/audio/FeedbackHost.d.ts +14 -0
- package/dist/audio/FeedbackHost.js +46 -0
- package/dist/audio/audioEngine.d.ts +25 -0
- package/dist/audio/audioEngine.js +267 -32
- package/dist/audio/audioProbe.d.ts +22 -0
- package/dist/audio/audioProbe.js +20 -0
- package/dist/audio/busMix.d.ts +4 -0
- package/dist/audio/busMix.js +7 -0
- package/dist/audio/captions.d.ts +48 -0
- package/dist/audio/captions.js +77 -0
- package/dist/audio/emitterTracking.d.ts +40 -0
- package/dist/audio/emitterTracking.js +153 -0
- package/dist/audio/eventFeedback.d.ts +137 -0
- package/dist/audio/eventFeedback.js +273 -0
- package/dist/audio/gestureResume.d.ts +10 -0
- package/dist/audio/gestureResume.js +25 -0
- package/dist/audio/hapticsRoute.d.ts +28 -0
- package/dist/audio/hapticsRoute.js +59 -0
- package/dist/audio/loopParams.js +20 -10
- package/dist/audio/playableAudio.d.ts +1 -1
- package/dist/audio/playableAudio.js +3 -1
- package/dist/devtools/FrameTelemetry.d.ts +1 -1
- package/dist/devtools/FrameTelemetry.js +4 -2
- package/dist/devtools/agentBridge.js +13 -0
- package/dist/devtools/frameSampler.js +4 -0
- package/dist/drivers/CanvasCoverFrameloop.d.ts +13 -0
- package/dist/drivers/CanvasCoverFrameloop.js +22 -0
- package/dist/drivers/FrameRateLimiter.d.ts +2 -1
- package/dist/drivers/FrameRateLimiter.js +6 -3
- package/dist/postfx/PostProcessing.d.ts +78 -2
- package/dist/postfx/PostProcessing.js +149 -76
- package/dist/postfx/postfxOverlay.d.ts +14 -4
- package/dist/postfx/postfxOverlay.js +90 -10
- package/dist/postfx/shadowPrepass.d.ts +6 -2
- package/dist/postfx/shadowPrepass.js +7 -3
- package/dist/render/characterLod.js +6 -0
- package/dist/render/useFootIk.js +54 -15
- package/dist/render/useModelAnimation.js +22 -5
- package/dist/settings/appliedSettings.js +3 -0
- package/dist/settings/settingsController.d.ts +10 -1
- package/dist/settings/settingsController.js +94 -67
- package/dist/vision/RevealVision.d.ts +3 -1
- package/dist/vision/RevealVision.js +34 -15
- package/dist/world/WorldItems.js +11 -3
- package/package.json +4 -4
- package/skills/jgengine/api.md +3 -2
- package/skills/jgengine-ui/SKILL.md +1 -0
- package/skills/jgengine-ui/api.md +94 -24
- package/skills/jgengine-ui/capabilities.md +40 -0
- package/skills/jgengine-ui/reference-primitives.md +2 -0
- package/skills/jgengine-ui/reference.md +1 -1
- package/skills/jgengine-world/api.md +18 -8
- package/skills/jgengine-world/capabilities.md +4 -0
|
@@ -1,37 +1,56 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useMemo } from "react";
|
|
2
|
+
import { useMemo, useState } from "react";
|
|
3
|
+
import { useFrame } from "@react-three/fiber";
|
|
3
4
|
import { DoubleSide } from "three";
|
|
4
5
|
import { createRevealQuery } from "@jgengine/core/sensor/revealQuery";
|
|
5
6
|
import { usePlayer, useSceneEntities } from "@jgengine/react/hooks";
|
|
6
|
-
|
|
7
|
+
function revealHits(entities, originId, options) {
|
|
8
|
+
const byId = new Map(entities.map((entity) => [entity.id, entity]));
|
|
9
|
+
const query = createRevealQuery({
|
|
10
|
+
resolvePosition: (id) => byId.get(id)?.position,
|
|
11
|
+
resolveTags: (id) => {
|
|
12
|
+
const entity = byId.get(id);
|
|
13
|
+
return entity === undefined ? [] : options.resolveTags(entity);
|
|
14
|
+
},
|
|
15
|
+
candidates: () => entities.map((entity) => entity.id),
|
|
16
|
+
});
|
|
17
|
+
return query.inRadius(originId, options.radius, options.tags);
|
|
18
|
+
}
|
|
19
|
+
/** Occlusion-ignoring tagged-entity radius query (#115), bound to the live scene and refreshed on render. */
|
|
7
20
|
export function useRevealHits(options) {
|
|
8
21
|
const entities = useSceneEntities();
|
|
9
22
|
const player = usePlayer();
|
|
10
23
|
const originId = options.originEntityId ?? player.userId;
|
|
11
24
|
const tagsKey = options.tags.join("|");
|
|
12
|
-
return useMemo(() =>
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
return query.inRadius(originId, options.radius, options.tags);
|
|
23
|
-
}, [entities, originId, options.radius, tagsKey, options.tags, options.resolveTags]);
|
|
25
|
+
return useMemo(() => revealHits(entities, originId, options), [entities, originId, options.radius, tagsKey, options.tags, options.resolveTags]);
|
|
26
|
+
}
|
|
27
|
+
/** Frames on a counter while `active`: the hook lists only change on membership, so a live overlay drives its own re-render. */
|
|
28
|
+
function useFrameRefresh(active) {
|
|
29
|
+
const [frame, setFrame] = useState(0);
|
|
30
|
+
useFrame(() => {
|
|
31
|
+
if (active)
|
|
32
|
+
setFrame((value) => (value + 1) % 1_000_000);
|
|
33
|
+
});
|
|
34
|
+
return frame;
|
|
24
35
|
}
|
|
25
36
|
/**
|
|
26
37
|
* Screen-space reveal effect (#115) — highlights tagged entities through
|
|
27
38
|
* occluders (Dark Sight / detective-vision / wallhack style). Renders with
|
|
28
39
|
* `depthTest: false` so the highlight draws over any wall standing between
|
|
29
40
|
* the origin and the revealed entity, rather than the usual depth-sorted scene.
|
|
41
|
+
* Highlights track the origin and their targets every frame while enabled, so
|
|
42
|
+
* a disabled mode costs no renders.
|
|
30
43
|
*/
|
|
31
44
|
export function RevealHighlights(props) {
|
|
32
45
|
const { enabled, color = "#7dd3fc" } = props;
|
|
33
|
-
const
|
|
46
|
+
const frame = useFrameRefresh(enabled);
|
|
34
47
|
const entities = useSceneEntities();
|
|
48
|
+
const player = usePlayer();
|
|
49
|
+
const originId = props.originEntityId ?? player.userId;
|
|
50
|
+
const tagsKey = props.tags.join("|");
|
|
51
|
+
const hits = useMemo(() => (enabled ? revealHits(entities, originId, props) : []),
|
|
52
|
+
// `frame` is the refresh: the list identity says nothing about where the origin or the targets moved.
|
|
53
|
+
[enabled, frame, entities, originId, props.radius, tagsKey, props.tags, props.resolveTags]);
|
|
35
54
|
if (!enabled)
|
|
36
55
|
return null;
|
|
37
56
|
return (_jsx(_Fragment, { children: hits.map((hit) => {
|
package/dist/world/WorldItems.js
CHANGED
|
@@ -5,12 +5,16 @@ import { useFrame } from "@react-three/fiber";
|
|
|
5
5
|
import { resolveWorldItemPresentation } from "@jgengine/core/game/worldItem";
|
|
6
6
|
import { useGameContext } from "@jgengine/react/provider";
|
|
7
7
|
import { useWorldItems } from "@jgengine/react/hooks";
|
|
8
|
+
import { emptyPoseMemo, writePoseIfChanged } from "./entityPose.js";
|
|
8
9
|
import { POINTER_ENTITY_KEY } from "../pointer/pointerService.js";
|
|
9
10
|
const DEFAULT_BEAM_HEIGHT = 2.5;
|
|
10
11
|
const FALLBACK_COLOR = "#e5e7eb";
|
|
11
12
|
const REST_HEIGHT = 0.32;
|
|
12
13
|
const SPAWN_DURATION = 0.4;
|
|
13
|
-
function WorldItemMarker({ instanceId,
|
|
14
|
+
function WorldItemMarker({ instanceId, color, beam, label, beamHeight, }) {
|
|
15
|
+
const ctx = useGameContext();
|
|
16
|
+
const groupRef = useRef(null);
|
|
17
|
+
const poseRef = useRef(emptyPoseMemo());
|
|
14
18
|
const beamPoints = useMemo(() => [
|
|
15
19
|
[0, 0, 0],
|
|
16
20
|
[0, beamHeight, 0],
|
|
@@ -21,6 +25,10 @@ function WorldItemMarker({ instanceId, position, color, beam, label, beamHeight,
|
|
|
21
25
|
const gemRef = useRef(null);
|
|
22
26
|
const ageRef = useRef(0);
|
|
23
27
|
useFrame((_state, delta) => {
|
|
28
|
+
const group = groupRef.current;
|
|
29
|
+
const live = ctx.scene.entity.get(instanceId);
|
|
30
|
+
if (group !== null && live !== null)
|
|
31
|
+
writePoseIfChanged(group, live, poseRef.current);
|
|
24
32
|
const gem = gemRef.current;
|
|
25
33
|
if (gem === null)
|
|
26
34
|
return;
|
|
@@ -31,7 +39,7 @@ function WorldItemMarker({ instanceId, position, color, beam, label, beamHeight,
|
|
|
31
39
|
gem.position.y = REST_HEIGHT + bob + spawn;
|
|
32
40
|
gem.rotation.y = age * 1.6;
|
|
33
41
|
});
|
|
34
|
-
return (_jsxs("group", {
|
|
42
|
+
return (_jsxs("group", { ref: groupRef, matrixAutoUpdate: false, userData: { [POINTER_ENTITY_KEY]: instanceId }, children: [_jsxs("mesh", { "rotation-x": -Math.PI / 2, "position-y": 0.03, children: [_jsx("ringGeometry", { args: [0.26, 0.4, 24] }), _jsx("meshBasicMaterial", { color: color, transparent: true, opacity: 0.85 })] }), _jsx("group", { ref: gemRef, "position-y": REST_HEIGHT, children: _jsxs("mesh", { children: [_jsx("octahedronGeometry", { args: [0.22, 0] }), _jsx("meshStandardMaterial", { color: color, emissive: color, emissiveIntensity: 0.6 })] }) }), beam ? _jsx(Line, { points: beamPoints, color: color, lineWidth: 2, transparent: true, opacity: 0.55 }) : null, label !== undefined && label.length > 0 ? (_jsx(Html, { position: [0, beamHeight + 0.2, 0], center: true, distanceFactor: 12, zIndexRange: [15, 0], children: _jsx("div", { className: "whitespace-nowrap rounded-sm border px-1.5 py-0.5 text-[11px] font-bold shadow", style: { borderColor: color, color, backgroundColor: "rgba(10,10,14,0.75)" }, children: label }) })) : null] }));
|
|
35
43
|
}
|
|
36
44
|
/** Rarity→beam/color/label render binding + loot-filter overlay (#32/#33) for every dropped `worldItem`. */
|
|
37
45
|
export function WorldItems({ config }) {
|
|
@@ -45,6 +53,6 @@ export function WorldItems({ config }) {
|
|
|
45
53
|
const presentation = resolveWorldItemPresentation(item, config?.rarityStyle, config?.filter);
|
|
46
54
|
if (presentation.hidden)
|
|
47
55
|
return null;
|
|
48
|
-
return (_jsx(WorldItemMarker, { instanceId: item.instanceId,
|
|
56
|
+
return (_jsx(WorldItemMarker, { instanceId: item.instanceId, color: presentation.color ?? FALLBACK_COLOR, beam: presentation.beam, label: presentation.label, beamHeight: beamHeight }, item.instanceId));
|
|
49
57
|
}) }));
|
|
50
58
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jgengine/shell",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.33.0",
|
|
4
4
|
"description": "Game player shell for JGengine: React Three Fiber canvas, orbit camera, input tracking, HUD mounting, GameUiPreview, and a demo game. Consumers supply a GameRegistry.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -35,9 +35,9 @@
|
|
|
35
35
|
"test": "bun test src"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@jgengine/core": "^0.
|
|
39
|
-
"@jgengine/react": "^0.
|
|
40
|
-
"@jgengine/ws": "^0.
|
|
38
|
+
"@jgengine/core": "^0.33.0",
|
|
39
|
+
"@jgengine/react": "^0.33.0",
|
|
40
|
+
"@jgengine/ws": "^0.33.0"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
43
|
"@react-three/drei": "^10.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.33.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.33.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.
|
|
@@ -186,16 +186,18 @@
|
|
|
186
186
|
- `SettingCategoryDef` (interface): interface SettingCategoryDef — Declares or relabels/reorders a category tab; use it for a custom category or to reshape the built-ins.
|
|
187
187
|
- `SettingKind` (type): type SettingKind = "slider" | "toggle" | "select" — ⚠ undocumented
|
|
188
188
|
- `SettingOption` (interface): interface SettingOption { value: string; label: string } — ⚠ undocumented · used by `COLORBLIND_OPTIONS` (@jgengine/core/settings/accessibilitySettings): Colorblind palette choices.
|
|
189
|
-
- `SettingValue` (type): type SettingValue = number | boolean | string — ⚠ undocumented · used by `
|
|
189
|
+
- `SettingValue` (type): type SettingValue = number | boolean | string — ⚠ undocumented · used by `asSettingType`: Read a stored value as the type the caller asked for.
|
|
190
190
|
- `SettingsActionDef` (interface): interface SettingsActionDef — A game-state action (Restart, Quit to menu, …) shown as rows in the first "Game" settings tab — never a floating button or a rebindable key.
|
|
191
191
|
- `SettingsStore` (interface): interface SettingsStore { get<T extends SettingValue>(id: string, fallback: T): T; set(id: string, value: SettingValue): void; subscribe(listener: () => void): () => void } — ⚠ undocumented · used by `createSettingsStore`: Reactive, localStorage-backed settings store shared by the shell wiring and React hooks.
|
|
192
192
|
- `SettingsSurface` (type): type SettingsSurface = "quick" — `quick` shows compact on-screen volume/graphics buttons; `false` (default) mounts no engine trigger — open the menu from your own UI with `<SettingsTrigger>` or `useSettings().open()`.
|
|
193
|
+
- `SettingsTypeError` (class): class SettingsTypeError extends Error — A stored setting holds a value the read asked for as another type, so no honest value exists.
|
|
193
194
|
- `SettingsVariant` (type): type SettingsVariant = "panel" | "sheet" | "sidebar" | "fullscreen" — The four themed settings layouts, chosen with `defineGame({ settings: { variant } })`. All read the game's `--jg-*` theme tokens.
|
|
194
195
|
- `UI_SCALE_MAX` (const): const UI_SCALE_MAX: 1.5 — ⚠ undocumented
|
|
195
196
|
- `UI_SCALE_MIN` (const): const UI_SCALE_MIN: 0.5 — ⚠ undocumented
|
|
197
|
+
- `asSettingType` (function): function asSettingType<T extends SettingValue>(id: string, stored: unknown, fallback: T): T — Read a stored value as the type the caller asked for. A value of another primitive type converts when the result is the same value in the asked-for type (a number as its digits, a boolean as `"true"`/`"false"`); otherwise it throws {@link SettingsTypeError}, naming the key and both types, instead of handing back the fallback as if nothing were stored.
|
|
196
198
|
- `busVolumeSettingId` (function): function busVolumeSettingId(busId: string): string — ⚠ undocumented
|
|
197
199
|
- `createSettingsStore` (function): function createSettingsStore(storage: Pick<WebStorageLike, "getItem" | "setItem"> | null | undefined = defaultStorage()): SettingsStore — Reactive, localStorage-backed settings store shared by the shell wiring and React hooks.
|
|
198
|
-
- `loadSettingValue` (function): function loadSettingValue<T extends SettingValue>(id: string, fallback: T, storage: Pick<WebStorageLike, "getItem"> | null | undefined = defaultStorage()): T —
|
|
200
|
+
- `loadSettingValue` (function): function loadSettingValue<T extends SettingValue>(id: string, fallback: T, storage: Pick<WebStorageLike, "getItem"> | null | undefined = defaultStorage()): T — Read one persisted setting as `fallback`'s type. An absent or unreadable cell yields the fallback; a stored value of an incompatible type throws {@link SettingsTypeError}.
|
|
199
201
|
- `saveSettingValue` (function): function saveSettingValue(id: string, value: SettingValue, storage: Pick<WebStorageLike, "setItem"> | null | undefined = defaultStorage()): void — ⚠ undocumented
|
|
200
202
|
- `settingStorageKey` (function): function settingStorageKey(id: string): string — ⚠ undocumented
|
|
201
203
|
|
|
@@ -310,7 +312,7 @@
|
|
|
310
312
|
- `SettingCategoryDef` (interface): interface SettingCategoryDef — Declares or relabels/reorders a category tab; use it for a custom category or to reshape the built-ins.
|
|
311
313
|
- `SettingKind` (type): type SettingKind = "slider" | "toggle" | "select" — ⚠ undocumented
|
|
312
314
|
- `SettingOption` (interface): interface SettingOption { value: string; label: string } — ⚠ undocumented · used by `COLORBLIND_OPTIONS` (@jgengine/core/settings/accessibilitySettings): Colorblind palette choices.
|
|
313
|
-
- `SettingValue` (type): type SettingValue = number | boolean | string — ⚠ undocumented · used by `
|
|
315
|
+
- `SettingValue` (type): type SettingValue = number | boolean | string — ⚠ undocumented · used by `asSettingType` (@jgengine/core/settings/settingsModel): Read a stored value as the type the caller asked for.
|
|
314
316
|
- `SettingsActionDef` (interface): interface SettingsActionDef — A game-state action (Restart, Quit to menu, …) shown as rows in the first "Game" settings tab — never a floating button or a rebindable key.
|
|
315
317
|
- `SettingsStore` (interface): interface SettingsStore { get<T extends SettingValue>(id: string, fallback: T): T; set(id: string, value: SettingValue): void; subscribe(listener: () => void): () => void } — ⚠ undocumented · used by `createSettingsStore`: Reactive, localStorage-backed settings store shared by the shell wiring and React hooks.
|
|
316
318
|
- `SettingsSurface` (type): type SettingsSurface = "quick" — `quick` shows compact on-screen volume/graphics buttons; `false` (default) mounts no engine trigger — open the menu from your own UI with `<SettingsTrigger>` or `useSettings().open()`.
|
|
@@ -682,6 +684,7 @@
|
|
|
682
684
|
- `CameraShakeMeter` (function): function CameraShakeMeter({ controller, animate = false, title = "CAMERA SHAKE", kindLabels, variation = "glass", className, style, }: CameraShakeMeterProps): ReactNode — A drop-in HUD panel that visualizes a core {@link CameraShakeController}: a trauma meter (reusing the shared {@link StaminaBar} building block) plus a live percentage and the current impact `kind` label. Presentation only — it reads the controller and never interprets `kind`, mapping it through `kindLabels` for display. A shell component applies the same controller's `offset()` to the camera; this panel is the on-screen readout of how much shake is in flight.
|
|
683
685
|
- `CameraShakeMeterProps` (interface): interface CameraShakeMeterProps — Props for {@link CameraShakeMeter}.
|
|
684
686
|
- `CameraShakeReadout` (interface): interface CameraShakeReadout — A live read of a camera-shake controller: current trauma `0..1` and the last game-owned `kind`.
|
|
687
|
+
- `CanvasCover` (interface): interface CanvasCover — A page-wide declaration that some surface paints over the whole game canvas.
|
|
685
688
|
- `CaptureOdds` (function): function CaptureOdds({ chance, className, fillClassName, }: { chance: number; className?: string; fillClassName?: string; }): React.JSX.Element — ⚠ undocumented
|
|
686
689
|
- `CardFace` (function): function CardFace({ rank, suit, faceDown = false, width = 64, height = 90, className, style, children, }: { rank: Rank; suit: Suit; faceDown?: boolean; width?: number; height?: number; className?: string; style?: CSSProperties; children?: ReactNode; }): React.JSX.Element — A single playing-card face: rank + suit pips, or a patterned back when `faceDown`.
|
|
687
690
|
- `CardStack` (function): function CardStack({ pile, zone, cardOf, ...rest }: { pile: CardPileState; zone: string; cardOf: (id: string) => PlayingCard; } & Omit<Parameters<typeof StackedPile>[0], "cards">): React.JSX.Element — `StackedPile` bound to a headless `CardPileState` zone: reads the ordered card ids from `pile.zones[zone]` and resolves each to a `PlayingCard` via `cardOf`.
|
|
@@ -866,7 +869,7 @@
|
|
|
866
869
|
- `MenuNavController` (interface): interface MenuNavController — The live navigation controller returned by {@link useMenuNav}.
|
|
867
870
|
- `MenuNavItemProps` (interface): interface MenuNavItemProps — Props a menu item needs so {@link useMenuNav} can focus it and track pointer/focus.
|
|
868
871
|
- `MenuNavRender` (type): type MenuNavRender = (nav: MenuNavController) => ReactNode — Render prop for a menu whose items come from {@link useMenuNav}.
|
|
869
|
-
- `MenuScreen` (function): function MenuScreen({ open = true, transition = "fade", scrim = "dim", enterMs = 220, exitMs = 160, reducedMotion, label, role = "dialog", backdrop, className, style, contentClassName, contentStyle, children, onClosed, }: MenuScreenProps): ReactNode — One animated screen of a game front-end: full-bleed, laid out by the game, with an enter/exit animation that runs on transform/opacity only (no layout, no frame hitch) and unmounts when the exit finishes. The scrim is translucent and the optional `backdrop` slot paints behind the content, so a live 3D scene stays visible behind a title, pause or settings screen. Durations come from the caller or collapse to zero under reduced motion.
|
|
872
|
+
- `MenuScreen` (function): function MenuScreen({ open = true, transition = "fade", scrim = "dim", coversCanvas = false, enterMs = 220, exitMs = 160, reducedMotion, label, role = "dialog", backdrop, className, style, contentClassName, contentStyle, children, onClosed, }: MenuScreenProps): ReactNode — One animated screen of a game front-end: full-bleed, laid out by the game, with an enter/exit animation that runs on transform/opacity only (no layout, no frame hitch) and unmounts when the exit finishes. The scrim is translucent and the optional `backdrop` slot paints behind the content, so a live 3D scene stays visible behind a title, pause or settings screen. Durations come from the caller or collapse to zero under reduced motion.
|
|
870
873
|
- `MenuScreenProps` (interface): interface MenuScreenProps — Props for {@link MenuScreen}.
|
|
871
874
|
- `MenuScreenScrim` (type): type MenuScreenScrim = "none" | "dim" | "blur" — How much a {@link MenuScreen} dims the scene behind it. `none` keeps a live 3D scene fully visible.
|
|
872
875
|
- `MenuScreenTransition` (type): type MenuScreenTransition = "fade" | "slide" | "scale" | "none" — Which motion a {@link MenuScreen} uses when it enters and leaves.
|
|
@@ -994,7 +997,7 @@
|
|
|
994
997
|
- `TimerReadoutSource` (type): type TimerReadoutSource = "auto" | "remaining" | "elapsed" — Which value a readout leads with. `"auto"` follows the timer's direction — remaining for a countdown, elapsed for a countup charge.
|
|
995
998
|
- `TimerRing` (function): function TimerRing({ timer, id, size = 72, thickness = 8, mode = "fill", color = "var(--jg-accent, #38bdf8)", trackColor = "var(--jg-bar-track, rgba(148,163,184,0.25))", children, active = true, className, style, }: TimerRingProps): ReactNode — A radial charge/cooldown ring driven off a {@link TimerSet}'s `progress01`. Re-reads every frame and sweeps an SVG arc as the timer advances — ability charge, cooldown wipe, respawn ring, capture progress, all the same widget. `mode` chooses filling vs. draining; center `children` can hold a readout. HudTheme-skinned via `--jg-accent` / `--jg-bar-track`.
|
|
996
999
|
- `TimerRingProps` (interface): interface TimerRingProps — Props for {@link TimerRing}.
|
|
997
|
-
- `TitleMenu` (function): function TitleMenu({ title, eyebrow, tagline, items, onSelect, onBack, footer, controlHint, backdrop, children, open = true, transition = "slide", scrim = "dim", enterMs, exitMs, reducedMotion, onClosed, className, style, contentClassName, contentStyle, }: TitleMenuProps): ReactNode — A game front-end title screen: wordmark, tagline, the game's verbs in a keyboard/gamepad-navigable list, a footer and an optional backdrop. The engine owns navigation, focus and the enter/exit animation; the game owns the title markup, wording, verbs, art and every colour through HudTheme tokens. Pass the live scene (or artwork) as `backdrop` so the title renders over 3D instead of a blank page.
|
|
1000
|
+
- `TitleMenu` (function): function TitleMenu({ title, eyebrow, tagline, items, onSelect, onBack, footer, controlHint, backdrop, children, open = true, transition = "slide", scrim = "dim", coversCanvas, enterMs, exitMs, reducedMotion, onClosed, className, style, contentClassName, contentStyle, }: TitleMenuProps): ReactNode — A game front-end title screen: wordmark, tagline, the game's verbs in a keyboard/gamepad-navigable list, a footer and an optional backdrop. The engine owns navigation, focus and the enter/exit animation; the game owns the title markup, wording, verbs, art and every colour through HudTheme tokens. Pass the live scene (or artwork) as `backdrop` so the title renders over 3D instead of a blank page.
|
|
998
1001
|
- `TitleMenuItem` (interface): interface TitleMenuItem — One verb on a {@link TitleMenu}: New Game, Continue, Settings, Credits, Quit.
|
|
999
1002
|
- `TitleMenuProps` (interface): interface TitleMenuProps — Props for {@link TitleMenu}.
|
|
1000
1003
|
- `ToastStack` (function): function ToastStack({ action, limit = 4, className, renderToast, }: { action: string; limit?: number; className?: string; renderToast?: (entry: FeedEntry, index: number) => ReactNode; }): React.JSX.Element | null — Render `ctx.game.feed`'s entries for `action` as a newest-first toast stack — the feed-backed sibling of `@jgengine/core/game/toasts`' `createToastQueue`. Reach for this when the message source is already an engine event bound onto `ctx.game.feed` (kill feed, quest updates, loot log); reach for a `createToastQueue` when the game raises ad-hoc messages that need their own TTL independent of the feed's ring buffer.
|
|
@@ -1033,6 +1036,7 @@
|
|
|
1033
1036
|
- `actionFromAbilitySlot` (function): function actionFromAbilitySlot(slot: AbilitySlotSnapshot, extra?: Partial<ActionDef> & { resourceId?: string }): ActionDef — Composable React renderers over the headless action view model (`@jgengine/core/ui/actionModel`) — the data/renderer/chrome split for an RTS command card, an ability action bar, a build menu, or a radial. `useActionBar` is the DATA/HOOK layer (resolution + focus/hover/keyboard/hotkey), `ActionBarChrome` is the composable RENDERER (swap `renderItem` for custom chrome, or ignore it and consume the model directly for a radial), and `ActionBar` is the batteries-included default. A game swaps renderer or chrome without forking the logic. Layout, dimensions, hotkeys, and catalog-id mapping stay caller-owned.
|
|
1034
1037
|
- `barTokens` (function): function barTokens(tokens: BarTokens): CSSProperties — A style object binding vitals tokens to CSS custom properties — spread onto any HUD ancestor to restyle every atomic bar under it at once. Only the keys you pass are set; the rest fall back to {@link DEFAULT_BAR_TOKENS} inside the bars. This is the single global-theming seam.
|
|
1035
1038
|
- `betterAuthIdentity` (function): function betterAuthIdentity(state: BetterAuthSessionState, options?: { signOut?: () => void }): IdentitySource — ⚠ undocumented
|
|
1039
|
+
- `canvasCover` (const): const canvasCover: CanvasCover — The page's canvas cover. A full-screen menu (or a game's own opaque overlay) holds it; the shell stops drawing the world, its shadow passes and postfx while it is held, and draws again from the untouched scene when it lifts. A translucent scrim or a side panel must not hold it — anything the scene shows through has to keep drawing.
|
|
1036
1040
|
- `chatTransportFromSync` (function): function chatTransportFromSync(sync: ChatSync): ChatTransport — Lifts a callback-style ChatSync (e.g. createWsBackend().chatSyncFor(serverId)) into the hook-shaped ChatTransport contract. Create once per sync — outside render or inside useMemo — so subscriptions survive re-renders.
|
|
1037
1041
|
- `clerkIdentity` (function): function clerkIdentity(state: ClerkUserState, options?: { signOut?: () => void }): IdentitySource — ⚠ undocumented
|
|
1038
1042
|
- `createHeldKeyTracker` (function): function createHeldKeyTracker(target: HeldKeyEventTarget): { isDown: (code: string) => boolean; dispose: () => void; } — ⚠ undocumented
|
|
@@ -1077,6 +1081,8 @@
|
|
|
1077
1081
|
- `useAxisChannel` (function): function useAxisChannel(config: AxisChannelConfig): UseAxisChannelResult — Wires useHeldKeys into a fresh AxisChannel, ready for a per-frame `channel.sample(dt, isDown)`. The channel is recreated when `config` identity changes, so pass a stable config (useMemo/module constant at the call site) unless a rebind is intended.
|
|
1078
1082
|
- `useBrowserSuspension` (function): function useBrowserSuspension(onSuspend: (reason: BrowserSuspensionReason) => void, options: BrowserSuspensionOptions = {}): void — Observe browser interruptions while mounted, invoking the latest callback without replacing listeners.
|
|
1079
1083
|
- `useCameraShake` (function): function useCameraShake(controller: CameraShakeController, animate = true): CameraShakeReadout — Subscribe to a camera-shake controller and re-render on every change (add / decay / clear). Optionally drives `update()` on an animation frame so the trauma meter bleeds down live even when no game loop ticks the controller. Returns the current trauma + kind for a HUD readout.
|
|
1084
|
+
- `useCanvasCover` (function): function useCanvasCover(declared: boolean): void — Declare the canvas fully covered while `declared` is true, and release on unmount.
|
|
1085
|
+
- `useCanvasCovered` (function): function useCanvasCovered(): boolean — True while a surface covers the canvas.
|
|
1080
1086
|
- `useChat` (function): function useChat(channelId: string, options?: { limit?: number }): ChatMessage[] — ⚠ undocumented
|
|
1081
1087
|
- `useChatBubbles` (function): function useChatBubbles(options?: ChatBubblesOptions): readonly ChatBubble[] — ⚠ undocumented
|
|
1082
1088
|
- `useCoachMarks` (function): function useCoachMarks(sequence: CoachMarkSequence): CoachMarkView | null — Subscribe to a coach-mark sequence and re-render on every change, returning the current step view (or `null` when the tour is complete or waiting on a gate).
|
|
@@ -1094,10 +1100,10 @@
|
|
|
1094
1100
|
- `useEventMeter` (function): function useEventMeter(meter: EventMeter, options?: AbilitySlotBindingOptions): EventMeterView — Bind a `createEventMeter` (`@jgengine/core/stats/eventMeter`) heat/streak gauge to a component — the react-render half of the ult/adrenaline and streak/combo meters (`event-meter` capability) that lets a HUD gauge re-render on tick without the game hand-rolling a `useEffect`/`setInterval` heartbeat around `meter.value()`.
|
|
1095
1101
|
- `useEventTicker` (function): function useEventTicker(ticker: EventTicker, animate = true): EventTickerView[] — Subscribe to an event ticker and re-render it on an animation frame (unless the game already ticks it), returning the live entries newest-first with `fade` applied. Driving the frame loop is what makes entries visibly fade and drop as they age out.
|
|
1096
1102
|
- `useFastTravel` (function): function useFastTravel<TMeta = unknown>(network: FastTravelNetwork<TMeta>): FastTravelNetwork<TMeta> — Subscribe a component to a fast-travel network's discovery changes. Returns the network unchanged; the hook only forces a re-render when a point is discovered or the network is restored, so callers can read `destinations`/`list` in render.
|
|
1097
|
-
- `useFeed` (function): function useFeed({ action, limit }: { action: string; limit?: number }): FeedEntry[] —
|
|
1103
|
+
- `useFeed` (function): function useFeed({ action, limit }: { action: string; limit?: number }): FeedEntry[] — Recent feed entries for `action`, newest last: the list keeps one identity until an entry is pushed, so a HUD ticker or kill feed no longer re-renders on every pose write (#2216).
|
|
1098
1104
|
- `useFog` (function): function useFog(fog: FogField): ReturnType<FogField["cells"]> — ⚠ undocumented
|
|
1099
1105
|
- `useFriendRequests` (function): function useFriendRequests(): FriendRequestEntry[] — ⚠ undocumented
|
|
1100
|
-
- `useFriends` (function): function useFriends(): FriendEntry[] —
|
|
1106
|
+
- `useFriends` (function): function useFriends(): FriendEntry[] — The local player's friends with their presence: the list keeps one identity until a request is accepted, a friend is removed or a friend's presence flips, so a friends HUD no longer re-renders on every pose write (#2216).
|
|
1101
1107
|
- `useGame` (function): function useGame(): { commands: Omit<GameContext["game"]["commands"], "run"> & { run(name: string, input: unknown): DispatchedCommandResult }; events: GameEvents } — UI command surface. Hosted commands return a promise; await it to display acknowledgement or rejection. Registration and runAs remain trusted local APIs.
|
|
1102
1108
|
- `useGameClock` (function): function useGameClock(): ClockSnapshot & { controls: SimClock } — ⚠ undocumented
|
|
1103
1109
|
- `useGameContext` (function): function useGameContext(): GameContext — ⚠ undocumented
|
|
@@ -1140,7 +1146,7 @@
|
|
|
1140
1146
|
- `usePlural` (function): function usePlural(): (key: string, count: number, params?: TParams) => string — Return the plural-aware translator bound to the current locale.
|
|
1141
1147
|
- `usePrefersReducedMotion` (function): function usePrefersReducedMotion(): boolean — OS "reduce motion" preference (`prefers-reduced-motion: reduce`) as a live boolean — SSR-safe (defaults false). Use it to seed an accessibility store's `reducedMotion` default.
|
|
1142
1148
|
- `usePresence` (function): function usePresence(userId: string): PresenceInfo — ⚠ undocumented
|
|
1143
|
-
- `useQuestJournal` (function): function useQuestJournal(): QuestInstance[] —
|
|
1149
|
+
- `useQuestJournal` (function): function useQuestJournal(): QuestInstance[] — The local player's journal: the list keeps one identity while the accepted quests, their status and their objective progress are unchanged, so a tracker HUD no longer re-renders on every pose write (#2216). Accepting, progressing, completing or revoking a quest re-renders once.
|
|
1144
1150
|
- `useRafLoop` (function): function useRafLoop(onFrame: (deltaSeconds: number) => void, active = true): void — Run a requestAnimationFrame loop while `active`, calling `onFrame(deltaSeconds)` each frame. The callback is kept in a ref so re-renders never restart the loop; cleanup cancels the pending frame. For scene-graph work inside a canvas prefer R3F's useFrame — this is for DOM-side animation outside the renderer.
|
|
1145
1151
|
- `useRebindSession` (function): function useRebindSession(session: RebindSession): RebindSession — Subscribe to a rebind session and re-render on every change (capture, reset, arm/disarm, restore). Returns the session unchanged for convenient inline use.
|
|
1146
1152
|
- `useReducedMotion` (function): function useReducedMotion(): boolean — True when motion should be skipped: the OS `prefers-reduced-motion` preference, or the nearest {@link AccessibilityProvider}'s `reducedMotion` setting. Works without a provider.
|
|
@@ -1149,10 +1155,10 @@
|
|
|
1149
1155
|
- `useRoster` (function): function useRoster(userId?: string): readonly RosterEntry[] — ⚠ undocumented
|
|
1150
1156
|
- `useSaveSlots` (function): function useSaveSlots(model: SaveSlots): SaveSlotMeta[] — Subscribe to a {@link SaveSlots} index and re-render on any change (write, clear, rename, restore). Returns the live list of slots in menu order. Wire it into a save-select screen and mutate the model in event handlers — the menu re-renders itself.
|
|
1151
1157
|
- `useSaveStore` (function): function useSaveStore<T>(store: SaveStore<T>, options?: { load?: boolean }): SaveStoreView<T> — Bind a {@link SaveStore} to React: returns the live `value` and `status` and re-renders whenever either changes. By default it calls `load()` once on mount (pass `{ load: false }` to hydrate yourself). The same hook works for offline (localStorage) and cloud (Convex/DB) saves — only the store's backend differs.
|
|
1152
|
-
- `useSceneEntities` (function): function useSceneEntities(): readonly SceneEntity[] —
|
|
1158
|
+
- `useSceneEntities` (function): function useSceneEntities(): readonly SceneEntity[] — Live entities: the returned list keeps one identity until an entity spawns, despawns, is replaced or the store is hydrated — a pose write does not re-render its readers (#2212). `entity.list()` allocates a new array per write against an `Object.is` comparison, so every pose write re-rendered every HUD reading this hook. The records themselves are mutated in place, so a reader that needs a pose right now can still take `entity.position`; render-loop work reads it in `useFrame`.
|
|
1153
1159
|
- `useSceneEntityIds` (function): function useSceneEntityIds(): readonly string[] — Membership-only entity id list: the returned array keeps a stable identity across per-frame pose writes and only changes when an entity spawns, despawns, or the store is hydrated (#625). A marker mapped from these ids reads its own live pose imperatively (useFrame), so the actor tree no longer re-reconciles every frame. Prefer this over {@link useSceneEntities} for large scenes.
|
|
1154
1160
|
- `useSceneObjectIds` (function): function useSceneObjectIds(): readonly string[] — Membership-only object id list — the object counterpart of {@link useSceneEntityIds}; stable across move/rotate/setVisual, changes only on place/remove.
|
|
1155
|
-
- `useSceneObjects` (function): function useSceneObjects(): readonly SceneObject[] —
|
|
1161
|
+
- `useSceneObjects` (function): function useSceneObjects(): readonly SceneObject[] — Placed objects: the returned list keeps one identity across move and rotate writes, so a HUD reading it no longer re-renders on every pose write (#2212). Place, remove and the non-pose fields (catalog id, parent space, animation, visual, state, slots) still re-render. `move`/`rotate` replace the record, so take live placement from `ctx.scene.object.get(instanceId)` rather than the record's `position`.
|
|
1156
1162
|
- `useSelectionView` (function): function useSelectionView(members: readonly EntitySummaryDef[], options?: UseSelectionViewOptions): SelectionModel — The DATA/HOOK layer: summarize a selected-entity list into a live model with a focused primary, ordered members, same-kind groups, keyboard focus stepping, and a virtualization window. Rendering-agnostic — feed it to {@link SelectionPanel} or read the `view` directly.
|
|
1157
1163
|
- `useSequenceDirector` (function): function useSequenceDirector(director: SequenceDirector): SequenceDirectorBinding — Bind a {@link SequenceDirector} to a component: subscribe to its state, and while it is playing run a `requestAnimationFrame` loop that ticks the director each frame off the same injected clock — so a game drops in one hook and the cutscene advances itself. Returns the current playback state (playhead, progress, playing/done) plus bound `play`/`pause`/`skip`/`seek`/`stop`. Cue side effects belong on `director.onCue`, not here — the hook never interprets a cue.
|
|
1158
1164
|
- `useSession` (function): function useSession(): IdentitySource — ⚠ undocumented
|
|
@@ -1171,7 +1177,7 @@
|
|
|
1171
1177
|
- `useWaypoints` (function): function useWaypoints(source: WaypointTracker | readonly Waypoint[]): readonly Waypoint[] — Subscribe to a {@link WaypointTracker} and re-render on every change, returning its current waypoints. Pass a plain array instead and it is returned as-is (no subscription), so a game can drive the host from a tracker or from its own derived state.
|
|
1172
1178
|
- `useWorldBrowser` (function): function useWorldBrowser(options: { fetchSessions: () => Promise<readonly SessionListing[]>; filter?: MatchFilter; limit?: number; refreshMs?: number; }): WorldBrowserState — Polls a host-supplied session fetcher (e.g. createWsBackend().browse) and filters through matchmaking's browseSessions. fetchSessions must be identity-stable (wrap in useCallback at the call site) or every render refetches.
|
|
1173
1179
|
- `useWorldInvites` (function): function useWorldInvites(): WorldInvite[] — ⚠ undocumented
|
|
1174
|
-
- `useWorldItems` (function): function useWorldItems(): readonly WorldItemRecord[] —
|
|
1180
|
+
- `useWorldItems` (function): function useWorldItems(): readonly WorldItemRecord[] — Live dropped-item records: the returned list keeps one identity until an item spawns, is picked up or is removed. `worldItem.list()` allocates a new array per call and the shared store signal fires on every pose write, so a raw read re-rendered every marker each frame (#2175). A marker reads its own live pose imperatively (useFrame).
|
|
1175
1181
|
|
|
1176
1182
|
## @jgengine/react/accessibility
|
|
1177
1183
|
|
|
@@ -1249,6 +1255,13 @@
|
|
|
1249
1255
|
- `CameraShakeReadout` (interface): interface CameraShakeReadout — A live read of a camera-shake controller: current trauma `0..1` and the last game-owned `kind`.
|
|
1250
1256
|
- `useCameraShake` (function): function useCameraShake(controller: CameraShakeController, animate = true): CameraShakeReadout — Subscribe to a camera-shake controller and re-render on every change (add / decay / clear). Optionally drives `update()` on an animation frame so the trauma meter bleeds down live even when no game loop ticks the controller. Returns the current trauma + kind for a HUD readout.
|
|
1251
1257
|
|
|
1258
|
+
## @jgengine/react/canvasCover
|
|
1259
|
+
|
|
1260
|
+
- `CanvasCover` (interface): interface CanvasCover — A page-wide declaration that some surface paints over the whole game canvas.
|
|
1261
|
+
- `canvasCover` (const): const canvasCover: CanvasCover — The page's canvas cover. A full-screen menu (or a game's own opaque overlay) holds it; the shell stops drawing the world, its shadow passes and postfx while it is held, and draws again from the untouched scene when it lifts. A translucent scrim or a side panel must not hold it — anything the scene shows through has to keep drawing.
|
|
1262
|
+
- `useCanvasCover` (function): function useCanvasCover(declared: boolean): void — Declare the canvas fully covered while `declared` is true, and release on unmount.
|
|
1263
|
+
- `useCanvasCovered` (function): function useCanvasCovered(): boolean — True while a surface covers the canvas.
|
|
1264
|
+
|
|
1252
1265
|
## @jgengine/react/cards
|
|
1253
1266
|
|
|
1254
1267
|
- `CardFace` (function): function CardFace({ rank, suit, faceDown = false, width = 64, height = 90, className, style, children, }: { rank: Rank; suit: Suit; faceDown?: boolean; width?: number; height?: number; className?: string; style?: CSSProperties; children?: ReactNode; }): React.JSX.Element — A single playing-card face: rank + suit pips, or a patterned back when `faceDown`.
|
|
@@ -1473,9 +1486,9 @@
|
|
|
1473
1486
|
- `useDomEvent` (function): function useDomEvent<E>(resolveTarget: () => DomEventTarget | null, type: string, handler: (event: E) => void, options?: { capture?: boolean; passive?: boolean }): void — Attach a DOM event listener with automatic cleanup. `resolveTarget` runs inside the effect, so `() => window` and ref-reading resolvers are SSR-safe; return null to skip attaching. The handler is kept in a ref, so a fresh closure per render never re-binds the listener. Re-binds only when `type` or `options.capture`/`options.passive` change.
|
|
1474
1487
|
- `useEntityStat` (function): function useEntityStat(instanceId: string, statId: string): StatValue | null — ⚠ undocumented
|
|
1475
1488
|
- `useEventMeter` (function): function useEventMeter(meter: EventMeter, options?: AbilitySlotBindingOptions): EventMeterView — Bind a `createEventMeter` (`@jgengine/core/stats/eventMeter`) heat/streak gauge to a component — the react-render half of the ult/adrenaline and streak/combo meters (`event-meter` capability) that lets a HUD gauge re-render on tick without the game hand-rolling a `useEffect`/`setInterval` heartbeat around `meter.value()`.
|
|
1476
|
-
- `useFeed` (function): function useFeed({ action, limit }: { action: string; limit?: number }): FeedEntry[] —
|
|
1489
|
+
- `useFeed` (function): function useFeed({ action, limit }: { action: string; limit?: number }): FeedEntry[] — Recent feed entries for `action`, newest last: the list keeps one identity until an entry is pushed, so a HUD ticker or kill feed no longer re-renders on every pose write (#2216).
|
|
1477
1490
|
- `useFriendRequests` (function): function useFriendRequests(): FriendRequestEntry[] — ⚠ undocumented
|
|
1478
|
-
- `useFriends` (function): function useFriends(): FriendEntry[] —
|
|
1491
|
+
- `useFriends` (function): function useFriends(): FriendEntry[] — The local player's friends with their presence: the list keeps one identity until a request is accepted, a friend is removed or a friend's presence flips, so a friends HUD no longer re-renders on every pose write (#2216).
|
|
1479
1492
|
- `useGame` (function): function useGame(): { commands: Omit<GameContext["game"]["commands"], "run"> & { run(name: string, input: unknown): DispatchedCommandResult }; events: GameEvents } — UI command surface. Hosted commands return a promise; await it to display acknowledgement or rejection. Registration and runAs remain trusted local APIs.
|
|
1480
1493
|
- `useGameClock` (function): function useGameClock(): ClockSnapshot & { controls: SimClock } — ⚠ undocumented
|
|
1481
1494
|
- `useGamePhase` (function): function useGamePhase(): { phase: GamePhase; setPhase: (phase: GamePhase) => void } — Live run phase + a setter that also gates the shell's touch controls. `menu`/`paused`/`ended` hide the touch dock; `playing` shows it.
|
|
@@ -1493,18 +1506,18 @@
|
|
|
1493
1506
|
- `usePartyInvites` (function): function usePartyInvites(): PartyInviteEntry[] — ⚠ undocumented
|
|
1494
1507
|
- `usePlayer` (function): function usePlayer(): { userId: string; isNew: boolean } — ⚠ undocumented
|
|
1495
1508
|
- `usePresence` (function): function usePresence(userId: string): PresenceInfo — ⚠ undocumented
|
|
1496
|
-
- `useQuestJournal` (function): function useQuestJournal(): QuestInstance[] —
|
|
1509
|
+
- `useQuestJournal` (function): function useQuestJournal(): QuestInstance[] — The local player's journal: the list keeps one identity while the accepted quests, their status and their objective progress are unchanged, so a tracker HUD no longer re-renders on every pose write (#2216). Accepting, progressing, completing or revoking a quest re-renders once.
|
|
1497
1510
|
- `useRafLoop` (function): function useRafLoop(onFrame: (deltaSeconds: number) => void, active = true): void — Run a requestAnimationFrame loop while `active`, calling `onFrame(deltaSeconds)` each frame. The callback is kept in a ref so re-renders never restart the loop; cleanup cancels the pending frame. For scene-graph work inside a canvas prefer R3F's useFrame — this is for DOM-side animation outside the renderer.
|
|
1498
1511
|
- `useRoster` (function): function useRoster(userId?: string): readonly RosterEntry[] — ⚠ undocumented
|
|
1499
|
-
- `useSceneEntities` (function): function useSceneEntities(): readonly SceneEntity[] —
|
|
1512
|
+
- `useSceneEntities` (function): function useSceneEntities(): readonly SceneEntity[] — Live entities: the returned list keeps one identity until an entity spawns, despawns, is replaced or the store is hydrated — a pose write does not re-render its readers (#2212). `entity.list()` allocates a new array per write against an `Object.is` comparison, so every pose write re-rendered every HUD reading this hook. The records themselves are mutated in place, so a reader that needs a pose right now can still take `entity.position`; render-loop work reads it in `useFrame`.
|
|
1500
1513
|
- `useSceneEntityIds` (function): function useSceneEntityIds(): readonly string[] — Membership-only entity id list: the returned array keeps a stable identity across per-frame pose writes and only changes when an entity spawns, despawns, or the store is hydrated (#625). A marker mapped from these ids reads its own live pose imperatively (useFrame), so the actor tree no longer re-reconciles every frame. Prefer this over {@link useSceneEntities} for large scenes.
|
|
1501
1514
|
- `useSceneObjectIds` (function): function useSceneObjectIds(): readonly string[] — Membership-only object id list — the object counterpart of {@link useSceneEntityIds}; stable across move/rotate/setVisual, changes only on place/remove.
|
|
1502
|
-
- `useSceneObjects` (function): function useSceneObjects(): readonly SceneObject[] —
|
|
1515
|
+
- `useSceneObjects` (function): function useSceneObjects(): readonly SceneObject[] — Placed objects: the returned list keeps one identity across move and rotate writes, so a HUD reading it no longer re-renders on every pose write (#2212). Place, remove and the non-pose fields (catalog id, parent space, animation, visual, state, slots) still re-render. `move`/`rotate` replace the record, so take live placement from `ctx.scene.object.get(instanceId)` rather than the record's `position`.
|
|
1503
1516
|
- `useTarget` (function): function useTarget(fromInstanceId: string): string | null — The entity `fromInstanceId` currently targets (set by `ctx.scene.entity.setTarget` or `cycleTarget`), for a target frame.
|
|
1504
1517
|
- `useTicker` (function): function useTicker(hz = 10): number — Re-render at a steady rate. Returns a monotonically increasing tick count driven by a setInterval, for HUD elements that display wall-clock-derived values (cooldowns, cast bars, swing timers) without an engine subscription to hang off. `hz <= 0` disables the ticker.
|
|
1505
1518
|
- `useWorldBrowser` (function): function useWorldBrowser(options: { fetchSessions: () => Promise<readonly SessionListing[]>; filter?: MatchFilter; limit?: number; refreshMs?: number; }): WorldBrowserState — Polls a host-supplied session fetcher (e.g. createWsBackend().browse) and filters through matchmaking's browseSessions. fetchSessions must be identity-stable (wrap in useCallback at the call site) or every render refetches.
|
|
1506
1519
|
- `useWorldInvites` (function): function useWorldInvites(): WorldInvite[] — ⚠ undocumented
|
|
1507
|
-
- `useWorldItems` (function): function useWorldItems(): readonly WorldItemRecord[] —
|
|
1520
|
+
- `useWorldItems` (function): function useWorldItems(): readonly WorldItemRecord[] — Live dropped-item records: the returned list keeps one identity until an item spawns, is picked up or is removed. `worldItem.list()` allocates a new array per call and the shared store signal fires on every pose write, so a raw read re-rendered every marker each frame (#2175). A marker reads its own live pose imperatively (useFrame).
|
|
1508
1521
|
|
|
1509
1522
|
## @jgengine/react/hud
|
|
1510
1523
|
|
|
@@ -1791,7 +1804,7 @@
|
|
|
1791
1804
|
|
|
1792
1805
|
## @jgengine/react/menuScreen
|
|
1793
1806
|
|
|
1794
|
-
- `MenuScreen` (function): function MenuScreen({ open = true, transition = "fade", scrim = "dim", enterMs = 220, exitMs = 160, reducedMotion, label, role = "dialog", backdrop, className, style, contentClassName, contentStyle, children, onClosed, }: MenuScreenProps): ReactNode — One animated screen of a game front-end: full-bleed, laid out by the game, with an enter/exit animation that runs on transform/opacity only (no layout, no frame hitch) and unmounts when the exit finishes. The scrim is translucent and the optional `backdrop` slot paints behind the content, so a live 3D scene stays visible behind a title, pause or settings screen. Durations come from the caller or collapse to zero under reduced motion.
|
|
1807
|
+
- `MenuScreen` (function): function MenuScreen({ open = true, transition = "fade", scrim = "dim", coversCanvas = false, enterMs = 220, exitMs = 160, reducedMotion, label, role = "dialog", backdrop, className, style, contentClassName, contentStyle, children, onClosed, }: MenuScreenProps): ReactNode — One animated screen of a game front-end: full-bleed, laid out by the game, with an enter/exit animation that runs on transform/opacity only (no layout, no frame hitch) and unmounts when the exit finishes. The scrim is translucent and the optional `backdrop` slot paints behind the content, so a live 3D scene stays visible behind a title, pause or settings screen. Durations come from the caller or collapse to zero under reduced motion.
|
|
1795
1808
|
- `MenuScreenProps` (interface): interface MenuScreenProps — Props for {@link MenuScreen}.
|
|
1796
1809
|
- `MenuScreenScrim` (type): type MenuScreenScrim = "none" | "dim" | "blur" — How much a {@link MenuScreen} dims the scene behind it. `none` keeps a live 3D scene fully visible.
|
|
1797
1810
|
- `MenuScreenTransition` (type): type MenuScreenTransition = "fade" | "slide" | "scale" | "none" — Which motion a {@link MenuScreen} uses when it enters and leaves.
|
|
@@ -2051,7 +2064,7 @@
|
|
|
2051
2064
|
|
|
2052
2065
|
## @jgengine/react/titleMenu
|
|
2053
2066
|
|
|
2054
|
-
- `TitleMenu` (function): function TitleMenu({ title, eyebrow, tagline, items, onSelect, onBack, footer, controlHint, backdrop, children, open = true, transition = "slide", scrim = "dim", enterMs, exitMs, reducedMotion, onClosed, className, style, contentClassName, contentStyle, }: TitleMenuProps): ReactNode — A game front-end title screen: wordmark, tagline, the game's verbs in a keyboard/gamepad-navigable list, a footer and an optional backdrop. The engine owns navigation, focus and the enter/exit animation; the game owns the title markup, wording, verbs, art and every colour through HudTheme tokens. Pass the live scene (or artwork) as `backdrop` so the title renders over 3D instead of a blank page.
|
|
2067
|
+
- `TitleMenu` (function): function TitleMenu({ title, eyebrow, tagline, items, onSelect, onBack, footer, controlHint, backdrop, children, open = true, transition = "slide", scrim = "dim", coversCanvas, enterMs, exitMs, reducedMotion, onClosed, className, style, contentClassName, contentStyle, }: TitleMenuProps): ReactNode — A game front-end title screen: wordmark, tagline, the game's verbs in a keyboard/gamepad-navigable list, a footer and an optional backdrop. The engine owns navigation, focus and the enter/exit animation; the game owns the title markup, wording, verbs, art and every colour through HudTheme tokens. Pass the live scene (or artwork) as `backdrop` so the title renders over 3D instead of a blank page.
|
|
2055
2068
|
- `TitleMenuItem` (interface): interface TitleMenuItem — One verb on a {@link TitleMenu}: New Game, Continue, Settings, Credits, Quit.
|
|
2056
2069
|
- `TitleMenuProps` (interface): interface TitleMenuProps — Props for {@link TitleMenu}.
|
|
2057
2070
|
|
|
@@ -2139,15 +2152,71 @@
|
|
|
2139
2152
|
- `EntityAudioEmitters` (function): function EntityAudioEmitters({ engine, entitySounds, }: { engine: AudioEngine; entitySounds: Record<string, string> | undefined; }): null — ⚠ undocumented
|
|
2140
2153
|
- `ObjectAudioEmitters` (function): function ObjectAudioEmitters({ engine, objectSounds, }: { engine: AudioEngine; objectSounds: Record<string, string> | undefined; }): null — ⚠ undocumented
|
|
2141
2154
|
|
|
2155
|
+
## @jgengine/shell/audio/CaptionOverlay
|
|
2156
|
+
|
|
2157
|
+
- `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.
|
|
2158
|
+
|
|
2159
|
+
## @jgengine/shell/audio/FeedbackHost
|
|
2160
|
+
|
|
2161
|
+
- `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.
|
|
2162
|
+
|
|
2142
2163
|
## @jgengine/shell/audio/audioEngine
|
|
2143
2164
|
|
|
2144
2165
|
- `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;
|
|
2166
|
+
- `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.
|
|
2167
|
+
- `AudioLevel` (interface): interface AudioLevel — Master-output level, `0..1`. Pooled by {@link AudioEngine.measure} - read it, don't retain it.
|
|
2146
2168
|
- `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
2169
|
- `ListenerPose` (interface): interface ListenerPose — Position and orientation for a spatial-audio listener.
|
|
2170
|
+
- `MAX_PENDING_CUES` (const): const MAX_PENDING_CUES: 16 — Cues held while the browser keeps the audio context suspended, before any user gesture.
|
|
2148
2171
|
- `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
2172
|
- `createAudioEngine` (function): function createAudioEngine(config: AudioSceneConfig = {}): AudioEngine — Game-owned sound catalog playback with bounded SFX reservations, spatial panning, and shared buses.
|
|
2150
2173
|
|
|
2174
|
+
## @jgengine/shell/audio/audioProbe
|
|
2175
|
+
|
|
2176
|
+
- `AudioProbe` (interface): interface AudioProbe — The read-only handle a capture script or devtools uses to prove the mix is producing sound.
|
|
2177
|
+
- `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.
|
|
2178
|
+
|
|
2179
|
+
## @jgengine/shell/audio/busMix
|
|
2180
|
+
|
|
2181
|
+
- `MASTER_MUTE_SETTING_ID` (const): const MASTER_MUTE_SETTING_ID: "sound.muted" — Persisted mute for the whole mix, next to `sound.master`.
|
|
2182
|
+
- `busMuteSettingId` (function): function busMuteSettingId(busId: string): string — Persisted mute for one bus, next to its `busVolumeSettingId(busId)` volume.
|
|
2183
|
+
|
|
2184
|
+
## @jgengine/shell/audio/captions
|
|
2185
|
+
|
|
2186
|
+
- `Caption` (interface): interface Caption — One caption line as the overlay renders it.
|
|
2187
|
+
- `CaptionFeed` (interface): interface CaptionFeed — A capped, self-expiring list of caption lines that the accessibility captions setting turns on.
|
|
2188
|
+
- `CaptionFeedConfig` (interface): interface CaptionFeedConfig — Tunables for {@link createCaptionFeed}; all retunable at runtime.
|
|
2189
|
+
- `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.
|
|
2190
|
+
|
|
2191
|
+
## @jgengine/shell/audio/eventFeedback
|
|
2192
|
+
|
|
2193
|
+
- `AnyFeedbackBinding` (type): type AnyFeedbackBinding = { [Name in FeedbackEventName]: FeedbackBinding<Name> }[FeedbackEventName] — Any row of the event mapping, whichever event it binds.
|
|
2194
|
+
- `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; an event that carries a `position` (damage, death) pans and attenuates its cue, and one without still plays centred. A wave start has no event of its own, so a game announces it through `notify("wave")` or `attachWaveFeedback`.
|
|
2195
|
+
- `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.
|
|
2196
|
+
- `EventFeedback` (interface): interface EventFeedback — The event-to-reaction seam. One instance per running world.
|
|
2197
|
+
- `EventFeedbackOptions` (interface): interface EventFeedbackOptions — Options a game or host may override.
|
|
2198
|
+
- `FeedbackAnnouncer` (interface): interface FeedbackAnnouncer — The slice of `ctx.game.feedback` a wave runner needs, so this runs headless in a test.
|
|
2199
|
+
- `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.
|
|
2200
|
+
- `FeedbackDeps` (interface): interface FeedbackDeps — Everything the feedback seam reaches outside itself. Injected so it runs headless.
|
|
2201
|
+
- `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`.
|
|
2202
|
+
- `FeedbackReaction` (interface): interface FeedbackReaction — One cue's reaction: what plays, what the hand feels, what the screen does, and what a caption says.
|
|
2203
|
+
- `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.
|
|
2204
|
+
- `ResolvedFeedback` (interface): interface ResolvedFeedback — A resolved trigger: pooled per binding, so a busy event stream allocates nothing to decide.
|
|
2205
|
+
- `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`.
|
|
2206
|
+
- `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.
|
|
2207
|
+
- `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.
|
|
2208
|
+
|
|
2209
|
+
## @jgengine/shell/audio/gestureResume
|
|
2210
|
+
|
|
2211
|
+
- `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.
|
|
2212
|
+
|
|
2213
|
+
## @jgengine/shell/audio/hapticsRoute
|
|
2214
|
+
|
|
2215
|
+
- `HapticsRoute` (interface): interface HapticsRoute — Route haptic pulses to whatever the player can feel them on.
|
|
2216
|
+
- `HapticsRouteOptions` (interface): interface HapticsRouteOptions — The browser reach of {@link createHapticsRoute}; injected so the route runs headless.
|
|
2217
|
+
- `HapticsSink` (type): type HapticsSink = "gamepad" | "vibration" | "none" — Where a pulse actually landed: a gamepad, the device's own motor, or nowhere.
|
|
2218
|
+
- `createHapticsRoute` (function): function createHapticsRoute(pad: (pulse: HapticPulse) => void, options: HapticsRouteOptions = {}): HapticsRoute — One pulse in, whichever motor the player has: a connected gamepad takes it through the haptic channels the gamepad loop already drives, and a phone with no pad falls back to `navigator.vibrate`. Pad presence is cached and refreshed on connection events, so a hit never asks the browser for its pad list. Reduced motion does not silence this: see `eventFeedback`.
|
|
2219
|
+
|
|
2151
2220
|
## @jgengine/shell/audio/loopParams
|
|
2152
2221
|
|
|
2153
2222
|
- `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.
|
|
@@ -2677,8 +2746,9 @@
|
|
|
2677
2746
|
## @jgengine/shell/postfx/postfxOverlay
|
|
2678
2747
|
|
|
2679
2748
|
- `POSTFX_OVERLAY_USERDATA` (const): const POSTFX_OVERLAY_USERDATA: { readonly jgPostfxOverlay: true } — Spread onto a group's `userData` to mark its subtree as a screen/world overlay effect — additive glows, telegraphs, tracers, debug gizmos — that post-processing scene prepasses (GTAO's normal/depth render, DOF's depth render) must skip. Those prepasses render the scene with `overrideMaterial`, which turns transparent overlay quads into opaque stamps in the AO/DOF buffers (visible as black squares over hit sparks). The shell marks its own combat VFX and collision-debug mounts; games mark custom overlay effects the same way.
|
|
2680
|
-
- `hidePostfxOverlays` (function): function hidePostfxOverlays(scene: THREE.Object3D, out: THREE.Object3D[]): void — Hide every currently visible
|
|
2749
|
+
- `hidePostfxOverlays` (function): function hidePostfxOverlays(scene: THREE.Object3D, out: THREE.Object3D[], withUnmodelled = false): void — Hide every currently visible object `scene`'s prepasses must skip, recording what was hidden into `out` (cleared first, no allocation on the hot path) so {@link restorePostfxOverlays} can undo exactly that set. `withUnmodelled` also hides points and lines, which three's GTAO pass excludes from its normal buffer but a depth prepass keeps.
|
|
2681
2750
|
- `isPostfxOverlay` (function): function isPostfxOverlay(object: { userData?: Record<string, unknown> }): boolean — True when the object opted out of postfx scene prepasses via {@link POSTFX_OVERLAY_USERDATA}.
|
|
2751
|
+
- `releasePostfxOverlayTracking` (function): function releasePostfxOverlayTracking(scene: THREE.Object3D): void — Drop the cached set and its listeners for a scene that is going away, so a torn-down world does not keep its node graph reachable through this module.
|
|
2682
2752
|
- `restorePostfxOverlays` (function): function restorePostfxOverlays(hidden: THREE.Object3D[]): void — Restore visibility for the objects hidden by {@link hidePostfxOverlays} and clear the list.
|
|
2683
2753
|
|
|
2684
2754
|
## @jgengine/shell/postfx/stylizePass
|
|
@@ -3459,12 +3529,12 @@
|
|
|
3459
3529
|
|
|
3460
3530
|
## @jgengine/shell/vision/RevealVision
|
|
3461
3531
|
|
|
3462
|
-
- `RevealHighlights` (function): function RevealHighlights(props: RevealHighlightsProps): React.JSX.Element | null — Screen-space reveal effect (#115) — highlights tagged entities through occluders (Dark Sight / detective-vision / wallhack style). Renders with `depthTest: false` so the highlight draws over any wall standing between the origin and the revealed entity, rather than the usual depth-sorted scene.
|
|
3532
|
+
- `RevealHighlights` (function): function RevealHighlights(props: RevealHighlightsProps): React.JSX.Element | null — Screen-space reveal effect (#115) — highlights tagged entities through occluders (Dark Sight / detective-vision / wallhack style). Renders with `depthTest: false` so the highlight draws over any wall standing between the origin and the revealed entity, rather than the usual depth-sorted scene. Highlights track the origin and their targets every frame while enabled, so a disabled mode costs no renders.
|
|
3463
3533
|
- `RevealHighlightsProps` (interface): interface RevealHighlightsProps extends RevealVisionOptions { enabled: boolean; color?: string } — ⚠ undocumented · used by `RevealHighlights`: Screen-space reveal effect (#115) — highlights tagged entities through occluders (Dark Sight / detective-vision / wallhack style).
|
|
3464
3534
|
- `RevealScreenTint` (function): function RevealScreenTint({ enabled, color = "rgba(56, 189, 248, 0.16)", className }: RevealScreenTintProps): React.JSX.Element | null — Full-screen desaturating tint that reads as "vision mode is on" (Dark Sight / thermal / detective vision).
|
|
3465
3535
|
- `RevealScreenTintProps` (interface): interface RevealScreenTintProps { enabled: boolean; color?: string; className?: string } — ⚠ undocumented · used by `RevealScreenTint`: Full-screen desaturating tint that reads as "vision mode is on" (Dark Sight / thermal / detective vision).
|
|
3466
|
-
- `RevealVisionOptions` (interface): interface RevealVisionOptions { originEntityId?: string; radius: number; tags: readonly string[]; resolveTags: (entity: SceneEntity) => readonly string[] } — ⚠ undocumented · used by `useRevealHits`: Occlusion-ignoring tagged-entity radius query (#115), bound to the live scene.
|
|
3467
|
-
- `useRevealHits` (function): function useRevealHits(options: RevealVisionOptions): readonly RevealHit[] — Occlusion-ignoring tagged-entity radius query (#115), bound to the live scene.
|
|
3536
|
+
- `RevealVisionOptions` (interface): interface RevealVisionOptions { originEntityId?: string; radius: number; tags: readonly string[]; resolveTags: (entity: SceneEntity) => readonly string[] } — ⚠ undocumented · used by `useRevealHits`: Occlusion-ignoring tagged-entity radius query (#115), bound to the live scene and refreshed on render.
|
|
3537
|
+
- `useRevealHits` (function): function useRevealHits(options: RevealVisionOptions): readonly RevealHit[] — Occlusion-ignoring tagged-entity radius query (#115), bound to the live scene and refreshed on render.
|
|
3468
3538
|
|
|
3469
3539
|
## @jgengine/shell/vision/frustumSampleEqual
|
|
3470
3540
|
|