@jgengine/react 0.12.0 → 0.14.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 +73 -1
- package/dist/characterSheet.d.ts +120 -0
- package/dist/characterSheet.js +134 -0
- package/dist/hooks.d.ts +11 -0
- package/dist/hooks.js +15 -0
- package/dist/hudLayout.js +138 -9
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/inventoryGrid.d.ts +20 -0
- package/dist/inventoryGrid.js +135 -0
- package/dist/panels.d.ts +115 -0
- package/dist/panels.js +221 -0
- package/dist/provider.d.ts +15 -0
- package/dist/provider.js +17 -1
- package/dist/slots.d.ts +7 -4
- package/package.json +2 -2
- package/skills/jgengine/SKILL.md +5 -2
- package/skills/jgengine/api.md +13 -5
- package/skills/jgengine-ui/SKILL.md +9 -4
- package/skills/jgengine-ui/api.md +138 -8
- package/skills/jgengine-ui/capabilities.md +72 -0
- package/skills/jgengine-ui/reference.md +40 -3
|
@@ -28,6 +28,22 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
28
28
|
|
|
29
29
|
- `ActionTooltip` (function) · `import { ActionTooltip } from "@jgengine/react"`
|
|
30
30
|
|
|
31
|
+
## camera-transparent-decor — let author decor pass through the orbit spring-arm; a child opts back in with jgCameraCollide
|
|
32
|
+
|
|
33
|
+
- `isCameraOccluderTransparent` (function) · `import { isCameraOccluderTransparent } from "@jgengine/shell/camera"`
|
|
34
|
+
|
|
35
|
+
## character-paperdoll — equip-slot paperdoll — flanking columns around a portrait, activatable slots
|
|
36
|
+
|
|
37
|
+
- `Paperdoll` (function) · `import { Paperdoll } from "@jgengine/react"`
|
|
38
|
+
|
|
39
|
+
## character-sheet — drop-in paperdoll + derived-stat sheet in a HudFrame — game supplies slots/stats
|
|
40
|
+
|
|
41
|
+
- `CharacterSheet` (function) · `import { CharacterSheet } from "@jgengine/react"`
|
|
42
|
+
|
|
43
|
+
## character-statlist — derived-stat readout — grouped small-caps label→value rows, token-themed
|
|
44
|
+
|
|
45
|
+
- `StatList` (function) · `import { StatList } from "@jgengine/react"`
|
|
46
|
+
|
|
31
47
|
## clock-format — format a signed time gap like a race split (+/- m:ss.ff)
|
|
32
48
|
|
|
33
49
|
- `formatDelta` (function) · `import { formatDelta } from "@jgengine/core/ui"`
|
|
@@ -49,6 +65,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
49
65
|
|
|
50
66
|
- `EntityPortrait` (function) · `import { EntityPortrait } from "@jgengine/react"`
|
|
51
67
|
|
|
68
|
+
## entity-preview — live 3D entity portrait — nested Canvas + context bridge + studio stage, driven by the game's own renderEntity
|
|
69
|
+
|
|
70
|
+
- `EntityPreview` (function) · `import { EntityPreview } from "@jgengine/shell/render/EntityPreview"`
|
|
71
|
+
|
|
52
72
|
## entity-render-cues — live velocity/bob/fire/reload/hit/death cues for a custom renderEntity or viewmodel component
|
|
53
73
|
|
|
54
74
|
- `useEntityRenderCues` (function) · `import { useEntityRenderCues } from "@jgengine/shell/render/useEntityRenderCues"`
|
|
@@ -61,6 +81,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
61
81
|
|
|
62
82
|
- `useEventMeter` (function) · `import { useEventMeter } from "@jgengine/react"`
|
|
63
83
|
|
|
84
|
+
## game-context-bridge — re-provide the GameContext across a nested Canvas/reconciler boundary
|
|
85
|
+
|
|
86
|
+
- `GameContextBridge` (function) · `import { GameContextBridge } from "@jgengine/react"`
|
|
87
|
+
|
|
64
88
|
## heightfield-mesh-update — in-place partial update of a displaced ground plane mesh
|
|
65
89
|
|
|
66
90
|
- `displaceHeightfieldGeometry` (function) · `import { displaceHeightfieldGeometry } from "@jgengine/shell/terrain"`
|
|
@@ -158,6 +182,46 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
158
182
|
|
|
159
183
|
- `formatOrdinal` (function) · `import { formatOrdinal } from "@jgengine/core/ui"`
|
|
160
184
|
|
|
185
|
+
## panel-close — close a window while remembering its last position
|
|
186
|
+
|
|
187
|
+
- `closePanel` (function) · `import { closePanel } from "@jgengine/core/ui"`
|
|
188
|
+
|
|
189
|
+
## panel-close-top — close the focused (topmost) closable window — the ESC behavior
|
|
190
|
+
|
|
191
|
+
- `closeTopPanel` (function) · `import { closeTopPanel } from "@jgengine/core/ui"`
|
|
192
|
+
|
|
193
|
+
## panel-focus — raise a window to the top of the focus/z stack
|
|
194
|
+
|
|
195
|
+
- `focusPanel` (function) · `import { focusPanel } from "@jgengine/core/ui"`
|
|
196
|
+
|
|
197
|
+
## panel-host — render a manager's open windows as draggable, closable, z-stacked dialogs above the HUD
|
|
198
|
+
|
|
199
|
+
- `PanelHost` (function) · `import { PanelHost } from "@jgengine/react"`
|
|
200
|
+
|
|
201
|
+
## panel-hotkey — resolve a keybind (code or key) to the panel id it toggles
|
|
202
|
+
|
|
203
|
+
- `panelByHotkey` (function) · `import { panelByHotkey } from "@jgengine/core/ui"`
|
|
204
|
+
|
|
205
|
+
## panel-move — override a window's on-surface position (the drag commit)
|
|
206
|
+
|
|
207
|
+
- `movePanel` (function) · `import { movePanel } from "@jgengine/core/ui"`
|
|
208
|
+
|
|
209
|
+
## panel-open — open a window, raise its z, and enforce exclusive-group membership
|
|
210
|
+
|
|
211
|
+
- `openPanel` (function) · `import { openPanel } from "@jgengine/core/ui"`
|
|
212
|
+
|
|
213
|
+
## panel-ordered-open — open panels' defs in z/render order for a window host
|
|
214
|
+
|
|
215
|
+
- `orderedOpen` (function) · `import { orderedOpen } from "@jgengine/core/ui"`
|
|
216
|
+
|
|
217
|
+
## panel-state — serializable open/z/position state for a set of toggleable game windows
|
|
218
|
+
|
|
219
|
+
- `createPanelState` (function) · `import { createPanelState } from "@jgengine/core/ui"`
|
|
220
|
+
|
|
221
|
+
## panel-toggle — toggle a window open/closed from a single keybind
|
|
222
|
+
|
|
223
|
+
- `togglePanel` (function) · `import { togglePanel } from "@jgengine/core/ui"`
|
|
224
|
+
|
|
161
225
|
## placement-ghost — render valid/invalid placement preview mesh
|
|
162
226
|
|
|
163
227
|
- `PlacementGhost` (function) · `import { PlacementGhost } from "@jgengine/shell/structures"`
|
|
@@ -202,10 +266,18 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
|
|
|
202
266
|
|
|
203
267
|
- `TransformGizmo` (const) · `import { TransformGizmo } from "@jgengine/shell/structures"`
|
|
204
268
|
|
|
269
|
+
## use-panels — headless toggleable-window manager with keybind + ESC handling over the core panel model
|
|
270
|
+
|
|
271
|
+
- `usePanels` (function) · `import { usePanels } from "@jgengine/react"`
|
|
272
|
+
|
|
205
273
|
## vital-bar — compact current/max vital meter for an entity summary
|
|
206
274
|
|
|
207
275
|
- `VitalBar` (function) · `import { VitalBar } from "@jgengine/react"`
|
|
208
276
|
|
|
277
|
+
## window — standalone draggable, closable window primitive over HudFrame — no manager required
|
|
278
|
+
|
|
279
|
+
- `Window` (function) · `import { Window } from "@jgengine/react"`
|
|
280
|
+
|
|
209
281
|
## world-object-highlights — ground-ring highlight over every selected placed object
|
|
210
282
|
|
|
211
283
|
- `WorldObjectHighlights` (function) · `import { WorldObjectHighlights } from "@jgengine/shell/world/WorldHud"`
|
|
@@ -6,7 +6,7 @@ This reference defines the required visual and interaction quality for JGengine
|
|
|
6
6
|
|
|
7
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
|
-
The engine
|
|
9
|
+
The engine ships both headless data/layout/accessibility seams *and* good drop-in building blocks — inventory grids, toggleable windows, character sheets, bars, action bars. `@jgengine/react` offers all of them. Composing and reskinning those building blocks into this game's arrangement is using the engine correctly. What's incomplete is shipping a whole *unarranged, unskinned generic face* — a page that looks like a dashboard or another game's untouched default stack, with no art direction, terminology, or placement of its own.
|
|
10
10
|
|
|
11
11
|
HTML and React are valid implementation tools. Website visual grammar is not the default. Engine default chrome is not the default either.
|
|
12
12
|
|
|
@@ -51,11 +51,14 @@ Identify the screens the game actually needs:
|
|
|
51
51
|
|
|
52
52
|
- boot/loading
|
|
53
53
|
- title or attract screen
|
|
54
|
+
- **main menu** (the game's front-end — see the Main menu section)
|
|
54
55
|
- mode selection
|
|
56
|
+
- character create / character select where the game has classes, characters, or profiles
|
|
55
57
|
- onboarding/tutorial
|
|
56
58
|
- gameplay HUD
|
|
57
59
|
- pause
|
|
58
60
|
- settings
|
|
61
|
+
- credits
|
|
59
62
|
- map/inventory/dialogue where relevant
|
|
60
63
|
- victory/results
|
|
61
64
|
- failure/retry
|
|
@@ -239,6 +242,12 @@ instead of mirroring external entities into `createMarkerSet`.
|
|
|
239
242
|
|
|
240
243
|
**Action cards / command bars / ability bars** are a headless data → renderer → chrome split, not a hand-rolled cooldown grid. The model is caller-owned data: build `ActionDef[]` (`@jgengine/core/ui/actionModel` — `id`, optional `label`/`icon`/`hotkey`/`group`/`active`/`disabled`, plus `cooldown` via `actionCooldown`/`actionCooldownFromFraction` and `costs` via `actionCost`); `resolveAction`/`resolveActionCollection` compute `enabled` + ordered blocking `reasons` (cooldown, then unmet cost, then caller reasons). React side (`@jgengine/react`): `useActionBar(defs, { columns, wrap, hotkeys, onActivate })` is the DATA/HOOK layer (`action-bar-model` — focus, hover, `moveGridFocus` keyboard/controller grid nav, `actionByHotkey` routing); `ActionBarChrome` is the composable RENDERER (`action-bar-chrome` — `role="toolbar"`, roving tabindex, `layout: "grid" | "list"`, swap per-item chrome with `renderItem`); `ActionButton` is the default item chrome (`action-button` — radial cooldown wipe, cost line, `Keycap` hotkey, full rest/hover/focus/pressed/active/disabled states, `aria-disabled`/`aria-pressed`/`aria-keyshortcuts`); `ActionBar` is the batteries-included skinned default. A radial UI ignores the chrome and lays out `ActionButton`s from the model. Bind a combat `AbilityKit` with `actionFromAbilitySlot(slot, { label, icon, hotkey, resourceId })` over `useAbilitySlots`. Tooltips use the shared seam (no more DOM `title=` hand-rolling): `actionTooltip(resolved)` → `TooltipContent`, rendered by `ActionTooltip` inside a `Popover` (`popover` — pure `placePopover` viewport flip/clamp math from `@jgengine/core/ui/tooltipModel`, SSR-safe). **Selection panels / party frames / squad summaries** are the same split for entities: feed caller-owned `EntitySummaryDef[]` (`@jgengine/core/ui/selectionModel` — `id`, `name`, `kind`, `icon`, `vitals`, `tags`) to `summarizeSelection` (primary focus, `kind` buckets, `grouped` past a threshold, `selectionWindow` virtualization) or the `useSelectionView` hook (`selection-model` — focus stepping + keyboard nav). Renderers (`@jgengine/react`): `EntityPortrait` (`entity-portrait` selectable tile + health strip), `EntitySummary` (`entity-summary` primary detail with `VitalBar` rows), `SelectionGroupChip` (`selection-group-chip` icon+count), `SelectionCollectionChrome` (`selection-collection` portrait strip that flips to group chips when large, roving tabindex), and `SelectionPanel` (`selection-panel` batteries-included primary-summary-beside-strip default). Keep layout, dimensions, hotkeys, catalog-id mapping, renderer, and chrome caller-owned — the defaults are examples, not fixed RTS rules; never fork the model to reskin.
|
|
241
244
|
|
|
245
|
+
**Inventory grid** is already shipped, not hand-rolled: `<InventoryGrid inventoryId columns>` (`@jgengine/react`, backed by the `useInventoryGrid` hook) is a drop-in NxN grid bound to a live inventory — it renders the bound inventory's slots and wires drag-to-move / swap / stack / split through the built-in `inventory.move` / `inventory.split` commands, so a bag is one element plus a reskin, never a re-derived slot matrix over raw divs. It is distinct from the atomic `EquipmentSlots` / `WeaponSlots`, which are single-slot targets for paperdoll and equip layouts — reach for `InventoryGrid` for the bag/backpack surface and the atomic slots for the worn/equipped layout. Reskin via `className`/`data-*` slots and HudTheme tokens; the mechanics stay caller-agnostic.
|
|
246
|
+
|
|
247
|
+
**Toggleable game windows** are already shipped, not hand-rolled open/close state: `usePanels(defs)` + `<PanelHost>` + `<Window>` (`@jgengine/react`) over the headless `@jgengine/core/ui/panelModel` are THE way to build openable windows — bag (`B`), character (`C`), spellbook, quest log — with keybind toggles, drag-to-move, close buttons, ESC-to-close, and z-stack ordering all handled. Declare each window in `defs` (id, keybind, title), mount one `<PanelHost>`, and render each panel's body inside a `<Window>`; the game owns the body content and skin, the model owns focus/stacking/keybind wiring. Never re-derive window toggling from local `useState` booleans and hand-rolled keydown listeners.
|
|
248
|
+
|
|
249
|
+
**Character sheet / paperdoll** is already shipped, not hand-rolled: `<CharacterSheet>` / `<Paperdoll>` / `<StatList>` (`@jgengine/react`) compose the paperdoll equip layout (worn `EquipmentSlots` arranged around a portrait) and the derived-stat readout into one window body. `Paperdoll` is the equip-slot arrangement, `StatList` the derived-stat rows, `CharacterSheet` the batteries-included default that pairs them — the game composes and reskins them (slot layout, stat labels/terminology, skin) rather than re-deriving a paperdoll from raw divs and manual stat math.
|
|
250
|
+
|
|
242
251
|
**Toast/announcer stacks**: two supported sources, pick the one that matches where the message comes from. An engine event already bound onto `ctx.game.feed` (kill feed, loot log, quest update) renders with `@jgengine/react`'s `ToastStack({ action })` directly. A game-raised one-off message with its own lifetime (boss-intro banner, objective callout) belongs in a `createToastQueue` (`@jgengine/core/game/toasts`, `toast-feed` capability — see `jgengine-gameplay`) rendered from `queue.list()`; don't reimplement the append-cap-plus-TTL-prune loop as a local `Toast`/`pushToast` pair.
|
|
243
252
|
|
|
244
253
|
Do not create a primitive whose only value is wrapping a `div` with border radius.
|
|
@@ -296,8 +305,28 @@ Three composable-chrome seams for FPS/TPS polish — a placement hook plus a goo
|
|
|
296
305
|
- **Render cues for any custom rig.** `useEntityRenderCues(instanceId, tuning?)` (`@jgengine/shell/render/useEntityRenderCues`) returns the same `cuesRef` the viewmodel gets, for a custom `renderEntity` component: velocity-driven `bobPhase`, `firing`/`recoil` from `ctx.game.playEntityAnimation(instanceId, "fire")`, `reloading` from `"reload"`/`"reloadEnd"`, `hit` from `combat.hitReaction`, `dead` from `entity.died` — no diffing the parent group's position, no game-side module map for attack timing. `RenderCueTuning` (`@jgengine/core/combat/renderCues`) overrides the bob rate / recoil decay / pulse durations; see `jgengine-combat`'s `combat/renderCues` for the underlying pure math.
|
|
297
306
|
`@jgengine/shell/terrain`'s `ProceduralGround`/`CarvedTerrain`/`GrassField` are the game-facing terrain surface; the height-field math underneath (`arenaField`, `flatField`, `fractalNoise`, `resolveGroundStep`, `valueNoise`, `withNormal`, re-exported from `@jgengine/core/world/terrain`) and `GamePlayerShell`'s physics helpers (`applyMotionImpulses`, `nearbyObstacles`, `resolvePhysicsTuning`, `hasEnvironmentTerrain`, sourced from `core/runtime/motionIntents` / `core/movement`) are internal building blocks the shell composes for you — reach for `defineGame({ movement, world })` and the ground primitives above instead of calling these directly.
|
|
298
307
|
|
|
308
|
+
## Main menu
|
|
309
|
+
|
|
310
|
+
**The main menu is the game's front-end, not a marketing splash.** The hosting website/runner loads straight into the game's own menu (see rejection criteria) — there is no engine-supplied main menu, and the page must not render a title card, tagline, or "enter game" button of its own. What the player first sees is a screen the game designs and skins in its own art direction: the same shape/material/type language as its HUD, not a landing page, hero banner, feature list, or store page. Homage/credit lines are fine as small print; the screen's job is to start play, not to sell it.
|
|
311
|
+
|
|
312
|
+
Compose it from the game's own markup over the headless `StartScreen` (`@jgengine/react`, `start-screen` capability) — a full-bleed `data-jg-menu` overlay that centers your content and offers an opt-in settings corner; it imposes no look. Menu rows are the copy-in `MenuList` / `MenuButton` / `TitleScreen` registry scaffolds (keyboard/controller focus + `KeybindBadge` that self-hides on touch already wired) restyled into the game's chrome — never shipped as raw scaffold.
|
|
313
|
+
|
|
314
|
+
**The front-end entries, when the game has them.** A menu is more than one Start button. Offer the real front-end verbs a player expects, gated to what the game actually supports:
|
|
315
|
+
|
|
316
|
+
- **New Game** — begin a fresh run. When a save exists, keep it distinct from Continue rather than silently overwriting.
|
|
317
|
+
- **Continue / Load** — resume the most recent save, and (when the game keeps more than one) a **Load** entry that lists save slots. Save slots are `createSaveStore` instances keyed by slot id; read them reactively with `useSaveStore` (`@jgengine/react`, offline `localSaveBackend` or cloud `remoteSaveBackend` — same hook, different backend). Disable Continue/Load with a real disabled state when there is nothing to resume; do not hide the verb entirely.
|
|
318
|
+
- **Create Character / Choose Class** — when the game has characters, classes, loadouts, or profiles, the create/select flow lives here as an authored screen, not a stray dropdown. Keep the character/roster model serializable and game-owned (persist it through the game's save store); the menu is its presentation. Do not reach for a genre "class picker" preset — compose the choices this game actually offers.
|
|
319
|
+
- **Load Character / Roster** — choose an existing profile or party when the game persists more than one.
|
|
320
|
+
- **Settings** — reachable from the main menu, always (see Settings menu below). This is one of the two canonical places settings live; pause is the other.
|
|
321
|
+
- **Credits** — a real, reachable screen. Player-facing games owe HUD and website credit (see governance) and record borrowed work in `CREDITS.md`; the menu's Credits entry surfaces that in-game. Read it from the game's own credits data — do not bury attribution only in a build artifact.
|
|
322
|
+
- **Quit / Back to site** — where it makes sense for the host.
|
|
323
|
+
|
|
324
|
+
Only show entries the game supports, and disable (don't delete) verbs that are temporarily unavailable so the menu shape stays legible. Character create/select and Credits are their own authored sub-screens reached from the menu, composed to the same art direction — not modal form dumps.
|
|
325
|
+
|
|
299
326
|
## Settings menu
|
|
300
327
|
|
|
328
|
+
**Settings is game-owned and reachable from a place that makes sense.** Every game ships settings — but as *its* settings, skinned in its own art direction and placed inline with its own UI, never the engine's stock face dropped into a corner. The two canonical homes are the **main menu** and the **pause** screen; put the entry where the player would look, not floating over live play. The engine gives you the menu's contents and behavior for free — you own its skin, layout choice, and placement. If the default four layouts do not match the game, drive `useSettings()` and render the rows entirely inside your own chrome.
|
|
329
|
+
|
|
301
330
|
**Settings menu (themed, four layouts, no forced chrome).** The engine builds the whole menu for free — Sound (master + per-bus volume), Graphics (quality/dpr + shadows), Gameplay (FOV slider, default 40–120), Controls (per-action key rebinding, inline click-to-rebind, persisted) — from the game's `audio.buses` and `input` map. What it does **not** do is bolt a fixed gear onto every game: **there is no auto trigger.** You place the entry yourself so it lives *inline with your game's own UI*, never a stray corner overlay. Drop `<SettingsTrigger className=…>` (from `@jgengine/react`) anywhere in your HUD or menu — headless button, `className` for skin/placement, optional `children` to replace the default gear glyph, renders nothing when there's nothing to show. Or call `useSettings().open()` from your own control. Tune the menu via `defineGame({ settings })` (`GameSettingsConfig` from `@jgengine/core/settings/settingsModel`):
|
|
302
331
|
|
|
303
332
|
- `variant: "panel" | "sheet" | "sidebar" | "fullscreen"` — the layout + skin (default `panel`; `sheet` is the mobile bottom-sheet). All four are fixed-size (no shrink-to-content jitter) and read the game's `--jg-*` theme tokens, falling back to a neutral dark skin.
|
|
@@ -472,7 +501,7 @@ Require revision when any of these are true:
|
|
|
472
501
|
- controls overlap each other
|
|
473
502
|
- keyboard instructions appear on touch devices
|
|
474
503
|
- large instruction panels remain visible during gameplay
|
|
475
|
-
- more than three
|
|
504
|
+
- more than three undifferentiated, equally-weighted card-like panels are persistently visible (this is about generic card soup — using the shipped windows/bars is fine; the fault is unstyled boxes with no hierarchy)
|
|
476
505
|
- the main action looks like a standard website button
|
|
477
506
|
- pause resembles a generic website modal
|
|
478
507
|
- victory/failure is only text plus restart
|
|
@@ -487,6 +516,11 @@ Require revision when any of these are true:
|
|
|
487
516
|
- ordinary document flow determines the HUD layout
|
|
488
517
|
- generic default styling is used because no art direction was written
|
|
489
518
|
- the hosting website page renders its own title, tagline, or "enter game" button instead of loading straight into the game's own menu
|
|
519
|
+
- the game's own main menu reads as a marketing/landing page (hero banner, feature list, store framing) rather than a game front-end
|
|
520
|
+
- the game supports saves, characters, or classes but the main menu offers no Continue/Load or character create/select entry
|
|
521
|
+
- Continue/Load is hidden rather than shown disabled when there is nothing to resume
|
|
522
|
+
- settings ship as the engine's unskinned stock menu instead of the game's own skin and placement, or sit floating over live play with no sensible home
|
|
523
|
+
- credits are absent from the game, or attribution lives only in a build artifact instead of a reachable in-game screen
|
|
490
524
|
|
|
491
525
|
## 15. Compact implementation API appendix
|
|
492
526
|
|
|
@@ -494,7 +528,7 @@ Use the main `jgengine` skill for the authoritative engine API routing. The Reac
|
|
|
494
528
|
|
|
495
529
|
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.
|
|
496
530
|
|
|
497
|
-
|
|
531
|
+
The shipped building blocks — inventory grids, toggleable windows, character sheets, bars — are legitimate to compose and are the right starting point; reach for them instead of re-deriving inventory, windowing, or a paperdoll. But neither they nor the headless seams are finished design on their own: the game must still arrange, place, name, and art-direct them into its own look. Compose the building blocks, then skin and lay them out — don't ship them unarranged.
|
|
498
532
|
|
|
499
533
|
## Definition of done
|
|
500
534
|
|
|
@@ -507,6 +541,9 @@ UI work is complete only when:
|
|
|
507
541
|
- mobile controls do not cover critical content
|
|
508
542
|
- touch controls match the genre and game identity
|
|
509
543
|
- title, pause, and results screens feel authored
|
|
544
|
+
- the main menu is a game-owned front-end (New Game / Continue / Load / character create+select / Settings / Credits as the game supports them), not a marketing splash
|
|
545
|
+
- settings are game-skinned and reachable from the main menu and pause, not a stock corner overlay
|
|
546
|
+
- credits are reachable in-game and record borrowed work
|
|
510
547
|
- interaction states and transitions are present
|
|
511
548
|
- screenshots have been reviewed and revised
|
|
512
549
|
- the implementation remains accessible and performant
|