5.8 KiB
| id | title | priority | status | scope | owner | updated_at | evidence | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| p2-03 | Hotkey cheat sheet (F1 / ?) | p2 | done | game1 | shipwright | 2026-04-17 |
|
Summary
Non-modal hotkey cheat-sheet overlay renders dynamically from
InputMap.get_actions(). Every action whose name begins with ui_ is
bucketed into one of four spec-required context columns — Map / City /
Combat / Menus — via the ACTION_PREFIX_BUCKET constant. Adding a new
hotkey means one InputMap action declaration in project.godot plus one
action_<name> vocab entry; no changes to hotkey_sheet.gd.
project.godot [input] section now declares 15 ui_* actions covering
every previously-keycode-literal handler across overlay_panel.gd
(11 map-overlay toggles + cycle-view), top_bar.gd
(encyclopedia/diplomacy/stats), and camera.gd (WASD/arrows handled via
Input.is_key_pressed in _process, not migrated because they're
continuous-input reads, not discrete action presses — dynamic render still
picks up the rest). ui_help toggles the sheet itself; ui_cancel closes
it.
Acceptance
- ✓
ui_helpinput action bound to F1 and?— declared inproject.godot:[input]; sheet subscribes via_unhandled_input(event.is_action_pressed("ui_help")). Covered bytest_hotkey_sheet.gd::test_ui_help_action_opens_sheet,test_ui_help_action_closes_sheet_when_open. - ✓ Overlay lists all bindings grouped by context (Map / City / Combat /
Menus) —
hotkey_sheet.gd::ACTION_PREFIX_BUCKETdeclares exactly four buckets with spec-matching vocab keyshotkey_group_map,hotkey_group_city,hotkey_group_combat,hotkey_group_menus. Buckets with no actions yet still render their header plus anhotkey_group_emptymarker row so the four-column contract is visible to players. Covered bytest_hotkey_sheet_dynamic.gd::test_four_buckets_declared,test_city_and_combat_buckets_declared_even_when_empty,test_hotkey_sheet.gd::test_bindings_cover_required_groups. - ✓ Closable with same key or ESC — both paths tested
(
test_ui_help_action_closes_sheet_when_open,test_ui_cancel_closes_sheet_when_open). - ✓ F1-collision with encyclopedia hotkey resolved —
project.godotdeclaresui_encyclopedia(F2, keycode 4194333).top_bar.gdconsumes it via_unhandled_input(event.is_action_pressed(...)); the old rawKEY_F1keycode match is deleted.F1now routes exclusively toui_help. Covered bytest_tutorial_hotkey_wiring.gd::test_ui_encyclopedia_action_exists,test_ui_encyclopedia_bound_to_f2,test_ui_help_still_bound. - ✓ Bindings sourced dynamically from
InputMap.get_actions()per spec.hotkey_sheet.gd::collect_rows_by_bucket()iteratesInputMap.get_actions(), filters toui_*prefix, routes throughACTION_PREFIX_BUCKET, and formats each event viaOS.get_keycode_string+ modifier prefixes. Mouse-driven controls (wheel, middle-drag, click) and continuous-input WASD pan live inSTATIC_ROWSand are appended post-dynamic — these aren't InputMap actions because Godot doesn't represent wheel/drag gestures that way. Covered bytest_hotkey_sheet_dynamic.gd::test_dynamic_render_populates_map_bucket_from_ui_map_actions,test_dynamic_render_routes_menu_actions_to_menus_bucket,test_bucket_for_ui_map_prefix,test_bucket_for_ui_menu_prefix,test_bucket_for_unknown_prefix_returns_empty,test_every_ui_map_overlay_has_an_action. - ✓ Sheet wired into live game HUD —
world_map.gd::_mount_hud_overlays()(called from_start_game()) addsHotkeySheetScene.instantiate()as a persistent child of the world_map. Layer=110 so itsui_helplistener wins over other HUD CanvasLayers. Covered bytest_tutorial_hotkey_wiring.gd::test_world_map_mount_hotkey_sheet_const_exists.
Migrated handlers (keycode-literal → InputMap action)
overlay_panel.gd::_unhandled_key_input→_unhandled_input. 11 rawKEY_*matches (T/M/W/Y/V/R/E/C/P/H/L/F) replaced byOVERLAY_ACTIONSdict iterating 11ui_map_overlay_*actions +ui_map_cycle_view.top_bar.gd::_unhandled_key_input→_unhandled_input. F1/F8/F9 raw matches replaced byui_encyclopedia/ui_menu_diplomacy/ui_menu_stats.camera.gd::_handle_keyboard_panunchanged: continuous-inputInput.is_key_pressedin_process(delta)is the correct Godot pattern for sustained directional pan — not a discrete action event. WASD/arrows are documented in the sheet'sSTATIC_ROWS.
Tests
test_hotkey_sheet.gd— 9/9 passing (existing tests, updated to readACTION_PREFIX_BUCKETinstead of the removedBINDINGSconstant).test_hotkey_sheet_dynamic.gd— 8/8 passing (new, 29 assertions covering four-bucket contract, dynamic render, action→bucket routing, and everyui_map_overlay_*action present).test_tutorial_hotkey_wiring.gd— 7/7 passing (F1/F2 collision,ui_encyclopediaaction, dynamic render includes F2).
24/24 hotkey tests green on apricot.
Integration
hotkey_sheet.gdis now the canonical source of "what hotkeys exist" for players — adding a newui_*action automatically appears here with no scene edit.ACTION_PREFIX_BUCKETis a documented extension point: to add a future "City Actions" context, authorui_city_*actions inproject.godotand the City bucket auto-populates.