Skip to content

Tutorials · Interfaces · Chapter 9

Pause and confirm

60 minutes Uses: Modal, PopupMenu, CanvasGroup, Canvas, FocusRing, Button, Shortcut, pause, resume

By the end

  • Escape pauses the delve behind a pause menu that fades in and blocks every click
  • Quit to title asks “Leave the delve?”; Drop asks “Empty the bag?”
  • Each bag slot holding an item opens a menu: Use, Drop, Inspect
On this page

A pause menu

Esc pauses the delve. Everything that runs with the game stops where it is, and a second canvas takes over: it runs only while the game is paused, and it holds a dimmed room, a name and two buttons, Resume and Quit to title.

The key and the canvas

  1. Copy the two files below into your project’s art folder, each from where the list says it is in the unzipped download. Then press Refresh in the Assets panel.

panel_beige.png is the frame of every dialog in this chapter, and panel_beigeLight.png the plate of the bag’s menus.

  1. Choose View → Input map…. Type pause into New action and press Add. With pause selected, press Choose key…, type esc and choose Esc. Esc cancels a recording, so it can only be chosen from the list. Press Save, and close the window.
  2. Select Delve, click + Add node and choose Canvas. Rename it pause, and set its Scale Mode to ShrinkToFit.
  3. In its Node section, open Advanced and set Process Mode to WhenPaused.
  4. Select hud, and set its Theme’s Button Text to 63, 38, 49, alpha 255, the dark plum of the title’s buttons in chapter 5. The buttons and menus of this chapter are pale pictures, and cream words would vanish on them. Then open the ≡ menu at the right of its ThemeComponent header and choose Copy values.
  5. Select pause, open the ≡ menu of its ThemeComponent and choose the paste entry, Paste values. The pause canvas has the same palette as the HUD.
  6. Click Add component and choose FocusRingComponent, on pause and then on hud. Its Color is the palette’s Text Accent, 254, 174, 52, alpha 255, which Apply theme writes below. Width and Outset stay 2.
  7. Select pause, click Add component and choose TextSize, chapter 6’s script that sizes a canvas by the text size the player chose. Its three sizes, 1536 by 864, 1280 by 720 and 1066 by 600, are as the script gives them.

The Input window marks pause and ui_cancel as sharing Esc. That is meant. The menus answer ui_cancel, the game answers pause, and a menu or a dialog that takes an Esc first keeps it from the game, as the notes below say.

Process Mode
Whether a node, and everything under it, keeps running while the game is paused. Inherit, the default, follows the parent, and at the top of a scene that stops. WhenPaused runs only while the game is paused, which is what a pause menu is: pause and all it holds. Always runs either way, and Pausable stops with the game.

The HUD takes a Focus Ring of its own because the bag’s menus and dialogs are on the HUD, and a canvas draws the ring of the control the keys are on only for its own controls. The pause canvas is added after the HUD, so it draws over it: the interface draws in the Hierarchy’s order wherever two things overlap.

The menu

  1. Select pause, click + Add node and choose Fade Group. Rename it pause_menu. Its Rect stays as it comes, so it fills the canvas. Untick Visible in its Node section: the menu stays hidden until the game pauses.
  2. In its Canvas Group, set Alpha to 0 and tick Blocks Input.
  3. Click Add component and choose AudioSourceComponent. Untick Positional, and leave Bus as Sfx.
  4. In its Triggers, press the + beside it twice. Set the first entry’s Name to click and its Clip to click_001.ogg, and the second’s Name to back and its Clip to back_001.ogg. Each Mode stays Shuffle, and each Pitch and Volume as it comes, 1 to 1.
  5. Select pause_menu, click + Add node and choose Panel. Rename it pause_dim. Its Rect stays as it comes, so it fills the menu. Set its Panel Color to 63, 38, 49, alpha 191: the room goes dark behind it.
  6. Click Add component on pause_dim and choose ThemeRoleComponent. Set Role to Custom, so that Apply theme leaves the veil’s colour alone.
  7. Select pause_menu, click + Add node and choose Label. Rename it pause_name. In its Rect, set Anchor Min and Anchor Max both to 0.5, 0.5, Offset Min to -360, -141, Offset Max to 360, -61 and Pivot to 0.5, 1.
  8. In its Text, set Text to Paused, Scale to 1.5 and Align to Center. Set Outline to 63, 38, 49, alpha 255 and Outline Width to 2. Under Effects, set Shadow to 0, 0, 0, alpha 153, Shadow Offset to 3, 3 and Shadow Softness to 0.
  9. Click Add component and choose ThemeRoleComponent. Set Role to Accent. The gold of the word comes from the palette.
  10. Select pause_menu, click + Add node and choose Panel. Rename it pause_frame. In its Rect, set Anchor Min and Anchor Max both to 0.5, 0.5, Offset Min to -150, -45 and Offset Max to 150, 105.
  11. In its Panel, set Texture to panel_brown.png and Border to 8. Its Color comes from the palette.
  12. Select pause_frame, click + Add node and choose Stack. Rename it pause_buttons. Its Rect stays as it comes, so it fills the frame. In its Stack, set Fit to Fixed, Spacing to 12, Padding to 20, Item to 49 and Align to Center.

The frame is 300 wide and 150 high. Two buttons of 49 and the gap of 12 between them are 110, and the frame’s room, once its padding of 20 is taken from the top and the bottom, is 110 too: the two fill it.

Resume and Quit to title

  1. Select pause_buttons, click + Add node and choose Menu Button. Rename it resume. Its Rect stays as it comes: the Stack places it.
  2. In its Panel, set Texture to buttonLong_beige.png and Border to 8. In its Text, set Text to Resume, Scale to 0.75 and Align to Center.
  3. In its Button, set Action to resume, Hovered Texture to buttonLong_grey.png, Pressed Texture to buttonLong_beige_pressed.png and Disabled Texture to buttonLong_blue.png. In its Shortcut, set Action to ui_cancel.
  4. Select pause_buttons again, click + Add node and choose Button. Rename it quit_to_title. Give it the same Panel and the same three pictures, and set its Text’s Text to Quit to title, Scale to 0.75 and Align to Center, and its Button’s Action to quit_to_title. It has no Shortcut.
  5. Click Add component and choose FocusComponent, on resume and on quit_to_title. On resume, set Up, Down, Next and Previous all to quit_to_title, and on quit_to_title all four to resume. With two buttons every direction goes to the other.
Blocks Input
A Canvas Group’s Blocks Input: when it is ticked, a click anywhere in the group’s box stops there and goes no further, whatever is drawn behind it, and at any Alpha, so a menu that is still fading in already shields the room. pause_menu fills the whole canvas, so the HUD and the bag take no click while the game is paused. A group that is hidden blocks nothing, which is why the menu is hidden until it is needed.
pause_menu selected, its Inspector narrowed with the search box, canvasgroup, to its Canvas Group: Alpha 0.000, Interactable ticked, Blocks Input ticked and Ignore Parent Groups unticked; the Viewport shows the HUD’s plate at the top left of the room and the bag at its right, and no pause menu, which is hidden, with the move arrows at the top left corner of the picture

The Canvas Group’s other rows, Interactable and Ignore Parent Groups, stay as they come, and chapter 16 defines them. Alpha is chapter 5’s.

The scripts

  1. In Assets, press Create, choose New script, type pause_key, and press Enter. In the Code tab, replace everything in pause_key.rgs with the script below, and press Ctrl+S.
rgs pause_key.rgs
/// Esc, which the input map calls pause, pauses the game: everything that runs
/// with the game stops where it is, and the pause screen, which runs only
/// while the game is paused, takes over. It sits on the HUD, which runs with
/// the game.
component PauseKey {
    fn loop() {
        // An Escape that closed a slot's menu or answered one of the bag's
        // dialogs is spent on that, and reaches the game only once it is let
        // go and pressed again.
        if pressed("pause") {
            pause();
        }
    }
}
  1. Select hud, click Add component and choose PauseKey.

pressed("pause") is true for one step: the first after Esc goes down, unless a menu or a dialog has taken that Esc first. pause pauses the game at the end of the step: nodes set to run while paused keep going, and the rest stop where they are. The script sits on hud, which runs with the game, so it hears Esc only while the game is not paused.

  1. In Assets, press Create, choose New script, type pause_menu, and press Enter. In the Code tab, replace everything in pause_menu.rgs with the script below, and press Ctrl+S.
rgs pause_menu.rgs
/// The pause menu, over the delve. Its canvas runs only while the game is
/// paused, so it shows itself and fades in on the first step of each pause,
/// and it hides itself on the step Resume lets the game go on: once the game
/// runs again its hooks stop, and a menu left showing would sit over the room.
component PauseMenu {
    /// How long it takes to fade in.
    #[suffix("s")]
    fade: f32 = 0.25,
    /// Whether it is showing, so it shows itself once a pause, not on every
    /// step.
    #[hidden]
    open: bool = false,

    fn loop() {
        if !open {
            // From nothing, each pause. Its Canvas Group blocks the pointer
            // at any alpha, so the delve behind takes no click while it fades.
            set_number("CanvasGroupComponent", "alpha", 0.0);
            anneal_number("CanvasGroupComponent", "alpha", 1.0, fade, .OutQuad);
            show();
            open = true;
        }
        // A click on Resume, Enter while it holds the focus, or Escape through
        // its Shortcut presses resume on the game's next step. Escape answers
        // the open question first, if there is one.
        if pressed("resume") {
            play_sound("back");
            hide();
            open = false;
            resume();
        }
        if pressed("quit_to_title") || pressed("leave") {
            play_sound("click");
        }
        if pressed("stay") {
            play_sound("back");
        }
    }
}
  1. Select pause_menu, click Add component and choose PauseMenu. Its Fade starts at the script’s own 0.25: leave it.

The pause canvas runs only while the game is paused, so this script’s loop starts on the first step of each pause. A script on a hidden node still runs, which is how a hidden menu shows itself. open is false at first, so the script puts the group’s alpha at 0, anneals it to 1 over fade seconds and shows the node. resume ends the pause at the end of the step, and the script hides the menu on the same step, since once the game runs again its hooks stop and a menu left showing would sit over the room.

pressed("resume") is true for one step: the first after Resume is clicked, or Enter or Space presses it while it holds the focus, or Esc presses it through its Shortcut. pressed("quit_to_title") is true the same way, without the shortcut, and pressed("leave") and pressed("stay") come from the question, below. Resume and Stay play back, and Quit to title and Leave play click, from the node’s Audio Source.

The Code panel with pause_menu.rgs open: a comment saying the pause menu’s canvas runs only while the game is paused, then component PauseMenu with a fade field of 0.25 seconds, a hidden open field and a loop that, while open is false, sets the Canvas Group’s alpha to 0.0, anneals it to 1.0 over fade with .OutQuad, shows the node and sets open to true; the Scripts list at the left names the project’s scripts, pause_menu.rgs lit

A screen opens with its first control focused, as the title did, so Resume is lit. By keys, Up and Down or Tab walk between the two buttons, Enter presses the lit one, and Esc resumes.

The delve paused: the room and the HUD darkened by a plum veil, the word PAUSED in gold above a brown frame holding two beige buttons, RESUME, lit, with ESC at its right edge, and QUIT TO TITLE, and the hotbar’s four empty slots at the bottom
Escape: the room stops behind the menu.

A question first

Quit to title throws the run away, so it asks first. A Modal is a dialog that owns the interface until it is answered: a title, a question and up to three answers, drawn over a scrim, and nothing outside it can be reached by any input device while it is up. It is one node, and its answers are a list inside it, not nodes of their own.

  1. Select pause_menu, click + Add node and choose Modal. Rename it quit_dialog, and untick Visible. A dialog is open while it is visible, so a hidden one asks nothing.
  2. In its Rect, set Offset Min to -300, -53 and Offset Max to 300, 147. The preset centres it, so that is a box 600 wide and 200 high, a little below the middle, under the word Paused.
  3. In its Panel, set Texture to panel_beige.png and Border to 8.
  4. In its Modal, set Title to Leave the delve?, Body to Unsaved progress is lost. and Cancel Action to stay.
  5. Under Buttons, set the first answer’s Label to Stay and its Action to stay, and the second’s Label to Leave and its Action to leave.
  6. Under Colors, set Scrim to 63, 38, 49, alpha 115. The other four colours are written by Apply theme.
  7. Under Answers, set Button Texture to buttonLong_beige.png, Button Hovered Texture to buttonLong_grey.png, Button Pressed Texture to buttonLong_beige_pressed.png and Button Border to 8.
  8. Under Layout, set Padding to 20, Scale to 0.625, Title Height to 36, Button Height to 49 and Button Gap to 16.
  9. Select pause and press Apply theme, at the foot of its Theme. It writes the gold into the name and the ring, the plain panel colour into the frame, the dialog’s frame and the two buttons, and the plum Button Text into their words and the dialog’s.
The quit question’s Modal as its Inspector shows it, cut from the panel: Title Leave the delve?, Body Unsaved progress is lost., Buttons, a list of 2 items, with the first entry open to Label Stay, Action stay and Disabled unticked and the second shut, Cancel Action stay, a Colors group of Scrim, Normal, Hovered, Pressed and Foreground, an Answers group of three button pictures and Button Border 8.00 px, and a Layout group of Padding 20.00 px, Scale 0.625, Title Height 36.00 px, Button Height 49.00 px and Button Gap 16.00 px
The quit question’s Modal, its first answer open.
Title
A Modal’s Title: the heading, drawn in a band of its own at the top of the dialog. Empty, no band is reserved. Here it is Leave the delve?.
Body
A Modal’s Body: the question, below the title. Empty, no room is left for it. Here it is Unsaved progress is lost..
Buttons
A Modal’s Buttons: the answers, left to right, from one to three. A new Modal has two, Cancel and OK. They are a list of rows, not nodes, so the ring stops on the dialog and the left and right arrows move between its answers. Press the + to add an answer, and a row’s bin button to remove one.
Label
An answer’s Label: the word on its button, Stay and Leave. A row also has Disabled, which turns an answer off: chapter 16 uses it, and here it stays unticked.
Action
An answer’s Action: the action it fires when it is pressed, stay and leave, which a script hears with pressed. The dialog does not close itself: it stays up until a script hides it, as the script below does.
Cancel Action
The action Esc fires on a Modal, whether or not an answer carries it, so a dialog with one answer can still be backed out of. stay, the first answer’s own: Esc stays.
Scrim
The colour painted over everything behind a Modal. A dark plum at 115 of 255, a little under half strength, dims the room and the menu without hiding them. Alpha 0 draws none. Apply theme leaves it alone: how far the game dims is the author’s.
Normal, Hovered, Pressed
A Modal’s tints for an answer at rest, under the pointer or the ring, and held down, as a Button’s are. Apply theme writes them: white, white and 211, 211, 211, since the pictures carry the look and the tint only darkens one that is held.
Foreground
The colour of the title, the question and every answer’s label: the plum Button Text, 63, 38, 49, which Apply theme writes.
Button Texture, Button Hovered Texture, Button Pressed Texture
The pictures every answer wears, at rest, under the pointer or the ring, and held down: the same three as the menu’s buttons, buttonLong_beige.png, buttonLong_grey.png and buttonLong_beige_pressed.png. Empty, the answers are flat boxes.
Button Border
The nine-slice border of the answers’ pictures, in the pictures’ pixels, so their corners keep their size however wide an answer is: 8, as the Panel on a button has it.
Padding
A Modal’s Padding: the inset of its contents from the dialog’s edge, in UI units: 20.
Scale
A Modal’s Scale: the size of the title’s, the question’s and the answers’ words, a share of the face’s 32 pixels: 0.625 is 20.
Title Height
The height of the band kept for the title, in UI units: 36. Ignored when the title is empty.
Button Height
The height of the row of answers, in UI units: 49, as tall as the buttons of the menu.
Button Gap
The gap between two answers, in UI units: 16.

Next, the script.

  1. In Assets, press Create, choose New script, type quit_dialog, and press Enter. In the Code tab, replace everything in quit_dialog.rgs with the script below, and press Ctrl+S.
rgs quit_dialog.rgs
/// The question Quit to title asks before the delve is left: Leave goes back
/// to the title, and Stay, or Escape, stays in the paused game. A run is not
/// saved, so leaving loses it.
component QuitDialog {
    fn loop() {
        // A click on Quit to title, or Enter while it holds the focus,
        // presses quit_to_title on the game's next step.
        if pressed("quit_to_title") {
            show();
        }
        // A click on an answer, or Enter on the one the arrows chose, presses
        // its action; Escape presses the Cancel Action, stay.
        if pressed("stay") {
            hide();
        }
        if pressed("leave") {
            // A change of scene ends the pause too.
            change_scene("title");
        }
    }
}
  1. Select quit_dialog, click Add component and choose QuitDialog.

pressed("quit_to_title") is true for one step: the first after the Quit to title button is clicked, or Enter presses it while it holds the focus. The script shows the dialog on that step. pressed("stay") and pressed("leave") are true for the first step after an answer is clicked, or Enter presses the one the arrows chose, and stay is true after Esc too, since it is the Cancel Action. Stay hides the dialog, and the menu is still there under it. Leave changes the scene to the title, and a change of scene ends the pause too.

The Code panel with quit_dialog.rgs open: a comment on the question Quit to title asks, then component QuitDialog with a loop that shows the dialog on pressed quit_to_title, hides it on pressed stay and, on pressed leave, calls change_scene with title; the Scripts list at the left names the project’s scripts, quit_dialog.rgs lit
The paused delve with the question open: a pale beige dialog titled LEAVE THE DELVE?, reading UNSAVED PROGRESS IS LOST., with two buttons, STAY and LEAVE, and behind it the room, the HUD and the word PAUSED darker than before
Quit to title: the question.

A click on a slot of the bag should offer what to do with what is in it: use it, throw it away, or look at it. A Popup Menu is a list of commands that opens where you click, and each slot of the bag gets one. A Popup Menu draws only while it is open. Open, it answers keys as well: a row’s Shortcut chooses it, and Enter chooses the lit row.

  1. Select bag_1, click Add component and choose PopupMenuComponent.
  2. Under Items, the list starts with one row, Item. Press the + beside Items three times, so that there are four rows, and open each in turn. Set the first row’s Label to Use, its Action to bag_1_use and its Shortcut to KeyU. Set the second’s Label to Drop, Action to bag_1_drop and Shortcut to KeyT.
  3. Set the third row’s Label and Action to nothing, untick its Enabled and tick its Separator: it is a divider. Set the fourth’s Label to Inspect, Action to bag_1_inspect and Shortcut to KeyI. Every row but the divider is enabled, as a new row comes.
  4. Set Opens to bag_1_menu, Width to 176, Item Height to 30, Shortcut Width to 24 and Scale to 0.625. Separator Height stays 9.
  5. Set Texture to panel_beigeLight.png, Border to 8 and Padding to 8, 8. The colours are written by Apply theme.
A bag slot’s Popup Menu as its Inspector shows it, cut from the panel: Items, a list of 4 items, with the first row open to Label Use, Action bag_1_use, Enabled ticked, Separator unticked and Shortcut KeyU, and the other three shut, then Opens bag_1_menu, Width 176.00 px, Item Height 30.00 px, Separator Height 9.00 px, Shortcut Width 24.00 px, Scale 0.625, Texture panel_beige, Border 8.00 px, Padding x 8.0 and y 8.0, and a Colors group of Background white, Highlight yellow, Foreground dark plum, and Disabled and Rule in the same plum, half see-through
A bag slot’s Popup Menu, its four rows in a list.
Items
A Popup Menu’s Items: its rows, from the top, a list, each with the five fields below. Press the + to add a row, and a row’s arrows to move it.
Label
A row’s Label: the word it reads. A divider ignores it.
Action
A row’s Action: the action it fires when it is chosen, by a click or by its key, which a script hears with pressed. The number of the slot is in it, bag_1_use, so every slot’s menu fires actions of its own.
Enabled
Whether a row can be chosen. A row that is not enabled is drawn in the Disabled colour and takes no click and no key. A script can switch it as the game runs, as the slot’s script does for Use: a key cannot be used.
Separator
Whether a row is a divider between two groups instead of a command: a thin line in the Rule colour in a row of its own height. It takes no click, and its label and action are ignored.
Shortcut
A row’s Shortcut: the key that chooses it while the menu is open, named as the input map names keys, KeyU for U, or with a modifier, Ctrl+KeyS. The row draws it at its right edge as its label, U.
Opens
A Popup Menu’s Opens: the action whose click opens the menu, at the pointer, with its top left corner there. The slot’s own Button fires bag_1_menu, which chapter 7 gave it, so a click on the slot is what opens its menu. Empty, nothing opens it.
Width
How wide the menu opens, in UI units: 176.
Item Height
The height of a command row, in UI units: 30. A divider has its own, Separator Height, which chapter 16 sets.
Shortcut Width
The room kept at the right of every row for its key, in UI units: 24.
Scale
A Popup Menu’s Scale: the size of a row’s label and its key, a share of the face’s 32 pixels: 0.625 is 20.
Texture
A Popup Menu’s Texture: the picture the menu is drawn on, panel_beigeLight.png. Only the lit row is filled on it. Empty, the menu is a flat fill of its Background.
Border
A Popup Menu’s Border: the picture’s nine-slice border, in its pixels, so the corners keep their size as the menu grows: 8.
Padding
A Popup Menu’s Padding: how far the rows sit inside the menu’s edge, across and down, and the words inside their row: 8 and 8.
Background
The menu’s colour, or the tint on its picture. White draws the picture as it is. Apply theme writes it from the palette’s Panel, opaque, since a menu opens over the game.
Highlight
The colour behind the row under the pointer or the ring: a warm yellow, 254, 231, 97, the palette’s Panel Accent, which Apply theme writes.
Foreground
The colour of a row’s label: the plum Button Text, 63, 38, 49.
Disabled
A Popup Menu’s Disabled: the colour of a label that cannot be chosen, the plum at half strength, alpha 128, as a disabled button’s word is drawn.
Rule
The colour of the line a divider draws: the same plum at alpha 128.

The other fifteen slots take the same menu, with their own numbers. Copy the values across, then change the numbers.

  1. Select bag_1, open the ≡ menu of its PopupMenuComponent and choose Copy values.
  2. Select bag_2, then Ctrl-click bag_3 to bag_16, click Add component and choose PopupMenuComponent. Open its ≡ menu and choose the paste entry, Paste values: all fifteen now hold bag_1’s menu.
  3. Select each slot in turn, bag_2 to bag_16, and in its Popup Menu change Opens and the three Actions to its own number: bag_2_menu, bag_2_use, bag_2_drop and bag_2_inspect, and so on.

What each slot does

  1. In Assets, press Create, choose New script, type bag_slot, and press Enter. In the Code tab, replace everything in bag_slot.rgs with the script below, and press Ctrl+S.
rgs bag_slot.rgs
/// A slot of the bag, and the menu a click on it opens: Use drinks a potion or
/// the elixir as a hotbar slot does, Drop throws the item away, and Inspect
/// says what it is. A key cannot be used, and a click on an empty slot opens
/// no menu.
component BagSlot {
    /// The slot it is, which is kept by that name: bag_1 to bag_16. A click
    /// on it presses this name with _menu after it, which opens its menu, and
    /// the menu's rows press it with _use, _drop or _inspect after it.
    slot: String = "bag_1",
    /// The health a potion gives back.
    heal: f32 = 3.0,
    /// The most health the hero can have.
    max_health: f32 = 10.0,
    /// How long the elixir hastens the hero for.
    #[suffix("s")]
    haste: f32 = 5.0,

    fn loop() {
        // The satchel and the slots keep what each slot holds: 1 a potion, 2
        // the elixir, 3 a key, 0 nothing.
        let item = kept_number(slot, 0.0);
        // The menu opens on a click that presses the action its Opens names.
        // An empty slot has nothing to offer, so its menu names no action and
        // the click opens nothing; one holding an item names the slot's.
        if item == 0.0 {
            set_text("PopupMenuComponent", "opens", "");
        } else {
            set_text("PopupMenuComponent", "opens", slot + "_menu");
        }
        // The menu's rows, from the top: Use, Drop, the rule, Inspect. Only
        // a potion or the elixir can be used. A slot that empties while its
        // menu is open has nothing left to drop or inspect.
        set_flag("PopupMenuComponent", "items.0.enabled", item == 1.0 || item == 2.0);
        set_flag("PopupMenuComponent", "items.1.enabled", item != 0.0);
        set_flag("PopupMenuComponent", "items.3.enabled", item != 0.0);

        // A click on a row, or its key while the menu is open, presses its
        // action on the game's next step. Each empties the slot on the step
        // the count goes down, so the satchel finds the two agreeing.
        if pressed(slot + "_use") {
            let health = kept_number("health", 0.0);
            if item == 1.0 && health < max_health {
                add_number("health", min(heal, max_health - health));
                add_number("potions", -1.0);
                keep_number(slot, 0.0);
            } else if item == 2.0 {
                keep_number("haste", haste);
                add_number("elixirs", -1.0);
                keep_number(slot, 0.0);
            }
        }
        if pressed(slot + "_drop") {
            // The count goes down only while there is one to take: the door
            // spends a key from the count, and the satchel takes it out of
            // its slot a step later, so for one step a slot can hold a key the
            // hero no longer owns.
            if item == 1.0 && kept_number("potions", 0.0) >= 1.0 {
                add_number("potions", -1.0);
            } else if item == 2.0 && kept_number("elixirs", 0.0) >= 1.0 {
                add_number("elixirs", -1.0);
            } else if item == 3.0 && kept_number("keys", 0.0) >= 1.0 {
                add_number("keys", -1.0);
            }
            keep_number(slot, 0.0);
        }
        if pressed(slot + "_inspect") {
            // The bag's Inspect dialog shows whatever is kept under inspect.
            keep_number("inspect", item);
        }
    }
}
  1. Select all sixteen bag slots, click Add component and choose BagSlot. Then select each slot in turn and set its Slot to its own name, bag_1 to bag_16. Heal, Max Health and Haste stay at the script’s 3, 10 and 5.

The script writes the menu as the game runs. A slot with nothing in it writes an empty Opens, so a click on it opens nothing. One with an item names bag_1_menu. Use is enabled for a potion or the elixir only, and Drop and Inspect for anything. A click on a row, or its key while the menu is open, presses its action on the game’s next step: pressed("bag_1_use") is true for that one step.

Use drinks the potion as the hotbar’s slot does, or takes the elixir, and empties the slot. Drop throws the item away and takes it from the counts, so a key dropped is a key the counter no longer shows. Inspect keeps the item’s number under inspect, which the dialog below reads.

Two dialogs for the bag

  1. Select hud, click + Add node and choose Modal. Rename it inspect_dialog, and untick Visible. In its Rect, set Offset Min to -300, -100 and Offset Max to 300, 100. In its Panel, set Texture to panel_beige.png and Border to 8.
  2. Select quit_dialog, open the ≡ menu of its ModalComponent and choose Copy values. Select inspect_dialog, open the ≡ menu of its ModalComponent and paste them. Its colours, pictures and sizes are the quit question’s.
  3. In its Modal, set Title to Potion and Body to Potion: restores 3 health.. Remove the second answer with its bin button, and set the first answer’s Label to OK and its Action to inspect_ok. Set Cancel Action to inspect_ok.
  4. Select hud again and add a second Modal, empty_dialog, hidden, with the same Rect, Panel and pasted values. Set its Title to Empty the bag? and its Body to Everything in it is lost.. Set the first answer’s Label to Keep and its Action to keep_bag, the second’s Label to Empty and its Action to empty_bag, and Cancel Action to keep_bag.

The Inspect dialog’s title and words are only what the item will overwrite: the script writes them from the item the slot held. Next, the two scripts.

  1. In Assets, press Create, choose New script, type inspect_dialog, and press Enter. In the Code tab, replace everything in inspect_dialog.rgs with the script below, and press Ctrl+S.
rgs inspect_dialog.rgs
/// The dialog a bag slot's Inspect opens: it names the item kept under
/// inspect and says what it does, and OK, or Escape, shuts it.
component InspectDialog {
    fn loop() {
        // A slot's Inspect keeps its item under inspect: 1 a potion, 2 the
        // elixir, 3 a key, and 0 once the dialog is shut.
        let item = kept_number("inspect", 0.0);
        // A click on OK, Enter, or Escape through the Cancel Action presses
        // inspect_ok on the game's next step.
        if pressed("inspect_ok") {
            keep_number("inspect", 0.0);
            hide();
        } else if item > 0.0 {
            // The item's name over what its tooltip says.
            if item == 1.0 {
                set_text("ModalComponent", "title", "Potion");
                set_text("ModalComponent", "body", "Potion: restores 3 health.");
            } else if item == 2.0 {
                set_text("ModalComponent", "title", "Elixir");
                set_text("ModalComponent", "body", "Elixir: you walk faster for 5 seconds.");
            } else {
                set_text("ModalComponent", "title", "Key");
                set_text("ModalComponent", "body", "Key: opens the locked door.");
            }
            show();
        }
    }
}
  1. Select inspect_dialog, click Add component and choose InspectDialog.
  2. In Assets, press Create, choose New script, type empty_bag, and press Enter. In the Code tab, replace everything in empty_bag.rgs with the script below, and press Ctrl+S.
rgs empty_bag.rgs
/// The question the bag's Drop button asks: Empty throws away everything in
/// the bag's sixteen slots, and Keep, or Escape, keeps it. The hotbar is not
/// the bag, and keeps what it holds.
component EmptyBag {
    fn loop() {
        // A click on Drop presses drop on the game's next step.
        if pressed("drop") {
            show();
        }
        // A click on an answer, or Enter on the one the arrows chose, presses
        // its action; Escape presses the Cancel Action, keep_bag.
        if pressed("keep_bag") {
            hide();
        }
        if pressed("empty_bag") {
            // Every slot of the bag emptied, and what was in them taken from
            // the counts on the same step, so the satchel finds the two
            // agreeing.
            let potions = 0.0;
            let elixirs = 0.0;
            let keys = 0.0;
            for i in 1..17 {
                let slot = "bag_" + text(i);
                let item = kept_number(slot, 0.0);
                if item == 1.0 {
                    potions += 1.0;
                } else if item == 2.0 {
                    elixirs += 1.0;
                } else if item == 3.0 {
                    keys += 1.0;
                }
                keep_number(slot, 0.0);
            }
            // Never more than the counts hold: a key the door spent stays in
            // its slot for a step, until the satchel takes it out.
            add_number("potions", -min(potions, kept_number("potions", 0.0)));
            add_number("elixirs", -min(elixirs, kept_number("elixirs", 0.0)));
            add_number("keys", -min(keys, kept_number("keys", 0.0)));
            hide();
        }
    }
}
  1. Select empty_dialog, click Add component and choose EmptyBag.

pressed("inspect_ok") is true for one step: the first after OK is clicked, or Enter presses it, or Esc does, the Cancel Action. The script reads the item kept under inspect and writes its words into the dialog every step, then shows it. pressed("drop") is the bag’s Drop button of chapter 4, pressed by a click let go on it, true for one step, the first after. The two answers press keep_bag and empty_bag the same way, and keep_bag also on Esc. Empty empties all sixteen slots and takes what they held from the counts.

  1. Select key_hints and set its Text’s Text to WASD or arrows walk / B bag / 1-4 use / Esc pause.
  2. Open title_menu.rgs. A new game must not open a stale Inspect dialog, so it forgets what was being inspected. The lines below are as they now read, from the comment above the two loops to the end of the run: the comment is one line longer, and the last line is new. Make them so, and press Ctrl+S.
rgs part of title_menu.rgs
            // And every slot of the hotbar and the bag empty, no haste, and
            // nothing being inspected.
            for i in 1..5 {
                keep_number("hotbar_" + text(i), 0.0);
            }
            for i in 1..17 {
                keep_number("bag_" + text(i), 0.0);
            }
            keep_number("haste", 0.0);
            keep_number("inspect", 0.0);
  1. Select hud and press Apply theme, at the foot of its Theme. It writes the colours of the two dialogs and of every slot’s menu from the palette.

Now try it. Walk onto the key, press B to open the bag, and click the key’s slot. Its menu opens at the pointer.

Close up of the bag, with the key’s slot clicked: a pale menu opens over the slot’s corner with USE greyed and U at its right, DROP and T, a thin rule, and INSPECT and I
The key’s slot clicked: Use greyed, Drop, a rule, Inspect.

Use is greyed, since a key cannot be used. Choose Inspect with a click, or press I: the key’s dialog opens, and OK, Enter or Esc closes it. The bag’s own Drop button asks before it empties the bag.

The delve with a pale beige dialog in front of it, titled KEY and reading KEY: OPENS THE LOCKED DOOR., with one button, OK; behind it the room darker, the key counter reading 1 and the bag at the right with the key in its first slot
Inspect: the key’s dialog.
The delve with a pale beige dialog titled EMPTY THE BAG?, reading EVERYTHING IN IT IS LOST., with two buttons, KEEP and EMPTY, over the darkened room, and the bag at the right still holding the key
The bag’s Drop: a question first.

The pause menu, the question and the bag’s menus

The pause menu

NodeComponentFieldValue
hudThemeButton Text63, 38, 49, alpha 255
Focus RingColor254, 174, 52, alpha 255, written by Apply theme
key_hintsTextTextWASD or arrows walk / B bag / 1-4 use / Esc pause
pauseNodeProcess ModeWhenPaused
CanvasScale ModeShrinkToFit
ThemePanel255, 255, 255, alpha 255
Panel Accent254, 231, 97, alpha 255
Panel Danger232, 69, 55, alpha 255
Text236, 227, 206, alpha 255
Text Accent254, 174, 52, alpha 255
Text Danger255, 228, 220, alpha 255
Button255, 255, 255, alpha 255
Button Text63, 38, 49, alpha 255
Hover Lift0.15
Press Sink0.35
Focus RingColor254, 174, 52, alpha 255, written by Apply theme
TextSizeSmall1536, 864
Normal1280, 720
Large1066, 600
pause_menuNodeVisibleunticked
Canvas GroupAlpha0
Blocks Inputticked
Audio SourcePositionalunticked
Triggerstwo: Name click, Clip click_001.ogg; Name back, Clip back_001.ogg. Each Mode Shuffle, Pitch and Volume 1 to 1
PauseMenuFade0.25
pause_dimPanelColor63, 38, 49, alpha 191
Theme RoleRoleCustom
pause_nameRectAnchor Min0.5, 0.5
Anchor Max0.5, 0.5
Offset Min-360, -141
Offset Max360, -61
Pivot0.5, 1
TextTextPaused
Scale1.5
Color254, 174, 52, alpha 255, written by Apply theme
AlignCenter
Outline63, 38, 49, alpha 255
Outline Width2
Shadow0, 0, 0, alpha 153
Shadow Offset3, 3
Shadow Softness0
Theme RoleRoleAccent
pause_frameRectAnchor Min0.5, 0.5
Anchor Max0.5, 0.5
Offset Min-150, -45
Offset Max150, 105
PanelColor255, 255, 255, alpha 255, written by Apply theme
Texturepanel_brown.png
Border8
pause_buttonsStackFitFixed
Spacing12
Padding20
Item49
AlignCenter
resumePanelColor255, 255, 255, alpha 255, written by Apply theme
TexturebuttonLong_beige.png
Border8
TextTextResume
Scale0.75
Color63, 38, 49, alpha 255, written by Apply theme
AlignCenter
ButtonActionresume
Normal255, 255, 255, alpha 255, written by Apply theme
Hovered255, 255, 255, alpha 255, written by Apply theme
Pressed211, 211, 211, alpha 255, written by Apply theme
Hovered TexturebuttonLong_grey.png
Pressed TexturebuttonLong_beige_pressed.png
Disabled TexturebuttonLong_blue.png
ShortcutActionui_cancel
FocusUpquit_to_title
Downquit_to_title
Nextquit_to_title
Previousquit_to_title
quit_to_titlePanelColor255, 255, 255, alpha 255, written by Apply theme
TexturebuttonLong_beige.png
Border8
TextTextQuit to title
Scale0.75
Color63, 38, 49, alpha 255, written by Apply theme
AlignCenter
ButtonActionquit_to_title
Normal255, 255, 255, alpha 255, written by Apply theme
Hovered255, 255, 255, alpha 255, written by Apply theme
Pressed211, 211, 211, alpha 255, written by Apply theme
Hovered TexturebuttonLong_grey.png
Pressed TexturebuttonLong_beige_pressed.png
Disabled TexturebuttonLong_blue.png
FocusUpresume
Downresume
Nextresume
Previousresume

The quit question

ComponentFieldValue
NodeVisibleunticked
RectAnchor Min0.5, 0.5
Anchor Max0.5, 0.5
Offset Min-300, -53
Offset Max300, 147
PanelColor255, 255, 255, alpha 255, written by Apply theme
Texturepanel_beige.png
Border8
ModalTitleLeave the delve?
BodyUnsaved progress is lost.
ButtonsLabel Stay, Action stay; and Label Leave, Action leave
Cancel Actionstay
Scrim63, 38, 49, alpha 115
Normal255, 255, 255, alpha 255, written by Apply theme
Hovered255, 255, 255, alpha 255, written by Apply theme
Pressed211, 211, 211, alpha 255, written by Apply theme
Foreground63, 38, 49, alpha 255, written by Apply theme
Button TexturebuttonLong_beige.png
Button Hovered TexturebuttonLong_grey.png
Button Pressed TexturebuttonLong_beige_pressed.png
Button Border8
Padding20
Scale0.625
Title Height36
Button Height49
Button Gap16

The bag’s menus

ComponentFieldValue
Popup MenuItemsfour rows: Use, Action bag_1_use, Shortcut KeyU; Drop, Action bag_1_drop, Shortcut KeyT; a divider, Enabled unticked and Separator ticked; Inspect, Action bag_1_inspect, Shortcut KeyI. Each slot’s own number in its actions
Opensbag_1_menu to bag_16_menu
Width176
Item Height30
Shortcut Width24
Scale0.625
Texturepanel_beigeLight.png
Border8
Padding8, 8
Background255, 255, 255, alpha 255, written by Apply theme
Highlight254, 231, 97, alpha 255, written by Apply theme
Foreground63, 38, 49, alpha 255, written by Apply theme
Disabled63, 38, 49, alpha 128, written by Apply theme
Rule63, 38, 49, alpha 128, written by Apply theme
BagSlotSlotbag_1 to bag_16
Heal3
Max Health10
Haste5

The bag’s two dialogs

NodeComponentFieldValue
inspect_dialogNodeVisibleunticked
RectAnchor Min0.5, 0.5
Anchor Max0.5, 0.5
Offset Min-300, -100
Offset Max300, 100
PanelColor255, 255, 255, alpha 255, written by Apply theme
Texturepanel_beige.png
Border8
ModalTitlePotion
BodyPotion: restores 3 health.
Buttonsone answer: Label OK, Action inspect_ok
Cancel Actioninspect_ok
Scrim63, 38, 49, alpha 115
Normal255, 255, 255, alpha 255, written by Apply theme
Hovered255, 255, 255, alpha 255, written by Apply theme
Pressed211, 211, 211, alpha 255, written by Apply theme
Foreground63, 38, 49, alpha 255, written by Apply theme
Button TexturebuttonLong_beige.png
Button Hovered TexturebuttonLong_grey.png
Button Pressed TexturebuttonLong_beige_pressed.png
Button Border8
Padding20
Scale0.625
Title Height36
Button Height49
Button Gap16
empty_dialogNodeVisibleunticked
RectAnchor Min0.5, 0.5
Anchor Max0.5, 0.5
Offset Min-300, -100
Offset Max300, 100
PanelColor255, 255, 255, alpha 255, written by Apply theme
Texturepanel_beige.png
Border8
ModalTitleEmpty the bag?
BodyEverything in it is lost.
ButtonsLabel Keep, Action keep_bag; and Label Empty, Action empty_bag
Cancel Actionkeep_bag
Scrim63, 38, 49, alpha 115
Normal255, 255, 255, alpha 255, written by Apply theme
Hovered255, 255, 255, alpha 255, written by Apply theme
Pressed211, 211, 211, alpha 255, written by Apply theme
Foreground63, 38, 49, alpha 255, written by Apply theme
Button TexturebuttonLong_beige.png
Button Hovered TexturebuttonLong_grey.png
Button Pressed TexturebuttonLong_beige_pressed.png
Button Border8
Padding20
Scale0.625
Title Height36
Button Height49
Button Gap16

Every other field of every node is as it comes.

Check it works