From 215599684ce9baf383de039f25687cb94038e7c3 Mon Sep 17 00:00:00 2001 From: Muzychenko Andrey <33288308+k4zmu2a@users.noreply.github.com> Date: Sat, 11 Feb 2023 13:18:29 +0300 Subject: [PATCH] Options refactor part 3: added key bindings for menu shortcuts. Issue #168. --- SpaceCadetPinball/options.cpp | 124 +++++++++++++++++++++++------ SpaceCadetPinball/options.h | 13 ++- SpaceCadetPinball/pb.cpp | 7 +- SpaceCadetPinball/translations.cpp | 6 ++ SpaceCadetPinball/translations.h | 1 + SpaceCadetPinball/winmain.cpp | 101 +++++++++++------------ SpaceCadetPinball/winmain.h | 4 + 7 files changed, 176 insertions(+), 80 deletions(-) diff --git a/SpaceCadetPinball/options.cpp b/SpaceCadetPinball/options.cpp index 6f954db..51f3bdb 100644 --- a/SpaceCadetPinball/options.cpp +++ b/SpaceCadetPinball/options.cpp @@ -63,6 +63,55 @@ optionsStruct options::Options {InputTypes::Mouse,SDL_BUTTON_X2 + 1}, {InputTypes::GameController, SDL_CONTROLLER_BUTTON_DPAD_UP} }, + { + "New Game", + Msg::Menu1_New_Game, + {InputTypes::Keyboard, SDLK_F2}, + {InputTypes::None,-1}, + {InputTypes::None, -1} + }, + { + "Toggle Pause", + Msg::Menu1_Pause_Resume_Game, + {InputTypes::Keyboard, SDLK_F3}, + {InputTypes::None,-1}, + {InputTypes::GameController, SDL_CONTROLLER_BUTTON_START} + }, + { + "Toggle FullScreen", + Msg::Menu1_Full_Screen, + {InputTypes::Keyboard, SDLK_F4}, + {InputTypes::None,-1}, + {InputTypes::None, -1} + }, + { + "Toggle Sounds", + Msg::Menu1_Sounds, + {InputTypes::Keyboard, SDLK_F5}, + {InputTypes::None,-1}, + {InputTypes::None, -1} + }, + { + "Toggle Music", + Msg::Menu1_Music, + {InputTypes::Keyboard, SDLK_F6}, + {InputTypes::None,-1}, + {InputTypes::None, -1} + }, + { + "Show Control Dialog", + Msg::Menu1_Player_Controls, + {InputTypes::Keyboard, SDLK_F8}, + {InputTypes::None,-1}, + {InputTypes::None, -1} + }, + { + "Toggle Menu Display", + Msg::Menu1_ToggleShowMenu, + {InputTypes::Keyboard, SDLK_F9}, + {InputTypes::None,-1}, + {InputTypes::None, -1} + }, }, {"Sounds", true}, {"Music", false}, @@ -276,14 +325,6 @@ void options::InputDown(GameInput input) { if (ControlWaitingForInput) { - // Skip function keys, just in case. - if (input.Type == InputTypes::Keyboard && input.Value >= SDLK_F1 && input.Value <= SDLK_F12) - return; - - // Start is reserved for pause - if (input.Type == InputTypes::GameController && input.Value == SDL_CONTROLLER_BUTTON_START) - return; - *ControlWaitingForInput = input; ControlWaitingForInput = nullptr; } @@ -311,13 +352,14 @@ void options::RenderControlDialog() ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2{550, 550}); if (ImGui::Begin(pb::get_rc_string(Msg::KEYMAPPER_Caption), &ShowDialog)) { - ImGui::TextUnformatted(pb::get_rc_string(Msg::KEYMAPPER_Groupbox2)); - ImGui::Separator(); + if (ImGui::TreeNode(pb::get_rc_string(Msg::KEYMAPPER_Groupbox2))) + { + ImGui::TextWrapped("%s", pb::get_rc_string(Msg::KEYMAPPER_Help1)); + ImGui::TextWrapped("%s", pb::get_rc_string(Msg::KEYMAPPER_Help2)); + ImGui::TreePop(); + } - ImGui::TextWrapped("%s", pb::get_rc_string(Msg::KEYMAPPER_Help1)); - ImGui::TextWrapped("%s", pb::get_rc_string(Msg::KEYMAPPER_Help2)); ImGui::Spacing(); - ImGui::TextUnformatted(pb::get_rc_string(Msg::KEYMAPPER_Groupbox1)); ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2{5, 10}); @@ -355,7 +397,7 @@ void options::RenderControlDialog() } else { - auto inputDescription = input.GetInputDescription(); + auto inputDescription = input.GetFullInputDescription(); if (ImGui::Button((inputDescription + "##" + std::to_string(rowHash++)).c_str(), ImVec2(-1, 0))) { @@ -447,7 +489,30 @@ void options::MyUserData_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handl buf->append("\n"); } -std::string GameInput::GetInputDescription() const +std::string GameInput::GetFullInputDescription() const +{ + std::string prefix; + switch (Type) + { + case InputTypes::Keyboard: + prefix = "Keyboard\n"; + break; + case InputTypes::Mouse: + prefix = "Mouse\n"; + break; + case InputTypes::GameController: + prefix = "Controller\n"; + break; + case InputTypes::None: + default: + prefix = "Unused"; + break; + } + + return prefix + GetShortInputDescription(); +} + +std::string GameInput::GetShortInputDescription() const { static LPCSTR mouseButtons[] { @@ -488,26 +553,23 @@ std::string GameInput::GetInputDescription() const switch (Type) { case InputTypes::Keyboard: - keyName = "Keyboard\n"; - keyName += SDL_GetKeyName(Value); + keyName = SDL_GetKeyName(Value); break; case InputTypes::Mouse: - keyName = "Mouse\n"; if (Value >= SDL_BUTTON_LEFT && Value <= SDL_BUTTON_X2) - keyName += mouseButtons[Value]; + keyName = mouseButtons[Value]; else - keyName += std::to_string(Value); + keyName = std::to_string(Value); break; case InputTypes::GameController: - keyName = "Controller\n"; if (Value >= SDL_CONTROLLER_BUTTON_A && Value <= SDL_CONTROLLER_BUTTON_TOUCHPAD) - keyName += controllerButtons[Value]; + keyName = controllerButtons[Value]; else - keyName += std::to_string(Value); + keyName = std::to_string(Value); break; case InputTypes::None: default: - keyName = "Unused"; + break; } return keyName; @@ -545,3 +607,17 @@ OptionBase::~OptionBase() if (position != vec.end()) vec.erase(position); } + +std::string ControlOption::GetShortcutDescription() const +{ + std::string result; + for (const auto& input : Inputs) + { + if (input.Type != InputTypes::None) + { + result = input.GetShortInputDescription(); + break; + } + } + return result; +} diff --git a/SpaceCadetPinball/options.h b/SpaceCadetPinball/options.h index c243c44..e64e36a 100644 --- a/SpaceCadetPinball/options.h +++ b/SpaceCadetPinball/options.h @@ -51,7 +51,9 @@ struct GameInput return Type == other.Type && Value == other.Value; } - std::string GetInputDescription() const; + std::string GetFullInputDescription() const; + + std::string GetShortInputDescription() const; }; enum class GameBindings @@ -63,6 +65,13 @@ enum class GameBindings LeftTableBump, RightTableBump, BottomTableBump, + NewGame, + TogglePause, + ToggleFullScreen, + ToggleSounds, + ToggleMusic, + ShowControlDialog, + ToggleMenuDisplay, Max }; @@ -234,6 +243,8 @@ struct ControlOption : OptionBase { std::copy(std::begin(Defaults), std::end(Defaults), std::begin(Inputs)); } + + std::string GetShortcutDescription() const; }; struct optionsStruct diff --git a/SpaceCadetPinball/pb.cpp b/SpaceCadetPinball/pb.cpp index 9051823..2becf0c 100644 --- a/SpaceCadetPinball/pb.cpp +++ b/SpaceCadetPinball/pb.cpp @@ -455,13 +455,18 @@ void pb::InputUp(GameInput input) void pb::InputDown(GameInput input) { options::InputDown(input); + const auto bindings = options::MapGameInput(input); + for (const auto binding : bindings) + { + winmain::HandleGameBinding(binding); + } + if (game_mode != GameModes::InGame || winmain::single_step || demo_mode) return; if (input.Type == InputTypes::Keyboard) control::pbctrl_bdoor_controller(static_cast(input.Value)); - const auto bindings = options::MapGameInput(input); for (const auto binding : bindings) { switch (binding) diff --git a/SpaceCadetPinball/translations.cpp b/SpaceCadetPinball/translations.cpp index 1dc1c6e..c80ca82 100644 --- a/SpaceCadetPinball/translations.cpp +++ b/SpaceCadetPinball/translations.cpp @@ -6289,4 +6289,10 @@ const TextArray translations::Translations = { Lang::TraditionalChinese, "使用最大解析度 (1024 x 768)" }, }, }, + { + Msg::Menu1_ToggleShowMenu, + { + { Lang::English, "Show Menu"}, + } + } }; diff --git a/SpaceCadetPinball/translations.h b/SpaceCadetPinball/translations.h index 713c484..173a181 100644 --- a/SpaceCadetPinball/translations.h +++ b/SpaceCadetPinball/translations.h @@ -218,6 +218,7 @@ enum class Msg : int Menu1_Select_Players, Menu1_Table_Resolution, Menu1_Help, + Menu1_ToggleShowMenu, Menu1_UseMaxResolution_640x480, Menu1_UseMaxResolution_800x600, diff --git a/SpaceCadetPinball/winmain.cpp b/SpaceCadetPinball/winmain.cpp index 187f8e8..ccc216f 100644 --- a/SpaceCadetPinball/winmain.cpp +++ b/SpaceCadetPinball/winmain.cpp @@ -469,19 +469,13 @@ void winmain::RenderUi() if (ImGui::BeginMenu(pb::get_rc_string(Msg::Menu1_Game))) { - if (ImGui::MenuItem(pb::get_rc_string(Msg::Menu1_New_Game), "F2")) - { - new_game(); - } + ImGuiMenuItemWShortcut(GameBindings::NewGame); if (ImGui::MenuItem(pb::get_rc_string(Msg::Menu1_Launch_Ball), nullptr, false, LaunchBallEnabled)) { end_pause(); pb::launch_ball(); } - if (ImGui::MenuItem(pb::get_rc_string(Msg::Menu1_Pause_Resume_Game), "F3")) - { - pause(); - } + ImGuiMenuItemWShortcut(GameBindings::TogglePause); ImGui::Separator(); if (ImGui::MenuItem(pb::get_rc_string(Msg::Menu1_High_Scores), nullptr, false, HighScoresEnabled)) @@ -504,14 +498,8 @@ void winmain::RenderUi() if (ImGui::BeginMenu(pb::get_rc_string(Msg::Menu1_Options))) { - if (ImGui::MenuItem("Show Menu", "F9", Options.ShowMenu)) - { - options::toggle(Menu1::Show_Menu); - } - if (ImGui::MenuItem(pb::get_rc_string(Msg::Menu1_Full_Screen), "F4", Options.FullScreen)) - { - options::toggle(Menu1::Full_Screen); - } + ImGuiMenuItemWShortcut(GameBindings::ToggleMenuDisplay, Options.ShowMenu); + ImGuiMenuItemWShortcut(GameBindings::ToggleFullScreen, Options.FullScreen); if (ImGui::BeginMenu(pb::get_rc_string(Msg::Menu1_Select_Players))) { if (ImGui::MenuItem(pb::get_rc_string(Msg::Menu1_1Player), nullptr, Options.Players == 1)) @@ -536,11 +524,7 @@ void winmain::RenderUi() } ImGui::EndMenu(); } - if (ImGui::MenuItem(pb::get_rc_string(Msg::Menu1_Player_Controls), "F8")) - { - pause(false); - options::ShowControlDialog(); - } + ImGuiMenuItemWShortcut(GameBindings::ShowControlDialog); if (ImGui::BeginMenu("Language")) { auto currentLanguage = translations::GetCurrentLanguage(); @@ -561,10 +545,7 @@ void winmain::RenderUi() if (ImGui::BeginMenu("Audio")) { - if (ImGui::MenuItem("Sound", "F5", Options.Sounds)) - { - options::toggle(Menu1::Sounds); - } + ImGuiMenuItemWShortcut(GameBindings::ToggleSounds, Options.Sounds); if (ImGui::MenuItem("Stereo Sound Effects", nullptr, Options.SoundStereo)) { options::toggle(Menu1::SoundStereo); @@ -584,10 +565,7 @@ void winmain::RenderUi() } ImGui::Separator(); - if (ImGui::MenuItem(pb::get_rc_string(Msg::Menu1_Music), "F6", Options.Music)) - { - options::toggle(Menu1::Music); - } + ImGuiMenuItemWShortcut(GameBindings::ToggleMusic, Options.Music); ImGui::TextUnformatted("Music Volume"); if (ImGui::SliderInt("##Music Volume", &Options.MusicVolume.V, options::MinVolume, options::MaxVolume, "%d", @@ -852,28 +830,6 @@ int winmain::event_handler(const SDL_Event* event) options::toggle(Menu1::Full_Screen); SDL_MinimizeWindow(MainWindow); break; - case SDLK_F2: - new_game(); - break; - case SDLK_F3: - pause(); - break; - case SDLK_F4: - options::toggle(Menu1::Full_Screen); - break; - case SDLK_F5: - options::toggle(Menu1::Sounds); - break; - case SDLK_F6: - options::toggle(Menu1::Music); - break; - case SDLK_F8: - pause(false); - options::ShowControlDialog(); - break; - case SDLK_F9: - options::toggle(Menu1::Show_Menu); - break; default: break; } @@ -1014,9 +970,6 @@ int winmain::event_handler(const SDL_Event* event) pb::InputDown({InputTypes::GameController, event->cbutton.button}); switch (event->cbutton.button) { - case SDL_CONTROLLER_BUTTON_START: - pause(); - break; case SDL_CONTROLLER_BUTTON_BACK: if (single_step) { @@ -1145,6 +1098,37 @@ void winmain::UpdateFrameRate() TargetFrameTime = DurationMs(1000.0 / ups); } +void winmain::HandleGameBinding(GameBindings binding) +{ + switch (binding) + { + case GameBindings::TogglePause: + pause(); + break; + case GameBindings::NewGame: + new_game(); + break; + case GameBindings::ToggleFullScreen: + options::toggle(Menu1::Full_Screen); + break; + case GameBindings::ToggleSounds: + options::toggle(Menu1::Sounds); + break; + case GameBindings::ToggleMusic: + options::toggle(Menu1::Music); + break; + case GameBindings::ShowControlDialog: + pause(false); + options::ShowControlDialog(); + break; + case GameBindings::ToggleMenuDisplay: + options::toggle(Menu1::Show_Menu); + break; + default: + break; + } +} + void winmain::RenderFrameTimeDialog() { ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2{300, 70}); @@ -1208,3 +1192,12 @@ void winmain::HybridSleep(DurationMs sleepTarget) // spin lock for (auto start = Clock::now(); DurationMs(Clock::now() - start) < sleepTarget;); } + +void winmain::ImGuiMenuItemWShortcut(GameBindings binding, bool selected) +{ + const auto& keyDef = Options.Key[~binding]; + if (ImGui::MenuItem(pb::get_rc_string(keyDef.Description), keyDef.GetShortcutDescription().c_str(), selected)) + { + HandleGameBinding(binding); + } +} diff --git a/SpaceCadetPinball/winmain.h b/SpaceCadetPinball/winmain.h index 39f4c94..4a59626 100644 --- a/SpaceCadetPinball/winmain.h +++ b/SpaceCadetPinball/winmain.h @@ -1,6 +1,8 @@ #pragma once #include "gdrv.h" +enum class GameBindings; + struct SdlTickClock { using duration = std::chrono::milliseconds; @@ -85,6 +87,7 @@ public: static void pause(bool toggle = true); static void Restart(); static void UpdateFrameRate(); + static void HandleGameBinding(GameBindings binding); private: static int return_value, DispFrameRate; static int mouse_down, last_mouse_x, last_mouse_y; @@ -108,4 +111,5 @@ private: static void RenderFrameTimeDialog(); static void HybridSleep(DurationMs seconds); static void MainLoop(); + static void ImGuiMenuItemWShortcut(GameBindings binding, bool selected = false); };