Decompiled source of SwimmingReworked v1.0.7

SwimmingReworked.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn.Entities;
using Jotunn.Extensions;
using Jotunn.Managers;
using Jotunn.Utils;
using SwimmingReworked.Effects;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("SwimmingReworked")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("SwimmingReworked")]
[assembly: AssemblyCopyright("Copyright ©  2021")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")]
[assembly: AssemblyFileVersion("1.0.6")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.6.0")]
namespace SwimmingReworked
{
	internal class BreathHud : MonoBehaviour
	{
		private GameObject _root;

		private RectTransform _container;

		private readonly List<Image> _bubbles = new List<Image>();

		private Sprite _bubbleSprite;

		private Text _breathText;

		private const int BubbleCount = 10;

		private float _cachedOffsetX;

		private float _cachedOffsetY;

		private float _cachedScale;

		private bool _cachedShowLabels;

		private void Awake()
		{
			CreateHud();
		}

		private void Update()
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)Camera.main == (Object)null)
			{
				SetVisible(visible: false);
				return;
			}
			CheckConfigChanges();
			float currentBreath = SwimAPI.GetCurrentBreath(localPlayer);
			float maxBreath = SwimAPI.GetMaxBreath(localPlayer);
			float percent = Mathf.Clamp01(SwimAPI.GetBreathPercent(localPlayer));
			bool flag = WaterHelper.IsBodyInWater(localPlayer) || currentBreath < maxBreath - 0.01f;
			SetVisible(flag);
			if (flag)
			{
				UpdateBubbles(percent);
				if ((Object)(object)_breathText != (Object)null && ((Component)_breathText).gameObject.activeSelf)
				{
					_breathText.text = $"{currentBreath:F0}/{maxBreath:F0}";
				}
			}
		}

		private void CheckConfigChanges()
		{
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)SwimmingReworked.Instance == (Object)null)
			{
				return;
			}
			float value = SwimmingReworked.BreathHudOffsetX.Value;
			float value2 = SwimmingReworked.BreathHudOffsetY.Value;
			float value3 = SwimmingReworked.BreathHudScale.Value;
			bool value4 = SwimmingReworked.BreathHudShowLabels.Value;
			if (!Mathf.Approximately(_cachedOffsetX, value) || !Mathf.Approximately(_cachedOffsetY, value2))
			{
				UpdateContainerPosition(value, value2);
				_cachedOffsetX = value;
				_cachedOffsetY = value2;
			}
			if (!Mathf.Approximately(_cachedScale, value3))
			{
				((Transform)_container).localScale = Vector3.one * value3;
				_cachedScale = value3;
			}
			if (_cachedShowLabels != value4)
			{
				if ((Object)(object)_breathText != (Object)null)
				{
					((Component)_breathText).gameObject.SetActive(value4);
				}
				_cachedShowLabels = value4;
			}
		}

		private void UpdateContainerPosition(float offsetX, float offsetY)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			_container.anchorMin = new Vector2(1f, 0.5f);
			_container.anchorMax = new Vector2(1f, 0.5f);
			_container.pivot = new Vector2(1f, 0.5f);
			_container.anchoredPosition = new Vector2(offsetX, offsetY);
		}

		private void UpdateBubbles(float percent)
		{
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			int num = Mathf.CeilToInt(percent * 10f);
			for (int i = 0; i < _bubbles.Count; i++)
			{
				Image val = _bubbles[i];
				if (((Behaviour)val).enabled = i < num)
				{
					float num2 = (float)i / 9f;
					float num3 = -60f + num2 * 120f + Mathf.PingPong(Time.time * 8f + (float)i * 0.5f, 15f);
					float num4 = Mathf.Sin(Time.time * 2f + (float)i) * 5f;
					RectTransform rectTransform = ((Graphic)val).rectTransform;
					rectTransform.anchoredPosition = new Vector2(num4, num3);
					float num5 = 0.45f + 0.45f * Mathf.PingPong(Time.time * 1.2f + (float)i * 0.3f, 1f);
					((Graphic)val).color = new Color(0.8f, 0.92f, 1f, num5);
				}
			}
		}

		private void SetVisible(bool visible)
		{
			if ((Object)(object)_root != (Object)null && _root.activeSelf != visible)
			{
				_root.SetActive(visible);
			}
		}

		private void CreateHud()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Expected O, but got Unknown
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Expected O, but got Unknown
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ed: Expected O, but got Unknown
			//IL_0259: Unknown result type (might be due to invalid IL or missing references)
			//IL_027d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0294: Unknown result type (might be due to invalid IL or missing references)
			_root = new GameObject("BreathHud");
			Object.DontDestroyOnLoad((Object)(object)_root);
			Canvas val = _root.AddComponent<Canvas>();
			val.renderMode = (RenderMode)0;
			val.sortingOrder = 5000;
			CanvasScaler val2 = _root.AddComponent<CanvasScaler>();
			val2.uiScaleMode = (ScaleMode)1;
			val2.referenceResolution = new Vector2(1920f, 1080f);
			_root.AddComponent<GraphicRaycaster>();
			GameObject val3 = new GameObject("Container");
			val3.transform.SetParent(_root.transform, false);
			_container = val3.AddComponent<RectTransform>();
			_container.sizeDelta = new Vector2(80f, 200f);
			float value = SwimmingReworked.BreathHudOffsetX.Value;
			float value2 = SwimmingReworked.BreathHudOffsetY.Value;
			float value3 = SwimmingReworked.BreathHudScale.Value;
			UpdateContainerPosition(value, value2);
			((Transform)_container).localScale = Vector3.one * value3;
			_cachedOffsetX = value;
			_cachedOffsetY = value2;
			_cachedScale = value3;
			_bubbleSprite = CreateBubbleSprite(64);
			for (int i = 0; i < 10; i++)
			{
				GameObject val4 = new GameObject($"Bubble_{i}");
				val4.transform.SetParent((Transform)(object)_container, false);
				RectTransform val5 = val4.AddComponent<RectTransform>();
				val5.sizeDelta = new Vector2(12f, 12f);
				Image val6 = val4.AddComponent<Image>();
				val6.sprite = _bubbleSprite;
				val6.preserveAspect = true;
				((Graphic)val6).color = new Color(0.8f, 0.92f, 1f, 0.8f);
				_bubbles.Add(val6);
			}
			GameObject val7 = new GameObject("BreathText");
			val7.transform.SetParent((Transform)(object)_container, false);
			_breathText = val7.AddComponent<Text>();
			_breathText.font = Resources.GetBuiltinResource<Font>("Arial.ttf");
			_breathText.fontSize = 12;
			_breathText.alignment = (TextAnchor)5;
			((Graphic)_breathText).color = new Color(0.8f, 0.92f, 1f, 0.8f);
			RectTransform rectTransform = ((Graphic)_breathText).rectTransform;
			rectTransform.sizeDelta = new Vector2(70f, 20f);
			rectTransform.anchoredPosition = new Vector2(-35f, -110f);
			((Component)_breathText).gameObject.SetActive(SwimmingReworked.BreathHudShowLabels.Value);
			_cachedShowLabels = SwimmingReworked.BreathHudShowLabels.Value;
			SetVisible(visible: false);
		}

		private Sprite CreateBubbleSprite(int size)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = new Texture2D(size, size, (TextureFormat)4, false);
			((Texture)val).filterMode = (FilterMode)1;
			((Texture)val).wrapMode = (TextureWrapMode)1;
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor((float)(size - 1) * 0.5f, (float)(size - 1) * 0.5f);
			float num = (float)size * 0.42f;
			float num2 = num * 0.72f;
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor(val2.x - (float)size * 0.12f, val2.y + (float)size * 0.12f);
			float num3 = (float)size * 0.12f;
			Vector2 val4 = default(Vector2);
			Color val5 = default(Color);
			for (int i = 0; i < size; i++)
			{
				for (int j = 0; j < size; j++)
				{
					((Vector2)(ref val4))..ctor((float)j, (float)i);
					float num4 = Vector2.Distance(val4, val2);
					if (num4 > num)
					{
						val.SetPixel(j, i, Color.clear);
						continue;
					}
					float num5 = Mathf.InverseLerp(num, num2, num4);
					float num6 = Mathf.Lerp(0.15f, 0.95f, num5);
					((Color)(ref val5))..ctor(0.82f, 0.93f, 1f, num6);
					float num7 = Vector2.Distance(val4, val3);
					if (num7 < num3)
					{
						float num8 = 1f - num7 / num3;
						val5 = Color.Lerp(val5, new Color(1f, 1f, 1f, num6), num8 * 0.65f);
					}
					val.SetPixel(j, i, val5);
				}
			}
			val.Apply();
			return Sprite.Create(val, new Rect(0f, 0f, (float)size, (float)size), new Vector2(0.5f, 0.5f), 100f);
		}
	}
	internal static class InputHelper
	{
		public static bool GetRun()
		{
			return GetButton("Run", "JoyRun");
		}

		public static bool GetJump()
		{
			return GetButton("Jump", "JoyJump");
		}

		public static bool GetCrouch()
		{
			return GetButton("Crouch", "JoyCrouch");
		}

		public static bool GetAttack()
		{
			return GetButton("Attack", "JoyAttack");
		}

		public static bool GetBlock()
		{
			return GetButton("Block", "JoyBlock");
		}

		public static float GetHorizontal()
		{
			return Input.GetAxisRaw("Horizontal");
		}

		public static float GetVertical()
		{
			return Input.GetAxisRaw("Vertical");
		}

		public static float GetDiveVertical()
		{
			float num = 0f;
			if (GetJump())
			{
				num += 1f;
			}
			if (GetCrouch())
			{
				num -= 1f;
			}
			return num;
		}

		private static bool GetButton(string keyboardAction, string gamepadAction)
		{
			return ZInput.GetButton(keyboardAction) || ZInput.GetButton(gamepadAction);
		}
	}
	internal static class RuntimeFlags
	{
		public static bool AllowStaminaUseFromSwimSprint;
	}
	public static class SwimAPI
	{
		public static event Action<Player> OnEnterWater;

		public static event Action<Player> OnExitWater;

		public static event Action<Player> OnHeadUnderwater;

		public static event Action<Player> OnHeadAboveWater;

		public static event Action<Player> OnOutOfBreath;

		public static float GetBreathPercent(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return 1f;
			}
			SwimState swimState = SwimStateManager.Get(player);
			float maxBreath = GetMaxBreath(player);
			if (maxBreath <= 0f)
			{
				return 0f;
			}
			return Mathf.Clamp01(swimState.Breath / maxBreath);
		}

		public static bool IsUnderwater(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			return WaterHelper.IsHeadUnderwater(player);
		}

		public static bool IsSwimming(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			return WaterHelper.IsBodyInWater(player);
		}

		public static float GetCurrentBreath(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return 0f;
			}
			return SwimStateManager.Get(player).Breath;
		}

		public static float GetBaseMaxBreath(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return SwimmingReworked.MaxBreathSeconds.Value;
			}
			float skillFactor = ((Character)player).GetSkillFactor((SkillType)103);
			return SwimmingReworked.MaxBreathSeconds.Value + SwimmingReworked.MaxBreathBonusAtSkill100.Value * skillFactor;
		}

		public static float GetMaxBreath(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return SwimmingReworked.MaxBreathSeconds.Value;
			}
			SwimState swimState = SwimStateManager.Get(player);
			return GetBaseMaxBreath(player) + swimState.ExtraBreathBonus;
		}

		public static bool IsOutOfBreath(Player player)
		{
			return GetCurrentBreath(player) <= 0f;
		}

		public static float GetSwimSkillFactor(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return 0f;
			}
			return ((Character)player).GetSkillFactor((SkillType)103);
		}

		public static bool HasInfiniteBreath(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			SwimState swimState = SwimStateManager.Get(player);
			return Time.time < swimState.InfiniteBreathUntil;
		}

		public static float GetExternalSwimSpeedMultiplier(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return 1f;
			}
			SwimState swimState = SwimStateManager.Get(player);
			if (Time.time >= swimState.SwimSpeedMultiplierUntil)
			{
				swimState.SwimSpeedMultiplier = 1f;
				return 1f;
			}
			return swimState.SwimSpeedMultiplier;
		}

		public static void RestoreBreath(Player player, float amount)
		{
			if (!((Object)(object)player == (Object)null) && !(amount <= 0f))
			{
				SwimState swimState = SwimStateManager.Get(player);
				swimState.Breath = Mathf.Min(GetMaxBreath(player), swimState.Breath + amount);
			}
		}

		public static void DrainBreath(Player player, float amount)
		{
			if (!((Object)(object)player == (Object)null) && !(amount <= 0f))
			{
				SwimState swimState = SwimStateManager.Get(player);
				swimState.Breath = Mathf.Max(0f, swimState.Breath - amount);
			}
		}

		public static void SetInfiniteBreath(Player player, float duration)
		{
			if (!((Object)(object)player == (Object)null) && !(duration <= 0f))
			{
				SwimState swimState = SwimStateManager.Get(player);
				swimState.InfiniteBreathUntil = Mathf.Max(swimState.InfiniteBreathUntil, Time.time + duration);
				swimState.Breath = GetMaxBreath(player);
			}
		}

		public static void AddBreathBonus(Player player, float bonusSeconds)
		{
			if (!((Object)(object)player == (Object)null))
			{
				SwimState swimState = SwimStateManager.Get(player);
				swimState.ExtraBreathBonus = Mathf.Max(0f, swimState.ExtraBreathBonus + bonusSeconds);
				swimState.Breath = Mathf.Min(swimState.Breath, GetMaxBreath(player));
			}
		}

		public static void SetBreathBonus(Player player, float bonusSeconds)
		{
			if (!((Object)(object)player == (Object)null))
			{
				SwimState swimState = SwimStateManager.Get(player);
				swimState.ExtraBreathBonus = Mathf.Max(0f, bonusSeconds);
				swimState.Breath = Mathf.Min(swimState.Breath, GetMaxBreath(player));
			}
		}

		public static void AddSwimSpeedBonus(Player player, float multiplier, float duration)
		{
			if (!((Object)(object)player == (Object)null) && !(multiplier <= 0f) && !(duration <= 0f))
			{
				SwimState swimState = SwimStateManager.Get(player);
				swimState.SwimSpeedMultiplier = Mathf.Max(swimState.SwimSpeedMultiplier, multiplier);
				swimState.SwimSpeedMultiplierUntil = Mathf.Max(swimState.SwimSpeedMultiplierUntil, Time.time + duration);
			}
		}

		internal static void UpdateEvents(Player player)
		{
			if (!((Object)(object)player == (Object)null))
			{
				SwimState swimState = SwimStateManager.Get(player);
				bool flag = IsSwimming(player);
				bool flag2 = IsUnderwater(player);
				bool flag3 = IsOutOfBreath(player);
				if (flag && !swimState.WasSwimming)
				{
					SwimAPI.OnEnterWater?.Invoke(player);
				}
				else if (!flag && swimState.WasSwimming)
				{
					SwimAPI.OnExitWater?.Invoke(player);
				}
				if (flag2 && !swimState.WasHeadUnderwater)
				{
					SwimAPI.OnHeadUnderwater?.Invoke(player);
				}
				else if (!flag2 && swimState.WasHeadUnderwater)
				{
					SwimAPI.OnHeadAboveWater?.Invoke(player);
				}
				if (flag3 && !swimState.WasOutOfBreath)
				{
					SwimAPI.OnOutOfBreath?.Invoke(player);
				}
				swimState.WasSwimming = flag;
				swimState.WasHeadUnderwater = flag2;
				swimState.WasOutOfBreath = flag3;
			}
		}
	}
	[BepInPlugin("dzk.SwimmingReworked", "SwimmingReworked", "1.0.6")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[SynchronizationMode(/*Could not decode attribute arguments.*/)]
	internal class SwimmingReworked : BaseUnityPlugin
	{
		public const string PluginGUID = "dzk.SwimmingReworked";

		public const string PluginName = "SwimmingReworked";

		public const string PluginVersion = "1.0.6";

		internal static SwimmingReworked Instance;

		internal static Harmony Harmony;

		public static CustomLocalization Localization = LocalizationManager.Instance.GetLocalization();

		internal static ConfigEntry<bool> EnableMod;

		internal static ConfigEntry<float> MaxBreathSeconds;

		internal static ConfigEntry<float> MaxBreathBonusAtSkill100;

		internal static ConfigEntry<float> BreathRecoveryPerSecond;

		internal static ConfigEntry<float> SuffocationDamagePercentPerTick;

		internal static ConfigEntry<float> SuffocationTickInterval;

		internal static ConfigEntry<float> SprintSwimMultiplier;

		internal static ConfigEntry<float> SprintStaminaPerSecond;

		internal static ConfigEntry<float> VerticalSwimSpeed;

		internal static ConfigEntry<float> MinSwimSpeed;

		internal static ConfigEntry<float> MaxSwimSpeedAtSkill100;

		internal static ConfigEntry<bool> AllowUnderwaterSprint;

		internal static ConfigEntry<bool> DisableVanillaSwimStaminaDrain;

		internal static ConfigEntry<bool> DisableVanillaDrowningFromStamina;

		internal static ConfigEntry<float> BaseUnderwaterStaminaRegenPerSecond;

		internal static ConfigEntry<float> MaxSwimSkillBonusStaminaRegenPerSecond;

		internal static ConfigEntry<bool> DebugLogging;

		private ConfigFileWatcher _configWatcher;

		internal static ConfigEntry<float> BreathHudOffsetX;

		internal static ConfigEntry<float> BreathHudOffsetY;

		internal static ConfigEntry<float> BreathHudScale;

		internal static ConfigEntry<bool> BreathHudShowLabels;

		private void Awake()
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Expected O, but got Unknown
			Instance = this;
			CreateConfig();
			CreateConfigWatcher();
			SynchronizationManager.OnConfigurationSynchronized += OnConfigurationSynchronized;
			SynchronizationManager.OnAdminStatusChanged += OnAdminStatusChanged;
			((Component)this).gameObject.AddComponent<BreathHud>();
			((Component)this).gameObject.AddComponent<DiveImmersionEffect>();
			Harmony = new Harmony("dzk.SwimmingReworked");
			Harmony.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"SwimmingReworked 1.0.6 loaded");
		}

		private void OnDestroy()
		{
			SynchronizationManager.OnConfigurationSynchronized -= OnConfigurationSynchronized;
			SynchronizationManager.OnAdminStatusChanged -= OnAdminStatusChanged;
			if (_configWatcher != null)
			{
				_configWatcher = null;
			}
		}

		private void CreateConfig()
		{
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Expected O, but got Unknown
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			BreathHudOffsetX = ((BaseUnityPlugin)this).Config.Bind<float>("Breath HUD", "OffsetX", -40f, "Horizontal offset from right edge of screen (negative = left).");
			BreathHudOffsetY = ((BaseUnityPlugin)this).Config.Bind<float>("Breath HUD", "OffsetY", 0f, "Vertical offset from center of screen.");
			BreathHudScale = ((BaseUnityPlugin)this).Config.Bind<float>("Breath HUD", "Scale", 1f, "Scale multiplier for the breath indicator.");
			AcceptableValueBase val = (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.3f, 3f);
			BreathHudScale = ((BaseUnityPlugin)this).Config.Bind<float>("Breath HUD", "Scale", 1f, new ConfigDescription("Scale multiplier for the breath indicator.", val, Array.Empty<object>()));
			BreathHudShowLabels = ((BaseUnityPlugin)this).Config.Bind<bool>("Breath HUD", "ShowLabels", false, "Show numeric breath value next to bubbles.");
			EnableMod = ConfigFileExtensions.BindConfig<bool>(((BaseUnityPlugin)this).Config, "General", "EnableMod", true, "Enable or disable the mod.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			ConfigFile config = ((BaseUnityPlugin)this).Config;
			AcceptableValueBase val2 = (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 600f);
			MaxBreathSeconds = ConfigFileExtensions.BindConfig<float>(config, "Breath", "MaxBreathSeconds", 30f, "Maximum underwater breath at Swim skill 0.", true, (int?)null, val2, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			ConfigFile config2 = ((BaseUnityPlugin)this).Config;
			val2 = (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 600f);
			MaxBreathBonusAtSkill100 = ConfigFileExtensions.BindConfig<float>(config2, "Breath", "MaxBreathBonusAtSkill100", 150f, "Additional breath time gained at Swim skill 100.", true, (int?)null, val2, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			ConfigFile config3 = ((BaseUnityPlugin)this).Config;
			val2 = (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 300f);
			BreathRecoveryPerSecond = ConfigFileExtensions.BindConfig<float>(config3, "Breath", "BreathRecoveryPerSecond", 30f, "Breath recovered per second when not underwater.", true, (int?)null, val2, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			ConfigFile config4 = ((BaseUnityPlugin)this).Config;
			val2 = (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f);
			SuffocationDamagePercentPerTick = ConfigFileExtensions.BindConfig<float>(config4, "Breath", "SuffocationDamagePercentPerTick", 10f, "Percent of max health lost each suffocation tick when breath reaches 0.", true, (int?)null, val2, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			ConfigFile config5 = ((BaseUnityPlugin)this).Config;
			val2 = (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 30f);
			SuffocationTickInterval = ConfigFileExtensions.BindConfig<float>(config5, "Breath", "SuffocationTickInterval", 2f, "Seconds between suffocation damage ticks when breath reaches 0.", true, (int?)null, val2, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			ConfigFile config6 = ((BaseUnityPlugin)this).Config;
			val2 = (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 5f);
			SprintSwimMultiplier = ConfigFileExtensions.BindConfig<float>(config6, "Movement", "SprintSwimMultiplier", 1.75f, "Sprint speed multiplier while swimming.", true, (int?)null, val2, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			ConfigFile config7 = ((BaseUnityPlugin)this).Config;
			val2 = (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f);
			SprintStaminaPerSecond = ConfigFileExtensions.BindConfig<float>(config7, "Movement", "SprintStaminaPerSecond", 8f, "Stamina drained per second while sprint swimming.", true, (int?)null, val2, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			ConfigFile config8 = ((BaseUnityPlugin)this).Config;
			val2 = (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 20f);
			VerticalSwimSpeed = ConfigFileExtensions.BindConfig<float>(config8, "Movement", "VerticalSwimSpeed", 3f, "Vertical swimming speed while diving.", true, (int?)null, val2, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			AllowUnderwaterSprint = ConfigFileExtensions.BindConfig<bool>(((BaseUnityPlugin)this).Config, "Movement", "AllowUnderwaterSprint", true, "Allow sprinting underwater.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			DisableVanillaSwimStaminaDrain = ConfigFileExtensions.BindConfig<bool>(((BaseUnityPlugin)this).Config, "Vanilla Overrides", "DisableVanillaSwimStaminaDrain", true, "Disables vanilla stamina drain for normal swimming.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			DisableVanillaDrowningFromStamina = ConfigFileExtensions.BindConfig<bool>(((BaseUnityPlugin)this).Config, "Vanilla Overrides", "DisableVanillaDrowningFromStamina", true, "Disables vanilla drowning behavior caused by stamina depletion.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			ConfigFile config9 = ((BaseUnityPlugin)this).Config;
			val2 = (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 50f);
			BaseUnderwaterStaminaRegenPerSecond = ConfigFileExtensions.BindConfig<float>(config9, "Movement", "BaseUnderwaterStaminaRegenPerSecond", 2f, "Base stamina regenerated per second while swimming and not sprinting.", true, (int?)null, val2, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			ConfigFile config10 = ((BaseUnityPlugin)this).Config;
			val2 = (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 50f);
			MaxSwimSkillBonusStaminaRegenPerSecond = ConfigFileExtensions.BindConfig<float>(config10, "Movement", "MaxSwimSkillBonusStaminaRegenPerSecond", 4f, "Additional stamina regenerated per second at Swim skill 100.", true, (int?)null, val2, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			ConfigFile config11 = ((BaseUnityPlugin)this).Config;
			val2 = (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 10f);
			MinSwimSpeed = ConfigFileExtensions.BindConfig<float>(config11, "Movement", "MinSwimSpeed", 1f, "Swim speed multiplier at Swim skill 0.", true, (int?)null, val2, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			ConfigFile config12 = ((BaseUnityPlugin)this).Config;
			val2 = (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 10f);
			MaxSwimSpeedAtSkill100 = ConfigFileExtensions.BindConfig<float>(config12, "Movement", "MaxSwimSpeedAtSkill100", 2f, "Swim speed multiplier at Swim skill 100.", true, (int?)null, val2, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			DebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugLogging", false, "Enable debug logs.");
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = true;
			((BaseUnityPlugin)this).Config.Save();
		}

		private void CreateConfigWatcher()
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Expected O, but got Unknown
			_configWatcher = new ConfigFileWatcher(((BaseUnityPlugin)this).Config, 1000L);
			_configWatcher.OnConfigFileReloaded += delegate
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Config file reloaded from disk.");
			};
		}

		private void OnConfigurationSynchronized(object sender, ConfigurationSynchronizationEventArgs e)
		{
			if (e.InitialSynchronization)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Initial config sync received.");
			}
			else
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Config sync update received.");
			}
		}

		private void OnAdminStatusChanged()
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Admin status changed: " + (SynchronizationManager.Instance.PlayerIsAdmin ? "admin" : "not admin")));
		}

		internal static bool IsEnabled()
		{
			return (Object)(object)Instance != (Object)null && EnableMod.Value;
		}

		internal static void LogDebug(string msg)
		{
			if ((Object)(object)Instance != (Object)null && DebugLogging.Value)
			{
				((BaseUnityPlugin)Instance).Logger.LogInfo((object)msg);
			}
		}
	}
	internal class SwimState
	{
		public float Breath;

		public bool WasHeadUnderwater;

		public bool WasSwimming;

		public float LastSurfaceTime;

		public float SuffocationTickTimer;

		public float ExtraBreathBonus;

		public float InfiniteBreathUntil;

		public float SwimSpeedMultiplier = 1f;

		public float SwimSpeedMultiplierUntil;

		public bool WasOutOfBreath;
	}
	internal static class SwimStateManager
	{
		private static readonly Dictionary<long, SwimState> States = new Dictionary<long, SwimState>();

		public static SwimState Get(Player player)
		{
			long playerID = player.GetPlayerID();
			if (!States.TryGetValue(playerID, out var value))
			{
				value = new SwimState
				{
					Breath = SwimAPI.GetBaseMaxBreath(player),
					WasHeadUnderwater = false,
					WasSwimming = false,
					LastSurfaceTime = 0f,
					SuffocationTickTimer = 0f,
					ExtraBreathBonus = 0f,
					InfiniteBreathUntil = 0f,
					SwimSpeedMultiplier = 1f,
					SwimSpeedMultiplierUntil = 0f,
					WasOutOfBreath = false
				};
				States[playerID] = value;
			}
			return value;
		}

		public static void Remove(Player player)
		{
			if (!((Object)(object)player == (Object)null))
			{
				States.Remove(player.GetPlayerID());
			}
		}
	}
	internal static class WaterHelper
	{
		public static bool TryGetWaterLevel(Vector3 position, out float waterLevel)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			waterLevel = Floating.GetLiquidLevel(position, 1f, (LiquidType)0);
			return waterLevel > -10000f;
		}

		public static Vector3 GetHeadPosition(Player player)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null)
			{
				return Vector3.zero;
			}
			return ((Character)player).GetHeadPoint();
		}

		public static Vector3 GetChestPosition(Player player)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null)
			{
				return Vector3.zero;
			}
			return ((Component)player).transform.position + Vector3.up * 1f;
		}

		public static bool IsBodyInWater(Player player)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			Vector3 chestPosition = GetChestPosition(player);
			float waterLevel;
			return TryGetWaterLevel(chestPosition, out waterLevel) && chestPosition.y < waterLevel;
		}

		public static bool IsHeadUnderwater(Player player)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			Vector3 headPosition = GetHeadPosition(player);
			float waterLevel;
			return TryGetWaterLevel(headPosition, out waterLevel) && headPosition.y < waterLevel - 0.02f;
		}

		public static bool IsNearSurface(Player player, float tolerance = 0.35f)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			Vector3 headPosition = GetHeadPosition(player);
			float waterLevel;
			return TryGetWaterLevel(headPosition, out waterLevel) && Mathf.Abs(headPosition.y - waterLevel) <= tolerance;
		}
	}
}
namespace SwimmingReworked.Patches
{
	[HarmonyPatch(typeof(CameraEffects), "SetSunShafts")]
	internal static class CameraEffectsPatch
	{
		private static void Prefix(ref bool enabled)
		{
			if (DiveImmersionEffect.IsUnderwaterImmersionActive)
			{
				enabled = false;
			}
		}
	}
	[HarmonyPatch(typeof(Character), "Damage")]
	internal static class CharacterDamageDebugPatch
	{
		private static void Prefix(Character __instance, HitData hit)
		{
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			if (SwimmingReworked.IsEnabled() && SwimmingReworked.DebugLogging.Value)
			{
				Player val = (Player)(object)((__instance is Player) ? __instance : null);
				if (val != null && !((Object)(object)val != (Object)(object)Player.m_localPlayer) && WaterHelper.IsBodyInWater(val))
				{
					float totalDamage = hit.GetTotalDamage();
					SwimmingReworked.LogDebug($"Damage underwater -> total:{totalDamage}, attacker:{hit.GetAttacker()}, point:{hit.m_point}, dir:{hit.m_dir}");
				}
			}
		}
	}
	[HarmonyPatch(typeof(Character), "Damage")]
	internal static class CharacterDamagePatch
	{
		private static bool Prefix(Character __instance, HitData hit)
		{
			if (!SwimmingReworked.IsEnabled())
			{
				return true;
			}
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if (val == null || (Object)(object)val != (Object)(object)Player.m_localPlayer)
			{
				return true;
			}
			if (hit == null)
			{
				return true;
			}
			SwimState swimState = SwimStateManager.Get(val);
			if (swimState.Breath > 0f && IsVanillaDrowningHit(val, hit))
			{
				if (SwimmingReworked.DebugLogging.Value)
				{
					SwimmingReworked.LogDebug($"Blocked vanilla drowning hit: total={hit.GetTotalDamage():F1}");
				}
				return false;
			}
			return true;
		}

		private static bool IsVanillaDrowningHit(Player player, HitData hit)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			if (!WaterHelper.IsHeadUnderwater(player))
			{
				return false;
			}
			if ((Object)(object)hit.GetAttacker() != (Object)null)
			{
				return false;
			}
			float totalDamage = hit.GetTotalDamage();
			if (totalDamage <= 0f)
			{
				return false;
			}
			Vector3 dir = hit.m_dir;
			bool flag = dir.y < -0.9f;
			bool flag2 = Mathf.Abs(totalDamage - 2f) < 0.01f;
			return flag && flag2;
		}
	}
	[HarmonyPatch]
	internal static class CharacterSwimmingPatch
	{
		private const float MinimumSwimDepth = 1.5f;

		private const float MaximumSwimDepth = 100f;

		private const float MaximumDiveTargetLead = 0.75f;

		[HarmonyPatch(typeof(Character), "CustomFixedUpdate")]
		[HarmonyPrefix]
		private static void CharacterCustomFixedUpdatePrefix(Character __instance, ref Vector3 ___m_lookDir)
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			if (!SwimmingReworked.IsEnabled())
			{
				return;
			}
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if (val == null || (Object)(object)val != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			if (!WaterHelper.IsBodyInWater(val))
			{
				((Character)val).m_swimDepth = 1.5f;
				return;
			}
			if (!WaterHelper.TryGetWaterLevel(((Component)val).transform.position, out var waterLevel))
			{
				((Character)val).m_swimDepth = 1.5f;
				return;
			}
			float num = Mathf.Max(1.5f, waterLevel - ((Component)val).transform.position.y);
			float num2 = Mathf.Clamp(num + 0.75f, 1.5f, 100f);
			((Character)val).m_swimDepth = Mathf.Clamp(((Character)val).m_swimDepth, 1.5f, num2);
			float diveVertical = InputHelper.GetDiveVertical();
			float num3 = SwimmingReworked.VerticalSwimSpeed.Value * Time.fixedDeltaTime;
			if (diveVertical < -0.01f)
			{
				((Character)val).m_swimDepth = Mathf.Min(((Character)val).m_swimDepth + num3, num2);
			}
			else if (diveVertical > 0.01f)
			{
				((Character)val).m_swimDepth = ((Character)val).m_swimDepth - num3;
			}
			((Character)val).m_swimDepth = Mathf.Clamp(((Character)val).m_swimDepth, 1.5f, num2);
		}

		[HarmonyPatch(typeof(Character), "UpdateMotion")]
		[HarmonyPrefix]
		private static void CharacterUpdateMotionPrefix(Character __instance, ref float ___m_lastGroundTouch, ref float ___m_swimTimer)
		{
			if (SwimmingReworked.IsEnabled())
			{
				Player val = (Player)(object)((__instance is Player) ? __instance : null);
				if (val != null && !((Object)(object)val != (Object)(object)Player.m_localPlayer) && WaterHelper.IsBodyInWater(val))
				{
					___m_lastGroundTouch = 0.3f;
					___m_swimTimer = 0f;
				}
			}
		}

		[HarmonyPatch(typeof(Character), "UpdateSwimming")]
		[HarmonyPrefix]
		private static bool CharacterUpdateSwimmingPrefix(float dt, ref Character __instance)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_0215: Unknown result type (might be due to invalid IL or missing references)
			//IL_021a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0222: Unknown result type (might be due to invalid IL or missing references)
			//IL_0227: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_020b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0210: Unknown result type (might be due to invalid IL or missing references)
			//IL_025c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0267: Unknown result type (might be due to invalid IL or missing references)
			//IL_026c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0271: Unknown result type (might be due to invalid IL or missing references)
			//IL_0296: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_030f: Unknown result type (might be due to invalid IL or missing references)
			//IL_032d: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0413: Unknown result type (might be due to invalid IL or missing references)
			//IL_034a: Unknown result type (might be due to invalid IL or missing references)
			//IL_037b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0380: Unknown result type (might be due to invalid IL or missing references)
			//IL_0384: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0474: Unknown result type (might be due to invalid IL or missing references)
			//IL_0499: Unknown result type (might be due to invalid IL or missing references)
			//IL_049e: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_04bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_04cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0456: Unknown result type (might be due to invalid IL or missing references)
			if (!SwimmingReworked.IsEnabled())
			{
				return true;
			}
			Character obj = __instance;
			Player val = (Player)(object)((obj is Player) ? obj : null);
			if (val == null || (Object)(object)val != (Object)(object)Player.m_localPlayer)
			{
				return true;
			}
			bool flag = ((Character)val).IsOnGround();
			Vector3 position = ((Component)val).transform.position;
			if (Mathf.Max(0f, ((Character)val).m_maxAirAltitude - position.y) > 0.5f && ((Character)val).m_onLand != null)
			{
				((Character)val).m_onLand(new Vector3(position.x, ((Character)val).m_waterLevel, position.z));
			}
			((Character)val).m_maxAirAltitude = position.y;
			float skillFactor = val.m_skills.GetSkillFactor((SkillType)103);
			float num = Mathf.Lerp(SwimmingReworked.MinSwimSpeed.Value, SwimmingReworked.MaxSwimSpeedAtSkill100.Value, skillFactor);
			num *= SwimAPI.GetExternalSwimSpeedMultiplier(val);
			num *= ((Character)val).GetAttackSpeedFactorMovement();
			bool flag2 = false;
			float num2 = SwimmingReworked.SprintStaminaPerSecond.Value * dt;
			if (((Character)val).IsEncumbered())
			{
				num *= 0.3f;
			}
			if (((Character)val).InMinorAction())
			{
				num = 0f;
			}
			else if ((SwimmingReworked.AllowUnderwaterSprint.Value || !WaterHelper.IsHeadUnderwater(val)) && InputHelper.GetRun() && !((Character)val).IsEncumbered() && val.GetStamina() > num2 + 0.01f)
			{
				num *= SwimmingReworked.SprintSwimMultiplier.Value;
				try
				{
					RuntimeFlags.AllowStaminaUseFromSwimSprint = true;
					((Character)val).UseStamina(num2);
				}
				finally
				{
					RuntimeFlags.AllowStaminaUseFromSwimSprint = false;
				}
			}
			((Character)val).m_seman.ApplyStatusEffectSpeedMods(ref num, ((Character)val).m_moveDir);
			Vector3 val2 = ((Character)val).m_moveDir * num;
			if (((Vector3)(ref val2)).magnitude > 0f && ((Character)val).IsOnGround())
			{
				Vector3 val3 = Vector3.ProjectOnPlane(val2, ((Character)val).m_lastGroundNormal);
				val2 = ((Vector3)(ref val3)).normalized * ((Vector3)(ref val2)).magnitude;
			}
			((Character)val).m_currentVel = Vector3.Lerp(((Character)val).m_currentVel, val2, ((Character)val).m_swimAcceleration);
			if (((Vector3)(ref val2)).magnitude > 0.1f)
			{
				((Character)val).AddNoise(15f);
			}
			((Character)val).AddPushbackForce(ref ((Character)val).m_currentVel);
			Vector3 val4 = ((Character)val).m_currentVel - ((Character)val).m_body.linearVelocity;
			val4.y = 0f;
			if (((Vector3)(ref val4)).magnitude > 20f)
			{
				val4 = ((Vector3)(ref val4)).normalized * 20f;
			}
			if (((Vector3)(ref val2)).magnitude > 0.1f)
			{
				val.m_swimSkillImproveTimer += dt;
				if (val.m_swimSkillImproveTimer > 1f)
				{
					val.m_swimSkillImproveTimer = 0f;
					((Character)val).RaiseSkill((SkillType)103, 1f);
				}
			}
			else
			{
				val.m_swimSkillImproveTimer = 0f;
			}
			((Character)val).m_body.AddForce(val4, (ForceMode)2);
			float num3 = ((Character)val).m_waterLevel - ((Character)val).m_swimDepth;
			if (((Component)val).transform.position.y < num3)
			{
				float num4 = Mathf.Clamp01((num3 - ((Component)val).transform.position.y) / 2f);
				float num5 = Mathf.Lerp(0f, 10f, num4);
				Vector3 linearVelocity = ((Character)val).m_body.linearVelocity;
				linearVelocity.y = Mathf.MoveTowards(linearVelocity.y, num5, 50f * dt);
				((Character)val).m_body.linearVelocity = linearVelocity;
			}
			else
			{
				float num6 = Mathf.Clamp01((((Component)val).transform.position.y - num3) / 1f);
				float num7 = Mathf.Lerp(0f, 10f, num6);
				Vector3 linearVelocity2 = ((Character)val).m_body.linearVelocity;
				linearVelocity2.y = Mathf.MoveTowards(linearVelocity2.y, 0f - num7, 30f * dt);
				((Character)val).m_body.linearVelocity = linearVelocity2;
			}
			float num8 = 0f;
			if (((Vector3)(ref ((Character)val).m_moveDir)).magnitude > 0.1f || ((Character)val).AlwaysRotateCamera())
			{
				float swimTurnSpeed = ((Character)val).m_swimTurnSpeed;
				((Character)val).m_seman.ApplyStatusEffectSpeedMods(ref swimTurnSpeed, ((Character)val).m_moveDir);
				num8 = ((Character)val).UpdateRotation(swimTurnSpeed, dt, true);
			}
			((Character)val).m_body.angularVelocity = Vector3.zero;
			((Character)val).UpdateEyeRotation();
			((Character)val).m_body.useGravity = true;
			Vector3 forward = ((Component)val).transform.forward;
			float num9 = Vector3.Dot(((Character)val).m_currentVel, forward);
			float num10 = Vector3.Dot(((Character)val).m_currentVel, ((Component)val).transform.right);
			float num11 = Vector3.Dot(((Character)val).m_body.linearVelocity, forward);
			((Character)val).m_currentTurnVel = Mathf.SmoothDamp(((Character)val).m_currentTurnVel, num8, ref ((Character)val).m_currentTurnVelChange, 0.5f, 99f);
			int hash = ZSyncAnimation.GetHash("forward_speed");
			int hash2 = ZSyncAnimation.GetHash("sideway_speed");
			int hash3 = ZSyncAnimation.GetHash("turn_speed");
			int hash4 = ZSyncAnimation.GetHash("inWater");
			int hash5 = ZSyncAnimation.GetHash("onGround");
			int hash6 = ZSyncAnimation.GetHash("encumbered");
			int hash7 = ZSyncAnimation.GetHash("flying");
			((Character)val).m_zanim.SetFloat(hash, ((Character)val).IsPlayer() ? num9 : num11);
			((Character)val).m_zanim.SetFloat(hash2, num10);
			((Character)val).m_zanim.SetFloat(hash3, ((Character)val).m_currentTurnVel);
			((Character)val).m_zanim.SetBool(hash4, !flag);
			((Character)val).m_zanim.SetBool(hash5, false);
			((Character)val).m_zanim.SetBool(hash6, false);
			((Character)val).m_zanim.SetBool(hash7, false);
			return false;
		}
	}
	[HarmonyPatch]
	internal static class CharacterWaterStatePatch
	{
		[HarmonyPrefix]
		[HarmonyPatch(typeof(Character), "IsSwimming")]
		private static bool CharacterIsSwimmingPrefix(Character __instance, ref bool __result)
		{
			if (!SwimmingReworked.IsEnabled())
			{
				return true;
			}
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if (val == null || (Object)(object)val != (Object)(object)Player.m_localPlayer)
			{
				return true;
			}
			StackTrace stackTrace = new StackTrace();
			for (int i = 2; i < stackTrace.FrameCount && i < 14; i++)
			{
				MethodBase methodBase = stackTrace.GetFrame(i)?.GetMethod();
				if (!(methodBase == null))
				{
					string text = methodBase.DeclaringType?.FullName + "." + methodBase.Name;
					if (text.Contains("EquipItem") || text.Contains("UpdateEquipment") || text.Contains("HideHandItems") || text.Contains("UnequipItem"))
					{
						__result = false;
						return false;
					}
				}
			}
			if (WaterHelper.IsBodyInWater(val))
			{
				__result = true;
				return false;
			}
			return true;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Character), "IsOnGround")]
		private static void CharacterIsOnGroundPostfix(Character __instance, ref bool __result)
		{
			if (SwimmingReworked.IsEnabled())
			{
				Player val = (Player)(object)((__instance is Player) ? __instance : null);
				if (val != null && !((Object)(object)val != (Object)(object)Player.m_localPlayer) && WaterHelper.IsBodyInWater(val))
				{
					__result = false;
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Character), "InWater")]
		private static void CharacterInWaterPostfix(Character __instance, ref bool __result)
		{
			if (SwimmingReworked.IsEnabled())
			{
				Player val = (Player)(object)((__instance is Player) ? __instance : null);
				if (val != null && !((Object)(object)val != (Object)(object)Player.m_localPlayer) && WaterHelper.IsBodyInWater(val))
				{
					__result = true;
				}
			}
		}
	}
	[HarmonyPatch(typeof(GameCamera), "UpdateCamera")]
	internal static class GameCameraPatch
	{
		private static float _originalMaxDistance;

		private static float _originalMinWaterDistance;

		private static bool _cachedOriginalValues;

		private static void Prefix(GameCamera __instance, Camera ___m_camera)
		{
			if (!SwimmingReworked.IsEnabled())
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null) && !((Object)(object)___m_camera == (Object)null))
			{
				bool flag = WaterHelper.IsBodyInWater(localPlayer);
				if (!_cachedOriginalValues)
				{
					_originalMaxDistance = __instance.m_maxDistance;
					_originalMinWaterDistance = __instance.m_minWaterDistance;
					_cachedOriginalValues = true;
				}
				if (flag)
				{
					__instance.m_minWaterDistance = -5000f;
					__instance.m_maxDistance = _originalMaxDistance;
				}
				else
				{
					__instance.m_minWaterDistance = _originalMinWaterDistance;
					__instance.m_maxDistance = _originalMaxDistance;
				}
			}
		}
	}
	[HarmonyPatch]
	internal static class PlayerEquipInWater
	{
		internal static bool _allowCombat;

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Player), "Update")]
		private static void PlayerUpdate_Prefix(Player __instance)
		{
			if (SwimmingReworked.IsEnabled() && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && WaterHelper.IsBodyInWater(__instance))
			{
				bool attack = InputHelper.GetAttack();
				bool block = InputHelper.GetBlock();
				if (attack || block)
				{
					_allowCombat = true;
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Player), "Update")]
		private static void PlayerUpdate_Postfix()
		{
			_allowCombat = false;
		}

		[HarmonyFinalizer]
		[HarmonyPatch(typeof(Player), "Update")]
		private static void PlayerUpdate_Finalizer()
		{
			_allowCombat = false;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Character), "InAttack")]
		private static void InAttack_Postfix(Character __instance, ref bool __result)
		{
			if (SwimmingReworked.IsEnabled() && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && _allowCombat && WaterHelper.IsBodyInWater(Player.m_localPlayer))
			{
				__result = false;
			}
		}
	}
	[HarmonyPatch(typeof(Player), "FixedUpdate")]
	internal static class PlayerFixedUpdatePatch
	{
		private static void Postfix(Player __instance)
		{
			if (!SwimmingReworked.IsEnabled() || (Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			bool flag = WaterHelper.IsBodyInWater(__instance);
			bool headUnderwater = WaterHelper.IsHeadUnderwater(__instance);
			bool flag2 = ((Character)__instance).IsDebugFlying();
			if (!flag)
			{
				Rigidbody component = ((Component)__instance).GetComponent<Rigidbody>();
				if ((Object)(object)component != (Object)null && !flag2)
				{
					component.useGravity = true;
				}
				HandleBreath(__instance, headUnderwater: false);
				return;
			}
			Rigidbody component2 = ((Component)__instance).GetComponent<Rigidbody>();
			if (!((Object)(object)component2 == (Object)null))
			{
				if (!flag2)
				{
					component2.useGravity = true;
				}
				HandleBreath(__instance, headUnderwater);
				HandleUnderwaterStaminaRegen(__instance);
			}
		}

		private static void HandleBreath(Player player, bool headUnderwater)
		{
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Expected O, but got Unknown
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			SwimState swimState = SwimStateManager.Get(player);
			float fixedDeltaTime = Time.fixedDeltaTime;
			float maxBreath = SwimAPI.GetMaxBreath(player);
			bool flag = SwimAPI.HasInfiniteBreath(player);
			if (headUnderwater)
			{
				if (flag)
				{
					swimState.Breath = maxBreath;
					swimState.SuffocationTickTimer = 0f;
				}
				else
				{
					swimState.Breath -= fixedDeltaTime;
					if (swimState.Breath < 0f)
					{
						swimState.Breath = 0f;
					}
					if (swimState.Breath <= 0f)
					{
						swimState.SuffocationTickTimer += fixedDeltaTime;
						if (swimState.SuffocationTickTimer >= SwimmingReworked.SuffocationTickInterval.Value)
						{
							swimState.SuffocationTickTimer = 0f;
							float maxHealth = ((Character)player).GetMaxHealth();
							float damage = maxHealth * (SwimmingReworked.SuffocationDamagePercentPerTick.Value / 100f);
							HitData val = new HitData();
							val.m_point = ((Component)player).transform.position;
							val.m_dir = Vector3.zero;
							val.m_damage.m_damage = damage;
							((Character)player).Damage(val);
						}
					}
					else
					{
						swimState.SuffocationTickTimer = 0f;
					}
				}
			}
			else
			{
				swimState.Breath += SwimmingReworked.BreathRecoveryPerSecond.Value * fixedDeltaTime;
				if (swimState.Breath > maxBreath)
				{
					swimState.Breath = maxBreath;
				}
				swimState.SuffocationTickTimer = 0f;
			}
			SwimAPI.UpdateEvents(player);
			if (SwimmingReworked.DebugLogging.Value && Time.frameCount % 60 == 0)
			{
				SwimmingReworked.LogDebug($"Breath={swimState.Breath:F1}/{maxBreath:F1}, underwater={headUnderwater}, infinite={flag}");
			}
			swimState.WasHeadUnderwater = headUnderwater;
		}

		private static void HandleUnderwaterStaminaRegen(Player player)
		{
			if (!InputHelper.GetRun() || !(player.GetStamina() > 0f))
			{
				float skillFactor = ((Character)player).GetSkillFactor((SkillType)103);
				float num = SwimmingReworked.BaseUnderwaterStaminaRegenPerSecond.Value + SwimmingReworked.MaxSwimSkillBonusStaminaRegenPerSecond.Value * skillFactor;
				float value = Traverse.Create((object)player).Field("m_stamina").GetValue<float>();
				float maxStamina = ((Character)player).GetMaxStamina();
				float num2 = num * Time.fixedDeltaTime;
				value += num2;
				if (value > maxStamina)
				{
					value = maxStamina;
				}
				Traverse.Create((object)player).Field("m_stamina").SetValue((object)value);
			}
		}
	}
	[HarmonyPatch(typeof(Player), "Update")]
	internal static class PlayerUpdatePatch
	{
		private static void Postfix(Player __instance)
		{
		}
	}
	[HarmonyPatch(typeof(Player), "UpdateStats", new Type[] { typeof(float) })]
	internal static class PlayerUpdateStatsPatch
	{
		private static void Postfix(Player __instance, float dt)
		{
			if (SwimmingReworked.IsEnabled() && !((Object)(object)__instance == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && WaterHelper.IsBodyInWater(__instance) && (!InputHelper.GetRun() || !(__instance.GetStamina() > 0f)))
			{
				Traverse.Create((object)__instance).Field("m_staminaRegenTimer").SetValue((object)0f);
			}
		}
	}
	[HarmonyPatch(typeof(WaterVolume), "UpdateMaterials")]
	internal static class SurfacePatch
	{
		private sealed class UnderwaterSurface
		{
			public readonly GameObject GameObject;

			public readonly MeshFilter MeshFilter;

			public readonly MeshRenderer MeshRenderer;

			public UnderwaterSurface(GameObject gameObject, MeshFilter meshFilter, MeshRenderer meshRenderer)
			{
				GameObject = gameObject;
				MeshFilter = meshFilter;
				MeshRenderer = meshRenderer;
			}
		}

		private const float SurfaceOffset = -0.025f;

		private const float OverlapScale = 1.02f;

		private const float UnderwaterCameraMargin = 0.15f;

		private const int CleanupIntervalFrames = 600;

		private static readonly Dictionary<MeshRenderer, UnderwaterSurface> UnderwaterCopies = new Dictionary<MeshRenderer, UnderwaterSurface>();

		private static readonly List<MeshRenderer> DeadRenderers = new List<MeshRenderer>();

		private static int _lastCameraCheckFrame = -1;

		private static bool _cachedCameraUnderwater;

		private static int _lastCleanupFrame;

		private static bool _copiesMayBeActive;

		private static GameCamera _cachedGameCamera;

		private static Camera _cachedCamera;

		private static void Postfix(WaterVolume __instance)
		{
			if (!SwimmingReworked.IsEnabled())
			{
				DisableAllCopies();
			}
			else
			{
				if ((Object)(object)__instance == (Object)null || (Object)(object)__instance.m_waterSurface == (Object)null)
				{
					return;
				}
				MeshRenderer waterSurface = __instance.m_waterSurface;
				Material sharedMaterial = ((Renderer)waterSurface).sharedMaterial;
				if (WaterShaderPatch.SupportsBackfaceRendering(sharedMaterial))
				{
					DisableCopy(waterSurface);
					CleanupDestroyedEntriesIfNeeded();
					return;
				}
				if (!UnderwaterCopies.TryGetValue(waterSurface, out var value) || value == null || (Object)(object)value.GameObject == (Object)null)
				{
					MeshFilter component = ((Component)waterSurface).GetComponent<MeshFilter>();
					if ((Object)(object)component == (Object)null || (Object)(object)component.sharedMesh == (Object)null)
					{
						return;
					}
					value = CreateUnderwaterSurfaceCopy(((Component)waterSurface).gameObject, waterSurface, component);
					UnderwaterCopies[waterSurface] = value;
				}
				MeshFilter component2 = ((Component)waterSurface).GetComponent<MeshFilter>();
				Mesh val = (((Object)(object)component2 != (Object)null) ? component2.sharedMesh : null);
				if ((Object)(object)value.MeshFilter.sharedMesh != (Object)(object)val)
				{
					value.MeshFilter.sharedMesh = val;
				}
				if ((Object)(object)((Renderer)value.MeshRenderer).sharedMaterial != (Object)(object)sharedMaterial)
				{
					((Renderer)value.MeshRenderer).sharedMaterial = sharedMaterial;
				}
				bool flag = IsCameraUnderwater();
				if (value.GameObject.activeSelf != flag)
				{
					value.GameObject.SetActive(flag);
				}
				_copiesMayBeActive |= flag;
				CleanupDestroyedEntriesIfNeeded();
			}
		}

		private static UnderwaterSurface CreateUnderwaterSurfaceCopy(GameObject original, MeshRenderer originalRenderer, MeshFilter originalFilter)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("SwimmingReworked_UnderwaterSurface");
			val.transform.SetParent(original.transform, false);
			val.transform.localPosition = new Vector3(0f, -0.025f, 0f);
			val.transform.localRotation = Quaternion.Euler(180f, 180f, 0f);
			val.transform.localScale = new Vector3(1.02f, 1f, 1.02f);
			MeshFilter val2 = val.AddComponent<MeshFilter>();
			val2.sharedMesh = originalFilter.sharedMesh;
			MeshRenderer val3 = val.AddComponent<MeshRenderer>();
			((Renderer)val3).sharedMaterial = ((Renderer)originalRenderer).sharedMaterial;
			((Renderer)val3).shadowCastingMode = (ShadowCastingMode)0;
			((Renderer)val3).receiveShadows = false;
			((Renderer)val3).lightProbeUsage = (LightProbeUsage)0;
			((Renderer)val3).reflectionProbeUsage = (ReflectionProbeUsage)0;
			((Renderer)val3).allowOcclusionWhenDynamic = false;
			((Renderer)val3).forceRenderingOff = false;
			((Renderer)val3).enabled = true;
			val.SetActive(false);
			return new UnderwaterSurface(val, val2, val3);
		}

		private static bool IsCameraUnderwater()
		{
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			if (_lastCameraCheckFrame == Time.frameCount)
			{
				return _cachedCameraUnderwater;
			}
			_lastCameraCheckFrame = Time.frameCount;
			_cachedCameraUnderwater = false;
			if ((Object)(object)GameCamera.instance == (Object)null)
			{
				_cachedGameCamera = null;
				_cachedCamera = null;
				return false;
			}
			if ((Object)(object)_cachedGameCamera != (Object)(object)GameCamera.instance || (Object)(object)_cachedCamera == (Object)null)
			{
				_cachedGameCamera = GameCamera.instance;
				_cachedCamera = ((Component)GameCamera.instance).GetComponent<Camera>();
			}
			if ((Object)(object)_cachedCamera == (Object)null || !WaterHelper.TryGetWaterLevel(((Component)_cachedCamera).transform.position, out var waterLevel))
			{
				return false;
			}
			_cachedCameraUnderwater = ((Component)_cachedCamera).transform.position.y < waterLevel - 0.15f;
			return _cachedCameraUnderwater;
		}

		private static void DisableCopy(MeshRenderer originalRenderer)
		{
			if (UnderwaterCopies.TryGetValue(originalRenderer, out var value) && value != null && (Object)(object)value.GameObject != (Object)null && value.GameObject.activeSelf)
			{
				value.GameObject.SetActive(false);
			}
		}

		private static void DisableAllCopies()
		{
			if (!_copiesMayBeActive)
			{
				return;
			}
			foreach (UnderwaterSurface value in UnderwaterCopies.Values)
			{
				if (value != null && (Object)(object)value.GameObject != (Object)null && value.GameObject.activeSelf)
				{
					value.GameObject.SetActive(false);
				}
			}
			_copiesMayBeActive = false;
		}

		private static void CleanupDestroyedEntriesIfNeeded()
		{
			if (Time.frameCount - _lastCleanupFrame < 600)
			{
				return;
			}
			_lastCleanupFrame = Time.frameCount;
			DeadRenderers.Clear();
			foreach (KeyValuePair<MeshRenderer, UnderwaterSurface> underwaterCopy in UnderwaterCopies)
			{
				if ((Object)(object)underwaterCopy.Key == (Object)null || underwaterCopy.Value == null || (Object)(object)underwaterCopy.Value.GameObject == (Object)null)
				{
					DeadRenderers.Add(underwaterCopy.Key);
				}
			}
			foreach (MeshRenderer deadRenderer in DeadRenderers)
			{
				UnderwaterCopies.Remove(deadRenderer);
			}
		}
	}
	[HarmonyPatch(typeof(Player), "UseStamina")]
	internal static class UseStaminaPatch
	{
		private static bool Prefix(Player __instance, ref float v)
		{
			if (!SwimmingReworked.IsEnabled())
			{
				return true;
			}
			if (!SwimmingReworked.DisableVanillaSwimStaminaDrain.Value)
			{
				return true;
			}
			if ((Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer)
			{
				return true;
			}
			if (RuntimeFlags.AllowStaminaUseFromSwimSprint)
			{
				return true;
			}
			if (!WaterHelper.IsBodyInWater(__instance))
			{
				return true;
			}
			StackTrace stackTrace = new StackTrace();
			string text = "";
			for (int i = 2; i < stackTrace.FrameCount && i < 10; i++)
			{
				MethodBase methodBase = stackTrace.GetFrame(i)?.GetMethod();
				if (methodBase != null)
				{
					text = text + methodBase.Name + "_";
				}
			}
			if (text.Contains("OnSwimming") || text.Contains("UpdateSwimming"))
			{
				return false;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(WaterVolume), "UpdateMaterials")]
	internal static class WaterShaderPatch
	{
		private sealed class OriginalMaterialValues
		{
			public bool HasColorTop;

			public bool HasColorBottom;

			public bool HasColorBottomShallow;

			public bool HasSurfaceColor;

			public bool HasDepthFade;

			public bool HasCull;

			public Color ColorTop;

			public Color ColorBottom;

			public Color ColorBottomShallow;

			public Color SurfaceColor;

			public float DepthFade;

			public int Cull;
		}

		private static readonly int ColorTopId = Shader.PropertyToID("_ColorTop");

		private static readonly int ColorBottomId = Shader.PropertyToID("_ColorBottom");

		private static readonly int ColorBottomShallowId = Shader.PropertyToID("_ColorBottomShallow");

		private static readonly int SurfaceColorId = Shader.PropertyToID("_SurfaceColor");

		private static readonly int DepthFadeId = Shader.PropertyToID("_DepthFade");

		private static readonly int CullId = Shader.PropertyToID("_Cull");

		private static readonly Color UnderwaterColorTop = new Color(0.05f, 0.35f, 0.35f, 0.95f);

		private static readonly Color UnderwaterColorBottom = new Color(0.02f, 0.05f, 0.15f, 0.98f);

		private static readonly Color UnderwaterColorBottomShallow = new Color(0.08f, 0.3f, 0.4f, 0.9f);

		private static readonly Color UnderwaterSurfaceColor = new Color(0.1f, 0.35f, 0.45f, 0.85f);

		private static readonly Dictionary<Material, OriginalMaterialValues> OriginalValues = new Dictionary<Material, OriginalMaterialValues>();

		private static void Postfix(WaterVolume __instance)
		{
			if (!SwimmingReworked.IsEnabled())
			{
				RestoreAllMaterials();
			}
			else
			{
				if ((Object)(object)__instance == (Object)null || (Object)(object)__instance.m_waterSurface == (Object)null)
				{
					return;
				}
				MeshRenderer waterSurface = __instance.m_waterSurface;
				Material sharedMaterial = ((Renderer)waterSurface).sharedMaterial;
				if (!((Object)(object)sharedMaterial == (Object)null))
				{
					if (!OriginalValues.TryGetValue(sharedMaterial, out var value))
					{
						value = CaptureOriginalValues(sharedMaterial);
						OriginalValues[sharedMaterial] = value;
					}
					ApplyUnderwaterSettings(sharedMaterial, value);
				}
			}
		}

		internal static bool SupportsBackfaceRendering(Material material)
		{
			return (Object)(object)material != (Object)null && material.HasProperty(CullId);
		}

		private static OriginalMaterialValues CaptureOriginalValues(Material material)
		{
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			OriginalMaterialValues originalMaterialValues = new OriginalMaterialValues
			{
				HasColorTop = material.HasProperty(ColorTopId),
				HasColorBottom = material.HasProperty(ColorBottomId),
				HasColorBottomShallow = material.HasProperty(ColorBottomShallowId),
				HasSurfaceColor = material.HasProperty(SurfaceColorId),
				HasDepthFade = material.HasProperty(DepthFadeId),
				HasCull = material.HasProperty(CullId)
			};
			if (originalMaterialValues.HasColorTop)
			{
				originalMaterialValues.ColorTop = material.GetColor(ColorTopId);
			}
			if (originalMaterialValues.HasColorBottom)
			{
				originalMaterialValues.ColorBottom = material.GetColor(ColorBottomId);
			}
			if (originalMaterialValues.HasColorBottomShallow)
			{
				originalMaterialValues.ColorBottomShallow = material.GetColor(ColorBottomShallowId);
			}
			if (originalMaterialValues.HasSurfaceColor)
			{
				originalMaterialValues.SurfaceColor = material.GetColor(SurfaceColorId);
			}
			if (originalMaterialValues.HasDepthFade)
			{
				originalMaterialValues.DepthFade = material.GetFloat(DepthFadeId);
			}
			if (originalMaterialValues.HasCull)
			{
				originalMaterialValues.Cull = material.GetInt(CullId);
			}
			return originalMaterialValues;
		}

		private static void ApplyUnderwaterSettings(Material material, OriginalMaterialValues original)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			if (original.HasCull && material.GetInt(CullId) != 0)
			{
				material.SetInt(CullId, 0);
			}
			SetColorIfDifferent(material, ColorTopId, original.HasColorTop, UnderwaterColorTop);
			SetColorIfDifferent(material, ColorBottomId, original.HasColorBottom, UnderwaterColorBottom);
			SetColorIfDifferent(material, ColorBottomShallowId, original.HasColorBottomShallow, UnderwaterColorBottomShallow);
			SetColorIfDifferent(material, SurfaceColorId, original.HasSurfaceColor, UnderwaterSurfaceColor);
			if (original.HasDepthFade)
			{
				float num = original.DepthFade * 2f;
				if (!Mathf.Approximately(material.GetFloat(DepthFadeId), num))
				{
					material.SetFloat(DepthFadeId, num);
				}
			}
		}

		private static void SetColorIfDifferent(Material material, int propertyId, bool hasProperty, Color target)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			if (hasProperty && material.GetColor(propertyId) != target)
			{
				material.SetColor(propertyId, target);
			}
		}

		private static void RestoreAllMaterials()
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			if (OriginalValues.Count == 0)
			{
				return;
			}
			foreach (KeyValuePair<Material, OriginalMaterialValues> originalValue in OriginalValues)
			{
				Material key = originalValue.Key;
				if (!((Object)(object)key == (Object)null))
				{
					OriginalMaterialValues value = originalValue.Value;
					if (value.HasColorTop)
					{
						key.SetColor(ColorTopId, value.ColorTop);
					}
					if (value.HasColorBottom)
					{
						key.SetColor(ColorBottomId, value.ColorBottom);
					}
					if (value.HasColorBottomShallow)
					{
						key.SetColor(ColorBottomShallowId, value.ColorBottomShallow);
					}
					if (value.HasSurfaceColor)
					{
						key.SetColor(SurfaceColorId, value.SurfaceColor);
					}
					if (value.HasDepthFade)
					{
						key.SetFloat(DepthFadeId, value.DepthFade);
					}
					if (value.HasCull)
					{
						key.SetInt(CullId, value.Cull);
					}
				}
			}
			OriginalValues.Clear();
		}
	}
}
namespace SwimmingReworked.Effects
{
	internal class DiveImmersionEffect : MonoBehaviour
	{
		internal static bool IsUnderwaterImmersionActive;

		private AudioLowPassFilter _lowPass;

		private bool _active;

		private float _intensity;

		private bool _fogSaved;

		private bool _savedFog;

		private FogMode _savedFogMode;

		private float _savedFogDensity;

		private Color _savedFogColor;

		private Behaviour _ownedHeatDistort;

		private Type _heatDistortType;

		private Behaviour _sunShafts;

		private bool _sunShaftsSaved;

		private bool _sunShaftsWasEnabled;

		private float _savedSunShaftIntensity;

		private float _savedSunShaftBlurRadius;

		private float _savedSunShaftMaxRadius;

		private int _savedSunShaftRadialBlurIterations;

		private const float EnterSwimDepth = 2.15f;

		private const float ExitSwimDepth = 1.75f;

		private const float EnterCameraDepth = 0.45f;

		private const float ExitCameraDepth = 0.2f;

		private const float FadeSpeed = 2.5f;

		private const float UnderwaterFogDensity = 0.018f;

		private const float HeatDistortionIntensity = 2f;

		private static readonly Color UnderwaterFogColor = new Color(0.025f, 0.1f, 0.13f, 1f);

		private static readonly Color HeatDistortionColor = new Color(0.05f, 0.35f, 0.55f, 0.2f);

		private void Update()
		{
			if (ShouldUseDiveEffect())
			{
				_active = true;
			}
			else if (_active && ShouldExitDiveEffect())
			{
				_active = false;
			}
			_intensity = Mathf.MoveTowards(_intensity, _active ? 1f : 0f, Time.deltaTime * 2.5f);
			bool flag = (IsUnderwaterImmersionActive = _intensity > 0.001f);
			EnsureHeatDistortion();
			if ((Object)(object)_ownedHeatDistort != (Object)null)
			{
				_ownedHeatDistort.enabled = flag;
				ApplyHeatDistortionValues();
			}
			UpdateSunShafts(flag);
			if (flag)
			{
				ApplyFog();
				ApplyAudio();
			}
			else
			{
				RestoreFog();
				RestoreAudio();
			}
		}

		private void LateUpdate()
		{
			if (IsUnderwaterImmersionActive)
			{
				UpdateSunShafts(underwater: true);
			}
		}

		private bool ShouldUseDiveEffect()
		{
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			if (!SwimmingReworked.IsEnabled())
			{
				return false;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)GameCamera.instance == (Object)null)
			{
				return false;
			}
			if (!WaterHelper.IsBodyInWater(localPlayer))
			{
				return false;
			}
			Camera component = ((Component)GameCamera.instance).GetComponent<Camera>();
			if ((Object)(object)component == (Object)null)
			{
				return false;
			}
			if (!WaterHelper.TryGetWaterLevel(((Component)component).transform.position, out var waterLevel))
			{
				return false;
			}
			float num = waterLevel - ((Component)component).transform.position.y;
			return ((Character)localPlayer).m_swimDepth >= 2.15f && num >= 0.45f;
		}

		private bool ShouldExitDiveEffect()
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)GameCamera.instance == (Object)null)
			{
				return true;
			}
			if (!WaterHelper.IsBodyInWater(localPlayer))
			{
				return true;
			}
			Camera component = ((Component)GameCamera.instance).GetComponent<Camera>();
			if ((Object)(object)component == (Object)null)
			{
				return true;
			}
			if (!WaterHelper.TryGetWaterLevel(((Component)component).transform.position, out var waterLevel))
			{
				return true;
			}
			float num = waterLevel - ((Component)component).transform.position.y;
			return ((Character)localPlayer).m_swimDepth <= 1.75f || num <= 0.2f;
		}

		private void EnsureHeatDistortion()
		{
			if ((Object)(object)_ownedHeatDistort != (Object)null)
			{
				return;
			}
			GameCamera instance = GameCamera.instance;
			Camera val = ((instance != null) ? ((Component)instance).GetComponent<Camera>() : null);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			_heatDistortType = AccessTools.TypeByName("HeatDistortImageEffect");
			if (_heatDistortType == null)
			{
				SwimmingReworked.LogDebug("[DiveImmersion] HeatDistortImageEffect type not found.");
				return;
			}
			Component component = ((Component)val).GetComponent(_heatDistortType);
			ref Behaviour ownedHeatDistort = ref _ownedHeatDistort;
			Component obj = ((Component)val).gameObject.AddComponent(_heatDistortType);
			ownedHeatDistort = (Behaviour)(object)((obj is Behaviour) ? obj : null);
			if ((Object)(object)_ownedHeatDistort == (Object)null)
			{
				Debug.LogWarning((object)"[DiveImmersion] Failed to add owned HeatDistortImageEffect.");
				return;
			}
			if ((Object)(object)component != (Object)null)
			{
				CopyFields(component, (Component)(object)_ownedHeatDistort);
			}
			_ownedHeatDistort.enabled = false;
			ApplyHeatDistortionValues();
			SwimmingReworked.LogDebug("[DiveImmersion] Created owned HeatDistortImageEffect.");
		}

		private void ApplyHeatDistortionValues()
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_ownedHeatDistort == (Object)null))
			{
				Type type = ((object)_ownedHeatDistort).GetType();
				SetField(type, _ownedHeatDistort, "m_intensity", 2f * _intensity);
				SetField<Color>(type, _ownedHeatDistort, "m_color", HeatDistortionColor);
			}
		}

		private void EnsureSunShafts()
		{
			if ((Object)(object)_sunShafts != (Object)null || (Object)(object)GameCamera.instance == (Object)null)
			{
				return;
			}
			Camera component = ((Component)GameCamera.instance).GetComponent<Camera>();
			if ((Object)(object)component == (Object)null)
			{
				return;
			}
			Behaviour[] components = ((Component)component).GetComponents<Behaviour>();
			foreach (Behaviour val in components)
			{
				if (!((Object)(object)val == (Object)null))
				{
					string text = ((object)val).GetType().FullName ?? ((object)val).GetType().Name;
					if (text == "UnityStandardAssets.ImageEffects.SunShafts" || text.Contains("SunShafts"))
					{
						_sunShafts = val;
						SwimmingReworked.LogDebug("[DiveImmersion] Found SunShafts: " + text);
						break;
					}
				}
			}
		}

		private void UpdateSunShafts(bool underwater)
		{
			EnsureSunShafts();
			if ((Object)(object)_sunShafts == (Object)null)
			{
				return;
			}
			Type type = ((object)_sunShafts).GetType();
			if (underwater)
			{
				if (!_sunShaftsSaved)
				{
					_sunShaftsWasEnabled = _sunShafts.enabled;
					_savedSunShaftIntensity = GetField(type, _sunShafts, "sunShaftIntensity", 1.15f);
					_savedSunShaftBlurRadius = GetField(type, _sunShafts, "sunShaftBlurRadius", 2.5f);
					_savedSunShaftMaxRadius = GetField(type, _sunShafts, "maxRadius", 0.75f);
					_savedSunShaftRadialBlurIterations = GetField(type, _sunShafts, "radialBlurIterations", 2);
					_sunShaftsSaved = true;
				}
				SetField(type, _sunShafts, "sunShaftIntensity", 0f);
				SetField(type, _sunShafts, "sunShaftBlurRadius", 0f);
				SetField(type, _sunShafts, "maxRadius", 0f);
				SetField(type, _sunShafts, "radialBlurIterations", 0);
				_sunShafts.enabled = false;
			}
			else if (_sunShaftsSaved)
			{
				SetField(type, _sunShafts, "sunShaftIntensity", _savedSunShaftIntensity);
				SetField(type, _sunShafts, "sunShaftBlurRadius", _savedSunShaftBlurRadius);
				SetField(type, _sunShafts, "maxRadius", _savedSunShaftMaxRadius);
				SetField(type, _sunShafts, "radialBlurIterations", _savedSunShaftRadialBlurIterations);
				_sunShafts.enabled = _sunShaftsWasEnabled;
				_sunShaftsSaved = false;
			}
		}

		private static void CopyFields(Component source, Component target)
		{
			if ((Object)(object)source == (Object)null || (Object)(object)target == (Object)null)
			{
				return;
			}
			Type type = ((object)source).GetType();
			FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (FieldInfo fieldInfo in fields)
			{
				if (!fieldInfo.IsInitOnly && !fieldInfo.IsLiteral)
				{
					try
					{
						fieldInfo.SetValue(target, fieldInfo.GetValue(source));
					}
					catch
					{
					}
				}
			}
		}

		private static void SetField<T>(Type type, object instance, string fieldName, T value)
		{
			FieldInfo field = type.GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field == null)
			{
				return;
			}
			try
			{
				field.SetValue(instance, value);
			}
			catch
			{
			}
		}

		private static T GetField<T>(Type type, object instance, string fieldName, T fallback)
		{
			FieldInfo field = type.GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field == null)
			{
				return fallback;
			}
			try
			{
				if (field.GetValue(instance) is T result)
				{
					return result;
				}
			}
			catch
			{
			}
			return fallback;
		}

		private void ApplyFog()
		{
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			if (!_fogSaved)
			{
				_savedFog = RenderSettings.fog;
				_savedFogMode = RenderSettings.fogMode;
				_savedFogDensity = RenderSettings.fogDensity;
				_savedFogColor = RenderSettings.fogColor;
				_fogSaved = true;
			}
			RenderSettings.fog = true;
			RenderSettings.fogMode = (FogMode)2;
			RenderSettings.fogDensity = Mathf.Lerp(_savedFogDensity, 0.018f, _intensity);
			RenderSettings.fogColor = Color.Lerp(_savedFogColor, UnderwaterFogColor, _intensity);
		}

		private void RestoreFog()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			if (_fogSaved)
			{
				RenderSettings.fog = _savedFog;
				RenderSettings.fogMode = _savedFogMode;
				RenderSettings.fogDensity = _savedFogDensity;
				RenderSettings.fogColor = _savedFogColor;
				_fogSaved = false;
			}
		}

		private void ApplyAudio()
		{
			AudioListener val = Object.FindFirstObjectByType<AudioListener>();
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			if ((Object)(object)_lowPass == (Object)null)
			{
				_lowPass = ((Component)val).gameObject.GetComponent<AudioLowPassFilter>();
				if ((Object)(object)_lowPass == (Object)null)
				{
					_lowPass = ((Component)val).gameObject.AddComponent<AudioLowPassFilter>();
				}
			}
			((Behaviour)_lowPass).enabled = true;
			_lowPass.cutoffFrequency = Mathf.Lerp(22000f, 1700f, _intensity);
			_lowPass.lowpassResonanceQ = Mathf.Lerp(1f, 1.25f, _intensity);
		}

		private void RestoreAudio()
		{
			if ((Object)(object)_lowPass != (Object)null)
			{
				((Behaviour)_lowPass).enabled = false;
				_lowPass = null;
			}
		}

		private void OnDestroy()
		{
			IsUnderwaterImmersionActive = false;
			UpdateSunShafts(underwater: false);
			RestoreFog();
			RestoreAudio();
			if ((Object)(object)_ownedHeatDistort != (Object)null)
			{
				Object.Destroy((Object)(object)_ownedHeatDistort);
			}
		}
	}
}