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::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::Spacing();
ImGui::TreePop();
}
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;
}

View file

@ -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

View file

@ -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<char>(input.Value));
const auto bindings = options::MapGameInput(input);
for (const auto binding : bindings)
{
switch (binding)

View file

@ -6289,4 +6289,10 @@ const TextArray translations::Translations =
{ 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_Table_Resolution,
Menu1_Help,
Menu1_ToggleShowMenu,
Menu1_UseMaxResolution_640x480,
Menu1_UseMaxResolution_800x600,

View file

@ -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);
}
}

View file

@ -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);
};