Options refactor part 3: added key bindings for menu shortcuts.

Issue #168.
This commit is contained in:
Muzychenko Andrey 2023-02-11 13:18:29 +03:00
parent 8df996f452
commit 215599684c
7 changed files with 176 additions and 80 deletions

View file

@ -63,6 +63,55 @@ optionsStruct options::Options
{InputTypes::Mouse,SDL_BUTTON_X2 + 1}, {InputTypes::Mouse,SDL_BUTTON_X2 + 1},
{InputTypes::GameController, SDL_CONTROLLER_BUTTON_DPAD_UP} {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}, {"Sounds", true},
{"Music", false}, {"Music", false},
@ -276,14 +325,6 @@ void options::InputDown(GameInput input)
{ {
if (ControlWaitingForInput) 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 = input;
ControlWaitingForInput = nullptr; ControlWaitingForInput = nullptr;
} }
@ -311,13 +352,14 @@ void options::RenderControlDialog()
ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2{550, 550}); ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2{550, 550});
if (ImGui::Begin(pb::get_rc_string(Msg::KEYMAPPER_Caption), &ShowDialog)) if (ImGui::Begin(pb::get_rc_string(Msg::KEYMAPPER_Caption), &ShowDialog))
{ {
ImGui::TextUnformatted(pb::get_rc_string(Msg::KEYMAPPER_Groupbox2)); if (ImGui::TreeNode(pb::get_rc_string(Msg::KEYMAPPER_Groupbox2)))
ImGui::Separator(); {
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::Spacing();
ImGui::TextUnformatted(pb::get_rc_string(Msg::KEYMAPPER_Groupbox1)); ImGui::TextUnformatted(pb::get_rc_string(Msg::KEYMAPPER_Groupbox1));
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2{5, 10}); ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2{5, 10});
@ -355,7 +397,7 @@ void options::RenderControlDialog()
} }
else else
{ {
auto inputDescription = input.GetInputDescription(); auto inputDescription = input.GetFullInputDescription();
if (ImGui::Button((inputDescription + "##" + std::to_string(rowHash++)).c_str(), if (ImGui::Button((inputDescription + "##" + std::to_string(rowHash++)).c_str(),
ImVec2(-1, 0))) ImVec2(-1, 0)))
{ {
@ -447,7 +489,30 @@ void options::MyUserData_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handl
buf->append("\n"); 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[] static LPCSTR mouseButtons[]
{ {
@ -488,26 +553,23 @@ std::string GameInput::GetInputDescription() const
switch (Type) switch (Type)
{ {
case InputTypes::Keyboard: case InputTypes::Keyboard:
keyName = "Keyboard\n"; keyName = SDL_GetKeyName(Value);
keyName += SDL_GetKeyName(Value);
break; break;
case InputTypes::Mouse: case InputTypes::Mouse:
keyName = "Mouse\n";
if (Value >= SDL_BUTTON_LEFT && Value <= SDL_BUTTON_X2) if (Value >= SDL_BUTTON_LEFT && Value <= SDL_BUTTON_X2)
keyName += mouseButtons[Value]; keyName = mouseButtons[Value];
else else
keyName += std::to_string(Value); keyName = std::to_string(Value);
break; break;
case InputTypes::GameController: case InputTypes::GameController:
keyName = "Controller\n";
if (Value >= SDL_CONTROLLER_BUTTON_A && Value <= SDL_CONTROLLER_BUTTON_TOUCHPAD) if (Value >= SDL_CONTROLLER_BUTTON_A && Value <= SDL_CONTROLLER_BUTTON_TOUCHPAD)
keyName += controllerButtons[Value]; keyName = controllerButtons[Value];
else else
keyName += std::to_string(Value); keyName = std::to_string(Value);
break; break;
case InputTypes::None: case InputTypes::None:
default: default:
keyName = "Unused"; break;
} }
return keyName; return keyName;
@ -545,3 +607,17 @@ OptionBase::~OptionBase()
if (position != vec.end()) if (position != vec.end())
vec.erase(position); 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;
}

View file

@ -51,7 +51,9 @@ struct GameInput
return Type == other.Type && Value == other.Value; return Type == other.Type && Value == other.Value;
} }
std::string GetInputDescription() const; std::string GetFullInputDescription() const;
std::string GetShortInputDescription() const;
}; };
enum class GameBindings enum class GameBindings
@ -63,6 +65,13 @@ enum class GameBindings
LeftTableBump, LeftTableBump,
RightTableBump, RightTableBump,
BottomTableBump, BottomTableBump,
NewGame,
TogglePause,
ToggleFullScreen,
ToggleSounds,
ToggleMusic,
ShowControlDialog,
ToggleMenuDisplay,
Max Max
}; };
@ -234,6 +243,8 @@ struct ControlOption : OptionBase
{ {
std::copy(std::begin(Defaults), std::end(Defaults), std::begin(Inputs)); std::copy(std::begin(Defaults), std::end(Defaults), std::begin(Inputs));
} }
std::string GetShortcutDescription() const;
}; };
struct optionsStruct struct optionsStruct

View file

@ -455,13 +455,18 @@ void pb::InputUp(GameInput input)
void pb::InputDown(GameInput input) void pb::InputDown(GameInput input)
{ {
options::InputDown(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) if (game_mode != GameModes::InGame || winmain::single_step || demo_mode)
return; return;
if (input.Type == InputTypes::Keyboard) if (input.Type == InputTypes::Keyboard)
control::pbctrl_bdoor_controller(static_cast<char>(input.Value)); control::pbctrl_bdoor_controller(static_cast<char>(input.Value));
const auto bindings = options::MapGameInput(input);
for (const auto binding : bindings) for (const auto binding : bindings)
{ {
switch (binding) switch (binding)

View file

@ -6289,4 +6289,10 @@ const TextArray translations::Translations =
{ Lang::TraditionalChinese, "使用最大解析度 (1024 x 768)" }, { Lang::TraditionalChinese, "使用最大解析度 (1024 x 768)" },
}, },
}, },
{
Msg::Menu1_ToggleShowMenu,
{
{ Lang::English, "Show Menu"},
}
}
}; };

View file

@ -218,6 +218,7 @@ enum class Msg : int
Menu1_Select_Players, Menu1_Select_Players,
Menu1_Table_Resolution, Menu1_Table_Resolution,
Menu1_Help, Menu1_Help,
Menu1_ToggleShowMenu,
Menu1_UseMaxResolution_640x480, Menu1_UseMaxResolution_640x480,
Menu1_UseMaxResolution_800x600, Menu1_UseMaxResolution_800x600,

View file

@ -469,19 +469,13 @@ void winmain::RenderUi()
if (ImGui::BeginMenu(pb::get_rc_string(Msg::Menu1_Game))) if (ImGui::BeginMenu(pb::get_rc_string(Msg::Menu1_Game)))
{ {
if (ImGui::MenuItem(pb::get_rc_string(Msg::Menu1_New_Game), "F2")) ImGuiMenuItemWShortcut(GameBindings::NewGame);
{
new_game();
}
if (ImGui::MenuItem(pb::get_rc_string(Msg::Menu1_Launch_Ball), nullptr, false, LaunchBallEnabled)) if (ImGui::MenuItem(pb::get_rc_string(Msg::Menu1_Launch_Ball), nullptr, false, LaunchBallEnabled))
{ {
end_pause(); end_pause();
pb::launch_ball(); pb::launch_ball();
} }
if (ImGui::MenuItem(pb::get_rc_string(Msg::Menu1_Pause_Resume_Game), "F3")) ImGuiMenuItemWShortcut(GameBindings::TogglePause);
{
pause();
}
ImGui::Separator(); ImGui::Separator();
if (ImGui::MenuItem(pb::get_rc_string(Msg::Menu1_High_Scores), nullptr, false, HighScoresEnabled)) 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::BeginMenu(pb::get_rc_string(Msg::Menu1_Options)))
{ {
if (ImGui::MenuItem("Show Menu", "F9", Options.ShowMenu)) ImGuiMenuItemWShortcut(GameBindings::ToggleMenuDisplay, Options.ShowMenu);
{ ImGuiMenuItemWShortcut(GameBindings::ToggleFullScreen, Options.FullScreen);
options::toggle(Menu1::Show_Menu);
}
if (ImGui::MenuItem(pb::get_rc_string(Msg::Menu1_Full_Screen), "F4", Options.FullScreen))
{
options::toggle(Menu1::Full_Screen);
}
if (ImGui::BeginMenu(pb::get_rc_string(Msg::Menu1_Select_Players))) 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)) if (ImGui::MenuItem(pb::get_rc_string(Msg::Menu1_1Player), nullptr, Options.Players == 1))
@ -536,11 +524,7 @@ void winmain::RenderUi()
} }
ImGui::EndMenu(); ImGui::EndMenu();
} }
if (ImGui::MenuItem(pb::get_rc_string(Msg::Menu1_Player_Controls), "F8")) ImGuiMenuItemWShortcut(GameBindings::ShowControlDialog);
{
pause(false);
options::ShowControlDialog();
}
if (ImGui::BeginMenu("Language")) if (ImGui::BeginMenu("Language"))
{ {
auto currentLanguage = translations::GetCurrentLanguage(); auto currentLanguage = translations::GetCurrentLanguage();
@ -561,10 +545,7 @@ void winmain::RenderUi()
if (ImGui::BeginMenu("Audio")) if (ImGui::BeginMenu("Audio"))
{ {
if (ImGui::MenuItem("Sound", "F5", Options.Sounds)) ImGuiMenuItemWShortcut(GameBindings::ToggleSounds, Options.Sounds);
{
options::toggle(Menu1::Sounds);
}
if (ImGui::MenuItem("Stereo Sound Effects", nullptr, Options.SoundStereo)) if (ImGui::MenuItem("Stereo Sound Effects", nullptr, Options.SoundStereo))
{ {
options::toggle(Menu1::SoundStereo); options::toggle(Menu1::SoundStereo);
@ -584,10 +565,7 @@ void winmain::RenderUi()
} }
ImGui::Separator(); ImGui::Separator();
if (ImGui::MenuItem(pb::get_rc_string(Msg::Menu1_Music), "F6", Options.Music)) ImGuiMenuItemWShortcut(GameBindings::ToggleMusic, Options.Music);
{
options::toggle(Menu1::Music);
}
ImGui::TextUnformatted("Music Volume"); ImGui::TextUnformatted("Music Volume");
if (ImGui::SliderInt("##Music Volume", &Options.MusicVolume.V, options::MinVolume, options::MaxVolume, if (ImGui::SliderInt("##Music Volume", &Options.MusicVolume.V, options::MinVolume, options::MaxVolume,
"%d", "%d",
@ -852,28 +830,6 @@ int winmain::event_handler(const SDL_Event* event)
options::toggle(Menu1::Full_Screen); options::toggle(Menu1::Full_Screen);
SDL_MinimizeWindow(MainWindow); SDL_MinimizeWindow(MainWindow);
break; 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: default:
break; break;
} }
@ -1014,9 +970,6 @@ int winmain::event_handler(const SDL_Event* event)
pb::InputDown({InputTypes::GameController, event->cbutton.button}); pb::InputDown({InputTypes::GameController, event->cbutton.button});
switch (event->cbutton.button) switch (event->cbutton.button)
{ {
case SDL_CONTROLLER_BUTTON_START:
pause();
break;
case SDL_CONTROLLER_BUTTON_BACK: case SDL_CONTROLLER_BUTTON_BACK:
if (single_step) if (single_step)
{ {
@ -1145,6 +1098,37 @@ void winmain::UpdateFrameRate()
TargetFrameTime = DurationMs(1000.0 / ups); 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() void winmain::RenderFrameTimeDialog()
{ {
ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2{300, 70}); ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2{300, 70});
@ -1208,3 +1192,12 @@ void winmain::HybridSleep(DurationMs sleepTarget)
// spin lock // spin lock
for (auto start = Clock::now(); DurationMs(Clock::now() - start) < sleepTarget;); 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);
}
}

View file

@ -1,6 +1,8 @@
#pragma once #pragma once
#include "gdrv.h" #include "gdrv.h"
enum class GameBindings;
struct SdlTickClock struct SdlTickClock
{ {
using duration = std::chrono::milliseconds; using duration = std::chrono::milliseconds;
@ -85,6 +87,7 @@ public:
static void pause(bool toggle = true); static void pause(bool toggle = true);
static void Restart(); static void Restart();
static void UpdateFrameRate(); static void UpdateFrameRate();
static void HandleGameBinding(GameBindings binding);
private: private:
static int return_value, DispFrameRate; static int return_value, DispFrameRate;
static int mouse_down, last_mouse_x, last_mouse_y; static int mouse_down, last_mouse_x, last_mouse_y;
@ -108,4 +111,5 @@ private:
static void RenderFrameTimeDialog(); static void RenderFrameTimeDialog();
static void HybridSleep(DurationMs seconds); static void HybridSleep(DurationMs seconds);
static void MainLoop(); static void MainLoop();
static void ImGuiMenuItemWShortcut(GameBindings binding, bool selected = false);
}; };