Debug Terminal — Commands and Cheats - UQcsse3200/2026-studio-3 GitHub Wiki

Team 5 | Debug terminal wiring and cheat commands for fast manual testing | Related: Save/Load System · Debug Terminal Test Plan

Background

The debug terminal (Terminal, KeyboardTerminalInputComponent, TerminalDisplay) already existed in the codebase, but was only ever wired into MainGameScreen — a screen no longer reachable in normal play. This meant the terminal was effectively dead: pressing F1 during an actual game session (map or battle) did nothing.

This work re-wires the existing terminal into the two screens players actually spend time in — BattleScreen and MapScreen — and adds a small set of cheat commands on top, so the team can test late-game content, specific bugs, or specific states without manually playing through a full run every time.

How it works

Press F1 on either the map or in battle to toggle the terminal open. Type a command (optionally with arguments) and hit Enter to run it. The terminal closes automatically after a successful command.

Commands are just Map<String, Command> entries on a Terminal component — see Terminal.addCommand(name, command). Adding a new one means implementing Command.action(ArrayList<String> args) and registering it where the terminal is created (BattleScreen.createUI() or MapScreen.createUi()).

The terminal accepts letters, digits, spaces, underscores, and hyphens as input — both matter since enum-based arguments (like ItemType names below) use underscores, and some newer string-ID arguments (like Chance encounter IDs) use hyphens.

Architecture

graph TB
    subgraph Screens["Screens (owners)"]
        BattleScreen["BattleScreen"]
        MapScreen["MapScreen"]
    end

    subgraph TerminalCore["Terminal Core"]
        Terminal["Terminal<br/>(Component, holds command map)"]
        KeyInput["KeyboardTerminalInputComponent<br/>(F1 toggle, keystrokes)"]
        Display["TerminalDisplay<br/>(renders '> message' label)"]
    end

    subgraph Commands["Command implementations"]
        DebugCmd["DebugCommand<br/>'debug on/off'"]
        SkipCmd["SkipBattleCommand<br/>'skipbattle'"]
        GoldCmd["GiveGoldCommand<br/>'givegold &lt;amt&gt;'"]
        HealthCmd["SetHealthCommand<br/>'sethealth &lt;amt&gt;'"]
        ItemCmd["GiveItemCommand<br/>'giveitem &lt;ItemType&gt;'"]
        UnlockCmd["UnlockNodeCommand<br/>'unlocknode &lt;id&gt;'"]
        ListCmd["ListNodesCommand<br/>'listnodes'"]
        GotoCmd["GotoCommand<br/>'goto &lt;floor&gt; &lt;roomtype&gt;'"]
        DiscoverCmd["DiscoverAllCardsCommand<br/>'discoverallcards'"]
    end

    subgraph GameSystems["Real game systems (cheats reuse, not fake)"]
        BattleController["BattleController<br/>forceEnemiesDefeated()"]
        Inventory["InventoryComponent<br/>addGold()"]
        CombatStats["CombatStatsComponent<br/>setHealth()"]
        ItemApplier["ItemEffectApplier<br/>applyItemEffect()"]
        MapGraph["MapGraph<br/>connectNodes(), node state"]
        MapSelection["MapSelectionController<br/>onNodeClicked()"]
        CardDiscovery["CardDiscoveryService<br/>recordSeenAll()"]
    end

    BattleScreen -->|creates & attaches| Terminal
    BattleScreen -->|attaches| KeyInput
    BattleScreen -->|attaches| Display
    MapScreen -->|creates & attaches| Terminal
    MapScreen -->|attaches| KeyInput
    MapScreen -->|attaches| Display

    KeyInput -->|F1 toggles, keystrokes forwarded to| Terminal
    Terminal -->|renders entered text via| Display
    Terminal -->|processMessage looks up & runs| Commands

    BattleScreen -.registers.-> DebugCmd
    BattleScreen -.registers.-> SkipCmd
    BattleScreen -.registers.-> GoldCmd
    BattleScreen -.registers.-> HealthCmd
    BattleScreen -.registers.-> ItemCmd
    MapScreen -.registers.-> UnlockCmd
    MapScreen -.registers.-> ListCmd
    MapScreen -.registers.-> GotoCmd
    MapScreen -.registers.-> DiscoverCmd

    SkipCmd --> BattleController
    GoldCmd --> Inventory
    HealthCmd --> CombatStats
    ItemCmd --> ItemApplier
    UnlockCmd --> MapGraph
    ListCmd --> MapGraph
    GotoCmd --> MapGraph
    GotoCmd --> MapSelection
    DiscoverCmd --> CardDiscovery
Loading

Available commands

Everywhere

  • debug on / debug off — toggles physics debug rendering (colliders). Pre-existing command. On the map screen, this also reveals node ID and floor labels above every node (see below).

Battle screen

  • skipbattle — instantly wins the current battle. Deals piercing damage (bypasses block/armour, same as a real killing blow) to every enemy, then lets the normal battle-outcome check fire naturally — this reuses the real win path (queueBattleOutcomeIfOver()), rather than faking a VICTORY transition directly. Required a small addition to BattleTransitions (PLAYER_TURN → ENEMIES_DEFEATED → VICTORY), since every other phase already allowed this transition but the idle player-turn phase didn't.
  • givegold <amount> — adds gold to the player's InventoryComponent.
  • sethealth <amount> — sets the player's current health via CombatStatsComponent.
  • giveitem <ItemType> — applies an item's effect directly via the existing ItemEffectApplier, without needing to win it as a battle reward (e.g. giveitem ENERGY_CRYSTAL). Takes any ItemType enum name, so new item types added later this sprint are automatically supported with no command changes.

Map screen

  • unlocknode <id> — unlocks a specific node (LOCKED → AVAILABLE) and connects it to the player's current node. Flipping state alone isn't enough: MapGraph.moveToNode() also requires the target to be a real graph connection of the current node, not just AVAILABLE, otherwise the node visually reacts to a click but nothing happens.
  • listnodes — logs every node's id, floor (height), room type, and state to the console/log output (not the in-game terminal display, which only shows the current typed input).
  • goto <floor> <roomtype> — jumps directly to a node matching a given floor and room type (e.g. goto 3 combat, goto 5 elite, goto 2 campfire), going through the real click entry point (MapSelectionController.onNodeClicked()) rather than faking a screen transition — so the actual production encounter-selection logic runs exactly as it would from a real click. Skips nodes that are already COMPLETED or the player's current position, so repeated calls on the same floor/type find a fresh match instead of silently failing on an already-visited node. Works for every RoomType that exists on the current map, including Campfire and Elite — no per-room-type command was ever needed, since this just finds and clicks a matching node generically.
  • discoverallcards — reveals every card in the library, marking each as SEEN via CardDiscoveryService.recordSeenAll(). Requested by Team 6 (Ray) to test the new card discovery UI without playing through a full run to naturally encounter every card (shop and chance encounters are currently the only discovery routes). Note: this only changes CardDiscoveryService state — to see the result, navigate to Main Menu → Library → Card Library afterward (the terminal isn't reachable from those screens).

Node ID overlay

Since map layouts (and node IDs/floors) regenerate on every new run, there's no fixed reference for "which ID is which node" or "which floor is this." Running debug on on the map screen shows each node's ID and floor/height directly above it on screen (e.g. 27 (h4) — node 27, floor 4), toggled off again by debug off. This is what makes unlocknode/listnodes/goto actually usable without digging through save files or logs.

Bug fixes made along the way

  • Underscore input dropped: KeyboardTerminalInputComponent originally only accepted letters, digits and space, silently dropping underscores. This meant ItemType names like ENERGY_CRYSTAL couldn't actually be typed. Fixed by widening the accepted character set.
  • Hyphen input remains unsupported: KeyboardTerminalInputComponent currently accepts letters, digits, spaces and _, but not -. Therefore a future hyphenated command such as start-event cannot be exposed through the current terminal input until hyphen handling and its test are added. See Debug Terminal Test Plan.
  • Terminal text hidden behind the hand: TerminalDisplay's label could render behind newly-dealt hand cards — same root cause as the earlier CardEffectDebugDisplay z-order issue, since the hand gets rebuilt on the same stage every time a card is played. Fixed with label.toFront() on every draw while the terminal is open.
  • Terminal text unreadable: originally rendered in the skin's default black, invisible against the map/battle backgrounds. Now explicitly white.
  • Map nodes visually frozen after a state change without a screen transition: MapNodeActor.checkNodeState() only ever ran once, at construction — it "worked" only by coincidence, since every real state change previously came bundled with a full screen rebuild (entering then returning from an encounter). goto/unlocknode mutate node state directly, with no screen transition, which exposed this: nodes wouldn't visually update at all. Fixed with an act() override that re-checks state every frame, and by resetting to baseline styling before applying each state's specific styling (the original switch only ever added dimming/enlarging, never restored it).

Known limitations / follow-ups

  • Cheats are currently only wired into BattleScreen and MapScreen — not the shop/event encounter screen.
  • Hyphenated input is currently rejected; use the documented command names only until the input contract is extended and tested.
  • No precise deck-manipulation commands yet (give-card <id> requested by Team 2, not yet built — no blocker, PlayerDeck.addCard(String cardId) already exists for it).
  • GdxGame.startEvent(String) is available on main, but the start-event <eventId> terminal command was deferred and is not part of the submitted build. Integrating it later still requires hyphen input support, tests and review.
  • listnodes output goes to console, not the in-game terminal — could be improved to print inline if that becomes annoying during testing.
⚠️ **GitHub.com Fallback** ⚠️