Debug console

July 6, 2026 · View on GitHub

Quake-style drop-down debug console for LBA2. The console is always compiled and works everywhere: in-game, menu, inventory, credits, and during video playback. DEBUG_TOOLS remains a separate optional layer for additional legacy hotkeys/features.

Build

The console is part of normal builds (no dedicated CMake flag required).

cmake -B build
cmake --build build

The console is supported with the SDL backend (default in this project).

Toggle and input

  • F12 by default: open/close the console.
  • Optional override in lba2.cfg: set ConsoleToggleKey=<SDL scancode int> (for example 41 for K_CARRE on AZERTY layouts).
  • When the console is open, all keyboard input is consumed by the console (no game/menu input).
  • Type a line and press Enter (or numpad Enter) to run a command or cheat.
  • Esc: clear the input line; pressing Esc on an already-empty line closes the console.
  • Up/Down: recall previous commands (input history).
  • Page Up / Page Down and the mouse wheel: scroll console output; the input line stays fixed at the bottom. When you are not at the newest lines, the last visible scrollback line shows ....
  • Left/Right/Home/End: move the edit cursor within the input line. Backspace deletes the character before the cursor; Delete the one at it.
  • Shift+Left/Right/Home/End: select text in the input line (shown as an inverted block). Ctrl+A selects the whole line. Typing, paste, or Backspace/Delete replaces the selection.
  • Mouse: while the console is open the OS cursor is shown. Drag with the left button to select text across the scrollback (and the input line); double-click selects a word; a click on the input line places the edit cursor. The selection is read-only; copy it with Ctrl+C, or right-click to copy and clear it. A left-click below the panel (in the game area) closes the console. (The engine normally hides the OS cursor and draws its own software pointer, which would sit under the overlay, so the console shows the real OS cursor while open.)
  • Tab: complete the command at the prompt against the known commands, cvars and built-ins. A unique prefix completes in full (and adds a trailing space); an ambiguous prefix extends to the longest common prefix and lists the candidates.
  • Casing and symbols: typing goes through the OS text-input layer, so Shift/Caps Lock give capitals and shifted symbols, and non-US keyboard layouts type the right characters. The numpad types digits and . / * + - when Num Lock is on.
  • Clipboard: Ctrl+C copies the selection (or the whole line if nothing is selected), Ctrl+X cuts it, Ctrl+V (or Shift+Insert) pastes, Ctrl+L clears the line.

Discovery

  • help – list all commands with short descriptions. With an argument, help <name> prints usage and context for that command or cvar (e.g. help cube, help fps, help status).
  • cmdlist – list command names only.
  • varlist – list cvars (variables) with descriptions.
  • buildinfo – print build timestamp and CMake options (SOUND_BACKEND, MVIDEO_BACKEND, ENABLE_ASM). The same string is written to the log at startup.

Unknown commands print a hint to use cmdlist. Commands that need an in-game scene (give, savebug) print a short reason if used while a video is playing, before a scene exists, or in the phantom-cube state.

Cheats (by name)

These mirror the classic key-sequence cheats; you can type their name directly at the prompt and press Enter:

  • life – max life
  • magic – max magic
  • full – full points
  • gold – add gold/kashes
  • speed – toggle frame rate display
  • clover – clover
  • box – clover box
  • pingouin – MecaPingouin

Commands

CommandDescription
cube <n>Request change to cube number <n> (applied next frame in game; cube changes no longer trigger autosave).
loadLoad save by player name as printed by listsaves, or by file name (with optional .lba).
loadbugLoad bug save by name or file (optional .lba), default bug.
listsavesList save games (player names from .lba files).
listbugsList bug saves in the bugs directory (same path as savebug).
savebug [name]Save current game to bugs directory; optional name (default bug).
timer [ms]Advance in-game timer by N ms (default 200).
statusPrint island, cube, chapter, object/zone counts, FPS, timer, position.
screenshotSave the next frame as PNG in the shoot directory without the console visible (uses SavePNG).
give <item> [count]Grant an inventory item by name — run give with no args to list item names; a numeric index still works as a fallback. Writes possession, refreshes the inventory, and plays the “found object” cinematic for real inventory slots. count applies to countable items (darts, money, gem, penguin, clover).
playvideo <name>Play ACF video by name.
listvideosList available ACF video names.
playjingle <1-26>Play jingle by number.
playmusic <num> [loop 0|1]Play music track by number (optional loop flag, default 1).
playsample <num> [freq] [decal] [repeat] [volume] [pan]Play sample by index with optional params: pitchbend (freq, default 0x1000), random pitch range (decal), repeat count (repeat, 0=loop, default 1), volume (0–127, default 127), pan (0–127, default 64).
audio ...Audio commands: audio sample play/stop_all, audio music play/stop, audio global pause/resume/stop_all/reset/reverse_stereo/log. Calls HQ/AIL functions directly (see AUDIO.md).
video ...Video commands: `video log <0
loglevel [debug|info|warn|error]Show or set the master log level. It gates every sink at once (this console, adeline.log, and the terminal), so lowering it to debug reveals Log_Debug detail everywhere. No args prints the current level. Default info; also set at launch with --log-level or the LBA2_LOG_LEVEL env var.
resolution [<n> | WxH | native | --all]Runtime render-resolution switch (see RUNTIME_RESOLUTION.md). No args = list recommended modes + current. Numeric picks by index from that list; WxH is arbitrary (W%8==0, range 320x200..1920x1080); native snaps to the current display; --all expands to the advanced catalog. After a successful switch, a 15 s keep/revert dialog appears: Keep persists to lba2.cfg ResolutionX/Y; Revert / timeout restores the previous mode.
exitExit the game immediately (clean shutdown).

CVars

Get/set with varname (print value) or varname value (set).

CvarTypeDescription
fpsboolFrame rate display.
horizonboolDraw horizon / cubes around.
zvboolDraw ZV boxes.

Layout

  • Console is a panel at the top of the screen.
  • Scrollback shows recent lines; the last line is the input with a ]> prompt and a blinking underline cursor at the edit position. When scrolled up, an ellipsis ... appears on the last visible scrollback line to indicate older messages above.
  • Output from commands and cheats appears in the scrollback.

Implementation notes

  • Independent of game buffer: The console uses its own 8-bit overlay buffer. It is drawn via AffStringToBuffer (LIB386/SVGA) and composited in the video layer’s pre-present callback (Console_PrePresent), so it never touches the game’s Log or dirty-box pipeline.
  • Event-driven input: Keys are fed from the event loop via Console_FeedEvent (registered with SetEventFilter). When the console is open, key events are queued and processed each frame by Console_Update() (no arguments). The configured toggle key is reserved from gameplay input to avoid double-handling. Character entry uses SDL text input (enabled on open, disabled on close) so casing, shifted symbols and non-US layouts resolve through the OS keyboard layout; physical keys (Enter, arrows, Backspace/Delete, Tab, Esc, Home/End, Page Up/Down, the Ctrl combos) are handled by scancode. The mouse wheel scrolls the scrollback.
  • Hooks: SetEventFilter(Console_FeedEvent) and SetPrePresentCallback(Console_PrePresent) are registered in main() after InitAdeline(). MyGetInput() reserves the configured toggle key, and when the console is open calls Console_Update() and returns.
  • Module: SOURCES/CONSOLE/CONSOLE.H, CONSOLE.CPP (core), CONSOLE_CMD.CPP (commands/cvars). Core links only LIB386 (AFFSTR for text) and SDL for events; no dependency on game Log or dirty-box.
  • Gameplay integration: Commands call existing engine functions (LoadGameNumCube, PlayAcf, DoFoundObj, etc.) rather than introducing console-only code paths. Cube changes no longer trigger autosave to keep debug teleports tidy; other save behavior is unchanged.
  • External call sites (outside SOURCES/CONSOLE/): INPUT.CPP (input path when open), PLAYACF.CPP (stall ACF while open), PERSO.CPP (event filter, pre-present, screenshot handoff), GAMEMENU.CPP (slide-show gate). Cheat names live in CHEATCOD.CPP. Build wiring: SOURCES/CMakeLists.txt, SOURCES/CONSOLE/CMakeLists.txt, tests/console/. A one-line map also lives in CONSOLE.H above the public API.

Extending commands and cheats

  • Commands: Add new handlers in SOURCES/CONSOLE/CONSOLE_CMD.CPP and register them via Console_RegisterCommandEx("name", handler, "short description", "usage line", "context line") (or Console_RegisterCommand if usage/context are omitted) inside Console_RegisterAll().
  • CVars: Register new tunables via Console_RegisterCvar in CONSOLE_CMD.CPP, wiring them to existing globals rather than duplicating state.
  • Cheats: To add a cheat that works both from key sequences and the console, extend CheatNames[] and ApplyCheat in SOURCES/CHEATCOD.CPP; the console will automatically route matching command names through TryExecuteCheatByName.
  • Safety: Prefer read-only inspection commands or one-shot state changes that leave the game in a consistent, save/load-safe state. If a command is inherently risky (e.g. heavy state mutation), call it out explicitly in its help text.