Repository navigation
Pause Menu
The pause menu allows players to pause the game during gameplay.
Players can:
- pause the game using the Esc key
- resume the game
- restart the current level
- quit to the main menu
- change settings
- check key binds for the game
- save game state
When paused:
- player movement is stopped
- enemy movement is stopped
- gameplay timers are stopped
- animations freezes (except loot item bobbing effect)
Additional menu features and visual improvements may be added later.
The pause menu and main menu use a display/action split: display components fire events, and a separate actions component listens for those events and performs the actual behaviour (changing screens, restarting the game, etc.).
-
Event Firing:
PauseMenuDisplaydoesn't perform any logic itself — clicking Resume, Restart, or Main Menu just firesresumeClicked,restartClicked, ormainMenuClickedthrough the entity's event system. -
Action Handling:
PauseMenuActionslistens for these events and performs the actual behaviour — togglingisPaused, or triggeringrestartGame. -
Restart:
MainGameActionslistens forrestartGameand callsgame.setScreen(GdxGame.ScreenType.MAIN_GAME), which rebuilds the entire game screen — regenerating the terrain, entities, and player state (including resetting health to full).
-
PauseMenuDisplay: Must fire the correct event names (
resumeClicked,restartClicked,mainMenuClicked,SettingsClicked,saveClicked) forPauseMenuActionsto respond to. -
MainGameScreen: Owns the
ForestGameAreaand service registration that gets rebuilt on restart or load to the saved game state; any changes to how the game area is constructed affect restart behaviour.
Displays fire named events rather than acting directly, keeping button UI decoupled from game logic:
private void restart() {
entity.getEvents().trigger("restartClicked");
}
PauseMenuActions listens and performs the actual action:
entity.getEvents().addListener("restartClicked", this::restart);
private void restart() {
pauseMenu.toggleIsPaused();
entity.getEvents().trigger("restartGame");
}
MainGameActions rebuilds the screen on restart:
private void onRestart() {
game.setScreen(GdxGame.ScreenType.MAIN_GAME);
}
Note: An earlier version reset only the player's health in-place (via CombatStatsComponent)
to avoid regenerating the map on restart. This was reverted so that restart fully regenerates
the game area, matching the original ticket scope.
Both the Pause Menu and Main Menu support keyboard navigation in addition to mouse interaction, letting players move between menu options, adjust the volume sliders (master, music, effects), and confirm a selection without a mouse.
- Input Detection: Listens for Up, Down, Left, Right, Enter, and Space, and translates them into navigation intent.
- Selection Movement: Up/Down move the highlighted option, wrapping around at the first and last item. In the Pause Menu, the volume slider is included as the final navigable item alongside the Resume/Restart/Main Menu buttons.
- Volume Adjustment: When the slider is the currently selected item, Left/Right adjust its value.
- Confirmation: Enter or Space triggers whichever action the currently highlighted option represents.
- Mouse Compatibility: Mouse hover still works alongside keyboard navigation; whichever input method was used most recently controls the highlight, so the two never conflict.
- Pause Menu UI / Main Menu UI: Provides the buttons (and, for the pause menu, the volume slider) that navigation moves between and confirms selections on.
-
Pause Trigger: Navigation only responds to input while the game is
actually paused, via
PauseMenuComponent.isPaused().
PauseMenuInputComponent and MainMenuInputComponent each extend
InputComponent and override keyDown(). Rather than acting directly, they
fire named events through the entity's event system:
if (keycode == KeybindSettings.getKey("attack")) {
entity.getEvents().trigger("attack");
entity.getEvents().trigger("attacking", direction);
return true;
}
Event names fired:
-
navigateUp/navigateDown— direction pressed -
navigateLeft/navigateRight(Pause Menu only) — adjusts the volume slider when it's selected -
confirmSelection— Enter or Space pressed
This keeps input detection separate from menu behaviour: the input component has no knowledge of what the menu looks like or does, and the display component has no knowledge of how input was captured.
The corresponding display component (PauseMenuDisplay / MainMenuDisplay)
listens for navigateUp and navigateDown, and maintains a selectedIndex
into its navigable items, wrapping with modulo arithmetic:
selectedIndex = (selectedIndex + 1) % itemCount;
navigateLeft() and navigateRight() only take effect when the volume
slider is the currently selected item. They adjust musicVol by a fixed
step, clamped between 0 and 1, via musicSlider.setValue() — which fires the
slider's existing ChangeListener, so the actual audio volume and the
on-screen percentage label update through the same path as a mouse drag,
rather than duplicating that logic. The navigateLeft() and navigateRight() also takes
long press in account to adjust the volume too.
Since both keyboard navigation and mouse hover can highlight a menu item, a
usingKeyboardNav flag determines which one currently owns the highlight
colour:
private boolean usingKeyboardNav = true;
- Pressing a navigation key sets
usingKeyboardNav = trueand highlights the selected item. - Any mouse movement sets
usingKeyboardNav = false, clearing the keyboard highlight so hover can take over. - Each button's
enter/exitlisteners only apply hover colour whileusingKeyboardNavisfalse.
This prevents both systems from highlighting different items at the same time.
confirmSelection maps selectedIndex onto the same events each button's
click listener already fires (e.g. resumeClicked, restartClicked,
mainMenuClicked, saveClicked, settingsClicked, audioClicked, keybindsClicked
for the pause menu), so keyboard and mouse confirmation
both funnel into the same downstream logic in PauseMenuActions /
MainMenuActions. It has no effect when the volume slider is selected.