Menus and input
These commands and selectors build menus, manage the menu stack and the scope stack, and run input bindings. For a walkthrough, see Menus and input.
Novel’s menus
Section titled “Novel’s menus”Your novel opens on the main menu when your project.json sets startup.bootstrapCommand to openMainMenu.
| Menu | Fields |
|---|---|
main | New Story, Continue, Load, Options, Language, and Back |
pause | Save, Load, Options, Main Menu, and Back |
options | Text and Voice languages, Controls, and Back |
controls | Back |
save | Three save slots, and Back |
load | Three save slots, and Back |
The save slots are bookmarks with the IDs slot-1, slot-2, and slot-3.
Back returns to the menu underneath. From the main menu, it closes every menu and returns to the story.
Each menu’s name is also one of its labels, so artists can give each menu its own look. The controls menu also has a label for the reader’s controller: generic, nintendo, playstation, or xbox. It uses generic when no controller is connected.
Novel’s menus draw their interface over the .ui layer by default. If a menu doesn’t name a layer, or its artwork has no matching layer, the menu covers the whole canvas.
Commands
Section titled “Commands”buildMenuSchema
Section titled “buildMenuSchema”buildMenuSchema returns a menu’s schema.
buildMenuSchema(ctx: { name: MenuName }): Promise<{ schema: MenuSchema }>To add fields to a menu, decorate this command and check ctx.name.
Example
const { buildMenuSchema } = getCommands();const { schema } = await buildMenuSchema({ name: "options" });openMainMenu
Section titled “openMainMenu”openMainMenu closes any open menus, and opens the main menu.
openMainMenu(): Promise<void>Example
const { openMainMenu } = getCommands();await openMainMenu();pushMenu
Section titled “pushMenu”pushMenu adds a menu to the menu stack. If the menu’s schema has a scope, it pushes that scope too.
pushMenu(ctx: PushMenuContext): Promise<PushMenuResult>
interface PushMenuContext { name: MenuName; cleanupCallback?: () => void;}interface PushMenuResult { release: () => Promise<void>;}name is keyof Menus. release removes the menu, and its scope if it pushed one, and then redraws. cleanupCallback runs when you call release.
Example
const { pushMenu } = getCommands();const { release } = await pushMenu({ name: "options" });pushScope
Section titled “pushScope”pushScope adds a scope to the scope stack. Only bindings for the scope on top of the stack, or for "global", can run. See Scopes.
pushScope(ctx: PushScopeContext): Promise<PushScopeResult>
interface PushScopeContext { name: Scope; cleanupCallback?: () => void;}interface PushScopeResult { release: () => void;}name is keyof Scopes. Call release to remove the scope. cleanupCallback runs when you do.
Example
const { pushScope } = getCommands();const { release } = await pushScope({ name: "journal" });toggleMenu
Section titled “toggleMenu”toggleMenu opens or closes menus.
toggleMenu(): Promise<void>If a menu is open, toggleMenu closes the menu on top, as long as there’s something to go back to: another menu underneath, or an open story. Closing the main menu closes every menu. If no menu is open, it opens the "pause" menu over an open story, or the "main" menu otherwise.
Example
const { toggleMenu } = getCommands();await toggleMenu();Selectors
Section titled “Selectors”getInputBindings
Section titled “getInputBindings”getInputBindings returns every input binding.
getInputBindings(): { bindings: Array<InputBinding> }
interface InputBinding { action: () => Promise<void>; inputs: Array<string>; name: string; scope: Scope;}| Field | Description |
|---|---|
action | Runs when the reader presses one of inputs. |
inputs | The binding’s inputs, in the input binding format. |
name | Identifies the binding. |
scope | Where the binding can run. See Scopes. |
Novel comes with these bindings:
| Name | Scope | Inputs |
|---|---|---|
toggleMenu | global | escape, Start (gamepad:9) |
advance | navigate | space, click, bottom face button (gamepad:0) |
stepBack | navigate | arrowleft, left bumper (gamepad:4) |
stepForward | navigate | arrowright, right bumper (gamepad:5) |
quickSave | navigate | meta + s |
quickLoad | navigate | meta + l |
Decorate it to add bindings of your own.
Example
decorateSelector("getInputBindings", (context, next) => { const result = next(context); return { bindings: [...result.bindings, openJournal] };});getInputAction
Section titled “getInputAction”getInputAction returns the action bound to an input. It checks the scope on top of the scope stack first, then "global".
getInputAction(ctx: { inputs: string }): { action?: () => Promise<void> }inputs is a single input, in the input binding format. Bindings come from getInputBindings. If more than one binding in a scope uses the input, the first one in the list wins.
Example
const { getInputAction } = getSelectors();const { action } = getInputAction({ inputs: "space" });action?.();