Options refactor part 2: input bindings.

This commit is contained in:
Muzychenko Andrey 2023-01-27 16:25:47 +03:00
parent 4192b12c29
commit d99fbb092e
5 changed files with 290 additions and 190 deletions

View File

@ -13,62 +13,55 @@ constexpr int options::MaxUps, options::MaxFps, options::MinUps, options::MinFps
constexpr int options::MaxSoundChannels, options::MinSoundChannels, options::DefSoundChannels; constexpr int options::MaxSoundChannels, options::MinSoundChannels, options::DefSoundChannels;
constexpr int options::MaxVolume, options::MinVolume, options::DefVolume; constexpr int options::MaxVolume, options::MinVolume, options::DefVolume;
std::map<std::string, std::string> options::settings{}; std::unordered_map<std::string, std::string> options::settings{};
ControlsStruct options::RebindControls{};
bool options::ShowDialog = false; bool options::ShowDialog = false;
GameInput* options::ControlWaitingForInput = nullptr; GameInput* options::ControlWaitingForInput = nullptr;
std::vector<OptionBase*> options::AllOptions{}; std::vector<OptionBase*> options::AllOptions{};
const ControlRef options::Controls[6]
{
{Msg::KEYMAPPER_FlipperL, RebindControls.LeftFlipper},
{Msg::KEYMAPPER_FlipperR, RebindControls.RightFlipper},
{Msg::KEYMAPPER_BumpLeft, RebindControls.LeftTableBump},
{Msg::KEYMAPPER_BumpRight, RebindControls.RightTableBump},
{Msg::KEYMAPPER_BumpBottom, RebindControls.BottomTableBump},
{Msg::KEYMAPPER_Plunger, RebindControls.Plunger},
};
const ControlsStruct options::KeyDft =
{
{
{InputTypes::Keyboard, SDLK_z},
{InputTypes::Mouse, SDL_BUTTON_LEFT},
{InputTypes::GameController, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
},
{
{InputTypes::Keyboard, SDLK_SLASH},
{InputTypes::Mouse, SDL_BUTTON_RIGHT},
{InputTypes::GameController, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
},
{
{InputTypes::Keyboard, SDLK_SPACE},
{InputTypes::Mouse, SDL_BUTTON_MIDDLE},
{InputTypes::GameController, SDL_CONTROLLER_BUTTON_A},
},
{
{InputTypes::Keyboard, SDLK_x},
{InputTypes::Mouse, SDL_BUTTON_X1},
{InputTypes::GameController, SDL_CONTROLLER_BUTTON_DPAD_LEFT},
},
{
{InputTypes::Keyboard, SDLK_PERIOD},
{InputTypes::Mouse, SDL_BUTTON_X2},
{InputTypes::GameController, SDL_CONTROLLER_BUTTON_DPAD_RIGHT},
},
{
{InputTypes::Keyboard, SDLK_UP},
{InputTypes::Mouse, SDL_BUTTON_X2 + 1},
{InputTypes::GameController, SDL_CONTROLLER_BUTTON_DPAD_UP},
},
};
optionsStruct options::Options optionsStruct options::Options
{ {
KeyDft, {
{
"Left Flipper key",
{InputTypes::Keyboard, SDLK_z},
{InputTypes::Mouse, SDL_BUTTON_LEFT},
{InputTypes::GameController, SDL_CONTROLLER_BUTTON_LEFTSHOULDER}
},
{
"Right Flipper key",
{InputTypes::Keyboard, SDLK_SLASH},
{InputTypes::Mouse,SDL_BUTTON_RIGHT},
{InputTypes::GameController, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER}
},
{
"Plunger key",
{InputTypes::Keyboard, SDLK_SPACE},
{InputTypes::Mouse,SDL_BUTTON_MIDDLE},
{InputTypes::GameController, SDL_CONTROLLER_BUTTON_A}
},
{
"Left Table Bump key",
{InputTypes::Keyboard, SDLK_x},
{InputTypes::Mouse,SDL_BUTTON_X1},
{InputTypes::GameController, SDL_CONTROLLER_BUTTON_DPAD_LEFT}
},
{
"Right Table Bump key",
{InputTypes::Keyboard, SDLK_PERIOD},
{InputTypes::Mouse,SDL_BUTTON_X2},
{InputTypes::GameController, SDL_CONTROLLER_BUTTON_DPAD_RIGHT}
},
{
"Bottom Table Bump key",
{InputTypes::Keyboard, SDLK_UP},
{InputTypes::Mouse,SDL_BUTTON_X2 + 1},
{InputTypes::GameController, SDL_CONTROLLER_BUTTON_DPAD_UP}
},
},
{"Sounds", true}, {"Sounds", true},
{"Music", false }, {"Music", false},
{"FullScreen", false }, {"FullScreen", false},
{"Players", 1 }, {"Players", 1},
{"Screen Resolution", -1 }, {"Screen Resolution", -1 },
{"UI Scale", 1.0f}, {"UI Scale", 1.0f},
{"Uniform scaling", true}, {"Uniform scaling", true},
@ -116,13 +109,6 @@ void options::InitPrimary()
imContext->SettingsLoaded = true; imContext->SettingsLoaded = true;
} }
GetInput("Left Flipper key", Options.Key.LeftFlipper);
GetInput("Right Flipper key", Options.Key.RightFlipper);
GetInput("Plunger key", Options.Key.Plunger);
GetInput("Left Table Bump key", Options.Key.LeftTableBump);
GetInput("Right Table Bump key", Options.Key.RightTableBump);
GetInput("Bottom Table Bump key", Options.Key.BottomTableBump);
for(const auto opt : AllOptions) for(const auto opt : AllOptions)
{ {
opt->Load(); opt->Load();
@ -150,13 +136,7 @@ void options::InitSecondary()
void options::uninit() void options::uninit()
{ {
SetInput("Left Flipper key", Options.Key.LeftFlipper); Options.Language.V = translations::GetCurrentLanguage()->ShortName;
SetInput("Right Flipper key", Options.Key.RightFlipper);
SetInput("Plunger key", Options.Key.Plunger);
SetInput("Left Table Bump key", Options.Key.LeftTableBump);
SetInput("Right Table Bump key", Options.Key.RightTableBump);
SetInput("Bottom Table Bump key", Options.Key.BottomTableBump);
for (const auto opt : AllOptions) for (const auto opt : AllOptions)
{ {
opt->Save(); opt->Save();
@ -308,27 +288,31 @@ void options::ShowControlDialog()
if (!ShowDialog) if (!ShowDialog)
{ {
ControlWaitingForInput = nullptr; ControlWaitingForInput = nullptr;
RebindControls = Options.Key;
ShowDialog = true; ShowDialog = true;
// Save previous controls in KVP storage.
for(const auto& control: Options.Key)
{
control.Save();
}
} }
} }
void options::RenderControlDialog() void options::RenderControlDialog()
{ {
static const char* mouseButtons[] static const Msg controlDescriptions[6]
{ {
nullptr, Msg::KEYMAPPER_FlipperL,
"Mouse Left", Msg::KEYMAPPER_FlipperR,
"Mouse Middle", Msg::KEYMAPPER_BumpLeft,
"Mouse Right", Msg::KEYMAPPER_BumpRight,
"Mouse X1", Msg::KEYMAPPER_BumpBottom,
"Mouse X2", Msg::KEYMAPPER_Plunger,
}; };
if (!ShowDialog) if (!ShowDialog)
return; return;
ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2{550, 450}); 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)); ImGui::TextUnformatted(pb::get_rc_string(Msg::KEYMAPPER_Groupbox2));
@ -341,7 +325,7 @@ void options::RenderControlDialog()
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});
if (ImGui::BeginTable("Controls", 4, ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_Borders)) if (ImGui::BeginTable("Controls", 4, ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_Borders| ImGuiTableFlags_SizingStretchSame))
{ {
ImGui::TableSetupColumn("Control"); ImGui::TableSetupColumn("Control");
ImGui::TableSetupColumn("Binding 1"); ImGui::TableSetupColumn("Binding 1");
@ -349,55 +333,37 @@ void options::RenderControlDialog()
ImGui::TableSetupColumn("Binding 3"); ImGui::TableSetupColumn("Binding 3");
ImGui::TableHeadersRow(); ImGui::TableHeadersRow();
int index = 0; int rowHash = 0;
for (auto& row : Controls) for (auto inputId = GameBindings::Min; inputId < GameBindings::Max; inputId++)
{ {
auto& option = Options.Key[~inputId];
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4{0.5, 0, 0, 1}); ImGui::PushStyleColor(ImGuiCol_Button, ImVec4{0.5, 0, 0, 1});
if (ImGui::Button(pb::get_rc_string(row.NameStringId))) if (ImGui::Button(pb::get_rc_string(controlDescriptions[~inputId])))
{ {
for (auto i = 0u; i <= 2; i++) for (auto& input : option.Inputs)
row.Option[i] = {}; input = {};
} }
ImGui::PopStyleColor(1); ImGui::PopStyleColor(1);
for (auto i = 0u; i <= 2; i++) for (auto& input : option.Inputs)
{ {
auto& ctrl = row.Option[i];
ImGui::TableNextColumn(); ImGui::TableNextColumn();
if (ControlWaitingForInput == &ctrl) if (ControlWaitingForInput == &input)
{ {
ImGui::Button("Press the key", ImVec2(-1, 0)); if(ImGui::Button("Press the key", ImVec2(-1, 0)))
{
ControlWaitingForInput = &input;
}
} }
else else
{ {
std::string tmp; auto inputDescription = input.GetInputDescription();
const char* keyName; if (ImGui::Button((inputDescription + "##" + std::to_string(rowHash++)).c_str(),
switch (ctrl.Type)
{
case InputTypes::Keyboard:
keyName = SDL_GetKeyName(ctrl.Value);
break;
case InputTypes::Mouse:
if (ctrl.Value >= SDL_BUTTON_LEFT && ctrl.Value <= SDL_BUTTON_X2)
keyName = mouseButtons[ctrl.Value];
else
keyName = (tmp += "Mouse " + std::to_string(ctrl.Value)).c_str();
break;
case InputTypes::GameController:
keyName = SDL_GameControllerGetStringForButton(
static_cast<SDL_GameControllerButton>(ctrl.Value));
break;
case InputTypes::None:
default:
keyName = "Unused";
}
if (!keyName || !keyName[0])
keyName = "Unknown key";
if (ImGui::Button((std::string{keyName} + "##" + std::to_string(index++)).c_str(),
ImVec2(-1, 0))) ImVec2(-1, 0)))
{ {
ControlWaitingForInput = &ctrl; ControlWaitingForInput = &input;
} }
} }
} }
@ -409,20 +375,26 @@ void options::RenderControlDialog()
if (ImGui::Button(pb::get_rc_string(Msg::KEYMAPPER_Ok))) if (ImGui::Button(pb::get_rc_string(Msg::KEYMAPPER_Ok)))
{ {
Options.Key = RebindControls;
ShowDialog = false; ShowDialog = false;
} }
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button(pb::get_rc_string(Msg::KEYMAPPER_Cancel))) if (ImGui::Button(pb::get_rc_string(Msg::KEYMAPPER_Cancel)))
{ {
for (auto& control : Options.Key)
{
control.Load();
}
ShowDialog = false; ShowDialog = false;
} }
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button(pb::get_rc_string(Msg::KEYMAPPER_Default))) if (ImGui::Button(pb::get_rc_string(Msg::KEYMAPPER_Default)))
{ {
RebindControls = KeyDft; for (auto& control : Options.Key)
{
control.Reset();
}
ControlWaitingForInput = nullptr; ControlWaitingForInput = nullptr;
} }
} }
@ -433,9 +405,26 @@ void options::RenderControlDialog()
ControlWaitingForInput = nullptr; ControlWaitingForInput = nullptr;
} }
std::vector<GameBindings> options::MapGameInput(GameInput key)
{
std::vector<GameBindings> result;
for (auto inputId = GameBindings::Min; inputId < GameBindings::Max; inputId++)
{
for (auto& inputValue : Options.Key[~inputId].Inputs)
{
if (key == inputValue)
{
result.push_back(inputId);
break;
}
}
}
return result;
}
void options::MyUserData_ReadLine(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* entry, const char* line) void options::MyUserData_ReadLine(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* entry, const char* line)
{ {
auto& keyValueStore = *static_cast<std::map<std::string, std::string>*>(entry); auto& keyValueStore = *static_cast<std::unordered_map<std::string, std::string>*>(entry);
std::string keyValue = line; std::string keyValue = line;
auto separatorPos = keyValue.find('='); auto separatorPos = keyValue.find('=');
if (separatorPos != std::string::npos) if (separatorPos != std::string::npos)
@ -462,6 +451,72 @@ void options::MyUserData_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handl
buf->append("\n"); buf->append("\n");
} }
std::string GameInput::GetInputDescription() const
{
static LPCSTR mouseButtons[]
{
nullptr,
"Left",
"Middle",
"Right",
"X1",
"X2",
};
static LPCSTR controllerButtons[] =
{
"A",
"B",
"X",
"Y",
"Back",
"Guide",
"Start",
"LeftStick",
"RightStick",
"LeftShoulder",
"RightShoulder",
"DpUp",
"DpDown",
"DpLeft",
"DpRight",
"Misc1",
"Paddle1",
"Paddle2",
"Paddle3",
"Paddle4",
"Touchpad",
};
std::string keyName;
switch (Type)
{
case InputTypes::Keyboard:
keyName = "Keyboard\n";
keyName += SDL_GetKeyName(Value);
break;
case InputTypes::Mouse:
keyName = "Mouse\n";
if (Value >= SDL_BUTTON_LEFT && Value <= SDL_BUTTON_X2)
keyName += mouseButtons[Value];
else
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];
else
keyName += std::to_string(Value);
break;
case InputTypes::None:
default:
keyName = "Unused";
}
return keyName;
}
const std::string& options::GetSetting(const std::string& key, const std::string& defaultValue) const std::string& options::GetSetting(const std::string& key, const std::string& defaultValue)
{ {
auto setting = settings.find(key); auto setting = settings.find(key);

View File

@ -33,12 +33,12 @@ enum class Menu1:int
Prefer3DPBGameData = 700, Prefer3DPBGameData = 700,
}; };
enum class InputTypes: unsigned enum class InputTypes
{ {
None = 0, None = 0,
Keyboard = 1, Keyboard,
Mouse = 2, Mouse,
GameController = 3, GameController,
}; };
struct GameInput struct GameInput
@ -50,23 +50,31 @@ struct GameInput
{ {
return Type == other.Type && Value == other.Value; return Type == other.Type && Value == other.Value;
} }
std::string GetInputDescription() const;
}; };
struct ControlsStruct enum class GameBindings
{ {
GameInput LeftFlipper[3]; Min = 0,
GameInput RightFlipper[3]; LeftFlipper = 0,
GameInput Plunger[3]; RightFlipper,
GameInput LeftTableBump[3]; Plunger,
GameInput RightTableBump[3]; LeftTableBump,
GameInput BottomTableBump[3]; RightTableBump,
BottomTableBump,
Max
}; };
struct ControlRef inline GameBindings& operator++(GameBindings& value, int)
{ {
Msg NameStringId; return value = static_cast<GameBindings>(static_cast<int>(value) + 1);
GameInput (&Option)[3]; }
};
constexpr int operator~(const GameBindings& value)
{
return static_cast<int>(value);
}
class options class options
{ {
@ -95,14 +103,12 @@ public:
static void InputDown(GameInput input); static void InputDown(GameInput input);
static void ShowControlDialog(); static void ShowControlDialog();
static void RenderControlDialog(); static void RenderControlDialog();
static bool WaitingForInput() { return ControlWaitingForInput != nullptr; } static bool WaitingForInput() { return ControlWaitingForInput; }
static std::vector<GameBindings> MapGameInput(GameInput key);
private: private:
static std::map<std::string, std::string> settings; static std::unordered_map<std::string, std::string> settings;
static ControlsStruct RebindControls;
static bool ShowDialog; static bool ShowDialog;
static const ControlRef Controls[6];
static GameInput* ControlWaitingForInput; static GameInput* ControlWaitingForInput;
static const ControlsStruct KeyDft;
static void MyUserData_ReadLine(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* entry, const char* line); static void MyUserData_ReadLine(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* entry, const char* line);
static void* MyUserData_ReadOpen(ImGuiContext* ctx, ImGuiSettingsHandler* handler, const char* name); static void* MyUserData_ReadOpen(ImGuiContext* ctx, ImGuiSettingsHandler* handler, const char* name);
@ -183,9 +189,53 @@ struct BoolOption : OptionBaseT<bool>
using OptionBaseT::operator=; using OptionBaseT::operator=;
}; };
struct ControlOption : OptionBase
{
GameInput Defaults[3];
GameInput Inputs[3];
ControlOption(LPCSTR name, GameInput defaultKeyboard, GameInput defaultMouse, GameInput defaultController) :
OptionBase(name),
Defaults{defaultKeyboard, defaultMouse, defaultController},
Inputs{
{InputTypes::Keyboard, -1},
{InputTypes::Mouse, -1},
{InputTypes::GameController, -1}
}
{
}
void Load() override
{
for (auto i = 0u; i <= 2; i++)
{
auto name = std::string{ Name } + " " + std::to_string(i);
Inputs[i].Type = static_cast<InputTypes>(options::get_int((name + " type").c_str(),
static_cast<int>(Defaults[i].Type)));
Inputs[i].Value = options::get_int((name + " input").c_str(), Defaults[i].Value);
}
}
void Save() const override
{
for (auto i = 0u; i <= 2; i++)
{
auto name = std::string{ Name } + " " + std::to_string(i);
options::set_int((name + " type").c_str(), static_cast<int>(Inputs[i].Type));
options::set_int((name + " input").c_str(), Inputs[i].Value);
}
}
void Reset() override
{
std::copy(std::begin(Defaults), std::end(Defaults), std::begin(Inputs));
}
};
struct optionsStruct struct optionsStruct
{ {
ControlsStruct Key; ControlOption Key[~GameBindings::Max];
BoolOption Sounds; BoolOption Sounds;
BoolOption Music; BoolOption Music;
BoolOption FullScreen; BoolOption FullScreen;

View File

@ -423,30 +423,32 @@ void pb::InputUp(GameInput input)
{ {
if (game_mode != GameModes::InGame || winmain::single_step || demo_mode) if (game_mode != GameModes::InGame || winmain::single_step || demo_mode)
return; return;
if (AnyBindingMatchesInput(options::Options.Key.LeftFlipper, input)) const auto bindings = options::MapGameInput(input);
for (const auto binding : bindings)
{ {
MainTable->Message(MessageCode::LeftFlipperInputReleased, time_now); switch (binding)
} {
if (AnyBindingMatchesInput(options::Options.Key.RightFlipper, input)) case GameBindings::LeftFlipper:
{ MainTable->Message(MessageCode::LeftFlipperInputReleased, time_now);
MainTable->Message(MessageCode::RightFlipperInputReleased, time_now); break;
} case GameBindings::RightFlipper:
if (AnyBindingMatchesInput(options::Options.Key.Plunger, input)) MainTable->Message(MessageCode::RightFlipperInputReleased, time_now);
{ break;
MainTable->Message(MessageCode::PlungerInputReleased, time_now); case GameBindings::Plunger:
} MainTable->Message(MessageCode::PlungerInputReleased, time_now);
if (AnyBindingMatchesInput(options::Options.Key.LeftTableBump, input)) break;
{ case GameBindings::LeftTableBump:
nudge::un_nudge_right(0, nullptr); nudge::un_nudge_right(0, nullptr);
} break;
if (AnyBindingMatchesInput(options::Options.Key.RightTableBump, input)) case GameBindings::RightTableBump:
{ nudge::un_nudge_left(0, nullptr);
nudge::un_nudge_left(0, nullptr); break;
} case GameBindings::BottomTableBump:
if (AnyBindingMatchesInput(options::Options.Key.BottomTableBump, input)) nudge::un_nudge_up(0, nullptr);
{ break;
nudge::un_nudge_up(0, nullptr); default: break;
}
} }
} }
@ -459,33 +461,35 @@ void pb::InputDown(GameInput input)
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));
if (AnyBindingMatchesInput(options::Options.Key.LeftFlipper, input)) const auto bindings = options::MapGameInput(input);
for (const auto binding : bindings)
{ {
MainTable->Message(MessageCode::LeftFlipperInputPressed, time_now); switch (binding)
} {
if (AnyBindingMatchesInput(options::Options.Key.RightFlipper, input)) case GameBindings::LeftFlipper:
{ MainTable->Message(MessageCode::LeftFlipperInputPressed, time_now);
MainTable->Message(MessageCode::RightFlipperInputPressed, time_now); break;
} case GameBindings::RightFlipper:
if (AnyBindingMatchesInput(options::Options.Key.Plunger, input)) MainTable->Message(MessageCode::RightFlipperInputPressed, time_now);
{ break;
MainTable->Message(MessageCode::PlungerInputPressed, time_now); case GameBindings::Plunger:
} MainTable->Message(MessageCode::PlungerInputPressed, time_now);
if (AnyBindingMatchesInput(options::Options.Key.LeftTableBump, input)) break;
{ case GameBindings::LeftTableBump:
if (!MainTable->TiltLockFlag) if (!MainTable->TiltLockFlag)
nudge::nudge_right(); nudge::nudge_right();
} break;
if (AnyBindingMatchesInput(options::Options.Key.RightTableBump, input)) case GameBindings::RightTableBump:
{ if (!MainTable->TiltLockFlag)
if (!MainTable->TiltLockFlag) nudge::nudge_left();
nudge::nudge_left(); break;
} case GameBindings::BottomTableBump:
if (AnyBindingMatchesInput(options::Options.Key.BottomTableBump, input)) if (!MainTable->TiltLockFlag)
{ nudge::nudge_up();
if (!MainTable->TiltLockFlag) break;
nudge::nudge_up(); default: break;
} }
}
if (cheat_mode && input.Type == InputTypes::Keyboard) if (cheat_mode && input.Type == InputTypes::Keyboard)
{ {
@ -660,14 +664,6 @@ void pb::PushCheat(const std::string& cheat)
control::pbctrl_bdoor_controller(ch); control::pbctrl_bdoor_controller(ch);
} }
bool pb::AnyBindingMatchesInput(GameInput (&options)[3], GameInput key)
{
for (auto& option : options)
if (key == option)
return true;
return false;
}
LPCSTR pb::get_rc_string(Msg uID) LPCSTR pb::get_rc_string(Msg uID)
{ {
return translations::GetTranslation(uID); return translations::GetTranslation(uID);

View File

@ -82,6 +82,4 @@ public:
static void ShowMessageBox(Uint32 flags, LPCSTR title, LPCSTR message); static void ShowMessageBox(Uint32 flags, LPCSTR title, LPCSTR message);
private: private:
static bool demo_mode; static bool demo_mode;
static bool AnyBindingMatchesInput(GameInput (&options)[3], GameInput key);
}; };

View File

@ -29,6 +29,7 @@
#include <string> #include <string>
#include <thread> #include <thread>
#include <map> #include <map>
#include <unordered_map>
#include <initializer_list> #include <initializer_list>
//#include <array> //#include <array>