From 49aa0febed16d92887733c4af94d8688d17a526a Mon Sep 17 00:00:00 2001 From: TheCrazy17 Date: Sun, 30 Aug 2026 08:54:42 -0300 Subject: [PATCH 1/3] Remember the camera view modes across sessions GTA only stores the view modes the change camera key cycles through in its save files, which MTA never uses, so they reset every session. Restore them from the client settings on startup and store them back whenever they change. --- .../mods/deathmatch/logic/CClientCamera.cpp | 64 +++++++++++++++++++ Client/mods/deathmatch/logic/CClientCamera.h | 6 ++ 2 files changed, 70 insertions(+) diff --git a/Client/mods/deathmatch/logic/CClientCamera.cpp b/Client/mods/deathmatch/logic/CClientCamera.cpp index e54dc094c54..293d09ec958 100644 --- a/Client/mods/deathmatch/logic/CClientCamera.cpp +++ b/Client/mods/deathmatch/logic/CClientCamera.cpp @@ -143,6 +143,68 @@ CClientCamera::CClientCamera(CClientManager* pManager) : ClassInit(this), CClien m_hasCenterOfWorld = false; } +// GTA only remembers the view modes the change camera key cycles through in its save files, which +// MTA never uses, so they reset every session; keep them in the client settings instead. +void CClientCamera::PersistViewModes() +{ + if (!m_bViewModesRestored) + { + // The camera is reinitialised while the game world comes up; restoring any earlier would + // get overwritten and the change tracker below would persist the defaults back + if (!g_pGame || g_pGame->GetSystemState() != SystemState::GS_PLAYING_GAME) + return; + + m_bViewModesRestored = true; + + // Read as text and validate strictly; the settings file is hand editable, and a lenient + // numeric read would turn garbage or an empty value into mode zero + const auto GetStoredViewMode = [](const char* szName, int iMinMode, int iMaxMode) + { + std::string strValue; + g_pCore->GetCVars()->Get(szName, strValue); + if (strValue.size() != 1 || strValue[0] < '0' || strValue[0] > '9') + return -1; + + const int iValue = strValue[0] - '0'; + return (iValue >= iMinMode && iValue <= iMaxMode) ? iValue : -1; + }; + + const int iVehicleViewMode = + GetStoredViewMode("camera_vehicle_view", static_cast(eVehicleCamMode::BUMPER), static_cast(eVehicleCamMode::CINEMATIC)); + // The native ped view modes run from 1 to 3; ePedCamMode does not match them + const int iPedViewMode = GetStoredViewMode("camera_ped_view", 1, 3); + + if (iVehicleViewMode >= 0) + SetCameraVehicleViewMode(static_cast(iVehicleViewMode)); + if (iPedViewMode >= 0) + SetCameraPedViewMode(static_cast(iPedViewMode)); + + m_ucLastVehicleViewMode = static_cast(GetCameraVehicleViewMode()); + m_ucLastPedViewMode = static_cast(GetCameraPedViewMode()); + + // Heal missing or mangled entries and flush right away; waiting for the quit save + // would lose them if the game does not close cleanly + if (iVehicleViewMode != m_ucLastVehicleViewMode || iPedViewMode != m_ucLastPedViewMode) + { + g_pCore->GetCVars()->Set("camera_vehicle_view", static_cast(m_ucLastVehicleViewMode)); + g_pCore->GetCVars()->Set("camera_ped_view", static_cast(m_ucLastPedViewMode)); + g_pCore->SaveConfig(); + } + return; + } + + const unsigned char ucVehicleViewMode = static_cast(GetCameraVehicleViewMode()); + const unsigned char ucPedViewMode = static_cast(GetCameraPedViewMode()); + if (ucVehicleViewMode == m_ucLastVehicleViewMode && ucPedViewMode == m_ucLastPedViewMode) + return; + + m_ucLastVehicleViewMode = ucVehicleViewMode; + m_ucLastPedViewMode = ucPedViewMode; + g_pCore->GetCVars()->Set("camera_vehicle_view", static_cast(ucVehicleViewMode)); + g_pCore->GetCVars()->Set("camera_ped_view", static_cast(ucPedViewMode)); + g_pCore->SaveConfig(); +} + CClientCamera::~CClientCamera() { // We need to be ingame @@ -156,6 +218,8 @@ CClientCamera::~CClientCamera() void CClientCamera::DoPulse() { + PersistViewModes(); + InvalidateCachedTransforms(); // If we're fixed, force the target vector diff --git a/Client/mods/deathmatch/logic/CClientCamera.h b/Client/mods/deathmatch/logic/CClientCamera.h index 0259e92c6ec..acb2c133094 100644 --- a/Client/mods/deathmatch/logic/CClientCamera.h +++ b/Client/mods/deathmatch/logic/CClientCamera.h @@ -117,6 +117,8 @@ class CClientCamera final : public CClientEntity void InvalidateEntity(CClientEntity* pEntity); void RestoreEntity(CClientEntity* pEntity); + void PersistViewModes(); + CClientPlayerManager* m_pPlayerManager; CClientPlayerPtr m_pFocusedPlayer; @@ -139,5 +141,9 @@ class CClientCamera final : public CClientEntity float m_lastCenterOfWorldRot; bool m_hasCenterOfWorld; + bool m_bViewModesRestored{false}; + unsigned char m_ucLastVehicleViewMode{0}; + unsigned char m_ucLastPedViewMode{0}; + CCamera* m_pCamera; }; From 1bd20adf94e713ca01870e6e5589cdb28b181e52 Mon Sep 17 00:00:00 2001 From: TheCrazy17 Date: Wed, 9 Sep 2026 21:01:43 -0300 Subject: [PATCH 2/3] Clean up and modernize view mode persistence --- .../mods/deathmatch/logic/CClientCamera.cpp | 48 ++++++++++--------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/Client/mods/deathmatch/logic/CClientCamera.cpp b/Client/mods/deathmatch/logic/CClientCamera.cpp index 293d09ec958..5faa03bf9fc 100644 --- a/Client/mods/deathmatch/logic/CClientCamera.cpp +++ b/Client/mods/deathmatch/logic/CClientCamera.cpp @@ -11,7 +11,9 @@ #include #include #include +#include #include +#include #include #define PI_2 6.283185307179586476925286766559f @@ -158,33 +160,35 @@ void CClientCamera::PersistViewModes() // Read as text and validate strictly; the settings file is hand editable, and a lenient // numeric read would turn garbage or an empty value into mode zero - const auto GetStoredViewMode = [](const char* szName, int iMinMode, int iMaxMode) + const auto GetStoredViewMode = [](const char* name, std::uint8_t minMode, std::uint8_t maxMode) -> std::optional { - std::string strValue; - g_pCore->GetCVars()->Get(szName, strValue); - if (strValue.size() != 1 || strValue[0] < '0' || strValue[0] > '9') - return -1; + std::string value; + g_pCore->GetCVars()->Get(name, value); - const int iValue = strValue[0] - '0'; - return (iValue >= iMinMode && iValue <= iMaxMode) ? iValue : -1; + std::uint8_t parsedMode = 0; + const auto [ptr, ec] = std::from_chars(value.data(), value.data() + value.size(), parsedMode); + if (ec != std::errc{} || ptr != value.data() + value.size()) + return std::nullopt; + + return (parsedMode >= minMode && parsedMode <= maxMode) ? std::optional{parsedMode} : std::nullopt; }; - const int iVehicleViewMode = - GetStoredViewMode("camera_vehicle_view", static_cast(eVehicleCamMode::BUMPER), static_cast(eVehicleCamMode::CINEMATIC)); + const std::optional vehicleViewMode = + GetStoredViewMode("camera_vehicle_view", static_cast(eVehicleCamMode::BUMPER), static_cast(eVehicleCamMode::CINEMATIC)); // The native ped view modes run from 1 to 3; ePedCamMode does not match them - const int iPedViewMode = GetStoredViewMode("camera_ped_view", 1, 3); + const std::optional pedViewMode = GetStoredViewMode("camera_ped_view", 1, 3); - if (iVehicleViewMode >= 0) - SetCameraVehicleViewMode(static_cast(iVehicleViewMode)); - if (iPedViewMode >= 0) - SetCameraPedViewMode(static_cast(iPedViewMode)); + if (vehicleViewMode) + SetCameraVehicleViewMode(static_cast(*vehicleViewMode)); + if (pedViewMode) + SetCameraPedViewMode(static_cast(*pedViewMode)); m_ucLastVehicleViewMode = static_cast(GetCameraVehicleViewMode()); m_ucLastPedViewMode = static_cast(GetCameraPedViewMode()); // Heal missing or mangled entries and flush right away; waiting for the quit save // would lose them if the game does not close cleanly - if (iVehicleViewMode != m_ucLastVehicleViewMode || iPedViewMode != m_ucLastPedViewMode) + if (vehicleViewMode != m_ucLastVehicleViewMode || pedViewMode != m_ucLastPedViewMode) { g_pCore->GetCVars()->Set("camera_vehicle_view", static_cast(m_ucLastVehicleViewMode)); g_pCore->GetCVars()->Set("camera_ped_view", static_cast(m_ucLastPedViewMode)); @@ -193,15 +197,15 @@ void CClientCamera::PersistViewModes() return; } - const unsigned char ucVehicleViewMode = static_cast(GetCameraVehicleViewMode()); - const unsigned char ucPedViewMode = static_cast(GetCameraPedViewMode()); - if (ucVehicleViewMode == m_ucLastVehicleViewMode && ucPedViewMode == m_ucLastPedViewMode) + const unsigned char vehicleViewMode = static_cast(GetCameraVehicleViewMode()); + const unsigned char pedViewMode = static_cast(GetCameraPedViewMode()); + if (vehicleViewMode == m_ucLastVehicleViewMode && pedViewMode == m_ucLastPedViewMode) return; - m_ucLastVehicleViewMode = ucVehicleViewMode; - m_ucLastPedViewMode = ucPedViewMode; - g_pCore->GetCVars()->Set("camera_vehicle_view", static_cast(ucVehicleViewMode)); - g_pCore->GetCVars()->Set("camera_ped_view", static_cast(ucPedViewMode)); + m_ucLastVehicleViewMode = vehicleViewMode; + m_ucLastPedViewMode = pedViewMode; + g_pCore->GetCVars()->Set("camera_vehicle_view", static_cast(vehicleViewMode)); + g_pCore->GetCVars()->Set("camera_ped_view", static_cast(pedViewMode)); g_pCore->SaveConfig(); } From 22c8dc446d544e1be1a625dbb41006f8e45a0d76 Mon Sep 17 00:00:00 2001 From: TheCrazy17 Date: Thu, 10 Sep 2026 22:20:52 -0300 Subject: [PATCH 3/3] Drop Hungarian notation on the view mode members m_bViewModesRestored, m_ucLastVehicleViewMode and m_ucLastPedViewMode lose their type prefixes, and the two view mode members become std::uint8_t; every local and cast that feeds or compares against them follows suit so the function isn't half unsigned char. --- .../mods/deathmatch/logic/CClientCamera.cpp | 24 +++++++++---------- Client/mods/deathmatch/logic/CClientCamera.h | 6 ++--- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Client/mods/deathmatch/logic/CClientCamera.cpp b/Client/mods/deathmatch/logic/CClientCamera.cpp index 5faa03bf9fc..58692f67ffb 100644 --- a/Client/mods/deathmatch/logic/CClientCamera.cpp +++ b/Client/mods/deathmatch/logic/CClientCamera.cpp @@ -149,14 +149,14 @@ CClientCamera::CClientCamera(CClientManager* pManager) : ClassInit(this), CClien // MTA never uses, so they reset every session; keep them in the client settings instead. void CClientCamera::PersistViewModes() { - if (!m_bViewModesRestored) + if (!m_viewModesRestored) { // The camera is reinitialised while the game world comes up; restoring any earlier would // get overwritten and the change tracker below would persist the defaults back if (!g_pGame || g_pGame->GetSystemState() != SystemState::GS_PLAYING_GAME) return; - m_bViewModesRestored = true; + m_viewModesRestored = true; // Read as text and validate strictly; the settings file is hand editable, and a lenient // numeric read would turn garbage or an empty value into mode zero @@ -183,27 +183,27 @@ void CClientCamera::PersistViewModes() if (pedViewMode) SetCameraPedViewMode(static_cast(*pedViewMode)); - m_ucLastVehicleViewMode = static_cast(GetCameraVehicleViewMode()); - m_ucLastPedViewMode = static_cast(GetCameraPedViewMode()); + m_lastVehicleViewMode = static_cast(GetCameraVehicleViewMode()); + m_lastPedViewMode = static_cast(GetCameraPedViewMode()); // Heal missing or mangled entries and flush right away; waiting for the quit save // would lose them if the game does not close cleanly - if (vehicleViewMode != m_ucLastVehicleViewMode || pedViewMode != m_ucLastPedViewMode) + if (vehicleViewMode != m_lastVehicleViewMode || pedViewMode != m_lastPedViewMode) { - g_pCore->GetCVars()->Set("camera_vehicle_view", static_cast(m_ucLastVehicleViewMode)); - g_pCore->GetCVars()->Set("camera_ped_view", static_cast(m_ucLastPedViewMode)); + g_pCore->GetCVars()->Set("camera_vehicle_view", static_cast(m_lastVehicleViewMode)); + g_pCore->GetCVars()->Set("camera_ped_view", static_cast(m_lastPedViewMode)); g_pCore->SaveConfig(); } return; } - const unsigned char vehicleViewMode = static_cast(GetCameraVehicleViewMode()); - const unsigned char pedViewMode = static_cast(GetCameraPedViewMode()); - if (vehicleViewMode == m_ucLastVehicleViewMode && pedViewMode == m_ucLastPedViewMode) + const std::uint8_t vehicleViewMode = static_cast(GetCameraVehicleViewMode()); + const std::uint8_t pedViewMode = static_cast(GetCameraPedViewMode()); + if (vehicleViewMode == m_lastVehicleViewMode && pedViewMode == m_lastPedViewMode) return; - m_ucLastVehicleViewMode = vehicleViewMode; - m_ucLastPedViewMode = pedViewMode; + m_lastVehicleViewMode = vehicleViewMode; + m_lastPedViewMode = pedViewMode; g_pCore->GetCVars()->Set("camera_vehicle_view", static_cast(vehicleViewMode)); g_pCore->GetCVars()->Set("camera_ped_view", static_cast(pedViewMode)); g_pCore->SaveConfig(); diff --git a/Client/mods/deathmatch/logic/CClientCamera.h b/Client/mods/deathmatch/logic/CClientCamera.h index acb2c133094..76c306f25c5 100644 --- a/Client/mods/deathmatch/logic/CClientCamera.h +++ b/Client/mods/deathmatch/logic/CClientCamera.h @@ -141,9 +141,9 @@ class CClientCamera final : public CClientEntity float m_lastCenterOfWorldRot; bool m_hasCenterOfWorld; - bool m_bViewModesRestored{false}; - unsigned char m_ucLastVehicleViewMode{0}; - unsigned char m_ucLastPedViewMode{0}; + bool m_viewModesRestored{false}; + std::uint8_t m_lastVehicleViewMode{0}; + std::uint8_t m_lastPedViewMode{0}; CCamera* m_pCamera; };