diff --git a/Client/mods/deathmatch/logic/CClientGame.cpp b/Client/mods/deathmatch/logic/CClientGame.cpp
index dcb73b17389..7c65ef82c79 100644
--- a/Client/mods/deathmatch/logic/CClientGame.cpp
+++ b/Client/mods/deathmatch/logic/CClientGame.cpp
@@ -406,7 +406,7 @@ CClientGame::~CClientGame()
     m_bBeingDeleted = true;
     // Remove active projectile references to local player
     if (auto pLocalPlayer = g_pClientGame->GetLocalPlayer())
-        g_pGame->GetProjectileInfo()->RemoveEntityReferences(pLocalPlayer->GetGameEntity());    
+        g_pGame->GetProjectileInfo()->RemoveEntityReferences(pLocalPlayer->GetGameEntity());
 
     // Stop all explosions. Unfortunately this doesn't fix the crash
     // if a vehicle is destroyed while it explodes.
@@ -1026,7 +1026,7 @@ void CClientGame::DoPulsePostFrame()
                     }
 
                     auto taskManager = pLocalPlayer->GetTaskManager();
-                    auto task = taskManager->GetActiveTask();                    
+                    auto task = taskManager->GetActiveTask();
                     auto pVehicle = pLocalPlayer->GetOccupiedVehicle();
                     bool useZoneName = true;
 
@@ -1080,7 +1080,7 @@ void CClientGame::DoPulsePostFrame()
 
                             discordState = taskState.strState;
                             useZoneName = taskState.bUseZone;
-                        }                                       
+                        }
 
                         if (useZoneName)
                         {
@@ -6928,6 +6928,10 @@ void CClientGame::ResetWorldProperties(const ResetWorldPropsInfo& resetPropsInfo
     GetManager()->GetWaterManager()->ResetWorldWaterLevel();
     GetManager()->GetWaterManager()->SetWaveLevel(0.0f);
 
+    // Underwater effects
+    g_pMultiplayer->ResetUnderwaterDarkness();
+    g_pMultiplayer->ResetUnderwaterEffect();
+
     // Reset volumetric shadows
     g_pGame->GetSettings()->ResetVolumetricShadows();
 
diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaCameraDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaCameraDefs.cpp
index 2d9c268dc36..d6e2e7d36ad 100644
--- a/Client/mods/deathmatch/logic/luadefs/CLuaCameraDefs.cpp
+++ b/Client/mods/deathmatch/logic/luadefs/CLuaCameraDefs.cpp
@@ -29,6 +29,8 @@ void CLuaCameraDefs::LoadFunctions()
         {"getCameraGoggleEffect", ArgumentParserWarn<false, GetCameraGoggleEffect>},
         {"getCameraFieldOfView", GetCameraFieldOfView},
         {"getCameraDrunkLevel", ArgumentParserWarn<false, GetCameraDrunkLevel>},
+        {"getCameraUnderwaterEffect", ArgumentParser<GetCameraUnderwaterEffect>},
+        {"getCameraUnderwaterDarkness", ArgumentParser<GetCameraUnderwaterDarkness>},
 
         // Cam set funcs
         {"setCameraMatrix", SetCameraMatrix},
@@ -42,8 +44,15 @@ void CLuaCameraDefs::LoadFunctions()
         {"setCameraGoggleEffect", SetCameraGoggleEffect},
         {"setCameraDrunkLevel", ArgumentParserWarn<false, SetCameraDrunkLevel>},
 
+        {"setCameraUnderwaterEffectEnabled", ArgumentParser<SetCameraUnderwaterEffectEnabled>},
+        {"setCameraUnderwaterEffectSpeed", ArgumentParser<SetCameraUnderwaterEffectSpeed>},
+        {"setCameraUnderwaterDarkness", ArgumentParser<SetCameraUnderwaterDarkness>},
+        {"resetCameraUnderwaterEffect", ArgumentParser<ResetCameraUnderwaterEffect>},
+        {"resetCameraUnderwaterDarkness", ArgumentParser<ResetCameraUnderwaterDarkness>},
+
         {"shakeCamera", ArgumentParser<ShakeCamera>},
         {"resetShakeCamera", ArgumentParser<ResetShakeCamera>},
+
     };
 
     // Add functions
@@ -71,6 +80,8 @@ void CLuaCameraDefs::AddClass(lua_State* luaVM)
     lua_classfunction(luaVM, "getFarClipDistance", "getFarClipDistance");
     lua_classfunction(luaVM, "getNearClipDistance", "getNearClipDistance");
     lua_classfunction(luaVM, "getType", ArgumentParser<GetElementType>);
+    lua_classfunction(luaVM, "getUnderwaterEffect", ArgumentParser<GetCameraUnderwaterEffect>);
+    lua_classfunction(luaVM, "getUnderwaterDarkness", ArgumentParser<GetCameraUnderwaterDarkness>);
 
     lua_classfunction(luaVM, "setPosition", OOP_SetCameraPosition);
     lua_classfunction(luaVM, "setRotation", OOP_SetCameraRotation);
@@ -83,6 +94,11 @@ void CLuaCameraDefs::AddClass(lua_State* luaVM)
     lua_classfunction(luaVM, "setClip", "setCameraClip");
     lua_classfunction(luaVM, "setFarClipDistance", "setFarClipDistance");
     lua_classfunction(luaVM, "setNearClipDistance", "setNearClipDistance");
+    lua_classfunction(luaVM, "setUnderwaterEffectEnabled", ArgumentParser<SetCameraUnderwaterEffectEnabled>);
+    lua_classfunction(luaVM, "setUnderwaterEffectSpeed", ArgumentParser<SetCameraUnderwaterEffectSpeed>);
+    lua_classfunction(luaVM, "setUnderwaterDarkness", ArgumentParser<SetCameraUnderwaterDarkness>);
+    lua_classfunction(luaVM, "resetUnderwaterEffect", ArgumentParser<ResetCameraUnderwaterEffect>);
+    lua_classfunction(luaVM, "resetUnderwaterDarkness", ArgumentParser<ResetCameraUnderwaterDarkness>);
 
     lua_classvariable(luaVM, "interior", "setCameraInterior", "getCameraInterior");
     lua_classvariable(luaVM, "target", "setCameraTarget", "getCameraTarget");
@@ -165,6 +181,22 @@ unsigned char CLuaCameraDefs::GetCameraDrunkLevel()
     return g_pGame->GetPlayerInfo()->GetCamDrunkLevel();
 }
 
+CLuaMultiReturn<bool, float, float> CLuaCameraDefs::GetCameraUnderwaterEffect() noexcept
+{
+    bool isEnabled;
+    float speed, frequency;
+    g_pMultiplayer->GetUnderwaterEffect(isEnabled, speed, frequency);
+    return {isEnabled, speed, frequency};
+}
+
+CLuaMultiReturn<bool, float> CLuaCameraDefs::GetCameraUnderwaterDarkness() noexcept
+{
+    bool isEnabled;
+    float fullDarknessDepth;
+    g_pMultiplayer->GetUnderwaterDarkness(isEnabled, fullDarknessDepth);
+    return {isEnabled, fullDarknessDepth};
+}
+
 int CLuaCameraDefs::SetCameraMatrix(lua_State* luaVM)
 {
     CVector          vecPosition;
@@ -467,6 +499,41 @@ int CLuaCameraDefs::SetCameraGoggleEffect(lua_State* luaVM)
     return 1;
 }
 
+bool CLuaCameraDefs::SetCameraUnderwaterEffectEnabled(bool bEnabled) noexcept
+{
+    g_pMultiplayer->SetUnderwaterEffectEnabled(bEnabled);
+
+    return true;
+}
+
+bool CLuaCameraDefs::SetCameraUnderwaterEffectSpeed(float fSpeed, std::optional<float> fFrequency)
+{
+    g_pMultiplayer->SetUnderwaterEffectSpeed(fSpeed, fFrequency.value_or(0.04f));
+
+    return true;
+}
+
+bool CLuaCameraDefs::SetCameraUnderwaterDarkness(bool bEnabled, std::optional<float> fFullDarknessDepth)
+{
+    g_pMultiplayer->SetUnderwaterDarkness(bEnabled, fFullDarknessDepth.value_or(90.0f));
+
+    return true;
+}
+
+bool CLuaCameraDefs::ResetCameraUnderwaterEffect() noexcept
+{
+    g_pMultiplayer->ResetUnderwaterEffect();
+
+    return true;
+}
+
+bool CLuaCameraDefs::ResetCameraUnderwaterDarkness() noexcept
+{
+    g_pMultiplayer->ResetUnderwaterDarkness();
+
+    return true;
+}
+
 bool CLuaCameraDefs::SetCameraDrunkLevel(short drunkLevel)
 {
     if (drunkLevel < 0 || drunkLevel > 255)
@@ -567,4 +634,4 @@ bool CLuaCameraDefs::ResetShakeCamera() noexcept
 {
     m_pManager->GetCamera()->ResetShakeCamera();
     return true;
-}
\ No newline at end of file
+}
diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaCameraDefs.h b/Client/mods/deathmatch/logic/luadefs/CLuaCameraDefs.h
index b233d4e39c8..946b7209f63 100644
--- a/Client/mods/deathmatch/logic/luadefs/CLuaCameraDefs.h
+++ b/Client/mods/deathmatch/logic/luadefs/CLuaCameraDefs.h
@@ -31,6 +31,8 @@ class CLuaCameraDefs : public CLuaDefs
     static std::string                                                             GetCameraGoggleEffect();
     LUA_DECLARE(GetCameraFieldOfView);
     static unsigned char GetCameraDrunkLevel();
+    static CLuaMultiReturn<bool, float, float> GetCameraUnderwaterEffect() noexcept;
+    static CLuaMultiReturn<bool, float>        GetCameraUnderwaterDarkness() noexcept;
 
     // Cam set funcs
     LUA_DECLARE(SetCameraMatrix);
@@ -43,10 +45,17 @@ class CLuaCameraDefs : public CLuaDefs
     LUA_DECLARE(SetCameraGoggleEffect);
     static bool SetCameraDrunkLevel(short drunkLevel);
 
+    static bool SetCameraUnderwaterEffectEnabled(bool isEnabled) noexcept;
+    static bool SetCameraUnderwaterEffectSpeed(float speed, float frequency);
+    static bool SetCameraUnderwaterDarkness(bool isEnabled, std::optional<float> fullDarknessDepth);
+    static bool ResetCameraUnderwaterEffect() noexcept;
+    static bool ResetCameraUnderwaterDarkness() noexcept;
+    
     // Cam do funcs
     static bool ShakeCamera(float radius, std::optional<float> x, std::optional<float> y, std::optional<float> z) noexcept;
     static bool ResetShakeCamera() noexcept;
 
+
     // For OOP only
     LUA_DECLARE(OOP_GetCameraPosition);
     LUA_DECLARE(OOP_SetCameraPosition);
diff --git a/Client/multiplayer_sa/CMultiplayerSA.h b/Client/multiplayer_sa/CMultiplayerSA.h
index 0dd642cb4b4..23739358bfb 100644
--- a/Client/multiplayer_sa/CMultiplayerSA.h
+++ b/Client/multiplayer_sa/CMultiplayerSA.h
@@ -255,6 +255,14 @@ class CMultiplayerSA : public CMultiplayer
     bool IsNightVisionEnabled();
     bool IsThermalVisionEnabled();
 
+    void GetUnderwaterEffect(bool& isEnabled, float& speed, float& frequency) const noexcept;
+    void GetUnderwaterDarkness(bool& isEnabled, float& fullDarknessDepth) const noexcept;
+    void SetUnderwaterEffectEnabled(bool isEnabled) const noexcept override;
+    void SetUnderwaterEffectSpeed(float speed, float frequency) const noexcept override;
+    void SetUnderwaterDarkness(bool isEnabled, float fullDarknessDepth) override;
+    void ResetUnderwaterEffect() noexcept override;
+    void ResetUnderwaterDarkness() noexcept override;
+
     void AllowWindowsCursorShowing(bool bAllow);
 
     CShotSyncData* GetLocalShotSyncData();
diff --git a/Client/multiplayer_sa/CMultiplayerSA_Postprocess.cpp b/Client/multiplayer_sa/CMultiplayerSA_Postprocess.cpp
index 79491f3ad9a..05381c05677 100644
--- a/Client/multiplayer_sa/CMultiplayerSA_Postprocess.cpp
+++ b/Client/multiplayer_sa/CMultiplayerSA_Postprocess.cpp
@@ -15,6 +15,18 @@
 #define HOOKPOS_GrainEffect_RainModifier 0x705078
 #define HOOKPOS_GrainEffect_OverlayModifier 0x705091
 
+#define VAR_CPostEffects_WaterEnable 0xC402D3
+#define VAR_CPostEffects_WaterSpeed 0x8D5138
+#define VAR_CPostEffects_WaterFreq 0x8D513C
+#define VAR_CPostEffects_WaterDepthDarkessEnabled 0x8D5144
+#define VAR_CPostEffects_WaterFullDarknessDepth 0x8D5148
+#define VAR_CPostEffects_WaterFxStartUnderWaterness 0x8D514C
+#define VAR_CWeather_UnderWaterness 0xC8132C
+
+#define DEFAULT_UNDERWATER_EFFECT_SPEED ( 0.0015f )
+#define DEFAULT_UNDERWATER_EFFECT_FREQUENCY ( 0.04f )
+#define DEFAULT_UNDERWATER_FULL_DARKNESS_DEPTH ( 90.0f )
+
 namespace GrainEffect
 {
 
@@ -119,7 +131,7 @@ void CMultiplayerSA::SetGrainLevel(BYTE ucLevel)
     GrainEffect::dwGrainStrength = static_cast<DWORD>(ucLevel);
 
     if (bEnable == bOverridden)
-        return;    
+        return;
 
     if (bEnable)
     {
@@ -154,6 +166,35 @@ void CMultiplayerSA::SetNightVisionEnabled(bool bEnabled, bool bNoiseEnabled)
     }
 }
 
+void CMultiplayerSA::SetUnderwaterEffectEnabled(bool isEnabled) const noexcept
+{
+    MemPutFast<std::uint8_t>(VAR_CPostEffects_WaterEnable, isEnabled ?  1 : 0);
+}
+
+void CMultiplayerSA::SetUnderwaterEffectSpeed(float speed, float frequency) const noexcept
+{
+    MemPutFast<float>(VAR_CPostEffects_WaterSpeed, speed);
+    MemPutFast<float>(VAR_CPostEffects_WaterFreq, frequency);
+}
+
+void CMultiplayerSA::SetUnderwaterDarkness(bool isEnabled, float fullDarknessDepth)
+{
+    MemPutFast<std::uint8_t>(VAR_CPostEffects_WaterDepthDarkessEnabled, isEnabled ? 1 : 0);
+
+    MemPutFast<float>(VAR_CPostEffects_WaterFullDarknessDepth, fullDarknessDepth);
+}
+
+void CMultiplayerSA::ResetUnderwaterEffect() noexcept
+{
+    this->SetUnderwaterEffectEnabled(false);
+    this->SetUnderwaterEffectSpeed(DEFAULT_UNDERWATER_EFFECT_SPEED, DEFAULT_UNDERWATER_EFFECT_FREQUENCY);
+}
+
+void CMultiplayerSA::ResetUnderwaterDarkness() noexcept
+{
+    this->SetUnderwaterDarkness(false, DEFAULT_UNDERWATER_FULL_DARKNESS_DEPTH);
+}
+
 void CMultiplayerSA::SetThermalVisionEnabled(bool bEnabled, bool bNoiseEnabled)
 {
     if (bEnabled)
@@ -184,6 +225,19 @@ bool CMultiplayerSA::IsThermalVisionEnabled()
     return (*(BYTE*)0xC402B9 == 1);
 }
 
+void CMultiplayerSA::GetUnderwaterEffect(bool& isEnabled, float& speed, float& frequency) const noexcept
+{
+    isEnabled = (*(uint8_t*)VAR_CPostEffects_WaterEnable == 1) || (*(float*)VAR_CWeather_UnderWaterness) >= (*(float*)VAR_CPostEffects_WaterFxStartUnderWaterness);
+    speed  = (*(float*)VAR_CPostEffects_WaterSpeed);
+    frequency = (*(float*)VAR_CPostEffects_WaterFreq);
+}
+
+void CMultiplayerSA::GetUnderwaterDarkness(bool& isEnabled, float& fullDarknessDepth) const noexcept
+{
+    isEnabled = (*(std::uint8_t*)VAR_CPostEffects_WaterDepthDarkessEnabled);
+    fullDarknessDepth = (*(float*)VAR_CPostEffects_WaterFullDarknessDepth);
+}
+
 void CMultiplayerSA::InitHooks_Postprocess()
 {
     HookInstallCall(HOOKPOS_GrainEffect_NightModifier, (DWORD)GrainEffect::NightModifier::ApplyEffect);
diff --git a/Client/sdk/multiplayer/CMultiplayer.h b/Client/sdk/multiplayer/CMultiplayer.h
index 33e349ea71a..c66e1470390 100644
--- a/Client/sdk/multiplayer/CMultiplayer.h
+++ b/Client/sdk/multiplayer/CMultiplayer.h
@@ -462,4 +462,12 @@ class CMultiplayer
     virtual unsigned int EntryInfoNodePool_NoOfUsedSpaces() const noexcept = 0;
     virtual unsigned int PtrNodeSingleLinkPool_NoOfUsedSpaces() const noexcept = 0;
     virtual unsigned int PtrNodeDoubleLinkPool_NoOfUsedSpaces() const noexcept = 0;
+
+    virtual void GetUnderwaterEffect(bool& isEnabled, float& speed, float& frequency) = 0;
+    virtual void GetUnderwaterDarkness(bool& isEnabled, float& fullDarknessDepth) = 0;
+    virtual void SetUnderwaterEffectEnabled(bool isEnabled) = 0;
+    virtual void SetUnderwaterEffectSpeed(float speed, float frequency) = 0;
+    virtual void SetUnderwaterDarkness(bool isEnabled, float fullDarknessDepth) = 0;
+    virtual void ResetUnderwaterEffect() = 0;
+    virtual void ResetUnderwaterDarkness() = 0;
 };