#pragma once enum class Msg : int; enum class Menu1:int { New_Game = 101, About_Pinball = 102, High_Scores = 103, Exit = 105, Sounds = 201, Music = 202, SoundStereo = 203, Help_Topics = 301, Launch_Ball = 401, Pause_Resume_Game = 402, Full_Screen = 403, Demo = 404, Select_Table = 405, Player_Controls = 406, OnePlayer = 408, TwoPlayers = 409, ThreePlayers = 410, FourPlayers = 411, Show_Menu = 412, MaximumResolution = 500, R640x480 = 501, R800x600 = 502, R1024x768 = 503, WindowUniformScale = 600, WindowLinearFilter = 601, WindowIntegerScale = 602, Prefer3DPBGameData = 700, }; enum class InputTypes { None = 0, Keyboard, Mouse, GameController, }; struct GameInput { InputTypes Type; int Value; GameInput() : GameInput(InputTypes::None, -1) { } GameInput(InputTypes type, int value) : Type(type), Value(value) { } bool operator==(const GameInput& other) const { return Type == other.Type && Value == other.Value; } std::string GetFullInputDescription() const; std::string GetShortInputDescription() const; }; enum class GameBindings { Min = 0, LeftFlipper = 0, RightFlipper, Plunger, LeftTableBump, RightTableBump, BottomTableBump, NewGame, TogglePause, ToggleFullScreen, ToggleSounds, ToggleMusic, ShowControlDialog, ToggleMenuDisplay, Exit, Max }; inline GameBindings& operator++(GameBindings& value, int) { return value = static_cast(static_cast(value) + 1); } constexpr int operator~(const GameBindings& value) { return static_cast(value); } class options { public: // Original does ~120 updates per second. static constexpr int MaxUps = 360, MaxFps = MaxUps, MinUps = 60, MinFps = MinUps, DefUps = 120, DefFps = 60; // Original uses 8 sound channels static constexpr int MaxSoundChannels = 32, MinSoundChannels = 1, DefSoundChannels = 8; static constexpr int MaxVolume = MIX_MAX_VOLUME, MinVolume = 0, DefVolume = MaxVolume; static struct optionsStruct Options; static std::vector AllOptions; static void InitPrimary(); static void InitSecondary(); static void uninit(); static const std::string& GetSetting(const std::string& key, const std::string& defaultValue); static void SetSetting(const std::string& key, const std::string& value); static int get_int(LPCSTR lpValueName, int defaultValue); static void set_int(LPCSTR lpValueName, int data); static float get_float(LPCSTR lpValueName, float defaultValue); static void set_float(LPCSTR lpValueName, float data); static void GetInput(const std::string& rowName, GameInput (&values)[3]); static void SetInput(const std::string& rowName, GameInput (&values)[3]); static void toggle(Menu1 uIDCheckItem); static void InputDown(GameInput input); static void ShowControlDialog(); static void RenderControlDialog(); static bool WaitingForInput() { return ControlWaitingForInput; } static std::vector MapGameInput(GameInput key); static void ResetAllOptions(); private: static std::unordered_map settings; static bool ShowDialog; static GameInput* ControlWaitingForInput; 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_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf); static void PostProcessOptions(); }; struct OptionBase { LPCSTR Name; OptionBase(LPCSTR name); virtual ~OptionBase(); virtual void Load() = 0; virtual void Save() const = 0; virtual void Reset() = 0; }; template struct OptionBaseT : OptionBase { const T DefaultValue; T V; OptionBaseT(LPCSTR name, T defaultValue) : OptionBase(name), DefaultValue(std::move(defaultValue)), V() { } void Reset() override { V = DefaultValue; } operator T&() { return V; } OptionBaseT& operator=(const T& v) { V = v; return *this; } }; struct IntOption : OptionBaseT { IntOption(LPCSTR name, int defaultValue) : OptionBaseT(name, defaultValue) { } void Load() override { V = options::get_int(Name, DefaultValue); } void Save() const override { options::set_int(Name, V); } using OptionBaseT::operator=; }; struct StringOption : OptionBaseT { StringOption(LPCSTR name, std::string defaultValue) : OptionBaseT(name, std::move(defaultValue)) { } void Load() override { V = options::GetSetting(Name, DefaultValue); } void Save() const override { options::SetSetting(Name, V); } }; struct FloatOption : OptionBaseT { FloatOption(LPCSTR name, float defaultValue) : OptionBaseT(name, defaultValue) { } void Load() override { V = options::get_float(Name, DefaultValue); } void Save() const override { options::set_float(Name, V); } }; struct BoolOption : OptionBaseT { BoolOption(LPCSTR name, bool defaultValue) : OptionBaseT(name, defaultValue) { } void Load() override { V = options::get_int(Name, DefaultValue); } void Save() const override { options::set_int(Name, V); } using OptionBaseT::operator=; }; struct ControlOption : OptionBase { const Msg Description; GameInput Defaults[3]; GameInput Inputs[3]; ControlOption(LPCSTR name, Msg description, GameInput defaultKeyboard, GameInput defaultMouse, GameInput defaultController) : OptionBase(name), Description(description), Defaults{defaultKeyboard, defaultMouse, defaultController}, Inputs{defaultKeyboard, defaultMouse, defaultController} { } void Load() override { for (auto i = 0u; i <= 2; i++) { auto name = std::string{ Name } + " " + std::to_string(i); Inputs[i].Type = static_cast(options::get_int((name + " type").c_str(), static_cast(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(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)); } std::string GetShortcutDescription() const; }; struct optionsStruct { ControlOption Key[~GameBindings::Max]; BoolOption Sounds; BoolOption Music; BoolOption FullScreen; IntOption Players; IntOption Resolution; FloatOption UIScale; BoolOption UniformScaling; BoolOption LinearFiltering; IntOption FramesPerSecond; IntOption UpdatesPerSecond; BoolOption ShowMenu; BoolOption UncappedUpdatesPerSecond; IntOption SoundChannels; BoolOption HybridSleep; BoolOption Prefer3DPBGameData; BoolOption IntegerScaling; IntOption SoundVolume; IntOption MusicVolume; BoolOption SoundStereo; BoolOption DebugOverlay; BoolOption DebugOverlayGrid; BoolOption DebugOverlayAllEdges; BoolOption DebugOverlayBallPosition; BoolOption DebugOverlayBallEdges; BoolOption DebugOverlayCollisionMask; BoolOption DebugOverlaySprites; BoolOption DebugOverlaySounds; BoolOption DebugOverlayBallDepthGrid; BoolOption DebugOverlayAabb; StringOption FontFileName; StringOption Language; BoolOption HideCursor; };