# Menus, theme, prompts and end screen (ui.* kits) The screens around your game: title and pause menus, options, the interface theme, key prompts, the end screen with credits and the game cursor. Readable offline: `gripforge docs ui-screens`, or the MCP tool `gripforge_docs` (topic `ui-screens`). To draw your own interface as a web page, see `gripforge docs ui-webui`. ## What it is for Each kit builds one model that every engine draws (the web and native players, Godot, Unity, Unreal), so the same buttons, texts, tabs and behaviour come out everywhere. | Kit | Role | In a new game | | --- | --- | --- | | `ui.menu` | title screen, pause menu, Options, Load / Save screen, loading line | yes (with `input.remap`, `settings.graphics`, `save.persistence`) | | `ui.theme` | one interface theme per game, as data | yes, with every value unset | | `ui.prompts` | key hints drawn from the live bindings | yes, with the help bar off | | `ui.endscreen` | victory, defeat and game over screen, recap, credits | yes (needs `ui.menu`) | | `ui.cursor` | a game cursor and the marker where an order is given | no | ```text gripforge_gamekit_configure { project_id: "gkp_…", id: "ui.menu", config: { "title": "My game", "titleButtons": ["play", "continue", "options", "quit"] } } gripforge_gamekit_configure { project_id: "gkp_…", id: "ui.theme", config: { "preset": "fantasy-medieval" } } gripforge_gamekit_remove { project_id: "gkp_…", id: "ui.menu" } # the game then starts straight into play ``` Remove a default kit only when you mean to. The full reference of each is `gripforge kit api `. ## ui.menu - **Title** (`titleScreen`, on): the game waits, paused. Buttons Play, Continue (only when `save.persistence` holds a save, the most recent one is loaded), Load, Options, Quit. With `game.engine`'s `sceneFlow.menu: "scene"` (every new game) the title is a scene of its own; see `gripforge docs scenes`. - **Pause** (`pauseKey` Escape, or the gamepad Start button `pausePad`): Resume, Save, Load, Options, Restart, Quit to title. - **Load / Save**: one row per slot with picture, date, play time, location and version; overwriting and deleting ask first. - **Options**: tabs `general` (language), `controls` (`input.remap`), `graphics` (`settings.graphics`), then tabs other kits register. A tab whose kit is absent is hidden. | Key | Default | Meaning | | --- | --- | --- | | `title`, `subtitle` | project name, `""` | texts of the title screen | | `logo`, `background` | `""` | image paths (a web URL, `res://` on Godot, a Resources path on Unity, an asset path on Unreal) | | `titleMode` | `artwork` | web: `artwork`, `scene` (the paused game behind), `character` (the bound model and its idle animation) | | `characterSlot` | `player_character` | model used by the `character` mode | | `titleLayout`, `titleText`, `playLabel`, `titleHelp`, `pageBar` | `side`, `""`, `""`, `[]`, `true` | layout and texts of the title | | `theme` | accent `#ff6b2c`, panel `#161a22`, text `#f4f1ea`, background `#0d1016`, scale 1 | colours and size | | `language` | `auto` | `auto`, `en`, `fr`; the player can change it (kept in the player settings, outside saves) | | `titleButtons` | play, continue, load, options, quit | buttons and order | | `pauseButtons` | resume, save, load, options, restart, quit_to_title | buttons and order (`quit` can be added) | | `tabs` | general, controls, graphics | Options tabs; add `saves` to manage saves from Options | | `pauseKey`, `pausePad` | `Escape`, 9 | pause controls (not moddable) | | `pauseGame` | `true` | pause while a menu is open (not moddable) | | `loadingLine` | `title` | where the loading progress of `asset.preload` shows: `title`, `menus`, `off` | | `waitFirstReady` | `false` | Play stays disabled until the start scene is loaded (Continue and Load stay usable) | | `keyHints` | `false` | key hints under the buttons | **Loading line**: while `asset.preload` loads, the title shows one line with a bar (`Loading 42% - 12.3 / 29.0 MB - 14/30 files - from the cache`), and nothing once it is done. **Add an Options tab**: expose on your kit's service `optionsTab()` (`{ id, order, label }`), `optionFields(lang)` (fields of kind `choice`, `toggle`, `slider`, `binding`, `button`, `actions`) and `setOption(id, value)`, then call `ui.menu.registerTab("")`. Service: `view(lang?)`, `open(screen)`, `back()`, `navigate(delta)`, `focus(id)`, `activate(id?)`, `pause()`, `resume()`, `togglePause()`, `perform(action)`, `press(control)`, `tabs()`, `selectTab(id)`, `nextTab(delta)`, `fields(tab?)`, `setOption(tab, id, value)`, `registerTab(capability)`, `canContinue()`, `setLanguage(lang)`, `text(key)`, `loading(lang?)`. Events: `menu.screen`, `menu.action { action, slot }` (play, continue, load, save, options, resume, restart, quit_to_title, quit), `menu.tab`, `menu.language`. The menu declares no hook: the player decides, and the kit that carries an action out owns the decision (a save goes through `save.write` of `save.persistence`). A mod can change texts, theme, staging, buttons and tabs, not images, the character slot, pause controls or `pauseGame`. ## ui.theme Every default is unset, and unset means each kit keeps its built-in look: a game that does not configure the theme looks as before. | Group | Keys | Values | | --- | --- | --- | | `colors` | `accent`, `accentText`, `panel`, `text`, `muted`, `background`, `danger`, `success`, `focus` | `#rrggbb` or `#rrggbbaa` | | `typography` | `font`, `fontFile`, `scale`, `size`, `titleSize`, `weight`, `letterCase` | family, font file, scale 0.5 to 2, body 10 to 32, title 20 to 96, `regular`/`medium`/`bold`, `none`/`upper` | | `shapes` | `radius`, `borderWidth`, `buttonShape`, `padding` | 0 to 32, 0 to 8, `square`/`rounded`/`pill`, 0 to 64 | | `glyphs` | `style`, `set` | `outlined`/`filled`/`text`; `letters`/`shapes`/`positions` for the gamepad face buttons | | `images` | `logo`, `background` | same path rules as `fontFile` | `preset` picks a complete look in one value; any explicit value of the config wins over it. Presets: `scifi-lowpoly`, `military`, `fantasy-medieval`, `cyberpunk-neon`, `cartoon-bright`, `horror-dark`, `minimal-clean`, `retro-pixel`. A preset never sets a file (`fontFile`, `images`). ```json { "preset": "scifi-lowpoly", "colors": { "accent": "#f2c14e" } } ``` Run time: `presets()`, `preset()`, `applyPreset(id)`, `apply(...)`, `reset()`; `ui.theme.changed { revision, theme }` makes interfaces redraw. Fonts: the web page loads the first family of the stack from a bundled open-licence file; Godot and Unity try the families in order; Unreal uses its own font unless `fontFile` is set. A mod can change `colors`, `shapes` and `glyphs`. ## ui.prompts Key hints such as `[E] Open door`, or `[A] Continue` with a gamepad. No key is written in the kit: every label comes from `input.actions` (`labelFor`, `promptFor`), so it follows the player's remapping and the device used last. **No key is shown on screen by default**: the help bar (`bar`) and the context row (`context`) are off, and the controls live in Options, Controls. Turn one on for a tutorial or a game that lives on contextual prompts; a tutorial step pushes a prompt with `show()`. Keys: `bar` (`false`), `context` (`false`), `barActions` (`[]`, the automatic bar), `exclude`, `maxItems` (6), `interaction` (`true`), `contexts`, `position` (`bottom`), `device` (`auto`), `language` (`auto`), `hideInMenus` (`true`); all moddable. Event `prompts.changed`. ## ui.endscreen Opens once, pausing the game (`pauseGame`), when: - a kit, script or code calls `show(result, stats?)` with `victory`, `defeat` or `end`; - an event of `victoryEvents` (`game.won`) or `defeatEvents` (`game.lost`) is emitted (patterns `*` and `family.*` work); - a mission listed in `missions` completes (victory) or fails (defeat), `*` for any; - the player dies (`onDeath`), or the game enters the `finished` phase (`onFinished`, on). | Key | Default | Meaning | | --- | --- | --- | | `template` | `classic` | screen layout | | `victoryEvents`, `defeatEvents` | `["game.won"]`, `["game.lost"]` | events that end the game | | `missions` | `[]` | missions that end the game | | `onFinished`, `onDeath` | `true`, `false` | | | `showTime` | `true` | play time in the recap | | `stats` | score, money, room | `{ id, var, label, format }` lines read from variables (`int`, `number`, `time`, `money`, `percent`, `text`) | | `buttons` | retry, menu, credits | order; `quit` can be added. `retry` and `menu` go through `ui.menu.perform` | | `victoryTitle`, `defeatTitle`, `victoryText`, `defeatText` | built-in | `{ en, fr }` texts | | `creditsTitle`, `credits`, `creditsSpeed`, `poweredBy` | project name, `[]`, 1.5, `true` | `credits` is `[{ title, names }]`, scrolled in order | | `loadingLine` | `next` | progress of the next scenes' preload by the buttons: `next`, `all`, `off` | A kit adds recap lines by exposing `endStats(lang)` on its service and calling `ui.endscreen.registerStats("")`; a kit may set the variables `endscreen.title` and `endscreen.text` to replace the heading (`game.endings` does). Hook `endscreen.show { result, trigger, mission, time }`: mutable `result` (a defeat that is the true ending), cancellable (the screen stays closed and `show()` answers `false`). The credits are the authors' and not moddable. ## ui.cursor Replaces the system pointer with a game cursor and marks the ground where the player gives an order. Any kit that turns a click into an order emits `pointer.command { point, kind: "move" | "attack", target? }`; this kit answers with the event `fx.ground_marker` (green arrows closing on the point for a move, red for an attack) and the host draws it. | Key | Default | Meaning | | --- | --- | --- | | `cursor` | `gauntlet` | `gauntlet`, `arrow`, `crosshair`, `system` | | `cursorColor`, `cursorSize` | `#d8dde6`, 32 | | | `marker` | `arrows` | `arrows`, `ring`, `none` | | `moveColor`, `attackColor` | `#3ee06a`, `#ff4d4d` | the cursor turns `attackColor` over an enemy | | `markerSeconds`, `markerSize` | 0.55, 1.8 | life in seconds, radius in metres | ## When it does not do what you expect | Symptom | Cause and fix | | --- | --- | | The game starts without a title | `ui.menu` was removed, or `titleScreen: false` | | Continue is missing | `save.persistence` holds no save | | A key hint is missing | `ui.prompts` `bar` and `context` are off on purpose: turn one on for a tutorial | | The end screen never opens | no `victoryEvents`, `defeatEvents`, mission or `show()` call reaches it, or a tap of `endscreen.show` cancelled it | | The theme looks unchanged | the value is unset in `ui.theme` (each kit keeps its look) or the `ui.menu` `theme` is used instead |