@jgengine/shell 0.11.0 → 0.12.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 +26 -0
- package/dist/CombatPresentation.d.ts +5 -0
- package/dist/CombatPresentation.js +8 -0
- package/dist/GameHost.d.ts +25 -1
- package/dist/GameHost.js +60 -4
- package/dist/GamePlayer.d.ts +5 -0
- package/dist/GamePlayer.js +5 -0
- package/dist/GamePlayerShell.d.ts +5 -0
- package/dist/GamePlayerShell.js +48 -358
- package/dist/GameUiPreview.js +3 -0
- package/dist/Shell3dPresentation.d.ts +63 -0
- package/dist/Shell3dPresentation.js +302 -0
- package/dist/ShellChrome.d.ts +51 -0
- package/dist/ShellChrome.js +51 -0
- package/dist/ShellHudPresentation.d.ts +41 -0
- package/dist/ShellHudPresentation.js +22 -0
- package/dist/audio/audioEngine.d.ts +4 -0
- package/dist/audio/audioEngine.js +111 -26
- package/dist/audio/audioWire.d.ts +22 -4
- package/dist/audio/audioWire.js +46 -3
- package/dist/audio/loopParams.d.ts +20 -0
- package/dist/audio/loopParams.js +28 -0
- package/dist/camera/GameCameraRig.js +6 -1
- package/dist/camera/cameraRigs.d.ts +3 -1
- package/dist/camera/cameraRigs.js +21 -3
- package/dist/camera/rigMath.d.ts +20 -0
- package/dist/camera/rigMath.js +29 -0
- package/dist/defineGame.d.ts +15 -1
- package/dist/defineGame.js +26 -4
- package/dist/devtools/DevtoolsOverlay.d.ts +4 -4
- package/dist/devtools/DevtoolsOverlay.js +28 -12
- package/dist/devtools/agentBridge.js +4 -0
- package/dist/devtools/perfDiagnose.js +1 -1
- package/dist/diagnostics/RuntimeDiagnostics.d.ts +7 -0
- package/dist/diagnostics/RuntimeDiagnostics.js +23 -12
- package/dist/environment/EnvironmentScene.js +12 -3
- package/dist/environment/terrainGroundColor.d.ts +9 -0
- package/dist/environment/terrainGroundColor.js +22 -0
- package/dist/gameKit.d.ts +27 -0
- package/dist/gameKit.js +29 -0
- package/dist/map/MapMarkerBeacons.d.ts +3 -2
- package/dist/postfx/PostProcessing.d.ts +9 -1
- package/dist/postfx/PostProcessing.js +11 -4
- package/dist/presentationResolve.d.ts +35 -0
- package/dist/presentationResolve.js +51 -0
- package/dist/registry.d.ts +1 -0
- package/dist/render/assetBase.d.ts +17 -0
- package/dist/render/assetBase.js +35 -0
- package/dist/render/modelLoad.js +4 -1
- package/dist/render/resolveModel.d.ts +15 -0
- package/dist/render/resolveModel.js +37 -2
- package/dist/scatter/InstancedScatter.js +5 -0
- package/dist/scatter/scatterProxies.js +24 -0
- package/dist/scene/AuthoredScene.d.ts +3 -1
- package/dist/scene/AuthoredScene.js +7 -2
- package/dist/scene/CityRenderer.d.ts +2 -0
- package/dist/scene/CityRenderer.js +1031 -0
- package/dist/scene/GrassFieldRenderer.js +53 -5
- package/dist/scene/PoleLineRenderer.d.ts +2 -0
- package/dist/scene/PoleLineRenderer.js +287 -0
- package/dist/scene/SoilPatchRenderer.js +70 -7
- package/dist/scene/WaterRenderer.js +34 -10
- package/dist/scene/builtinSceneKindRenderers.js +4 -0
- package/dist/scene/cityWindowMaterial.d.ts +16 -0
- package/dist/scene/cityWindowMaterial.js +81 -0
- package/dist/scene/sceneKindRenderers.d.ts +2 -0
- package/dist/settings/appliedSettings.d.ts +2 -1
- package/dist/settings/appliedSettings.js +4 -2
- package/dist/structures/TransformGizmo.d.ts +11 -1
- package/dist/structures/TransformGizmo.js +9 -5
- package/dist/terrain/GrassField.d.ts +11 -3
- package/dist/terrain/GrassField.js +16 -6
- package/dist/terrain/grassGeometry.d.ts +33 -1
- package/dist/terrain/grassGeometry.js +83 -21
- package/dist/terrain/grassMaterial.d.ts +18 -0
- package/dist/terrain/grassMaterial.js +66 -13
- package/dist/terrain/heightfieldGeometry.d.ts +34 -0
- package/dist/terrain/heightfieldGeometry.js +109 -0
- package/dist/terrain/index.d.ts +1 -0
- package/dist/terrain/index.js +1 -0
- package/dist/terrain/soilPatchMaterial.d.ts +15 -2
- package/dist/terrain/soilPatchMaterial.js +46 -8
- package/dist/terrain/terrainDetailMaterial.js +15 -2
- package/dist/water/Ocean.d.ts +7 -1
- package/dist/water/Ocean.js +16 -3
- package/dist/water/OceanConfig.d.ts +9 -0
- package/dist/water/OceanConfig.js +6 -0
- package/dist/water/OceanMaterial.d.ts +12 -0
- package/dist/water/OceanMaterial.js +9 -0
- package/dist/water/OceanShader.d.ts +2 -2
- package/dist/water/OceanShader.js +78 -10
- package/dist/world/PlaceScene.d.ts +11 -0
- package/dist/world/PlaceScene.js +27 -0
- package/dist/world/WorldScene.js +29 -2
- package/dist/worldSky.d.ts +7 -1
- package/dist/worldSky.js +11 -1
- package/package.json +4 -4
- package/skills/game-design/SKILL.md +28 -0
- package/skills/game-design/agents/openai.yaml +4 -0
- package/skills/game-design/references/application-playbook.md +170 -0
- package/skills/game-design/references/field-guide.md +288 -0
- package/skills/jgengine/SKILL.md +7 -4
- package/skills/jgengine/api.md +172 -79
- package/skills/jgengine/capabilities.md +104 -2
- package/skills/jgengine/recipes/minimal-game.md +88 -0
- package/skills/jgengine-ui/SKILL.md +15 -10
- package/skills/jgengine-ui/api.md +202 -47
- package/skills/jgengine-ui/capabilities.md +71 -2
- package/skills/jgengine-ui/recipes/portable-minimap.md +150 -0
- package/skills/jgengine-ui/reference.md +20 -7
- package/skills/jgengine-ui/references/visual-scorecard.md +1 -1
- package/skills/jgengine-world/SKILL.md +13 -0
- package/skills/jgengine-world/api.md +419 -15
- package/skills/jgengine-world/capabilities.md +108 -2
- package/skills/jgengine-world/recipes/commanding-entities.md +128 -0
- package/skills/jgengine-world/reference.md +43 -4
- package/skills/level-design/SKILL.md +54 -0
- package/skills/level-design/agents/openai.yaml +4 -0
- package/skills/level-design/references/application-playbook.md +192 -0
- package/skills/level-design/references/field-guide.md +337 -0
- package/skills/level-design/references/live-editor-loop.md +179 -0
- package/dist/cartridge/runtime.d.ts +0 -15
- package/dist/cartridge/runtime.js +0 -556
- package/dist/cartridge/spec.d.ts +0 -188
- package/dist/cartridge/spec.js +0 -19
- package/dist/cartridge/validate.d.ts +0 -2
- package/dist/cartridge/validate.js +0 -78
- package/dist/cartridge.d.ts +0 -145
- package/dist/cartridge.js +0 -246
- package/skills/jgengine/reference-cartridge.md +0 -72
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
Reach for these before hand-rolling. Each row is *the thing you need* → *the primitive that already does it*.
|
|
6
6
|
|
|
7
|
-
## action-bar —
|
|
7
|
+
## action-bar — optional skinned action-bar composition over the headless model — games own chrome
|
|
8
8
|
|
|
9
9
|
- `ActionBar` (function) · `import { ActionBar } from "@jgengine/react"`
|
|
10
10
|
|
|
@@ -61,6 +61,67 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
61
61
|
|
|
62
62
|
- `useEventMeter` (function) · `import { useEventMeter } from "@jgengine/react"`
|
|
63
63
|
|
|
64
|
+
## heightfield-mesh-update — in-place partial update of a displaced ground plane mesh
|
|
65
|
+
|
|
66
|
+
- `displaceHeightfieldGeometry` (function) · `import { displaceHeightfieldGeometry } from "@jgengine/shell/terrain"`
|
|
67
|
+
|
|
68
|
+
## hud-canvas — the HUD root surface — design-resolution scaling plus live panel-placement editing (F2+C)
|
|
69
|
+
|
|
70
|
+
- `HudCanvas` (function) · `import { HudCanvas } from "@jgengine/react"`
|
|
71
|
+
|
|
72
|
+
## hud-clock — in-game time-of-day readout bound to the sim clock
|
|
73
|
+
|
|
74
|
+
- `Clock` (function) · `import { Clock } from "@jgengine/react"`
|
|
75
|
+
|
|
76
|
+
## hud-coins — currency counter widget bound to a currency id
|
|
77
|
+
|
|
78
|
+
- `Coins` (function) · `import { Coins } from "@jgengine/react"`
|
|
79
|
+
|
|
80
|
+
## hud-frame — optional framed panel skins (glass/plate/retro/themed) — games own product chrome
|
|
81
|
+
|
|
82
|
+
- `HudFrame` (function) · `import { HudFrame } from "@jgengine/react"`
|
|
83
|
+
|
|
84
|
+
## hud-hotbar — optional inventory hotbar building block — games own slot chrome
|
|
85
|
+
|
|
86
|
+
- `Hotbar` (function) · `import { Hotbar } from "@jgengine/react"`
|
|
87
|
+
|
|
88
|
+
## hud-icons — painted-icon treatment for hotbar/action slots — glyph + gradient + count badge
|
|
89
|
+
|
|
90
|
+
- `IconSchool` (type) · `import { IconSchool } from "@jgengine/react"`
|
|
91
|
+
|
|
92
|
+
## hud-layout — persistent, live-editable HUD panel layout state for `HudCanvas`
|
|
93
|
+
|
|
94
|
+
- `useHudLayout` (function) · `import { useHudLayout } from "@jgengine/react"`
|
|
95
|
+
|
|
96
|
+
## hud-layout-persist — inject a host callback so canvas HUD edits commit into the scene document
|
|
97
|
+
|
|
98
|
+
- `HudLayoutPersistProvider` (function) · `import { HudLayoutPersistProvider } from "@jgengine/react"`
|
|
99
|
+
|
|
100
|
+
## hud-layout-persist-hook — read the host panel-commit port from a HUD layout widget
|
|
101
|
+
|
|
102
|
+
- `useHudLayoutPersist` (function) · `import { useHudLayoutPersist } from "@jgengine/react"`
|
|
103
|
+
|
|
104
|
+
## hud-panel — anchored HUD slot — drop any widget in a named, player-movable panel
|
|
105
|
+
|
|
106
|
+
- `HudPanel` (function) · `import { HudPanel } from "@jgengine/react"`
|
|
107
|
+
|
|
108
|
+
## hud-slots — atomic themed equipment/weapon/armor/potion slot grids with painted icons
|
|
109
|
+
|
|
110
|
+
- `SlotItem` (interface) · `import { SlotItem } from "@jgengine/react"`
|
|
111
|
+
|
|
112
|
+
## hud-stat-bar — optional stat-bound bar building block (health, mana, stamina) — prefer game-owned chrome
|
|
113
|
+
|
|
114
|
+
- `StatBar` (function) · `import { StatBar } from "@jgengine/react"`
|
|
115
|
+
|
|
116
|
+
## hud-theme — game-authored token object for bars, frames, slots, and minimap ring
|
|
117
|
+
|
|
118
|
+
- `HudThemePalette` (interface) · `import { HudThemePalette } from "@jgengine/react"`
|
|
119
|
+
|
|
120
|
+
## hud-vitals — atomic purpose-named vitals bars (token-themed parts, not a finished HUD)
|
|
121
|
+
|
|
122
|
+
- `BarTokens` (interface) · `import { BarTokens } from "@jgengine/react"`
|
|
123
|
+
- `barTokens` (function) · `import { barTokens } from "@jgengine/react"`
|
|
124
|
+
|
|
64
125
|
## key-hint — keyboard/mouse control hint that hides itself on touch
|
|
65
126
|
|
|
66
127
|
- `KeyHint` (function) · `import { KeyHint } from "@jgengine/react"`
|
|
@@ -69,6 +130,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
69
130
|
|
|
70
131
|
- `Keycap` (function) · `import { Keycap } from "@jgengine/react"`
|
|
71
132
|
|
|
133
|
+
## live-markers — a self-ticking MarkerSet kept in sync from the live scene each HUD tick — pass a rebuild(markers, ctx) that clears+repopulates, no hand-rolled setInterval
|
|
134
|
+
|
|
135
|
+
- `useLiveMarkers` (function) · `import { useLiveMarkers } from "@jgengine/react"`
|
|
136
|
+
|
|
72
137
|
## minimap — framed circular minimap with terrain bake, fog, markers, and facing arrow
|
|
73
138
|
|
|
74
139
|
- `Minimap` (function) · `import { Minimap } from "@jgengine/react"`
|
|
@@ -81,6 +146,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
81
146
|
|
|
82
147
|
- `MinimapPanel` (function) · `import { MinimapPanel } from "@jgengine/react"`
|
|
83
148
|
|
|
149
|
+
## minimap-track-hud — horizontal linear track minimap (corridor/route progress) — colored zone spans and gate/player pips positioned by 0..1 fraction, sharing chrome with the radial Minimap
|
|
150
|
+
|
|
151
|
+
- `MinimapTrack` (function) · `import { MinimapTrack } from "@jgengine/react"`
|
|
152
|
+
|
|
84
153
|
## nameplates — billboarded name + HP bar over nearby entities
|
|
85
154
|
|
|
86
155
|
- `WorldNameplates` (function) · `import { WorldNameplates } from "@jgengine/shell/world/WorldHud"`
|
|
@@ -113,7 +182,7 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
113
182
|
|
|
114
183
|
- `useSelectionView` (function) · `import { useSelectionView } from "@jgengine/react"`
|
|
115
184
|
|
|
116
|
-
## selection-panel —
|
|
185
|
+
## selection-panel — optional selection-panel composition: summary + portrait strip / group chips
|
|
117
186
|
|
|
118
187
|
- `SelectionPanel` (function) · `import { SelectionPanel } from "@jgengine/react"`
|
|
119
188
|
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Add the minimap to an existing React game
|
|
2
|
+
|
|
3
|
+
Use this recipe when a React game already owns its entities in an array, ECS
|
|
4
|
+
query, Zustand/Redux-style store, or another observable collection. JGengine
|
|
5
|
+
only needs a display view of those records; do not copy them into a
|
|
6
|
+
`MarkerSet`.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
Keep the React and React DOM versions already used by the project, then add the
|
|
11
|
+
two focused JGengine packages:
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
bun add @jgengine/core @jgengine/react
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The minimap imports neither Three.js nor React Three Fiber and does not require
|
|
18
|
+
`GameProvider`.
|
|
19
|
+
|
|
20
|
+
## Static entity arrays
|
|
21
|
+
|
|
22
|
+
Project an existing array directly. Keep the array stable with the same memo or
|
|
23
|
+
selector the project already uses.
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
import { useMemo } from "react";
|
|
27
|
+
import { Minimap } from "@jgengine/react/map";
|
|
28
|
+
|
|
29
|
+
interface Unit {
|
|
30
|
+
id: string;
|
|
31
|
+
x: number;
|
|
32
|
+
z: number;
|
|
33
|
+
team: "ally" | "enemy";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function MapHud({
|
|
37
|
+
units,
|
|
38
|
+
cameraTarget,
|
|
39
|
+
}: {
|
|
40
|
+
units: readonly Unit[];
|
|
41
|
+
cameraTarget: { x: number; z: number };
|
|
42
|
+
}) {
|
|
43
|
+
const markers = useMemo(
|
|
44
|
+
() =>
|
|
45
|
+
units.map((unit) => ({
|
|
46
|
+
id: unit.id,
|
|
47
|
+
position: [unit.x, 0, unit.z] as const,
|
|
48
|
+
kind: unit.team,
|
|
49
|
+
})),
|
|
50
|
+
[units],
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<Minimap
|
|
55
|
+
markers={markers}
|
|
56
|
+
center={[cameraTarget.x, cameraTarget.z]}
|
|
57
|
+
worldRadius={120}
|
|
58
|
+
className="game-minimap"
|
|
59
|
+
/>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Only `id` and `position` are required. `kind`, `label`, `heading`, and `meta`
|
|
65
|
+
are optional display data; lifecycle timestamps are not part of this view.
|
|
66
|
+
|
|
67
|
+
## Observable external store
|
|
68
|
+
|
|
69
|
+
Create the adapter once per store. The source caches its projected marker array
|
|
70
|
+
until the external store emits, so React snapshot reads do not remap the world
|
|
71
|
+
on every frame.
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
import { useMemo } from "react";
|
|
75
|
+
import { createMarkerSource } from "@jgengine/core/world/markers";
|
|
76
|
+
import { Minimap } from "@jgengine/react/map";
|
|
77
|
+
|
|
78
|
+
interface Unit {
|
|
79
|
+
id: string;
|
|
80
|
+
x: number;
|
|
81
|
+
z: number;
|
|
82
|
+
team: string;
|
|
83
|
+
name: string;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
interface ExistingWorldStore {
|
|
87
|
+
subscribe(listener: () => void): () => void;
|
|
88
|
+
getState(): { units: readonly Unit[] };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function ExistingGameMinimap({
|
|
92
|
+
world,
|
|
93
|
+
cameraTarget,
|
|
94
|
+
}: {
|
|
95
|
+
world: ExistingWorldStore;
|
|
96
|
+
cameraTarget: { x: number; z: number };
|
|
97
|
+
}) {
|
|
98
|
+
const markers = useMemo(
|
|
99
|
+
() =>
|
|
100
|
+
createMarkerSource({
|
|
101
|
+
subscribe: (listener) => world.subscribe(listener),
|
|
102
|
+
getSnapshot: () => world.getState().units,
|
|
103
|
+
project: (unit) => ({
|
|
104
|
+
id: unit.id,
|
|
105
|
+
position: [unit.x, 0, unit.z] as const,
|
|
106
|
+
kind: unit.team,
|
|
107
|
+
label: unit.name,
|
|
108
|
+
}),
|
|
109
|
+
}),
|
|
110
|
+
[world],
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
return (
|
|
114
|
+
<Minimap
|
|
115
|
+
markers={markers}
|
|
116
|
+
center={[cameraTarget.x, cameraTarget.z]}
|
|
117
|
+
worldRadius={120}
|
|
118
|
+
/>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
For SSR, make the store's first client snapshot match the server snapshot. When
|
|
124
|
+
they differ structurally, pass `getServerSnapshot` to `createMarkerSource` and
|
|
125
|
+
read the same serialized initial units used to hydrate the external store.
|
|
126
|
+
|
|
127
|
+
## Ownership
|
|
128
|
+
|
|
129
|
+
The existing project still owns entities, updates, persistence, simulation,
|
|
130
|
+
camera, renderer, input, scheduling, and the subscription. JGengine owns only
|
|
131
|
+
the cached entity-to-marker projection, core world-to-map projection math, and
|
|
132
|
+
the React SVG presentation. Save and restore the project's unit state as usual;
|
|
133
|
+
markers are derived data and need no parallel save format.
|
|
134
|
+
|
|
135
|
+
Use `kindStyles`, `className`, and `style` to skin the production `Minimap`.
|
|
136
|
+
When the project owns every chrome element, compose `MinimapChrome` inside its
|
|
137
|
+
own SVG instead of adopting the framed default.
|
|
138
|
+
|
|
139
|
+
## Common traps
|
|
140
|
+
|
|
141
|
+
- Keep `getSnapshot()` array identity stable until `subscribe()` emits. This is
|
|
142
|
+
the React external-store contract.
|
|
143
|
+
- Create `createMarkerSource` outside render or in `useMemo`; recreating it each
|
|
144
|
+
render discards its projection cache.
|
|
145
|
+
- Pass world coordinates, not Three.js screen coordinates. Marker positions are
|
|
146
|
+
`[x, y, z]`; the minimap projects X/Z.
|
|
147
|
+
- Do not poll the source from a render loop. Notify subscribers when the
|
|
148
|
+
caller-owned collection changes.
|
|
149
|
+
- Do not add `createdAt` or `expiresAt` unless the project intentionally adopts
|
|
150
|
+
`MarkerSet` lifecycle behavior.
|
|
@@ -4,9 +4,11 @@ This reference defines the required visual and interaction quality for JGengine
|
|
|
4
4
|
|
|
5
5
|
## The rule
|
|
6
6
|
|
|
7
|
-
A game must visually own its viewport. It must not resemble a dashboard, landing page, documentation page,
|
|
7
|
+
A game must visually own its viewport **and its entire interface**. Presentation is game content: each title designs a custom HUD, menus, feedback, and art direction. It must not resemble a dashboard, landing page, documentation page, ordinary responsive web app, **or another JGengine game's default widget stack**.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
The engine does not ship a finished game UI. `@jgengine/react` offers headless data/layout/accessibility seams and optional building blocks. Dropping in stock `StatBar` / `Hotbar` / `Coins` / glass `HudFrame` / theme presets without game-specific composition is incomplete work — not "using the engine correctly."
|
|
10
|
+
|
|
11
|
+
HTML and React are valid implementation tools. Website visual grammar is not the default. Engine default chrome is not the default either.
|
|
10
12
|
|
|
11
13
|
## 1. Start with a concise UI art direction
|
|
12
14
|
|
|
@@ -95,13 +97,13 @@ The active game should behave like an application mode:
|
|
|
95
97
|
Recommended layer contract:
|
|
96
98
|
|
|
97
99
|
```tsx
|
|
98
|
-
<
|
|
100
|
+
<GameHost>
|
|
99
101
|
<WorldLayer />
|
|
100
102
|
<HudLayer />
|
|
101
103
|
<ControlLayer />
|
|
102
104
|
<ScreenLayer />
|
|
103
105
|
<SystemLayer />
|
|
104
|
-
</
|
|
106
|
+
</GameHost>
|
|
105
107
|
```
|
|
106
108
|
|
|
107
109
|
- `WorldLayer`: game renderer
|
|
@@ -142,9 +144,11 @@ Design-resolution fit is on by default for every game: each `HudCanvas` auto-sca
|
|
|
142
144
|
|
|
143
145
|
**Overflow is an error, not a style note.** `HudCanvas` measures every `HudPanel` against the viewport at runtime; offenders land in a `data-hud-overflow` attribute (and a console warning), and `bun run shoot <game> --device mobile` (or `both`) exits non-zero naming the escaping panels. A game is not mobile-done while shoot reports HUD OVERFLOW.
|
|
144
146
|
|
|
147
|
+
**Authoring placement lives in the editor.** HUD panel placement is authored from the scene editor's **HUD** mode (F2+E → **HUD** toolbar button), not a separate devtools chord — the button mounts the game's real `HudCanvas` over the frozen world with drag/resize editing on. Dragging a panel writes an undoable `setUiPanel` patch into the scene document's `ui.panels` section (the same document the `canvas_move_panel` / `canvas_resize_panel` RPC verbs still target), so authored layout ships in `editor.scene.json`. The author's canvas **Done** button (or Esc / F2+C / F2+E) leaves HUD mode. Per-panel resize axes come from the panel `type`'s `registerHudPanelType` spec.
|
|
148
|
+
|
|
145
149
|
### Phase-gated HUD visibility (`showDuring`)
|
|
146
150
|
|
|
147
|
-
`HudCanvas` and `HudPanel` take an opt-in `showDuring?: GamePhase[]` — the element renders only while `gamePhase` is one of the listed phases (`"menu" | "playing" | "paused" | "ended"`), so a
|
|
151
|
+
`HudCanvas` and `HudPanel` take an opt-in `showDuring?: GamePhase[]` — the element renders only while `gamePhase` is one of the listed phases (`"menu" | "playing" | "paused" | "ended"`), so a game hides its HUD under menu/end overlays without hand-rolling a phase check. Omit it for the default always-visible behavior. Gate the whole HUD with `<HudCanvas showDuring={["playing"]}>`, or keep a single results panel up with a per-`HudPanel` value. The read degrades to `"playing"` when the component renders outside a `GameProvider` (component showcases, previews), so it never throws there.
|
|
148
152
|
|
|
149
153
|
### Shared viewport allocation (`GameViewportProvider`, region collision)
|
|
150
154
|
|
|
@@ -220,7 +224,16 @@ Prefer small headless or lightly styled primitives over a giant universal design
|
|
|
220
224
|
|
|
221
225
|
A primitive should expose game-oriented choices such as shape, material, urgency, placement, hierarchy, entry motion, icon treatment, compactness, and diegetic-versus-overlay presentation.
|
|
222
226
|
|
|
223
|
-
**
|
|
227
|
+
**Portable minimap adoption.** Existing React games pass `MarkerView[]`, a
|
|
228
|
+
`MarkerSource`, or their existing `MarkerSet` to `Minimap`/`Compass`/`WorldMap`.
|
|
229
|
+
Use `createMarkerSource` (`@jgengine/core/world/markers`) to cache a projection
|
|
230
|
+
from an external ECS/store snapshot; it needs no `GameProvider`, Three.js, or
|
|
231
|
+
React Three Fiber. Follow [the portable minimap recipe](recipes/portable-minimap.md)
|
|
232
|
+
instead of mirroring external entities into `createMarkerSet`.
|
|
233
|
+
|
|
234
|
+
**Minimap** is already shipped, not hand-rolled: `Minimap` / `WorldMap` / `Compass` from `@jgengine/react/map` render a framed SVG minimap over a `MarkerSet` (`createMarkerSet`, `@jgengine/core/world/markers`) and optional `FogField`, projecting via `@jgengine/core/world/minimap` (`projectToMinimap`, `headingToBearing`, edge-clamp). A game feeds flat props (`markers`, `center: [x,z]`, `worldRadius`, `size`, `facingYaw`, `rotate`) and repopulates the marker set each HUD tick from live entities — no custom canvas painter. `MinimapPanel` (same module) composes zone-label + clock header slots and a `Compass` strip around a `Minimap` — pass `zoneLabel`/`clock` nodes (e.g. from `useGameClock().calendar`) and `showCompass`/`compassProps`; omit either header slot and it collapses, `showCompass={false}` drops the strip. **Baked terrain background**: to draw the authored terrain under the markers, pass `background={document.minimap?.background}` and `mapBounds={document.minimap?.bounds}` to `Minimap`/`WorldMap` (or `MinimapPanel`, which forwards both through) — the editor's `bake_minimap` action stores a top-down terrain PNG + world bounds on `EditorDocument.minimap` (see `jgengine-editor`), and runtime reads that stored PNG with no re-rasterization. Both are undefined until baked, so an unbaked game renders the plain map unchanged. Rolling a **custom** minimap around your own course/route content (not the full `Minimap` panel)? `MinimapChrome` is the headless chrome layer other games were re-drawing by hand: nest it inside your own `<svg>` for the ring `frame`, and pass `markers: MinimapChromeMarker[]` (`{ id, position, heading?, color?, radius?, glyph? }`) for edge-clamped dots that draw as a rotated arrow whenever a marker carries a `heading` (the compass-arrow / player-blip layer) — never hand-roll `clampToMinimapEdge` + a `<polygon>` again. **Nameplates**: `WorldNameplates` (`@jgengine/shell/world/WorldHud`) billboards a name + optional 78×6px HP bar over every nearby non-local entity — turn it on declaratively via `defineGame({ nameplates: { roles, maxDistance } })` (mirrors `worldHealthBars`) rather than mounting it by hand; skin every part via `className`/`data-*` slots (`nameplateClassName`, `nameClassName`, `barClassName`, `fillClassName`) or swap the whole markup with `renderNameplate`. **Selection rings**: `WorldObjectHighlights` (`@jgengine/shell/world/WorldHud`) draws a ground ring over every `ctx.scene.object.selection`-ed placed object — the object-layer counterpart to `WorldEntityBars`/`WorldNameplates`; mount it once in the game's scene (`color`/`radius`/`y` props, `radius` otherwise derived from the object's catalog `halfExtents`) instead of hand-rolling a selection highlight through `WorldOverlay` against external state. **Playing cards** are already shipped, not hand-rolled: `CardFace` / `StackedPile` / `CardStack` (with `Suit`, `Rank`, `PlayingCard`) from `@jgengine/react/cards` render a French-deck face (rank + suit pips, patterned back when `faceDown`) and an overlapping fan. `StackedPile` composes the existing drag system (`useDragLayer`/`DraggableCard`/`DropZone`) — pass a `layer` for drag and `dropId` for a drop target; `CardStack` binds a fan to a headless `CardPileState` zone (`@jgengine/core/cards/cardPile`). Never draw cards on a canvas or reinvent pointer handling. **Swing timer**: `swingTimerState(player, target, prevPeriod, prevTimer)` (`@jgengine/core/ui/swingTimer`) is the pure, parameter-in/next-state-out core for a melee swing bar (recovers period on the reset edge; hidden unless auto-attacking a live non-object target) — thread the returned `nextPeriod`/`nextTimer` back via refs, render a thin `Meter`.
|
|
235
|
+
|
|
236
|
+
**HUD widgets split into three reusable layers** — data, renderer, chrome — so a second presentation drops in without a forked widget. **Chrome**: `HudFrame` (`@jgengine/react/hudFrame`, `hud-frame` capability) is the single framed-panel primitive — `<HudFrame variation="glass"|"plate"|"retro" shape="rounded"|"circle"|"square" title aside padding width style>` renders a `data-hud-frame` div with an optional small-caps `title`/`aside` header; `glass` is byte-identical to the built-in widgets' dark-glass panel, `plate` a heavier opaque plate, `retro` a hard black-outlined frame. Need only the CSS on your own element? spread `hudFrameStyle(variation, shape)`. Reach for it instead of hand-rolling a bordered panel `<div>` per widget. **Renderer**: `MinimapTrack` (`@jgengine/react/map`, `minimap-track-hud` capability) is a structurally-different second minimap — a horizontal linear progress rail (vs the radial `Minimap`) with colored zone `spans` and gate/exit/player `pips` placed by 0..1 fraction, driven by core `trackFraction([x,z], { from, to })` (`@jgengine/core/world/minimapTrack`, `minimap-track` capability — the 1D analogue of `projectToMinimap`). It reads no store; the caller supplies fractions. Use it for corridor/route progress; use the radial `Minimap` for open worlds — both share `HudFrame` chrome. **Data**: `useLiveMarkers(rebuild, { intervalMs?, deps? })` (`@jgengine/react/hooks`, `live-markers` capability) returns a self-ticking `MarkerSet` that each HUD tick clears and re-runs your `rebuild(markers, ctx)` against the live scene — replaces the hand-rolled `useState` ticker + `useEffect` + `createMarkerSet` + `markers.clear()` + rescan boilerplate; feed the returned set straight into `Minimap`/`MinimapPanel`.
|
|
224
237
|
|
|
225
238
|
**Heat/streak gauges** are already shipped, not hand-rolled `clamp(value/max)`: build the gauge with `createEventMeter` (`@jgengine/core/stats/eventMeter`, `event-meter` capability — ult/adrenaline `"hold"` mode or streak/combo `"reset"` mode over tagged event gains) and bind it to a component with `useEventMeter(meter)` (`@jgengine/react`, `event-meter-hud` capability) — it self-ticks a heartbeat and returns `{ value, fraction, tier, ready }` for a `Meter`/`BossBar`-style fill, no manual `setInterval` polling of `meter.value()`.
|
|
226
239
|
|
|
@@ -477,7 +490,7 @@ Require revision when any of these are true:
|
|
|
477
490
|
|
|
478
491
|
## 15. Compact implementation API appendix
|
|
479
492
|
|
|
480
|
-
Use the main `jgengine` skill for the authoritative engine API routing. The React package exposes `GameProvider`, hooks, and headless primitives from `@jgengine/react` and its documented subpaths. Common hooks include player/game state, entities, stats, inventory, quests, prompts, clocks, markers, fog, and engine stores. The shell provides `
|
|
493
|
+
Use the main `jgengine` skill for the authoritative engine API routing. The React package exposes `GameProvider`, hooks, and headless primitives from `@jgengine/react` and its documented subpaths. Common hooks include player/game state, entities, stats, inventory, quests, prompts, clocks, markers, fog, and engine stores. The shell provides `GameHost` (the game mount), input integration, devtools, and `GameUiPreview`; `@jgengine/shell/GamePlayerShell` (the internal player `GameHost` composes) also re-exports the movement primitives it wires up internally (`applyMotionImpulses`, `nearbyObstacles`, `resolvePhysicsTuning`, `hasEnvironmentTerrain`) for a host that needs to replicate its physics loop, and `@jgengine/shell/terrain` re-exports the `jgengine-world` field builders (`arenaField`, `flatField`, `fractalNoise`, `resolveGroundStep`, `valueNoise`, `withNormal`) alongside its own `CarvedTerrain`/`GrassField` components so a shell consumer never needs a second import from core.
|
|
481
494
|
|
|
482
495
|
Use these APIs to bind state; do not let API wiring dictate visual composition. Keybind labels should derive from the game’s binding table, and UI actions should dispatch through game commands rather than existing only as click handlers.
|
|
483
496
|
|
|
@@ -27,7 +27,7 @@ Score all ten from one `--mode play` shot plus deterministic world/document evid
|
|
|
27
27
|
6. **Materials/textures** — terrain and props carry texture and color variation (`environment({ sculpt, materialRegions })`, catalog GLBs via `jgengine-assets`), not flat untextured solids.
|
|
28
28
|
7. **Lighting/render** — a tuned daylight/sun+hemisphere rig and post stack (per `look: "cinematic"`, #773), not the bare untouched three.js default light.
|
|
29
29
|
8. **VFX/motion** — particle/motion feedback tied to actual gameplay events (hits, pickups, weather), not absent, and not one looping default effect standing in everywhere.
|
|
30
|
-
9. **UI/HUD** — a game-specific `HudCanvas`/`HudPanel` composition; fails automatically if it could pass for a generic admin dashboard.
|
|
30
|
+
9. **UI/HUD** — a game-specific `HudCanvas`/`HudPanel` composition with unique art direction; fails automatically if it could pass for a generic admin dashboard **or** for another game shipping stock engine widgets/theme presets unskinned.
|
|
31
31
|
10. **Performance evidence** — the look holds at the scale the game actually runs (`InstancedScatter` density, many entities), not only a hand-posed hero shot with three objects in frame.
|
|
32
32
|
|
|
33
33
|
## Measured-evidence row
|
|
@@ -11,8 +11,16 @@ This skill owns runtime spatial behavior: authored-scene consumption, environmen
|
|
|
11
11
|
|
|
12
12
|
Search [capabilities.md](capabilities.md) by intent before designing a primitive. Use [api.md](api.md) for exact exports and [reference.md](reference.md) for environment, camera, navigation, physics, audio, and authored-scene recipes.
|
|
13
13
|
|
|
14
|
+
For a *connected* walkthrough that wires several primitives into a running loop, read the goal-oriented recipes under [recipes/](recipes/) — organized by composition seam (what connects to what), never by genre. Start there before reading a game's source to learn how pieces fit; a hybrid game is just a different composition of the same primitives.
|
|
15
|
+
|
|
16
|
+
- [recipes/commanding-entities.md](recipes/commanding-entities.md) — select → order → tick → resolve: direct command over a group of entities (the command loop behind squad control, base-defense, herding, autobattle, or a mutating game's real-time phase).
|
|
17
|
+
|
|
14
18
|
## Canonical workflows
|
|
15
19
|
|
|
20
|
+
### Declare the place
|
|
21
|
+
|
|
22
|
+
A world is the place you play in: substrate + laws, via `world()` from `@jgengine/core/world/place` — `{ id, ground: { mode: flat|round|voxel|board, size, surface?, generator? }, physics? }`. Thin is complete: flat with `Infinity` axes for 3D, `board` for 2D, or omit `world` for pure UI/rules games. Never put seed, sky, or vegetation in a world definition — seeds derive from world id + save/run (`seedForPlace`); sky look and foliage scatter are editor-authored scene content, with the engine default sky when the document has none. `ground.surface` carries matter/feel laws (metal vs slime vs felt) that the same rule systems read — swap the surface, not the rules. See [reference.md](reference.md) for the full shape; `environment()` is the legacy consumption path for editor/preset-written scene data, never the starter.
|
|
23
|
+
|
|
16
24
|
### Authored world
|
|
17
25
|
|
|
18
26
|
1. Load the scene document through the shared authored-scene feature.
|
|
@@ -28,8 +36,13 @@ Choose input intent, controller/motor, collision/navigation, and camera as separ
|
|
|
28
36
|
|
|
29
37
|
Compose perception, selection, planning/behavior, movement, and lifecycle independently. Inject randomness and scheduling. Use spatial indexes, interest tiers, or bounded candidate sets for scale.
|
|
30
38
|
|
|
39
|
+
### Fallback-seam diagnostics
|
|
40
|
+
|
|
41
|
+
Render seams fall back to placeholders when content is unauthored: default green ground (no environment), primitive capsule/box actors (no `entityModels`/`objectModels` mapping), stylized proxy foliage (no scatter `resolveItem`). These placeholders are sometimes intended, so runtime never changes — but a dev-only, opt-in probe (`@jgengine/core/devtools/fallbackSeams`) tells "unauthored/misconfigured" apart from "intended". The shell arms it via `armFallbackSeams(devtoolsEnabled)`; each seam reports at its choice site (`reportFallbackSeam`), and counts surface through the devtools `fallbacks` probe. Disarmed (production) it is a pure, allocation-free no-op.
|
|
42
|
+
|
|
31
43
|
## Traps
|
|
32
44
|
|
|
45
|
+
- Do not put seed, sky presets, or vegetation/scatter fields in a world definition — `world()` rejects them; dressing belongs to the editor's scene document.
|
|
33
46
|
- Do not put UI layout, touch chrome, combat resolution, or asset acquisition here.
|
|
34
47
|
- Do not duplicate editor coordinates in runtime state.
|
|
35
48
|
- Public APIs accept semantic policies and caller data, not renderer mechanics or internal tuning rolls.
|