#pragma once #include #include #include #include "Runtime/CSaveWorld.hpp" #include "Runtime/RetroTypes.hpp" #include "Runtime/Audio/CAudioSys.hpp" namespace urde { struct CFinalInput; class CStateManager; /** Options presented in UI */ enum class EGameOption { VisorOpacity, HelmetOpacity, HUDLag, HintSystem, ScreenBrightness, ScreenOffsetX, ScreenOffsetY, ScreenStretch, SFXVolume, MusicVolume, SoundMode, ReverseYAxis, Rumble, SwapBeamControls, RestoreDefaults }; /** Option UI type */ enum class EOptionType { Float, DoubleEnum, TripleEnum, RestoreDefaults }; /** Option UI presentation information */ struct SGameOption { EGameOption option; u32 stringId; float minVal, maxVal, increment; EOptionType type; }; /** Static registry of Option UI presentation information */ extern const std::array, 5> GameOptionsRegistry; extern const std::array, 5> GameOptionsRegistryNew; /** Options tracked persistently between game sessions */ class CPersistentOptions { friend class CGameState; std::array x0_nesState{}; std::array x68_{}; std::vector> xac_cinematicStates; /* (MLVL, Cinematic) */ u32 xbc_autoMapperKeyState = 0; u32 xc0_frozenFpsCount = 0; u32 xc4_frozenBallCount = 0; u32 xc8_powerBombAmmoCount = 0; u32 xcc_logScanPercent = 0; bool xd0_24_fusionLinked : 1; bool xd0_25_normalModeBeat : 1; bool xd0_26_hardModeBeat : 1; bool xd0_27_fusionBeat : 1; bool xd0_28_fusionSuitActive : 1; bool xd0_29_allItemsCollected : 1; public: CPersistentOptions() : xd0_24_fusionLinked(false) , xd0_25_normalModeBeat(false) , xd0_26_hardModeBeat(false) , xd0_27_fusionBeat(false) , xd0_28_fusionSuitActive(false) , xd0_29_allItemsCollected(false) {} explicit CPersistentOptions(CBitStreamReader& stream); bool GetCinematicState(CAssetId mlvlId, TEditorId cineId) const; void SetCinematicState(CAssetId mlvlId, TEditorId cineId, bool state); u32 GetAutoMapperKeyState() const { return xbc_autoMapperKeyState; } void SetAutoMapperKeyState(u32 s) { xbc_autoMapperKeyState = s; } bool GetPlayerLinkedFusion() const { return xd0_24_fusionLinked; } void SetPlayerLinkedFusion(bool v) { xd0_24_fusionLinked = v; } bool GetPlayerBeatNormalMode() const { return xd0_25_normalModeBeat; } void SetPlayerBeatNormalMode(bool v) { xd0_25_normalModeBeat = v; } bool GetPlayerBeatHardMode() const { return xd0_26_hardModeBeat; } void SetPlayerBeatHardMode(bool v) { xd0_26_hardModeBeat = v; } bool GetPlayerBeatFusion() const { return xd0_27_fusionBeat; } void SetPlayerBeatFusion(bool v) { xd0_27_fusionBeat = v; } bool GetPlayerFusionSuitActive() const { return xd0_28_fusionSuitActive; } void SetPlayerFusionSuitActive(bool v) { xd0_28_fusionSuitActive = v; } bool GetAllItemsCollected() const { return xd0_29_allItemsCollected; } void SetAllItemsCollected(bool v) { xd0_29_allItemsCollected = v; } u32 GetLogScanPercent() const { return xcc_logScanPercent; } void SetLogScanPercent(u32 v) { xcc_logScanPercent = v; } void IncrementFrozenFpsCount() { xc0_frozenFpsCount = std::min(int(xc0_frozenFpsCount + 1), 3); } bool GetShowFrozenFpsMessage() const { return xc0_frozenFpsCount != 3; } void IncrementFrozenBallCount() { xc4_frozenBallCount = std::min(int(xc4_frozenBallCount + 1), 3); } bool GetShowFrozenBallMessage() const { return xc4_frozenBallCount != 3; } bool GetShowPowerBombAmmoMessage() const { return xc8_powerBombAmmoCount != 1; } void IncrementPowerBombAmmoCount() { xc8_powerBombAmmoCount = std::min(1, xc8_powerBombAmmoCount + 1); } void PutTo(CBitStreamWriter& w) const; u8* GetNESState() { return x0_nesState.data(); } const u8* GetNESState() const { return x0_nesState.data(); } }; /** Options tracked per game session */ class CGameOptions { std::array x0_{}; CAudioSys::ESurroundModes x44_soundMode = CAudioSys::ESurroundModes::Stereo; u32 x48_screenBrightness = 4; s32 x4c_screenXOffset = 0; s32 x50_screenYOffset = 0; s32 x54_screenStretch = 0; u32 x58_sfxVol = 0x7f; u32 x5c_musicVol = 0x7f; u32 x60_hudAlpha = 0xff; u32 x64_helmetAlpha = 0xff; bool x68_24_hudLag : 1; bool x68_25_invertY : 1; bool x68_26_rumble : 1; bool x68_27_swapBeamsControls : 1; bool x68_28_hintSystem : 1; std::vector> x6c_controlTxtrMap; s32 m_gamma = 0; public: CGameOptions(); explicit CGameOptions(CBitStreamReader& stream); void ResetToDefaults(); void InitSoundMode(); void EnsureSettings(); void PutTo(CBitStreamWriter& writer) const; float TuneScreenBrightness(); void SetScreenBrightness(s32, bool); s32 GetScreenBrightness() const { return x48_screenBrightness; } void ApplyGamma(); void SetGamma(s32, bool); s32 GetGamma() const { return m_gamma; } void SetScreenPositionX(s32, bool); s32 GetScreenPositionX() const { return x4c_screenXOffset; } void SetScreenPositionY(s32, bool); s32 GetScreenPositionY() const { return x50_screenYOffset; } void SetScreenStretch(s32, bool); s32 GetScreenStretch() const { return x54_screenStretch; } void SetSfxVolume(s32, bool); s32 GetSfxVolume() const { return x58_sfxVol; } void SetMusicVolume(s32, bool); s32 GetMusicVolume() const { return x5c_musicVol; } void SetHUDAlpha(u32); u32 GetHUDAlpha() const { return x60_hudAlpha; } void SetHelmetAlpha(u32); u32 GetHelmetAlpha() const { return x64_helmetAlpha; } void SetHUDLag(bool); bool GetHUDLag() const { return x68_24_hudLag; } void SetSurroundMode(int mode, bool apply); CAudioSys::ESurroundModes GetSurroundMode() const; void SetInvertYAxis(bool); bool GetInvertYAxis() const { return x68_25_invertY; } void SetIsRumbleEnabled(bool); bool GetIsRumbleEnabled() const { return x68_26_rumble; } void SetSwapBeamControls(bool); bool GetSwapBeamControls() const { return x68_27_swapBeamsControls; } void SetIsHintSystemEnabled(bool); bool GetIsHintSystemEnabled() const { return x68_28_hintSystem; } void SetControls(int controls); void ResetControllerAssets(int controls); const std::vector>& GetControlTXTRMap() const { return x6c_controlTxtrMap; } static void TryRestoreDefaults(const CFinalInput& input, int category, int option, bool frontend, bool forceRestore); static void SetOption(EGameOption option, int value); static int GetOption(EGameOption option); }; class CHintOptions { public: enum class EHintState { Zero, Waiting, Displaying, Delayed }; struct SHintState { EHintState x0_state = EHintState::Zero; float x4_time = 0.f; bool x8_dismissed = false; SHintState() = default; SHintState(EHintState state, float time, bool flag) : x0_state(state), x4_time(time), x8_dismissed(flag) {} bool CanContinue() const { return x4_time / 3.f <= 1.f; } }; private: std::vector x0_hintStates; u32 x10_nextHintIdx = -1; public: CHintOptions() = default; explicit CHintOptions(CBitStreamReader& stream); void PutTo(CBitStreamWriter& writer) const; void SetNextHintTime(); void InitializeMemoryState(); const SHintState* GetCurrentDisplayedHint() const; void DelayHint(std::string_view name); void ActivateImmediateHintTimer(std::string_view name); void ActivateContinueDelayHintTimer(std::string_view name); void DismissDisplayedHint(); u32 GetNextHintIdx() const; const std::vector& GetHintStates() const { return x0_hintStates; } void Update(float dt, const CStateManager& stateMgr); }; } // namespace urde