From 6979312539c77e122ad8b2ed241e11b22c8a83d3 Mon Sep 17 00:00:00 2001 From: Spartan322 Date: Sat, 7 Dec 2024 20:45:34 -0500 Subject: [PATCH] Add shortcut GUI support Add pause menu shortcut Add time speed up, time speed down, and time pause shortcuts Replace WASD map movement with Shift+WASD map movement --- extension/deps/openvic-simulation | 2 +- .../src/openvic-extension/utility/UITools.cpp | 98 ++++++++++++++++++- game/project.godot | 13 ++- game/src/Game/GameSession/Menubar.gd | 14 ++- game/src/Game/GameSession/Topbar.gd | 12 +++ 5 files changed, 128 insertions(+), 11 deletions(-) diff --git a/extension/deps/openvic-simulation b/extension/deps/openvic-simulation index 9a895296..12157ce8 160000 --- a/extension/deps/openvic-simulation +++ b/extension/deps/openvic-simulation @@ -1 +1 @@ -Subproject commit 9a895296b3152fabe5679700af94ffa56b8851a4 +Subproject commit 12157ce86d6a1a1637f9ef5f7feabe5650b7a993 diff --git a/extension/src/openvic-extension/utility/UITools.cpp b/extension/src/openvic-extension/utility/UITools.cpp index 3d5e5b83..0dd17678 100644 --- a/extension/src/openvic-extension/utility/UITools.cpp +++ b/extension/src/openvic-extension/utility/UITools.cpp @@ -1,7 +1,9 @@ #include "UITools.hpp" +#include #include #include +#include #include #include #include @@ -22,6 +24,10 @@ #include "openvic-extension/singletons/AssetManager.hpp" #include "openvic-extension/singletons/GameSingleton.hpp" #include "openvic-extension/utility/Utilities.hpp" +#include "godot_cpp/classes/global_constants.hpp" +#include "godot_cpp/classes/input_event_key.hpp" +#include "godot_cpp/classes/shortcut.hpp" +#include "godot_cpp/core/error_macros.hpp" using namespace godot; using namespace OpenVic; @@ -60,6 +66,63 @@ GUI::Position const* UITools::get_gui_position(String const& gui_scene, String c return position; } +static Array get_events_from_shortcut_key(String const& key) { + Array events; + if (key.length() == 0) { + return events; + } + + Key key_value = OS::get_singleton()->find_keycode_from_string(key); + if (key_value == Key::KEY_UNKNOWN) { + if (key.nocasecmp_to("DEL") == 0) { + key_value = Key::KEY_DELETE; + } else if (key.nocasecmp_to("PAGE_UP") == 0) { + key_value = Key::KEY_PAGEUP; + } else if (key.nocasecmp_to("PAGE_DOWN") == 0) { + key_value = Key::KEY_PAGEDOWN; + } else if (key == "+") { + // on most keyboards + and = are on the same key, Godot does not see them as different + key_value = Key::KEY_EQUAL; + } else if (key == "-") { + key_value = Key::KEY_MINUS; + } else if (key == ">") { + // on many keyboards > and . are on the same key, Godot does not see them as different + key_value = Key::KEY_PERIOD; + } else if (key == "<") { + // on many keyboards < and , are on the same key, Godot does not see them as different + key_value = Key::KEY_COMMA; + } + } + + if (key_value == Key::KEY_UNKNOWN) { + return events; + } + + Ref event; + event.instantiate(); + event->set_pressed(true); + events.append(event); + + if (key.length() == 1) { + if (key_value >= Key::KEY_A && key_value <= Key::KEY_Z) { + event->set_shift_pressed(std::isupper(key[0])); + } else if (key == "+") { + event->set_shift_pressed(true); + + Ref second_event; + second_event.instantiate(); + second_event->set_pressed(true); + second_event->set_key_label(godot::KEY_KP_ADD); + events.append(second_event); + } else if (key == ">") { + event->set_shift_pressed(true); + } + } + + event->set_key_label(key_value); + return events; +} + /* GUI::Element tree -> godot::Control tree conversion code below: */ namespace OpenVic { @@ -217,9 +280,15 @@ static bool generate_icon(generate_gui_args_t&& args) { static bool generate_button(generate_gui_args_t&& args) { GUI::Button const& button = static_cast(args.element); - // TODO - shortcut, clicksound, rotation (?) + // TODO - clicksound, rotation (?) const String button_name = Utilities::std_to_godot_string(button.get_name()); + const String shortcut_key_name = Utilities::std_to_godot_string(button.get_shortcut()); + Array event_array = get_events_from_shortcut_key(shortcut_key_name); + ERR_FAIL_COND_V_MSG( + shortcut_key_name.length() != 0 && event_array.size() == 0, false, + vformat("Unknown shortcut key '%s' for GUI button %s", shortcut_key_name, button_name) + ); ERR_FAIL_NULL_V_MSG(button.get_sprite(), false, vformat("Null sprite for GUI button %s", button_name)); GUIButton* gui_button = nullptr; @@ -266,6 +335,16 @@ static bool generate_button(generate_gui_args_t&& args) { ret &= gui_button->set_gfx_font(button.get_font()) == OK; } + if (shortcut_key_name.length() != 0) { + Ref shortcut; + shortcut.instantiate(); + shortcut->set_events(event_array); + gui_button->set_shortcut(shortcut); + } + + gui_button->set_shortcut_feedback(false); + gui_button->set_shortcut_in_tooltip(false); + args.result = gui_button; return ret; } @@ -273,9 +352,14 @@ static bool generate_button(generate_gui_args_t&& args) { static bool generate_checkbox(generate_gui_args_t&& args) { GUI::Checkbox const& checkbox = static_cast(args.element); - // TODO - shortcut const String checkbox_name = Utilities::std_to_godot_string(checkbox.get_name()); + const String shortcut_key_name = Utilities::std_to_godot_string(checkbox.get_shortcut()); + Array event_array = get_events_from_shortcut_key(shortcut_key_name); + ERR_FAIL_COND_V_MSG( + shortcut_key_name.length() != 0 && event_array.size() == 0, false, + vformat("Unknown shortcut key '%s' for GUI checkbox %s", shortcut_key_name, checkbox_name) + ); ERR_FAIL_NULL_V_MSG(checkbox.get_sprite(), false, vformat("Null sprite for GUI checkbox %s", checkbox_name)); GFX::IconTextureSprite const* texture_sprite = checkbox.get_sprite()->cast_to(); @@ -306,6 +390,16 @@ static bool generate_checkbox(generate_gui_args_t&& args) { ret &= gui_icon_button->set_gfx_font(checkbox.get_font()) == OK; } + if (shortcut_key_name.length() != 0) { + Ref shortcut; + shortcut.instantiate(); + shortcut->set_events(event_array); + gui_icon_button->set_shortcut(shortcut); + } + + gui_icon_button->set_shortcut_feedback(false); + gui_icon_button->set_shortcut_in_tooltip(false); + args.result = gui_icon_button; return ret; } diff --git a/game/project.godot b/game/project.godot index 7a82986a..13883395 100644 --- a/game/project.godot +++ b/game/project.godot @@ -71,25 +71,25 @@ theme/custom="res://assets/graphics/theme/default_theme.tres" map_north={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194320,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) -, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":87,"physical_keycode":0,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":true,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":87,"physical_keycode":0,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null) ] } map_east={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194321,"physical_keycode":4194321,"key_label":4194321,"unicode":0,"location":0,"echo":false,"script":null) -, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":68,"physical_keycode":68,"key_label":68,"unicode":100,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":true,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":68,"physical_keycode":68,"key_label":68,"unicode":100,"location":0,"echo":false,"script":null) ] } map_south={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194322,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) -, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":83,"physical_keycode":0,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":true,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":83,"physical_keycode":0,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null) ] } map_west={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194319,"physical_keycode":4194319,"key_label":4194319,"unicode":0,"location":0,"echo":false,"script":null) -, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":65,"physical_keycode":65,"key_label":65,"unicode":97,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":true,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":65,"physical_keycode":65,"key_label":65,"unicode":97,"location":0,"echo":false,"script":null) ] } map_zoom_in={ @@ -136,6 +136,11 @@ time_speed_decrease={ , Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194435,"physical_keycode":0,"key_label":0,"unicode":45,"location":0,"echo":false,"script":null) ] } +menu_pause={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194305,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} [internationalization] diff --git a/game/src/Game/GameSession/Menubar.gd b/game/src/Game/GameSession/Menubar.gd index 5123464f..7f606ad0 100644 --- a/game/src/Game/GameSession/Menubar.gd +++ b/game/src/Game/GameSession/Menubar.gd @@ -7,6 +7,8 @@ signal zoom_in_button_pressed signal zoom_out_button_pressed signal minimap_clicked(pos_clicked : Vector2) +var _menu_button : GUIIconButton + var _mapmode_button_group : ButtonGroup # We use this instead of the ButtonGroup's get_buttons() as we can add null # entries for any missing buttons, ensuring each button is at the right index. @@ -53,10 +55,10 @@ func _ready() -> void: # TODO: add keyboard shortcuts (and shortcut tooltips) where vanilla does by default + use key bindings in settings - var menu_button : GUIIconButton = get_gui_icon_button_from_nodepath(^"./menubar/menu_button") - if menu_button: - menu_button.tooltip_string = "M_MENU_BUTTON" - menu_button.pressed.connect(_on_game_session_menu_button_pressed) + _menu_button = get_gui_icon_button_from_nodepath(^"./menubar/menu_button") + if _menu_button: + _menu_button.tooltip_string = "M_MENU_BUTTON" + _menu_button.pressed.connect(_on_game_session_menu_button_pressed) # TODO: implement ledger var ledger_button : GUIIconButton = get_gui_icon_button_from_nodepath(^"./menubar/ledger_button") @@ -93,6 +95,10 @@ func _ready() -> void: # This will set the mapmode in GameSingleton which in turn updates the buttons so that the right one is pressed _mapmode_pressed(0) +func _unhandled_input(event: InputEvent) -> void: + if event.is_action_pressed("menu_pause"): + _menu_button.pressed.emit() + # REQUIREMENTS: # * UIFUN-10 func _on_game_session_menu_button_pressed() -> void: diff --git a/game/src/Game/GameSession/Topbar.gd b/game/src/Game/GameSession/Topbar.gd index 569af67d..11e584cd 100644 --- a/game/src/Game/GameSession/Topbar.gd +++ b/game/src/Game/GameSession/Topbar.gd @@ -111,13 +111,25 @@ func _ready() -> void: if _speed_up_button: _speed_up_button.pressed.connect(_on_increase_speed_button_pressed) _speed_up_button.set_tooltip_string("TOPBAR_INC_SPEED") + var speed_up_action := InputEventAction.new() + speed_up_action.action = "time_speed_increase" + _speed_up_button.shortcut = Shortcut.new() + _speed_up_button.shortcut.events.append(speed_up_action) _speed_down_button = get_gui_icon_button_from_nodepath(^"./topbar/button_speeddown") if _speed_down_button: _speed_down_button.pressed.connect(_on_decrease_speed_button_pressed) _speed_down_button.set_tooltip_string("TOPBAR_DEC_SPEED") + var speed_down_action := InputEventAction.new() + speed_down_action.action = "time_speed_decrease" + _speed_down_button.shortcut = Shortcut.new() + _speed_down_button.shortcut.events.append(speed_down_action) _pause_bg_button = get_gui_icon_button_from_nodepath(^"./topbar/pause_bg") if _pause_bg_button: _pause_bg_button.pressed.connect(_on_play_pause_button_pressed) + var time_pause_action := InputEventAction.new() + time_pause_action.action = "time_pause" + _pause_bg_button.shortcut = Shortcut.new() + _pause_bg_button.shortcut.events.append(time_pause_action) _speed_indicator_button = get_gui_icon_button_from_nodepath(^"./topbar/speed_indicator") if _speed_indicator_button: _speed_indicator_button.pressed.connect(_on_play_pause_button_pressed)