2020-11-05 16:44:34 +01:00
|
|
|
#include "pch.h"
|
|
|
|
#include "options.h"
|
2020-12-02 18:12:34 +01:00
|
|
|
|
|
|
|
#include "fullscrn.h"
|
|
|
|
#include "midi.h"
|
2020-11-06 14:56:32 +01:00
|
|
|
#include "Sound.h"
|
2020-12-03 15:47:36 +01:00
|
|
|
#include "winmain.h"
|
2020-11-05 16:44:34 +01:00
|
|
|
|
2021-10-01 08:05:38 +02:00
|
|
|
constexpr int options::MaxUps, options::MaxFps, options::MinUps, options::MinFps, options::DefUps, options::DefFps;
|
2021-10-09 16:28:30 +02:00
|
|
|
constexpr int options::MaxSoundChannels, options::MinSoundChannels, options::DefSoundChannels;
|
2020-11-06 14:56:32 +01:00
|
|
|
|
2021-10-01 08:05:38 +02:00
|
|
|
optionsStruct options::Options{};
|
|
|
|
std::map<std::string, std::string> options::settings{};
|
|
|
|
ControlsStruct options::RebindControls{};
|
|
|
|
bool options::ShowDialog = false;
|
|
|
|
const ControlRef* options::ControlWaitingForKey = nullptr;
|
|
|
|
const ControlRef options::Controls[6]
|
2020-12-13 14:05:19 +01:00
|
|
|
{
|
2021-10-01 08:05:38 +02:00
|
|
|
{"Left Flipper", RebindControls.LeftFlipper},
|
|
|
|
{"Right Flipper", RebindControls.RightFlipper},
|
|
|
|
{"Left Table Bump", RebindControls.LeftTableBump},
|
|
|
|
{"Right Table Bump", RebindControls.RightTableBump},
|
|
|
|
{"Bottom Table Bump", RebindControls.BottomTableBump},
|
|
|
|
{"Plunger", RebindControls.Plunger},
|
2020-12-13 14:05:19 +01:00
|
|
|
};
|
|
|
|
|
2021-09-16 09:57:46 +02:00
|
|
|
|
2021-09-06 15:27:58 +02:00
|
|
|
void options::init()
|
2020-11-06 14:56:32 +01:00
|
|
|
{
|
2021-09-16 09:57:46 +02:00
|
|
|
auto imContext = ImGui::GetCurrentContext();
|
|
|
|
ImGuiSettingsHandler ini_handler;
|
|
|
|
ini_handler.TypeName = "Pinball";
|
|
|
|
ini_handler.TypeHash = ImHashStr(ini_handler.TypeName);
|
|
|
|
ini_handler.ReadOpenFn = MyUserData_ReadOpen;
|
|
|
|
ini_handler.ReadLineFn = MyUserData_ReadLine;
|
|
|
|
ini_handler.WriteAllFn = MyUserData_WriteAll;
|
|
|
|
imContext->SettingsHandlers.push_back(ini_handler);
|
|
|
|
|
|
|
|
// Settings are loaded from disk on the first frame
|
|
|
|
if (!imContext->SettingsLoaded)
|
|
|
|
{
|
|
|
|
ImGui::NewFrame();
|
|
|
|
ImGui::EndFrame();
|
|
|
|
}
|
|
|
|
|
2021-10-01 08:05:38 +02:00
|
|
|
Options.KeyDft.LeftFlipper = SDLK_z;
|
|
|
|
Options.KeyDft.RightFlipper = SDLK_SLASH;
|
|
|
|
Options.KeyDft.Plunger = SDLK_SPACE;
|
|
|
|
Options.KeyDft.LeftTableBump = SDLK_x;
|
|
|
|
Options.KeyDft.RightTableBump = SDLK_PERIOD;
|
|
|
|
Options.KeyDft.BottomTableBump = SDLK_UP;
|
|
|
|
Options.Key = Options.KeyDft;
|
|
|
|
|
2021-10-09 16:28:30 +02:00
|
|
|
Options.Sounds = get_int("Sounds", true);
|
|
|
|
Options.Music = get_int("Music", false);
|
|
|
|
Options.FullScreen = get_int("FullScreen", false);
|
|
|
|
Options.Players = get_int("Players", 1);
|
2021-10-01 08:05:38 +02:00
|
|
|
Options.Key.LeftFlipper = get_int("Left Flipper key", Options.Key.LeftFlipper);
|
|
|
|
Options.Key.RightFlipper = get_int("Right Flipper key", Options.Key.RightFlipper);
|
|
|
|
Options.Key.Plunger = get_int("Plunger key", Options.Key.Plunger);
|
|
|
|
Options.Key.LeftTableBump = get_int("Left Table Bump key", Options.Key.LeftTableBump);
|
|
|
|
Options.Key.RightTableBump = get_int("Right Table Bump key", Options.Key.RightTableBump);
|
|
|
|
Options.Key.BottomTableBump = get_int("Bottom Table Bump key", Options.Key.BottomTableBump);
|
2021-10-09 16:28:30 +02:00
|
|
|
Options.UniformScaling = get_int("Uniform scaling", true);
|
2021-09-22 14:50:07 +02:00
|
|
|
ImGui::GetIO().FontGlobalScale = get_float("UI Scale", 1.0f);
|
|
|
|
Options.Resolution = get_int("Screen Resolution", -1);
|
2021-09-23 11:16:58 +02:00
|
|
|
Options.LinearFiltering = get_int("Linear Filtering", true);
|
2021-09-28 07:14:18 +02:00
|
|
|
Options.FramesPerSecond = std::min(MaxFps, std::max(MinUps, get_int("Frames Per Second", DefFps)));
|
|
|
|
Options.UpdatesPerSecond = std::min(MaxUps, std::max(MinUps, get_int("Updates Per Second", DefUps)));
|
|
|
|
Options.UpdatesPerSecond = std::max(Options.UpdatesPerSecond, Options.FramesPerSecond);
|
2021-09-29 06:46:13 +02:00
|
|
|
Options.ShowMenu = get_int("ShowMenu", true);
|
2021-10-02 06:42:08 +02:00
|
|
|
Options.UncappedUpdatesPerSecond = get_int("Uncapped Updates Per Second", false);
|
2021-10-09 16:28:30 +02:00
|
|
|
Options.SoundChannels = get_int("Sound Channels", DefSoundChannels);
|
|
|
|
Options.SoundChannels = std::min(MaxSoundChannels, std::max(MinSoundChannels, Options.SoundChannels));
|
2021-09-28 07:14:18 +02:00
|
|
|
|
|
|
|
winmain::UpdateFrameRate();
|
2021-09-22 14:50:07 +02:00
|
|
|
|
|
|
|
auto maxRes = fullscrn::GetMaxResolution();
|
|
|
|
if (Options.Resolution >= 0 && Options.Resolution > maxRes)
|
|
|
|
Options.Resolution = maxRes;
|
|
|
|
fullscrn::SetResolution(Options.Resolution == -1 ? maxRes : Options.Resolution);
|
2020-11-06 14:56:32 +01:00
|
|
|
}
|
2020-11-05 16:44:34 +01:00
|
|
|
|
2020-12-04 16:35:47 +01:00
|
|
|
void options::uninit()
|
|
|
|
{
|
2021-09-16 09:57:46 +02:00
|
|
|
set_int("Sounds", Options.Sounds);
|
|
|
|
set_int("Music", Options.Music);
|
|
|
|
set_int("FullScreen", Options.FullScreen);
|
|
|
|
set_int("Players", Options.Players);
|
2021-10-01 08:05:38 +02:00
|
|
|
set_int("Left Flipper key", Options.Key.LeftFlipper);
|
|
|
|
set_int("Right Flipper key", Options.Key.RightFlipper);
|
|
|
|
set_int("Plunger key", Options.Key.Plunger);
|
|
|
|
set_int("Left Table Bump key", Options.Key.LeftTableBump);
|
|
|
|
set_int("Right Table Bump key", Options.Key.RightTableBump);
|
|
|
|
set_int("Bottom Table Bump key", Options.Key.BottomTableBump);
|
2021-09-16 09:57:46 +02:00
|
|
|
set_int("Screen Resolution", Options.Resolution);
|
|
|
|
set_int("Uniform scaling", Options.UniformScaling);
|
2021-09-22 14:50:07 +02:00
|
|
|
set_float("UI Scale", ImGui::GetIO().FontGlobalScale);
|
2021-09-23 11:16:58 +02:00
|
|
|
set_int("Linear Filtering", Options.LinearFiltering);
|
2021-09-28 07:14:18 +02:00
|
|
|
set_int("Frames Per Second", Options.FramesPerSecond);
|
|
|
|
set_int("Updates Per Second", Options.UpdatesPerSecond);
|
2021-09-29 06:46:13 +02:00
|
|
|
set_int("ShowMenu", Options.ShowMenu);
|
2021-10-02 06:42:08 +02:00
|
|
|
set_int("Uncapped Updates Per Second", Options.UncappedUpdatesPerSecond);
|
2021-10-09 16:28:30 +02:00
|
|
|
set_int("Sound Channels", Options.SoundChannels);
|
2020-11-05 16:44:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-09-16 09:57:46 +02:00
|
|
|
int options::get_int(LPCSTR lpValueName, int defaultValue)
|
2020-11-05 16:44:34 +01:00
|
|
|
{
|
2021-09-16 09:57:46 +02:00
|
|
|
auto value = GetSetting(lpValueName, std::to_string(defaultValue));
|
|
|
|
return std::stoi(value);
|
2020-11-05 16:44:34 +01:00
|
|
|
}
|
|
|
|
|
2021-09-16 09:57:46 +02:00
|
|
|
void options::set_int(LPCSTR lpValueName, int data)
|
2020-11-05 16:44:34 +01:00
|
|
|
{
|
2021-09-16 09:57:46 +02:00
|
|
|
SetSetting(lpValueName, std::to_string(data));
|
2020-11-05 16:44:34 +01:00
|
|
|
}
|
|
|
|
|
2021-09-16 09:57:46 +02:00
|
|
|
std::string options::get_string(LPCSTR lpValueName, LPCSTR defaultValue)
|
2020-11-05 16:44:34 +01:00
|
|
|
{
|
2021-09-16 09:57:46 +02:00
|
|
|
return GetSetting(lpValueName, defaultValue);
|
2020-11-05 16:44:34 +01:00
|
|
|
}
|
|
|
|
|
2021-09-16 09:57:46 +02:00
|
|
|
void options::set_string(LPCSTR lpValueName, LPCSTR value)
|
2020-11-05 16:44:34 +01:00
|
|
|
{
|
2021-09-16 09:57:46 +02:00
|
|
|
SetSetting(lpValueName, value);
|
2020-11-05 16:44:34 +01:00
|
|
|
}
|
2020-11-06 14:56:32 +01:00
|
|
|
|
2021-09-22 14:50:07 +02:00
|
|
|
float options::get_float(LPCSTR lpValueName, float defaultValue)
|
|
|
|
{
|
|
|
|
auto value = GetSetting(lpValueName, std::to_string(defaultValue));
|
|
|
|
return std::stof(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
void options::set_float(LPCSTR lpValueName, float data)
|
|
|
|
{
|
|
|
|
SetSetting(lpValueName, std::to_string(data));
|
|
|
|
}
|
|
|
|
|
2020-11-06 14:56:32 +01:00
|
|
|
|
2021-09-23 11:16:58 +02:00
|
|
|
void options::toggle(Menu1 uIDCheckItem)
|
2020-12-02 18:12:34 +01:00
|
|
|
{
|
|
|
|
switch (uIDCheckItem)
|
|
|
|
{
|
2021-09-23 11:16:58 +02:00
|
|
|
case Menu1::Sounds:
|
2021-10-09 16:28:30 +02:00
|
|
|
Options.Sounds ^= true;
|
|
|
|
Sound::Enable(Options.Sounds);
|
2020-12-02 18:12:34 +01:00
|
|
|
return;
|
2021-09-23 11:16:58 +02:00
|
|
|
case Menu1::Music:
|
2021-10-09 16:28:30 +02:00
|
|
|
Options.Music ^= true;
|
|
|
|
if (!Options.Music)
|
2020-12-02 18:12:34 +01:00
|
|
|
midi::music_stop();
|
|
|
|
else
|
2021-09-29 16:53:49 +02:00
|
|
|
midi::play_pb_theme();
|
2020-12-02 18:12:34 +01:00
|
|
|
return;
|
2021-09-29 02:21:21 +02:00
|
|
|
case Menu1::Show_Menu:
|
|
|
|
Options.ShowMenu = Options.ShowMenu == 0;
|
|
|
|
return;
|
2021-09-23 11:16:58 +02:00
|
|
|
case Menu1::Full_Screen:
|
2021-10-09 16:28:30 +02:00
|
|
|
Options.FullScreen ^= true;
|
|
|
|
fullscrn::set_screen_mode(Options.FullScreen);
|
2020-12-02 18:12:34 +01:00
|
|
|
return;
|
2021-09-23 11:16:58 +02:00
|
|
|
case Menu1::OnePlayer:
|
|
|
|
case Menu1::TwoPlayers:
|
|
|
|
case Menu1::ThreePlayers:
|
|
|
|
case Menu1::FourPlayers:
|
|
|
|
Options.Players = static_cast<int>(uIDCheckItem) - static_cast<int>(Menu1::OnePlayer) + 1;
|
2021-02-06 14:53:47 +01:00
|
|
|
break;
|
2021-09-23 11:16:58 +02:00
|
|
|
case Menu1::MaximumResolution:
|
|
|
|
case Menu1::R640x480:
|
|
|
|
case Menu1::R800x600:
|
|
|
|
case Menu1::R1024x768:
|
2021-02-06 14:53:47 +01:00
|
|
|
{
|
2021-09-22 14:50:07 +02:00
|
|
|
auto restart = false;
|
2021-09-23 11:16:58 +02:00
|
|
|
int newResolution = static_cast<int>(uIDCheckItem) - static_cast<int>(Menu1::R640x480);
|
|
|
|
if (uIDCheckItem == Menu1::MaximumResolution)
|
2021-02-06 14:53:47 +01:00
|
|
|
{
|
2021-09-22 14:50:07 +02:00
|
|
|
restart = fullscrn::GetResolution() != fullscrn::GetMaxResolution();
|
2021-02-06 14:53:47 +01:00
|
|
|
Options.Resolution = -1;
|
|
|
|
}
|
2021-09-22 14:50:07 +02:00
|
|
|
else if (newResolution <= fullscrn::GetMaxResolution())
|
2021-02-06 14:53:47 +01:00
|
|
|
{
|
2021-09-22 14:50:07 +02:00
|
|
|
restart = newResolution != (Options.Resolution == -1
|
|
|
|
? fullscrn::GetMaxResolution()
|
|
|
|
: fullscrn::GetResolution());
|
2021-02-06 14:53:47 +01:00
|
|
|
Options.Resolution = newResolution;
|
|
|
|
}
|
2021-09-22 14:50:07 +02:00
|
|
|
|
|
|
|
if (restart)
|
|
|
|
winmain::Restart();
|
2021-02-06 14:53:47 +01:00
|
|
|
break;
|
|
|
|
}
|
2021-09-23 11:16:58 +02:00
|
|
|
case Menu1::WindowUniformScale:
|
2021-02-09 16:09:44 +01:00
|
|
|
Options.UniformScaling ^= true;
|
|
|
|
fullscrn::window_size_changed();
|
|
|
|
break;
|
2021-09-23 11:16:58 +02:00
|
|
|
case Menu1::WindowLinearFilter:
|
|
|
|
Options.LinearFiltering ^= true;
|
|
|
|
winmain::Restart();
|
|
|
|
break;
|
2021-02-06 14:53:47 +01:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-01 08:05:38 +02:00
|
|
|
void options::KeyDown(int key)
|
|
|
|
{
|
|
|
|
if (ControlWaitingForKey)
|
|
|
|
{
|
|
|
|
// Skip function keys, just in case.
|
|
|
|
if (key < SDLK_F1 || key > SDLK_F12)
|
|
|
|
{
|
|
|
|
ControlWaitingForKey->Option = key;
|
|
|
|
ControlWaitingForKey = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void options::ShowControlDialog()
|
2020-12-02 18:12:34 +01:00
|
|
|
{
|
2021-10-01 08:05:38 +02:00
|
|
|
if (!ShowDialog)
|
|
|
|
{
|
|
|
|
ControlWaitingForKey = nullptr;
|
|
|
|
RebindControls = Options.Key;
|
|
|
|
ShowDialog = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void options::RenderControlDialog()
|
|
|
|
{
|
|
|
|
if (!ShowDialog)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2{500, 400});
|
|
|
|
if (ImGui::Begin("3D Pinball: Player Controls", &ShowDialog))
|
|
|
|
{
|
|
|
|
ImGui::TextUnformatted("Instructions");
|
|
|
|
ImGui::Separator();
|
|
|
|
|
|
|
|
ImGui::TextWrapped(
|
|
|
|
"To change game controls, click the control button, press the new key, and then choose OK.");
|
|
|
|
ImGui::TextWrapped(
|
|
|
|
"To restore 3D Pinball to its original settings, choose Default, and then choose OK.");
|
|
|
|
ImGui::TextWrapped("Original warns against binding the same key to multiple controls, but does not forbid it.");
|
|
|
|
ImGui::Spacing();
|
|
|
|
|
|
|
|
ImGui::TextUnformatted("Control Options");
|
|
|
|
ImGui::Separator();
|
|
|
|
|
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2{5, 10});
|
|
|
|
if (ImGui::BeginTable("Controls", 2, ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_Borders))
|
|
|
|
{
|
|
|
|
for (auto& ctrl : Controls)
|
|
|
|
{
|
|
|
|
ImGui::TableNextColumn();
|
|
|
|
if (ImGui::BeginTable("Control", 2, ImGuiTableFlags_NoSavedSettings))
|
|
|
|
{
|
|
|
|
ImGui::TableNextColumn();
|
|
|
|
ImGui::TextWrapped("%s", ctrl.Name);
|
|
|
|
|
|
|
|
ImGui::TableNextColumn();
|
|
|
|
if (ControlWaitingForKey == &ctrl)
|
|
|
|
{
|
|
|
|
ImGui::Button("Press the key", ImVec2(-1, 0));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
auto keyName = SDL_GetKeyName(ctrl.Option);
|
|
|
|
if (!keyName[0])
|
|
|
|
keyName = "Unknown key";
|
|
|
|
if (ImGui::Button(keyName, ImVec2(-1, 0)))
|
|
|
|
{
|
|
|
|
ControlWaitingForKey = &ctrl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ImGui::EndTable();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ImGui::EndTable();
|
|
|
|
}
|
|
|
|
ImGui::PopStyleVar();
|
|
|
|
ImGui::Spacing();
|
|
|
|
|
|
|
|
if (ImGui::Button("OK"))
|
|
|
|
{
|
|
|
|
Options.Key = RebindControls;
|
|
|
|
ShowDialog = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::SameLine();
|
|
|
|
if (ImGui::Button("Cancel"))
|
|
|
|
{
|
|
|
|
ShowDialog = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::SameLine();
|
|
|
|
if (ImGui::Button("Default"))
|
|
|
|
{
|
|
|
|
RebindControls = Options.KeyDft;
|
|
|
|
ControlWaitingForKey = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ImGui::End();
|
|
|
|
ImGui::PopStyleVar();
|
|
|
|
|
|
|
|
if (!ShowDialog)
|
|
|
|
ControlWaitingForKey = nullptr;
|
2020-12-02 18:12:34 +01:00
|
|
|
}
|
2021-09-16 09:57:46 +02:00
|
|
|
|
|
|
|
void options::MyUserData_ReadLine(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* entry, const char* line)
|
|
|
|
{
|
|
|
|
auto& keyValueStore = *static_cast<std::map<std::string, std::string>*>(entry);
|
|
|
|
std::string keyValue = line;
|
|
|
|
auto separatorPos = keyValue.find('=');
|
|
|
|
if (separatorPos != std::string::npos)
|
|
|
|
{
|
|
|
|
auto key = keyValue.substr(0, separatorPos);
|
|
|
|
auto value = keyValue.substr(separatorPos + 1, keyValue.size());
|
|
|
|
keyValueStore[key] = value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void* options::MyUserData_ReadOpen(ImGuiContext* ctx, ImGuiSettingsHandler* handler, const char* name)
|
|
|
|
{
|
|
|
|
// There is only one custom entry
|
|
|
|
return strcmp(name, "Settings") == 0 ? &settings : nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void options::MyUserData_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
|
|
|
|
{
|
|
|
|
buf->appendf("[%s][%s]\n", handler->TypeName, "Settings");
|
|
|
|
for (const auto& setting : settings)
|
|
|
|
{
|
|
|
|
buf->appendf("%s=%s\n", setting.first.c_str(), setting.second.c_str());
|
|
|
|
}
|
|
|
|
buf->append("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
const std::string& options::GetSetting(const std::string& key, const std::string& value)
|
|
|
|
{
|
|
|
|
auto setting = settings.find(key);
|
|
|
|
if (setting == settings.end())
|
|
|
|
{
|
|
|
|
settings[key] = value;
|
|
|
|
if (ImGui::GetCurrentContext())
|
|
|
|
ImGui::MarkIniSettingsDirty();
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
return setting->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
void options::SetSetting(const std::string& key, const std::string& value)
|
|
|
|
{
|
|
|
|
settings[key] = value;
|
|
|
|
if (ImGui::GetCurrentContext())
|
|
|
|
ImGui::MarkIniSettingsDirty();
|
|
|
|
}
|