Decompiled source of Dive In v1.1.9

DiveIn.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using JetBrains.Annotations;
using Microsoft.CodeAnalysis;
using ServerSync;
using TMPro;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.UI;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Core.ObjectPool;
using YamlDotNet.Core.Tokens;
using YamlDotNet.Helpers;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.BufferedDeserialization;
using YamlDotNet.Serialization.BufferedDeserialization.TypeDiscriminators;
using YamlDotNet.Serialization.Callbacks;
using YamlDotNet.Serialization.Converters;
using YamlDotNet.Serialization.EventEmitters;
using YamlDotNet.Serialization.NamingConventions;
using YamlDotNet.Serialization.NodeDeserializers;
using YamlDotNet.Serialization.NodeTypeResolvers;
using YamlDotNet.Serialization.ObjectFactories;
using YamlDotNet.Serialization.ObjectGraphTraversalStrategies;
using YamlDotNet.Serialization.ObjectGraphVisitors;
using YamlDotNet.Serialization.Schemas;
using YamlDotNet.Serialization.TypeInspectors;
using YamlDotNet.Serialization.TypeResolvers;
using YamlDotNet.Serialization.Utilities;
using YamlDotNet.Serialization.ValueDeserializers;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("DiveIn")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("sighsorry")]
[assembly: AssemblyProduct("DiveIn")]
[assembly: AssemblyCopyright("Copyright ©  2021")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("4358610B-F3F4-4843-B7AF-98B7BC60DCDE")]
[assembly: AssemblyFileVersion("1.1.9")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.9.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class ExtensionMarkerAttribute : Attribute
	{
		private readonly string <Name>k__BackingField;

		public string Name => <Name>k__BackingField;

		public ExtensionMarkerAttribute(string name)
		{
			<Name>k__BackingField = name;
		}
	}
}
namespace ServerSyncModTemplate
{
	[BepInPlugin("sighsorry.DiveIn", "DiveIn", "1.1.9")]
	[BepInIncompatibility("Searica.Valheim.UnderTheSea")]
	[BepInIncompatibility("blacks7ar.VikingsDoSwim")]
	public class ServerSyncModTemplatePlugin : BaseUnityPlugin
	{
		public enum Toggle
		{
			On = 1,
			Off = 0
		}

		private sealed class MonsterDiveYamlRoot : Dictionary<string, MonsterDiveYamlGroup>
		{
			public MonsterDiveYamlRoot()
				: base((IEqualityComparer<string>?)StringComparer.OrdinalIgnoreCase)
			{
			}
		}

		private sealed class MonsterDiveYamlGroup
		{
			public float PassiveMinDepth { get; set; }

			public float PassiveCenterDepth { get; set; }

			public float PassiveMaxDepth { get; set; }

			public float? ActiveMinDepth { get; set; }

			public float? ActiveDepthAdjustSpeed { get; set; }

			public float? ShallowWaterFleeDepth { get; set; }

			public bool? PreserveSpawnDepth { get; set; }

			public bool? AvoidanceSteering { get; set; }

			public List<string> Prefabs { get; set; } = new List<string>();
		}

		public enum FastSwimInputMode
		{
			Press,
			Toggle
		}

		private sealed class ConfigurationManagerAttributes
		{
			public int? Order;
		}

		private sealed class AcceptableShortcuts : AcceptableValueBase
		{
			internal AcceptableShortcuts()
				: base(typeof(KeyboardShortcut))
			{
			}

			public override object Clamp(object value)
			{
				return value;
			}

			public override bool IsValid(object value)
			{
				return true;
			}

			public override string ToDescriptionString()
			{
				return "# Acceptable values: " + string.Join(", ", UnityInput.Current.SupportedKeyCodes);
			}
		}

		private readonly struct PassiveDepthProfile
		{
			public readonly float CenterDepth;

			public readonly float MinDepth;

			public readonly float MaxDepth;

			public PassiveDepthProfile(float centerDepth, float minDepth, float maxDepth)
			{
				CenterDepth = centerDepth;
				MinDepth = minDepth;
				MaxDepth = maxDepth;
			}
		}

		private readonly struct ConfiguredDiveProfile
		{
			public readonly string GroupName;

			public readonly PassiveDepthProfile PassiveDepthProfile;

			public readonly float ActiveMinDepth;

			public readonly float ActiveDepthAdjustSpeed;

			public readonly float ShallowWaterFleeDepth;

			public readonly bool PreserveSpawnDepth;

			public readonly bool AvoidanceSteering;

			public ConfiguredDiveProfile(string groupName, PassiveDepthProfile passiveDepthProfile, float activeMinDepth, float activeDepthAdjustSpeed, float shallowWaterFleeDepth, bool preserveSpawnDepth, bool avoidanceSteering)
			{
				GroupName = groupName;
				PassiveDepthProfile = passiveDepthProfile;
				ActiveMinDepth = activeMinDepth;
				ActiveDepthAdjustSpeed = activeDepthAdjustSpeed;
				ShallowWaterFleeDepth = shallowWaterFleeDepth;
				PreserveSpawnDepth = preserveSpawnDepth;
				AvoidanceSteering = avoidanceSteering;
			}
		}

		private readonly struct OriginalDiveFlags
		{
			public readonly MonsterAI MonsterAI;

			public readonly bool AvoidWater;

			public readonly bool AvoidLand;

			public readonly bool CanSwim;

			public readonly float? SwimDepth;

			public OriginalDiveFlags(MonsterAI monsterAI, bool avoidWater, bool avoidLand, bool canSwim, float? swimDepth)
			{
				MonsterAI = monsterAI;
				AvoidWater = avoidWater;
				AvoidLand = avoidLand;
				CanSwim = canSwim;
				SwimDepth = swimDepth;
			}
		}

		private readonly struct ShallowWaterFleeRequest
		{
			public readonly bool ShouldFlee;

			public readonly Vector3 FleeFrom;

			public ShallowWaterFleeRequest(Vector3 fleeFrom)
			{
				//IL_0008: Unknown result type (might be due to invalid IL or missing references)
				//IL_0009: Unknown result type (might be due to invalid IL or missing references)
				ShouldFlee = true;
				FleeFrom = fleeFrom;
			}
		}

		private readonly struct SteeringMemoryEntry
		{
			public readonly float Time;

			public readonly Vector3 Position;

			public readonly Vector3 Target;

			public readonly float Side;

			public SteeringMemoryEntry(float time, Vector3 position, Vector3 target, float side)
			{
				//IL_0008: Unknown result type (might be due to invalid IL or missing references)
				//IL_0009: 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_0010: Unknown result type (might be due to invalid IL or missing references)
				Time = time;
				Position = position;
				Target = target;
				Side = side;
			}
		}

		private readonly struct UnderwaterNavigationPlan
		{
			public readonly bool HasRoute;

			public readonly Vector3 Direction;

			public readonly float LookaheadDistance;

			public UnderwaterNavigationPlan(bool hasRoute, Vector3 direction, float lookaheadDistance)
			{
				//IL_0008: Unknown result type (might be due to invalid IL or missing references)
				//IL_0009: Unknown result type (might be due to invalid IL or missing references)
				HasRoute = hasRoute;
				Direction = direction;
				LookaheadDistance = lookaheadDistance;
			}
		}

		private readonly struct SwimDepthGoal
		{
			public readonly float DesiredDepth;

			public readonly float ClampedTargetY;

			public readonly bool RequestedOutsideRange;

			public readonly float AdjustSpeed;

			public SwimDepthGoal(float desiredDepth, float clampedTargetY, bool requestedOutsideRange, float adjustSpeed)
			{
				DesiredDepth = desiredDepth;
				ClampedTargetY = clampedTargetY;
				RequestedOutsideRange = requestedOutsideRange;
				AdjustSpeed = adjustSpeed;
			}
		}

		[HarmonyPatch(typeof(BaseAI), "OnDestroy")]
		private static class BaseAIOnDestroyPatch
		{
			private static void Postfix(BaseAI __instance)
			{
				MonsterAI val = (MonsterAI)(object)((__instance is MonsterAI) ? __instance : null);
				if (val != null)
				{
					int instanceID = ((Object)val).GetInstanceID();
					SteeringMemory.Remove(instanceID);
					if (OriginalDiveFlagsByInstance.TryGetValue(instanceID, out var value) && value.MonsterAI == val)
					{
						RemoveTrackedMonsterState(instanceID);
					}
				}
			}
		}

		[HarmonyPatch(typeof(MonsterAI), "Awake")]
		private static class MonsterAIAwakePatch
		{
			private static void Postfix(MonsterAI __instance)
			{
				if (TryGetConfiguredDiveProfile(__instance, out var configuredDiveProfile))
				{
					EnsureDiveFlags(__instance, configuredDiveProfile);
				}
			}
		}

		[HarmonyPatch(typeof(MonsterAI), "UpdateAI")]
		private static class MonsterAIUpdateAIPatch
		{
			private static void Prefix(MonsterAI __instance, out ShallowWaterFleeRequest __state)
			{
				__state = default(ShallowWaterFleeRequest);
				if (TryGetConfiguredDiveProfile(__instance, out var configuredDiveProfile))
				{
					EnsureDiveFlags(__instance, configuredDiveProfile);
					__state = GetShallowWaterFleeRequest(__instance, configuredDiveProfile);
				}
			}

			private static void Postfix(MonsterAI __instance, float dt, ShallowWaterFleeRequest __state, ref bool __result)
			{
				if (__state.ShouldFlee && __result)
				{
					if (HasVanillaMonsterAIPriority(__instance))
					{
						ShallowWaterFleeingByInstance.Remove(((Object)__instance).GetInstanceID());
						return;
					}
					ApplyShallowWaterFlee(__instance, dt, __state);
					__result = true;
				}
			}
		}

		[HarmonyPatch(typeof(BaseAI), "HavePath")]
		private static class BaseAIHavePathPatch
		{
			private static bool Prefix(BaseAI __instance, Vector3 target, ref bool __result)
			{
				//IL_002b: Unknown result type (might be due to invalid IL or missing references)
				if (!TryGetConfiguredMonster(__instance, out MonsterAI monsterAI, out ConfiguredDiveProfile configuredDiveProfile) || !ShouldUseWaterDiveMode(monsterAI))
				{
					return true;
				}
				Character character = ((BaseAI)monsterAI).m_character;
				if ((Object)(object)character == (Object)null)
				{
					return true;
				}
				__result = BuildUnderwaterNavigationPlan(__instance, character, target, configuredDiveProfile).HasRoute;
				return false;
			}
		}

		[HarmonyPatch(typeof(BaseAI), "MoveTo")]
		private static class BaseAIMoveToPatch
		{
			private static bool Prefix(BaseAI __instance, float dt, Vector3 point, float dist, bool run, ref bool __result)
			{
				//IL_002a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0034: Unknown result type (might be due to invalid IL or missing references)
				//IL_0070: Unknown result type (might be due to invalid IL or missing references)
				//IL_0077: Unknown result type (might be due to invalid IL or missing references)
				//IL_0081: Unknown result type (might be due to invalid IL or missing references)
				//IL_008d: 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_00f8: 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_013c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0124: Unknown result type (might be due to invalid IL or missing references)
				if (!TryGetConfiguredMonster(__instance, out MonsterAI monsterAI, out ConfiguredDiveProfile configuredDiveProfile) || !ShouldUseWaterDiveMode(monsterAI))
				{
					return true;
				}
				Character character = ((BaseAI)monsterAI).m_character;
				if ((Object)(object)character == (Object)null)
				{
					return true;
				}
				SwimDepthGoal goal = CalculateSwimDepthGoal(monsterAI, character, point, configuredDiveProfile);
				UnderwaterNavigationPlan underwaterNavigationPlan = BuildUnderwaterNavigationPlan(__instance, character, point, configuredDiveProfile);
				ApplySwimDepthGoal(character, goal, dt);
				float num = (((BaseAI)monsterAI).m_serpentMovement ? 3f : (run ? 1f : 0.5f));
				float num2 = Mathf.Max(dist, num);
				float num3 = Utils.DistanceXZ(point, ((Component)__instance).transform.position);
				float num4 = Mathf.Abs(point.y - ((Component)__instance).transform.position.y);
				float num5 = Mathf.Abs(goal.ClampedTargetY - ((Component)__instance).transform.position.y);
				bool flag = num4 < 0.75f || (goal.RequestedOutsideRange && num5 < 0.35f);
				if (num3 < num2 && (((BaseAI)monsterAI).m_serpentMovement || flag))
				{
					__instance.StopMoving();
					__result = true;
					return false;
				}
				Vector3 direction = underwaterNavigationPlan.Direction;
				if (((Vector3)(ref direction)).sqrMagnitude <= 0.0001f)
				{
					__instance.StopMoving();
					__result = true;
					return false;
				}
				if (((BaseAI)monsterAI).m_serpentMovement)
				{
					__instance.MoveTowardsSwoop(underwaterNavigationPlan.Direction, run, underwaterNavigationPlan.LookaheadDistance);
				}
				else
				{
					__instance.MoveTowards(underwaterNavigationPlan.Direction, run);
				}
				__result = false;
				return false;
			}
		}

		internal const string ModName = "DiveIn";

		internal const string ModVersion = "1.1.9";

		internal const string Author = "sighsorry";

		private const string ModGUID = "sighsorry.DiveIn";

		private static readonly string ConfigFileName = "sighsorry.DiveIn.cfg";

		private static readonly string ConfigFileFullPath;

		private readonly Harmony _harmony = new Harmony("sighsorry.DiveIn");

		public static readonly ManualLogSource ServerSyncModTemplateLogger;

		private static readonly ConfigSync ConfigSync;

		private FileSystemWatcher _watcher;

		private readonly object _reloadLock = new object();

		private string? _lastConfigFileText;

		private bool _isShuttingDown;

		private static ConfigEntry<Toggle> _serverConfigLocked;

		private static readonly string MonsterDiveYamlFileName;

		private static readonly string MonsterDiveYamlFileFullPath;

		private static readonly object MonsterDiveYamlLock;

		private static readonly IDeserializer MonsterDiveYamlDeserializer;

		private FileSystemWatcher _monsterDiveYamlWatcher;

		private static CustomSyncedValue<string> _monsterDiveYamlSync;

		private static string? _lastAppliedMonsterDiveYamlText;

		internal const float DefaultUnderwaterDarknessFactor = 0.5f;

		internal const float DefaultUnderwaterVisibilityFalloff = 0.25f;

		internal static ConfigEntry<string> _waterEquipmentBlacklist;

		internal static ConfigEntry<float> _surfaceStaminaRegenRateMultiplier;

		internal static ConfigEntry<float> _midwaterStaminaRegenRateMultiplier;

		internal static ConfigEntry<float> _surfaceEitrRegenRateMultiplier;

		internal static ConfigEntry<float> _midwaterEitrRegenRateMultiplier;

		internal static ConfigEntry<float> _midwaterIdleStaminaDrainPerDepth;

		internal static ConfigEntry<float> _swimStaminaDrainMultiplierPerDepth;

		internal static ConfigEntry<Toggle> _multiplicativeSwimStaminaModifiers;

		internal static ConfigEntry<float> _swimStaminaDrainBaseMultiplier;

		internal static ConfigEntry<float> _playerSwimSkillSpeedMultiplier;

		internal static ConfigEntry<float> _fastSwimSpeedMultiplier;

		internal static ConfigEntry<float> _fastSwimStaminaDrainMultiplier;

		internal static ConfigEntry<float> _encumberedSwimSpeedMultiplier;

		internal static ConfigEntry<FastSwimInputMode> _fastSwimInputMode;

		internal static ConfigEntry<float> _playerProjectileUnderwaterTtlMultiplier;

		internal static ConfigEntry<float> _playerProjectileUnderwaterSpeedMultiplier;

		internal static ConfigEntry<float> _playerProjectileUnderwaterDamageMultiplier;

		internal static ConfigEntry<KeyboardShortcut> _playerDiveAscendShortcut;

		internal static ConfigEntry<KeyboardShortcut> _playerDiveDescendShortcut;

		internal static ConfigEntry<float> _underwaterDarknessFactor;

		internal static ConfigEntry<float> _underwaterVisibilityFalloff;

		private static IReadOnlyDictionary<string, ConfiguredDiveProfile> _configuredDiveProfilesByPrefabName;

		private static readonly Dictionary<int, OriginalDiveFlags> OriginalDiveFlagsByInstance;

		private static readonly HashSet<int> InitialSpawnDepthPreservedByInstance;

		private static readonly HashSet<int> ShallowWaterFleeingByInstance;

		private const int MaxCacheEntries = 2048;

		private const bool DefaultPreserveSpawnDepth = false;

		private const bool DefaultAvoidanceSteering = true;

		private const float ShallowWaterFleeExitBuffer = 1f;

		private const float ShallowWaterRetargetDelay = 3f;

		private const float PassiveWavePeriodSeconds = 12f;

		private const float DefaultActiveSwimDepthMin = 0.25f;

		private const float DefaultShallowWaterFleeDepth = 0f;

		private const float ActiveSwimDepthMax = 300f;

		private const float SwimDepthAdjustSpeed = 2f;

		private const float SteeringMemorySeconds = 0.1f;

		private const float SteeringMemoryDistance = 1f;

		private const float PreferredSteerAngleMax = 70f;

		private const float MaximumSteeringLookaheadDistance = 6f;

		private static readonly float[] SteerAngles;

		private static readonly Dictionary<int, SteeringMemoryEntry> SteeringMemory;

		public void Awake()
		{
			_isShuttingDown = false;
			bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			try
			{
				_serverConfigLocked = config("1 - General", "Lock Configuration", Toggle.On, "If on, the configuration is locked and can be changed by server admins only.");
				ConfigSync.AddLockingConfigEntry<Toggle>(_serverConfigLocked);
				InitializePlayerDiveConfig();
				InitializeMonsterDiveYaml();
				_harmony.PatchAll(Assembly.GetExecutingAssembly());
				DiveLocalization.Register();
				SetupConfigWatcher();
				SetupMonsterDiveYamlWatcher();
				((BaseUnityPlugin)this).Config.Save();
				_lastConfigFileText = ReadFileTextIfExists(ConfigFileFullPath);
			}
			catch
			{
				Cleanup(saveConfig: false);
				throw;
			}
			finally
			{
				((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet;
			}
		}

		private void OnDestroy()
		{
			Cleanup(saveConfig: true);
		}

		private void Cleanup(bool saveConfig)
		{
			if (_isShuttingDown)
			{
				return;
			}
			_isShuttingDown = true;
			TryCleanup((Action)_harmony.UnpatchSelf, "Harmony patches");
			TryCleanup(delegate
			{
				PlayerDiveController localInstance = PlayerDiveController.LocalInstance;
				if (!((Object)(object)localInstance == (Object)null))
				{
					localInstance.DisableUnderwaterMovement();
					Object.Destroy((Object)(object)localInstance);
				}
			}, "player dive state");
			TryCleanup(PlayerDiveKeyHints.DestroyHints, "player key hints");
			TryCleanup(UnderwaterVisualState.ResetAll, "underwater visuals");
			TryCleanup(delegate
			{
				int num = RestoreAllTrackedMonsterDiveFlags();
				if (num > 0)
				{
					ServerSyncModTemplateLogger.LogInfo((object)$"Restored original dive flags for {num} monster instances.");
				}
			}, "monster dive flags");
			TryCleanup(ClearSteeringMemory, "monster steering memory");
			TryCleanup(DisposeMonsterDiveYamlWatcher, "monster YAML watcher");
			TryCleanup(DisposeConfigWatcher, "configuration watcher");
			if (saveConfig)
			{
				TryCleanup(delegate
				{
					SaveWithRespectToConfigSet();
				}, "configuration save");
			}
		}

		private static void TryCleanup(Action cleanup, string description)
		{
			try
			{
				cleanup();
			}
			catch (Exception ex)
			{
				ServerSyncModTemplateLogger.LogError((object)("Failed to clean up " + description + ": " + ex.Message));
			}
		}

		private void SetupConfigWatcher()
		{
			_watcher = new FileSystemWatcher(Paths.ConfigPath, ConfigFileName);
			_watcher.Changed += ReadConfigValues;
			_watcher.Created += ReadConfigValues;
			_watcher.Renamed += ReadConfigValues;
			_watcher.SynchronizingObject = ThreadingHelper.SynchronizingObject;
			_watcher.EnableRaisingEvents = true;
		}

		private void DisposeConfigWatcher()
		{
			if (_watcher != null)
			{
				_watcher.Changed -= ReadConfigValues;
				_watcher.Created -= ReadConfigValues;
				_watcher.Renamed -= ReadConfigValues;
				_watcher.Dispose();
				_watcher = null;
			}
		}

		private void ReadConfigValues(object sender, FileSystemEventArgs e)
		{
			if (_isShuttingDown)
			{
				return;
			}
			lock (_reloadLock)
			{
				if (!File.Exists(ConfigFileFullPath))
				{
					ServerSyncModTemplateLogger.LogWarning((object)"Config file does not exist. Skipping reload.");
					return;
				}
				try
				{
					string b = File.ReadAllText(ConfigFileFullPath);
					if (!string.Equals(_lastConfigFileText, b, StringComparison.Ordinal))
					{
						SaveWithRespectToConfigSet(reload: true);
						_lastConfigFileText = ReadFileTextIfExists(ConfigFileFullPath);
						UnderwaterVisualState.ResetAll();
						ClearSteeringMemory();
						ServerSyncModTemplateLogger.LogInfo((object)"Configuration reload complete.");
					}
				}
				catch (Exception ex)
				{
					ServerSyncModTemplateLogger.LogError((object)("Error reloading configuration: " + ex.Message));
				}
			}
		}

		private static string? ReadFileTextIfExists(string path)
		{
			if (!File.Exists(path))
			{
				return null;
			}
			return File.ReadAllText(path);
		}

		private void SaveWithRespectToConfigSet(bool reload = false)
		{
			bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			try
			{
				if (reload)
				{
					((BaseUnityPlugin)this).Config.Reload();
				}
				((BaseUnityPlugin)this).Config.Save();
			}
			finally
			{
				((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet;
			}
		}

		private ConfigEntry<T> config<T>(string group, string name, T value, ConfigDescription description, bool synchronizedSetting = true)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected O, but got Unknown
			ConfigDescription val = new ConfigDescription(description.Description + (synchronizedSetting ? " [Synced with Server]" : " [Not Synced with Server]"), description.AcceptableValues, description.Tags);
			ConfigEntry<T> val2 = ((BaseUnityPlugin)this).Config.Bind<T>(group, name, value, val);
			ConfigSync.AddConfigEntry<T>(val2).SynchronizedConfig = synchronizedSetting;
			return val2;
		}

		private ConfigEntry<T> config<T>(string group, string name, T value, string description, bool synchronizedSetting = true)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Expected O, but got Unknown
			return config(group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>()), synchronizedSetting);
		}

		private void InitializeMonsterDiveYaml()
		{
			_monsterDiveYamlSync = new CustomSyncedValue<string>(ConfigSync, "MonsterDiveYaml", string.Empty);
			_monsterDiveYamlSync.ValueChanged += OnMonsterDiveYamlValueChanged;
			ConfigSync.SourceOfTruthChanged += OnMonsterDiveSourceOfTruthChanged;
			if (ConfigSync.IsSourceOfTruth)
			{
				LoadMonsterDiveYamlFromDisk("startup");
			}
			else if (!string.IsNullOrWhiteSpace(_monsterDiveYamlSync.Value))
			{
				ApplyMonsterDiveYaml(_monsterDiveYamlSync.Value, "startup synced value");
			}
		}

		private void SetupMonsterDiveYamlWatcher()
		{
			_monsterDiveYamlWatcher = new FileSystemWatcher(Paths.ConfigPath, MonsterDiveYamlFileName);
			_monsterDiveYamlWatcher.Changed += ReadMonsterDiveYamlValues;
			_monsterDiveYamlWatcher.Created += ReadMonsterDiveYamlValues;
			_monsterDiveYamlWatcher.Renamed += ReadMonsterDiveYamlValues;
			_monsterDiveYamlWatcher.SynchronizingObject = ThreadingHelper.SynchronizingObject;
			_monsterDiveYamlWatcher.EnableRaisingEvents = true;
		}

		private void DisposeMonsterDiveYamlWatcher()
		{
			if (_monsterDiveYamlSync != null)
			{
				_monsterDiveYamlSync.ValueChanged -= OnMonsterDiveYamlValueChanged;
			}
			ConfigSync.SourceOfTruthChanged -= OnMonsterDiveSourceOfTruthChanged;
			if (_monsterDiveYamlWatcher != null)
			{
				_monsterDiveYamlWatcher.Changed -= ReadMonsterDiveYamlValues;
				_monsterDiveYamlWatcher.Created -= ReadMonsterDiveYamlValues;
				_monsterDiveYamlWatcher.Renamed -= ReadMonsterDiveYamlValues;
				_monsterDiveYamlWatcher.Dispose();
				_monsterDiveYamlWatcher = null;
			}
		}

		private void ReadMonsterDiveYamlValues(object sender, FileSystemEventArgs e)
		{
			if (_isShuttingDown)
			{
				return;
			}
			lock (_reloadLock)
			{
				if (!ConfigSync.IsSourceOfTruth)
				{
					ServerSyncModTemplateLogger.LogInfo((object)"Ignoring local monster dive YAML reload because remote synced values are active.");
					return;
				}
				try
				{
					string lastAppliedMonsterDiveYamlText = _lastAppliedMonsterDiveYamlText;
					if (LoadMonsterDiveYamlFromDisk("yaml reload") && !string.Equals(lastAppliedMonsterDiveYamlText, _lastAppliedMonsterDiveYamlText, StringComparison.Ordinal))
					{
						ServerSyncModTemplateLogger.LogInfo((object)"Monster dive YAML reload complete.");
					}
				}
				catch (Exception ex)
				{
					ServerSyncModTemplateLogger.LogError((object)("Error reloading monster dive YAML: " + ex.Message));
				}
			}
		}

		private void OnMonsterDiveSourceOfTruthChanged(bool isSourceOfTruth)
		{
			if (isSourceOfTruth)
			{
				LoadMonsterDiveYamlFromDisk("source of truth changed to local");
			}
			else if (!string.IsNullOrWhiteSpace(_monsterDiveYamlSync.Value))
			{
				ApplyMonsterDiveYaml(_monsterDiveYamlSync.Value, "source of truth changed to remote");
			}
		}

		private void OnMonsterDiveYamlValueChanged()
		{
			if (!ConfigSync.IsSourceOfTruth && !string.IsNullOrWhiteSpace(_monsterDiveYamlSync.Value))
			{
				ApplyMonsterDiveYaml(_monsterDiveYamlSync.Value, "synced value changed");
			}
		}

		private bool LoadMonsterDiveYamlFromDisk(string reason)
		{
			lock (MonsterDiveYamlLock)
			{
				if (!File.Exists(MonsterDiveYamlFileFullPath))
				{
					string contents = BuildDefaultMonsterDiveYaml();
					Directory.CreateDirectory(Paths.ConfigPath);
					File.WriteAllText(MonsterDiveYamlFileFullPath, contents);
				}
				string text = File.ReadAllText(MonsterDiveYamlFileFullPath);
				if (!ApplyMonsterDiveYaml(text, reason))
				{
					return false;
				}
				if (_monsterDiveYamlSync.Value != text)
				{
					_monsterDiveYamlSync.Value = text;
				}
				return true;
			}
		}

		private static bool ApplyMonsterDiveYaml(string yamlText, string reason)
		{
			if (string.IsNullOrWhiteSpace(yamlText))
			{
				ServerSyncModTemplateLogger.LogWarning((object)("Monster dive YAML is empty during " + reason + ". Keeping previous settings."));
				return false;
			}
			if (string.Equals(_lastAppliedMonsterDiveYamlText, yamlText, StringComparison.Ordinal))
			{
				return true;
			}
			MonsterDiveYamlRoot root;
			try
			{
				root = MonsterDiveYamlDeserializer.Deserialize<MonsterDiveYamlRoot>(yamlText) ?? new MonsterDiveYamlRoot();
			}
			catch (YamlException ex)
			{
				string text = ((ex.Start.Line > 0) ? $" at line {ex.Start.Line}, column {ex.Start.Column}" : string.Empty);
				ServerSyncModTemplateLogger.LogError((object)("Failed to parse monster dive YAML during " + reason + text + ": " + ex.Message));
				return false;
			}
			catch (Exception ex2)
			{
				ServerSyncModTemplateLogger.LogError((object)("Failed to parse monster dive YAML during " + reason + ": " + ex2.Message));
				return false;
			}
			Dictionary<string, MonsterDiveYamlGroup> definedGroups = GetDefinedGroups(root);
			Dictionary<string, ConfiguredDiveProfile> dictionary = new Dictionary<string, ConfiguredDiveProfile>(StringComparer.OrdinalIgnoreCase);
			foreach (KeyValuePair<string, MonsterDiveYamlGroup> item in definedGroups)
			{
				string key = item.Key;
				MonsterDiveYamlGroup value = item.Value;
				if (!TryNormalizePassiveDepthProfile(key, value.PassiveMinDepth, value.PassiveCenterDepth, value.PassiveMaxDepth, out var passiveDepthProfile) || !TryNormalizeActiveMinDepth(key, value.ActiveMinDepth, out var normalizedMinDepth) || !TryNormalizeActiveDepthAdjustSpeed(key, value.ActiveDepthAdjustSpeed, out var normalizedAdjustSpeed) || !TryNormalizeShallowWaterFleeDepth(key, value.ShallowWaterFleeDepth, out var normalizedFleeDepth))
				{
					return false;
				}
				bool valueOrDefault = value.PreserveSpawnDepth == true;
				bool avoidanceSteering = value.AvoidanceSteering ?? true;
				AddYamlGroupEntries(configuredDiveProfile: new ConfiguredDiveProfile(key, passiveDepthProfile, normalizedMinDepth, normalizedAdjustSpeed, normalizedFleeDepth, valueOrDefault, avoidanceSteering), configuredProfilesByPrefabName: dictionary, mobs: value.Prefabs);
			}
			if (dictionary.Count == 0)
			{
				ServerSyncModTemplateLogger.LogWarning((object)("Monster dive YAML loaded during " + reason + ", but no prefabs are assigned to any group."));
			}
			_configuredDiveProfilesByPrefabName = dictionary;
			_lastAppliedMonsterDiveYamlText = yamlText;
			int num = RestoreRemovedMonsterDiveFlags();
			ClearSteeringMemory();
			ServerSyncModTemplateLogger.LogInfo((object)$"Loaded monster dive YAML ({reason}). passiveGroups={definedGroups.Count}, prefabs={dictionary.Count}, active[defaultMin={0.25f:F2}, max={300f:F2}, defaultAdjust={2f:F2}], shallowFleeDefault={0f:F2}, preserveSpawnDefault={false}, restoredRemovedInstances={num}.");
			return true;
		}

		private static bool TryNormalizeActiveMinDepth(string groupName, float? activeMinDepth, out float normalizedMinDepth)
		{
			if (!activeMinDepth.HasValue)
			{
				normalizedMinDepth = 0.25f;
				return true;
			}
			float value = activeMinDepth.Value;
			if (!ValidateFiniteYamlNumber(groupName, "active_min_depth", value))
			{
				normalizedMinDepth = 0f;
				return false;
			}
			normalizedMinDepth = Mathf.Clamp(value, 0f, 300f);
			if (!Mathf.Approximately(value, normalizedMinDepth))
			{
				ServerSyncModTemplateLogger.LogWarning((object)("Monster dive YAML normalized active profile '" + groupName + "': active_min_depth " + value.ToString("0.###", CultureInfo.InvariantCulture) + " -> " + normalizedMinDepth.ToString("0.###", CultureInfo.InvariantCulture) + "."));
			}
			return true;
		}

		private static bool TryNormalizeActiveDepthAdjustSpeed(string groupName, float? activeDepthAdjustSpeed, out float normalizedAdjustSpeed)
		{
			if (!activeDepthAdjustSpeed.HasValue)
			{
				normalizedAdjustSpeed = 2f;
				return true;
			}
			float value = activeDepthAdjustSpeed.Value;
			if (!ValidateFiniteYamlNumber(groupName, "active_depth_adjust_speed", value))
			{
				normalizedAdjustSpeed = 0f;
				return false;
			}
			normalizedAdjustSpeed = Mathf.Max(0f, value);
			if (!Mathf.Approximately(normalizedAdjustSpeed, value))
			{
				ServerSyncModTemplateLogger.LogWarning((object)("Monster dive YAML normalized active profile '" + groupName + "': active_depth_adjust_speed " + value.ToString("0.###", CultureInfo.InvariantCulture) + " -> " + normalizedAdjustSpeed.ToString("0.###", CultureInfo.InvariantCulture) + "."));
			}
			return true;
		}

		private static bool TryNormalizeShallowWaterFleeDepth(string groupName, float? shallowWaterFleeDepth, out float normalizedFleeDepth)
		{
			if (!shallowWaterFleeDepth.HasValue)
			{
				normalizedFleeDepth = 0f;
				return true;
			}
			float value = shallowWaterFleeDepth.Value;
			if (!ValidateFiniteYamlNumber(groupName, "shallow_water_flee_depth", value))
			{
				normalizedFleeDepth = 0f;
				return false;
			}
			normalizedFleeDepth = Mathf.Clamp(value, 0f, 300f);
			if (!Mathf.Approximately(value, normalizedFleeDepth))
			{
				ServerSyncModTemplateLogger.LogWarning((object)("Monster dive YAML normalized active profile '" + groupName + "': shallow_water_flee_depth " + value.ToString("0.###", CultureInfo.InvariantCulture) + " -> " + normalizedFleeDepth.ToString("0.###", CultureInfo.InvariantCulture) + "."));
			}
			return true;
		}

		private static bool TryNormalizePassiveDepthProfile(string groupName, float minDepth, float centerDepth, float maxDepth, out PassiveDepthProfile passiveDepthProfile)
		{
			if (!ValidateFiniteYamlNumber(groupName, "passive_min_depth", minDepth) || !ValidateFiniteYamlNumber(groupName, "passive_center_depth", centerDepth) || !ValidateFiniteYamlNumber(groupName, "passive_max_depth", maxDepth))
			{
				passiveDepthProfile = default(PassiveDepthProfile);
				return false;
			}
			float num = minDepth;
			float num2 = centerDepth;
			float num3 = maxDepth;
			float num4 = Mathf.Clamp(num, 0f, 300f);
			float num5 = Mathf.Clamp(num3, 0f, 300f);
			if (num5 < num4)
			{
				float num6 = num5;
				num5 = num4;
				num4 = num6;
			}
			float num7 = Mathf.Clamp(num2, num4, num5);
			if (!Mathf.Approximately(num4, num) || !Mathf.Approximately(num5, num3) || !Mathf.Approximately(num7, num2))
			{
				ServerSyncModTemplateLogger.LogWarning((object)("Monster dive YAML normalized passive profile '" + groupName + "': passive_min_depth " + num.ToString("0.###", CultureInfo.InvariantCulture) + " -> " + num4.ToString("0.###", CultureInfo.InvariantCulture) + ", passive_center_depth " + num2.ToString("0.###", CultureInfo.InvariantCulture) + " -> " + num7.ToString("0.###", CultureInfo.InvariantCulture) + ", passive_max_depth " + num3.ToString("0.###", CultureInfo.InvariantCulture) + " -> " + num5.ToString("0.###", CultureInfo.InvariantCulture) + "."));
			}
			passiveDepthProfile = new PassiveDepthProfile(num7, num4, num5);
			return true;
		}

		private static bool ValidateFiniteYamlNumber(string groupName, string fieldName, float value)
		{
			if (!float.IsNaN(value) && !float.IsInfinity(value))
			{
				return true;
			}
			ServerSyncModTemplateLogger.LogError((object)("Monster dive YAML contains non-finite " + fieldName + " in profile '" + groupName + "'. Keeping previous settings."));
			return false;
		}

		private static Dictionary<string, MonsterDiveYamlGroup> GetDefinedGroups(MonsterDiveYamlRoot root)
		{
			Dictionary<string, MonsterDiveYamlGroup> dictionary = new Dictionary<string, MonsterDiveYamlGroup>(StringComparer.OrdinalIgnoreCase);
			if (root.Count == 0)
			{
				return dictionary;
			}
			foreach (KeyValuePair<string, MonsterDiveYamlGroup> item in root)
			{
				string text = item.Key?.Trim() ?? string.Empty;
				if (text.Length == 0)
				{
					ServerSyncModTemplateLogger.LogWarning((object)"Monster dive YAML contains an empty top-level group name. Skipping it.");
				}
				else
				{
					dictionary[text] = item.Value ?? new MonsterDiveYamlGroup();
				}
			}
			return dictionary;
		}

		private static void AddYamlGroupEntries(Dictionary<string, ConfiguredDiveProfile> configuredProfilesByPrefabName, IEnumerable<string>? mobs, ConfiguredDiveProfile configuredDiveProfile)
		{
			if (mobs == null)
			{
				return;
			}
			foreach (string mob in mobs)
			{
				string text = mob?.Trim() ?? string.Empty;
				if (text.Length != 0)
				{
					if (configuredProfilesByPrefabName.ContainsKey(text))
					{
						ServerSyncModTemplateLogger.LogWarning((object)("Monster dive YAML duplicate mob '" + text + "' found in " + configuredDiveProfile.GroupName + ". Keeping first assignment."));
					}
					else
					{
						configuredProfilesByPrefabName[text] = configuredDiveProfile;
					}
				}
			}
		}

		private static string BuildDefaultMonsterDiveYaml()
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("# Monster dive configuration for DiveIn.");
			stringBuilder.AppendLine("# Unknown keys and duplicate keys are treated as errors and keep the previous applied settings.");
			stringBuilder.AppendLine();
			AppendDefaultGroup(stringBuilder, "surface_patrol", 0f, 10f, 20f, 2f, includeGroupHeaderComment: true, includeFieldComments: true, avoidanceSteering: true, new string[4] { "Leech", "Abomination", "Serpent", "BonemawSerpent" });
			stringBuilder.AppendLine();
			AppendDefaultGroup(stringBuilder, "mid_water", 0f, 15f, 30f, 2f);
			stringBuilder.AppendLine();
			AppendDefaultGroup(stringBuilder, "deep_patrol", 10f, 20f, 30f, 2f);
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("## Mod prefabs sample");
			stringBuilder.AppendLine();
			AppendDefaultGroup(stringBuilder, "mods_surface", 0f, 10f, 20f, 2f, includeGroupHeaderComment: false, includeFieldComments: false, avoidanceSteering: true, new string[29]
			{
				"Neck_RtD", "Animal_Dolphin_RtD", "Animal_Cod_RtD", "Monster_GreatWhiteShark_RtD", "Animal_Turtle_RtD", "Mirmaid_RtD", "BoneFish_RtD", "BoneSquid_RtD", "LuminousLooker_RtD", "MurkPod_RtD",
				"Animal_HumpbackWhale_RtD", "RDB_crocodile", "RDB_white_shark", "RDB_turtle", "Shark_TW", "ArcticSerpent_TW", "SA_Orca", "SA_Dolphin", "SA_HumboldtSquid", "SA_LeatherbackSeaTurtle",
				"SA_RightWhale", "SA_WhaleShark", "SA_BlueShark", "SA_HammerHeadShark", "SA_TigerShark", "SA_BlueTurtle", "SA_GreenTurtle", "SA_RedTurtle", "SA_YellowTurtle"
			});
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("# Disable angled avoidance only for prefabs that jitter while diving.");
			AppendDefaultGroup(stringBuilder, "mods_surface_nosteering", 0f, 10f, 20f, 2f, includeGroupHeaderComment: false, includeFieldComments: false, avoidanceSteering: false, new string[1] { "SA_WhiteShark" });
			stringBuilder.AppendLine();
			AppendDefaultGroup(stringBuilder, "mods_midwater", 0f, 15f, 30f, 2f, includeGroupHeaderComment: false, includeFieldComments: false, avoidanceSteering: true, new string[6] { "Belzor_RtD", "Monster_HammerheadShark_RtD", "Animal_Marlin_RtD", "Shark_RtD", "Animal_SpermWhale_RtD", "Monster_Orca_RtD" });
			stringBuilder.AppendLine();
			AppendDefaultGroup(stringBuilder, "mods_deep", 10f, 20f, 30f, 2f, includeGroupHeaderComment: false, includeFieldComments: false, avoidanceSteering: true, new string[2] { "Animal_Tuna_RtD", "Animal_Squid_RtD" });
			stringBuilder.AppendLine();
			AppendDefaultGroup(stringBuilder, "mods_bottom", 20f, 30f, 40f, 2f, includeGroupHeaderComment: false, includeFieldComments: false, avoidanceSteering: true, new string[4] { "CatFish_RtD", "Reptile_RtD", "MirRake_RtD", "Animal_Manta_RtD" });
			return stringBuilder.ToString();
		}

		private static void AppendDefaultGroup(StringBuilder builder, string groupName, float minDepth, float centerDepth, float maxDepth, float activeDepthAdjustSpeed, bool includeGroupHeaderComment = false, bool includeFieldComments = false, bool avoidanceSteering = true, IEnumerable<string>? examplePrefabs = null)
		{
			string text = (includeGroupHeaderComment ? " # You can use any group name. Add your own groups" : string.Empty);
			string text2 = (includeFieldComments ? " # Shallowest passive dive depth used while the monster has no target and is not alerted." : string.Empty);
			string text3 = (includeFieldComments ? " # Center depth used by the passive sine-wave swimming pattern." : string.Empty);
			string text4 = (includeFieldComments ? " # Deepest passive dive depth used while the monster has no target and is not alerted." : string.Empty);
			string text5 = (includeFieldComments ? " # Shallowest active target depth used while alerted or chasing a target." : string.Empty);
			string text6 = (includeFieldComments ? " # How quickly this group adjusts swim depth while alerted or chasing a target." : string.Empty);
			string text7 = (includeFieldComments ? " # Current terrain water depth below this value makes the monster flee from its target. 0 disables it." : string.Empty);
			string text8 = (includeFieldComments ? " # If true, monsters spawned underwater keep their initial spawn depth instead of surfacing first." : string.Empty);
			string text9 = (includeFieldComments ? " # If false, skips DiveIn's angled obstacle avoidance and swims directly toward the current target." : string.Empty);
			string text10 = (includeFieldComments ? " # Monster prefab names assigned to this passive profile group." : string.Empty);
			builder.AppendLine(groupName + ":" + text);
			builder.AppendLine("  passive_min_depth: " + FormatYamlFloat(minDepth) + text2);
			builder.AppendLine("  passive_center_depth: " + FormatYamlFloat(centerDepth) + text3);
			builder.AppendLine("  passive_max_depth: " + FormatYamlFloat(maxDepth) + text4);
			builder.AppendLine("  active_min_depth: " + FormatYamlFloat(0.25f) + text5);
			builder.AppendLine("  active_depth_adjust_speed: " + FormatYamlFloat(activeDepthAdjustSpeed) + text6);
			builder.AppendLine("  shallow_water_flee_depth: " + FormatYamlFloat(0f) + text7);
			builder.AppendLine("  preserve_spawn_depth: " + FormatYamlBool(value: false) + text8);
			builder.AppendLine("  avoidance_steering: " + FormatYamlBool(avoidanceSteering) + text9);
			if (examplePrefabs != null)
			{
				string[] array = examplePrefabs.Where((string prefab) => !string.IsNullOrWhiteSpace(prefab)).ToArray();
				if (array.Length != 0)
				{
					builder.AppendLine("  prefabs:" + text10);
					string[] array2 = array;
					foreach (string text11 in array2)
					{
						builder.AppendLine("    - " + text11);
					}
					return;
				}
			}
			builder.AppendLine("  prefabs: []" + text10);
		}

		private static string FormatYamlFloat(float value)
		{
			return value.ToString("0.###", CultureInfo.InvariantCulture);
		}

		private static string FormatYamlBool(bool value)
		{
			if (!value)
			{
				return "false";
			}
			return "true";
		}

		private void InitializePlayerDiveConfig()
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected O, but got Unknown
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Expected O, but got Unknown
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Expected O, but got Unknown
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Expected O, but got Unknown
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Expected O, but got Unknown
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c7: Expected O, but got Unknown
			//IL_020b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0216: Expected O, but got Unknown
			//IL_0248: Unknown result type (might be due to invalid IL or missing references)
			//IL_0253: Expected O, but got Unknown
			//IL_0297: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a2: Expected O, but got Unknown
			//IL_02e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f1: Expected O, but got Unknown
			//IL_0335: Unknown result type (might be due to invalid IL or missing references)
			//IL_0340: Expected O, but got Unknown
			//IL_0384: Unknown result type (might be due to invalid IL or missing references)
			//IL_038f: Expected O, but got Unknown
			//IL_03d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_03de: Expected O, but got Unknown
			//IL_0422: Unknown result type (might be due to invalid IL or missing references)
			//IL_042d: Expected O, but got Unknown
			//IL_0471: Unknown result type (might be due to invalid IL or missing references)
			//IL_047c: Expected O, but got Unknown
			//IL_04c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_04cb: Expected O, but got Unknown
			//IL_04e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_050c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0517: Expected O, but got Unknown
			//IL_0531: Unknown result type (might be due to invalid IL or missing references)
			//IL_055b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0566: Expected O, but got Unknown
			//IL_0598: Unknown result type (might be due to invalid IL or missing references)
			//IL_05a3: Expected O, but got Unknown
			//IL_05e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_05f2: Expected O, but got Unknown
			//IL_0636: Unknown result type (might be due to invalid IL or missing references)
			//IL_0641: Expected O, but got Unknown
			_waterEquipmentBlacklist = config("2 - Player Diving", "Water Equipment Blacklist", "", new ConfigDescription("Comma-separated item prefab names that keep their own vanilla water restriction. Listed armor and accessories do not hide unrelated hand equipment. A listed hand item still uses vanilla hand-item hiding, which can stow both hands. Example: BowFineWood,ShieldBronzeBuckler.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 100
				}
			}));
			_surfaceStaminaRegenRateMultiplier = config("3 - Regen Rate", "Surface Stamina Regen Rate", 0.5f, new ConfigDescription("Multiplier applied to vanilla stamina regeneration while swimming on the surface with your head above water. 0 matches vanilla swimming behavior, 1 matches normal non-swimming stamina regeneration timing and rate.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 110
				}
			}));
			_midwaterStaminaRegenRateMultiplier = config("3 - Regen Rate", "Midwater Stamina Regen Rate", 0f, new ConfigDescription("Multiplier applied to vanilla stamina regeneration while your head is underwater. 0 makes stamina recover only after surfacing.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 109
				}
			}));
			_surfaceEitrRegenRateMultiplier = config("3 - Regen Rate", "Surface Eitr Regen Rate", 0.7f, new ConfigDescription("Multiplier applied to vanilla eitr regeneration while swimming on the surface with your head above water. 0 disables eitr regeneration while surface swimming, 1 keeps vanilla eitr regeneration.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 108
				}
			}));
			_midwaterEitrRegenRateMultiplier = config("3 - Regen Rate", "Midwater Eitr Regen Rate", 0.3f, new ConfigDescription("Multiplier applied to vanilla eitr regeneration while your head is underwater. 0 makes eitr recover only after surfacing.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 107
				}
			}));
			_midwaterIdleStaminaDrainPerDepth = config("4 - Stamina Drain", "Midwater Idle Stamina Drain Per Depth", 0.02f, new ConfigDescription("Idle stamina drained per second per 1m of current liquid depth while your head is underwater. 0 disables idle underwater stamina drain. Example: 0.1 drains 3 stamina per second at 30m depth.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 109
				}
			}));
			_swimStaminaDrainMultiplierPerDepth = config("4 - Stamina Drain", "Swim Stamina Drain Multiplier Per Depth", 2.5f, new ConfigDescription("Additional moving swim stamina drain percent per 1m of current liquid depth. 1 means 30% extra at 30m; 2.5 means 75% extra at 30m. Applied multiplicatively with base and Fast Swim stamina drain.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 108
				}
			}));
			_multiplicativeSwimStaminaModifiers = config("4 - Stamina Drain", "Multiplicative Swim Stamina Modifiers", Toggle.On, new ConfigDescription("If on, status-effect swim stamina use modifiers stack multiplicatively during actual swim stamina consumption. Off is vanilla. Example: -50% and -60% leaves 20% cost instead of 0%. Tooltips keep vanilla display behavior.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 110
				}
			}));
			_swimStaminaDrainBaseMultiplier = config("4 - Stamina Drain", "Swim Stamina Drain Base Multiplier", 1f, new ConfigDescription("Multiplier applied to vanilla moving swim stamina drain before depth and Fast Swim multipliers. 1 keeps vanilla cost, 0.5 halves it, 2 doubles it.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 2f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 107
				}
			}));
			_fastSwimSpeedMultiplier = config("5 - Swim Speed", "Fast Swim Speed Multiplier", 2f, new ConfigDescription("Swim speed multiplier while Fast Swim is enabled with the vanilla run key. 1 disables Fast Swim and hides its key hint. Swim skill separately increases base swim speed.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 3f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 109
				}
			}));
			_fastSwimStaminaDrainMultiplier = config("5 - Swim Speed", "Fast Swim Stamina Drain Multiplier", 2f, new ConfigDescription("Moving swim stamina drain multiplier while Fast Swim is enabled. Applied multiplicatively with base and depth stamina drain.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 5f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 108
				}
			}));
			_playerSwimSkillSpeedMultiplier = config("5 - Swim Speed", "Swim Skill Speed Multiplier", 1.5f, new ConfigDescription("Base swim speed multiplier at Swim skill 100. 1.5 means +50%.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 3f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 110
				}
			}));
			_encumberedSwimSpeedMultiplier = config("5 - Swim Speed", "Encumbered Swim Speed Multiplier", 0.5f, new ConfigDescription("Swim speed multiplier while encumbered. Fast Swim is unavailable while encumbered. 1 keeps normal swim speed; 0.5 halves it.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 1f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 107
				}
			}));
			_playerProjectileUnderwaterTtlMultiplier = config("6 - Underwater Projectiles", "Player Projectile Underwater TTL Multiplier", 0.5f, new ConfigDescription("Multiplier applied once to player-owned projectiles when they are fired underwater. 1 keeps vanilla lifetime; lower values shorten underwater range.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 1f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 110
				}
			}));
			_playerProjectileUnderwaterSpeedMultiplier = config("6 - Underwater Projectiles", "Player Projectile Underwater Speed Multiplier", 0.5f, new ConfigDescription("Multiplier applied once to player-owned projectile velocity when fired underwater. 1 keeps vanilla speed; lower values slow underwater projectiles.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 1f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 109
				}
			}));
			_playerProjectileUnderwaterDamageMultiplier = config("6 - Underwater Projectiles", "Player Projectile Underwater Damage Multiplier", 0.5f, new ConfigDescription("Multiplier applied once to player-owned projectile damage when fired underwater. 1 keeps vanilla damage; 0 removes projectile damage underwater.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 108
				}
			}));
			_playerDiveAscendShortcut = config<KeyboardShortcut>("2 - Player Diving", "Dive Ascend Key", new KeyboardShortcut((KeyCode)32, Array.Empty<KeyCode>()), new ConfigDescription("Client-side key used to ascend while swimming underwater.", (AcceptableValueBase)(object)new AcceptableShortcuts(), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 110
				}
			}), synchronizedSetting: false);
			_playerDiveDescendShortcut = config<KeyboardShortcut>("2 - Player Diving", "Dive Descend Key", new KeyboardShortcut((KeyCode)306, Array.Empty<KeyCode>()), new ConfigDescription("Client-side key used to descend while swimming.", (AcceptableValueBase)(object)new AcceptableShortcuts(), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 109
				}
			}), synchronizedSetting: false);
			_fastSwimInputMode = config("2 - Player Diving", "Fast Swim Input Mode", FastSwimInputMode.Toggle, new ConfigDescription("Client-side input behavior for Fast Swim. Press keeps Fast Swim active only while the vanilla run key is held. Toggle switches Fast Swim on or off whenever the vanilla run key is pressed.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 108
				}
			}), synchronizedSetting: false);
			_underwaterDarknessFactor = config("2 - Player Diving", "Darkness Factor", 0.5f, new ConfigDescription("Underwater darkness added per meter of swim depth. 1 means 1% per meter, so 30m gives 30%.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 92
				}
			}));
			_underwaterVisibilityFalloff = config("2 - Player Diving", "Murkiness Factor", 0.25f, new ConfigDescription("Underwater fog density added per meter of swim depth. 1 means 1% per meter, so 30m adds 30%.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 91
				}
			}));
		}

		internal static float GetUnderwaterDarknessFactor()
		{
			return Mathf.Max(0f, _underwaterDarknessFactor.Value) * 0.01f;
		}

		internal static float GetUnderwaterVisibilityFalloff()
		{
			return Mathf.Max(0f, _underwaterVisibilityFalloff.Value) * 0.01f;
		}

		internal static bool IsSwimRunEnabled()
		{
			if (_fastSwimSpeedMultiplier != null)
			{
				return _fastSwimSpeedMultiplier.Value > 1.001f;
			}
			return false;
		}

		internal static bool UseFastSwimToggleInput()
		{
			ConfigEntry<FastSwimInputMode> fastSwimInputMode = _fastSwimInputMode;
			if (fastSwimInputMode == null)
			{
				return true;
			}
			return fastSwimInputMode.Value != FastSwimInputMode.Press;
		}

		internal static bool UseMultiplicativeSwimStaminaModifiers()
		{
			ConfigEntry<Toggle> multiplicativeSwimStaminaModifiers = _multiplicativeSwimStaminaModifiers;
			if (multiplicativeSwimStaminaModifiers == null)
			{
				return false;
			}
			return multiplicativeSwimStaminaModifiers.Value == Toggle.On;
		}

		internal static bool HasPlayerProjectileUnderwaterPenalty()
		{
			if (!(GetPlayerProjectileUnderwaterTtlMultiplier() < 0.999f) && !(GetPlayerProjectileUnderwaterSpeedMultiplier() < 0.999f))
			{
				return GetPlayerProjectileUnderwaterDamageMultiplier() < 0.999f;
			}
			return true;
		}

		internal static float GetPlayerProjectileUnderwaterTtlMultiplier()
		{
			return Mathf.Clamp(_playerProjectileUnderwaterTtlMultiplier?.Value ?? 1f, 0.05f, 1f);
		}

		internal static float GetPlayerProjectileUnderwaterSpeedMultiplier()
		{
			return Mathf.Clamp(_playerProjectileUnderwaterSpeedMultiplier?.Value ?? 1f, 0.05f, 1f);
		}

		internal static float GetPlayerProjectileUnderwaterDamageMultiplier()
		{
			return Mathf.Clamp01(_playerProjectileUnderwaterDamageMultiplier?.Value ?? 1f);
		}

		internal static bool IsDiveAscendInputHeld()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			ConfigEntry<KeyboardShortcut> playerDiveAscendShortcut = _playerDiveAscendShortcut;
			if (playerDiveAscendShortcut == null || !playerDiveAscendShortcut.Value.IsKeyHeld())
			{
				return ZInput.GetButton("JoyJump");
			}
			return true;
		}

		internal static bool IsDiveDescendInputHeld()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			ConfigEntry<KeyboardShortcut> playerDiveDescendShortcut = _playerDiveDescendShortcut;
			if (playerDiveDescendShortcut == null || !playerDiveDescendShortcut.Value.IsKeyHeld())
			{
				return ZInput.GetButton("JoyCrouch");
			}
			return true;
		}

		internal static string GetDiveAscendKeyHint()
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			if (ShouldShowGamepadKeyHints())
			{
				return GetBoundKeyHint("JoyJump", "A");
			}
			return FormatShortcutForKeyHint((KeyboardShortcut)(((??)_playerDiveAscendShortcut?.Value) ?? new KeyboardShortcut((KeyCode)32, Array.Empty<KeyCode>())));
		}

		internal static string GetDiveDescendKeyHint()
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			if (ShouldShowGamepadKeyHints())
			{
				return GetBoundKeyHint("JoyCrouch", "B");
			}
			return FormatShortcutForKeyHint((KeyboardShortcut)(((??)_playerDiveDescendShortcut?.Value) ?? new KeyboardShortcut((KeyCode)306, Array.Empty<KeyCode>())));
		}

		internal static string GetDiveRunKeyHint()
		{
			if (!ShouldShowGamepadKeyHints())
			{
				return GetBoundKeyHint("Run", "Left Shift");
			}
			return GetBoundKeyHint("JoyRun", "LT");
		}

		private static bool ShouldShowGamepadKeyHints()
		{
			return ZInput.IsGamepadActive();
		}

		private static string GetBoundKeyHint(string bindingName, string fallback)
		{
			ZInput instance = ZInput.instance;
			string text = ((instance != null) ? instance.GetBoundKeyString(bindingName, true) : null) ?? string.Empty;
			if (string.IsNullOrWhiteSpace(text))
			{
				return fallback;
			}
			if (Localization.instance == null)
			{
				return text;
			}
			return Localization.instance.Localize(text);
		}

		private static string FormatShortcutForKeyHint(KeyboardShortcut shortcut)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			if ((int)((KeyboardShortcut)(ref shortcut)).MainKey == 0)
			{
				return "None";
			}
			List<string> list = ((KeyboardShortcut)(ref shortcut)).Modifiers.Where((KeyCode key) => (int)key > 0).Select(FormatKeyCodeForHint).ToList();
			list.Add(FormatKeyCodeForHint(((KeyboardShortcut)(ref shortcut)).MainKey));
			return string.Join(" + ", list);
		}

		private unsafe static string FormatKeyCodeForHint(KeyCode key)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected I4, but got Unknown
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Expected I4, but got Unknown
			return (key - 303) switch
			{
				3 => "Left Ctrl", 
				2 => "Right Ctrl", 
				1 => "Left Shift", 
				0 => "Right Shift", 
				5 => "Left Alt", 
				4 => "Right Alt", 
				_ => (key - 323) switch
				{
					0 => "Mouse-1", 
					1 => "Mouse-2", 
					2 => "Mouse-3", 
					3 => "Mouse-4", 
					4 => "Mouse-5", 
					5 => "Mouse-6", 
					6 => "Mouse-7", 
					_ => ((object)(*(KeyCode*)(&key))/*cast due to .constrained prefix*/).ToString(), 
				}, 
			};
		}

		private static bool TryGetConfiguredDiveProfile(MonsterAI monsterAI, out ConfiguredDiveProfile configuredDiveProfile)
		{
			if ((Object)(object)monsterAI == (Object)null)
			{
				configuredDiveProfile = default(ConfiguredDiveProfile);
				return false;
			}
			string prefabName = Utils.GetPrefabName(((Component)monsterAI).gameObject);
			return _configuredDiveProfilesByPrefabName.TryGetValue(prefabName, out configuredDiveProfile);
		}

		private static bool IsConfiguredMonster(MonsterAI monsterAI)
		{
			ConfiguredDiveProfile configuredDiveProfile;
			return TryGetConfiguredDiveProfile(monsterAI, out configuredDiveProfile);
		}

		private static bool TryGetConfiguredMonster(BaseAI ai, out MonsterAI monsterAI, out ConfiguredDiveProfile configuredDiveProfile)
		{
			MonsterAI val = (MonsterAI)(object)((ai is MonsterAI) ? ai : null);
			if (val != null && TryGetConfiguredDiveProfile(val, out configuredDiveProfile))
			{
				monsterAI = val;
				return true;
			}
			monsterAI = null;
			configuredDiveProfile = default(ConfiguredDiveProfile);
			return false;
		}

		private static bool ShouldUseWaterDiveMode(MonsterAI monsterAI)
		{
			Character character = ((BaseAI)monsterAI).m_character;
			if ((Object)(object)character == (Object)null)
			{
				return false;
			}
			if (character.InWater())
			{
				return character.InLiquidDepth() > 0.05f;
			}
			return false;
		}

		private static bool IsPassiveDiveState(MonsterAI monsterAI)
		{
			if (!((BaseAI)monsterAI).IsAlerted() && (Object)(object)monsterAI.m_targetCreature == (Object)null)
			{
				return (Object)(object)monsterAI.m_targetStatic == (Object)null;
			}
			return false;
		}

		private static ShallowWaterFleeRequest GetShallowWaterFleeRequest(MonsterAI monsterAI, ConfiguredDiveProfile profile)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			if (!ShouldFleeFromShallowWater(monsterAI, profile))
			{
				return default(ShallowWaterFleeRequest);
			}
			return new ShallowWaterFleeRequest(((Object)(object)monsterAI.m_targetCreature != (Object)null) ? ((Component)monsterAI.m_targetCreature).transform.position : (((Object)(object)monsterAI.m_targetStatic != (Object)null) ? monsterAI.m_targetStatic.GetCenter() : monsterAI.m_lastKnownTargetPos));
		}

		private static bool HasVanillaMonsterAIPriority(MonsterAI monsterAI)
		{
			if (((BaseAI)monsterAI).IsSleeping())
			{
				return true;
			}
			if ((Object)(object)((BaseAI)monsterAI).m_tamable != (Object)null && (Object)(object)((BaseAI)monsterAI).m_tamable.m_saddle != (Object)null && ((BaseAI)monsterAI).m_tamable.m_saddle.HaveValidUser())
			{
				return true;
			}
			if (monsterAI.DespawnInDay() && EnvMan.IsDay())
			{
				return true;
			}
			if (monsterAI.IsEventCreature())
			{
				return !RandEventSystem.HaveActiveEvent();
			}
			return false;
		}

		private static void ApplyShallowWaterFlee(MonsterAI monsterAI, float dt, ShallowWaterFleeRequest fleeRequest)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: 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)
			monsterAI.m_lastKnownTargetPos = fleeRequest.FleeFrom;
			monsterAI.m_targetCreature = null;
			monsterAI.m_targetStatic = null;
			monsterAI.m_updateTargetTimer = Mathf.Max(monsterAI.m_updateTargetTimer, 3f);
			((BaseAI)monsterAI).Flee(dt, fleeRequest.FleeFrom);
		}

		private static bool ShouldFleeFromShallowWater(MonsterAI monsterAI, ConfiguredDiveProfile profile)
		{
			if ((Object)(object)monsterAI == (Object)null || (Object)(object)((BaseAI)monsterAI).m_character == (Object)null)
			{
				if ((Object)(object)monsterAI != (Object)null)
				{
					ShallowWaterFleeingByInstance.Remove(((Object)monsterAI).GetInstanceID());
				}
				return false;
			}
			float shallowWaterFleeDepth = profile.ShallowWaterFleeDepth;
			if (shallowWaterFleeDepth <= 0f)
			{
				ShallowWaterFleeingByInstance.Remove(((Object)monsterAI).GetInstanceID());
				return false;
			}
			if ((Object)(object)((BaseAI)monsterAI).m_nview == (Object)null || !((BaseAI)monsterAI).m_nview.IsOwner())
			{
				ShallowWaterFleeingByInstance.Remove(((Object)monsterAI).GetInstanceID());
				return false;
			}
			int instanceID = ((Object)monsterAI).GetInstanceID();
			bool flag = ShallowWaterFleeingByInstance.Contains(instanceID);
			if (!ShouldUseWaterDiveMode(monsterAI))
			{
				ShallowWaterFleeingByInstance.Remove(instanceID);
				return false;
			}
			if (!flag && IsPassiveDiveState(monsterAI))
			{
				return false;
			}
			if (!TryGetTerrainWaterDepth(((BaseAI)monsterAI).m_character, out var terrainWaterDepth))
			{
				ShallowWaterFleeingByInstance.Remove(instanceID);
				return false;
			}
			if (terrainWaterDepth < shallowWaterFleeDepth)
			{
				ShallowWaterFleeingByInstance.Add(instanceID);
				return true;
			}
			if (!flag)
			{
				return false;
			}
			float num = shallowWaterFleeDepth + Mathf.Max(1f, shallowWaterFleeDepth * 0.2f);
			if (terrainWaterDepth < num)
			{
				return true;
			}
			ShallowWaterFleeingByInstance.Remove(instanceID);
			monsterAI.m_updateTargetTimer = Mathf.Max(monsterAI.m_updateTargetTimer, 3f);
			return false;
		}

		private static bool TryGetTerrainWaterDepth(Character character, out float terrainWaterDepth)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			terrainWaterDepth = 0f;
			if ((Object)(object)character == (Object)null || !character.InWater() || (Object)(object)ZoneSystem.instance == (Object)null)
			{
				return false;
			}
			float liquidLevel = character.GetLiquidLevel();
			float solidHeight = ZoneSystem.instance.GetSolidHeight(((Component)character).transform.position);
			if (solidHeight >= liquidLevel)
			{
				return false;
			}
			terrainWaterDepth = liquidLevel - solidHeight;
			return true;
		}

		private static void EnsureDiveFlags(MonsterAI monsterAI, ConfiguredDiveProfile profile)
		{
			TrackOriginalDiveFlags(monsterAI);
			PreserveInitialUnderwaterSpawnDepth(monsterAI, profile);
			if (((BaseAI)monsterAI).m_avoidWater)
			{
				((BaseAI)monsterAI).m_avoidWater = false;
			}
			EnsureAvoidLandForCurrentDiveState(monsterAI);
			Character character = ((BaseAI)monsterAI).m_character;
			if ((Object)(object)character != (Object)null && !character.m_canSwim)
			{
				character.m_canSwim = true;
			}
		}

		private static void PreserveInitialUnderwaterSpawnDepth(MonsterAI monsterAI, ConfiguredDiveProfile profile)
		{
			if ((Object)(object)monsterAI == (Object)null || (Object)(object)((BaseAI)monsterAI).m_character == (Object)null)
			{
				return;
			}
			int instanceID = ((Object)monsterAI).GetInstanceID();
			if (profile.PreserveSpawnDepth && !InitialSpawnDepthPreservedByInstance.Contains(instanceID))
			{
				InitialSpawnDepthPreservedByInstance.Add(instanceID);
				Character character = ((BaseAI)monsterAI).m_character;
				if (TryGetCurrentWaterDepth(character, out var currentWaterDepth) && currentWaterDepth > character.m_swimDepth)
				{
					character.m_swimDepth = currentWaterDepth;
				}
			}
		}

		private static bool TryGetCurrentWaterDepth(Character character, out float currentWaterDepth)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			currentWaterDepth = 0f;
			if ((Object)(object)character == (Object)null)
			{
				return false;
			}
			Vector3 position = ((Component)character).transform.position;
			float liquidLevel = Floating.GetLiquidLevel(position, 1f, (LiquidType)0);
			if (liquidLevel <= -10000f)
			{
				liquidLevel = character.GetLiquidLevel();
			}
			if (liquidLevel <= -10000f || position.y >= liquidLevel)
			{
				return false;
			}
			currentWaterDepth = liquidLevel - position.y;
			return currentWaterDepth > 0f;
		}

		private static void EnsureAvoidLandForCurrentDiveState(MonsterAI monsterAI)
		{
			if (ShouldUseWaterDiveMode(monsterAI))
			{
				if (monsterAI.m_avoidLand)
				{
					monsterAI.m_avoidLand = false;
				}
				return;
			}
			int instanceID = ((Object)monsterAI).GetInstanceID();
			if (OriginalDiveFlagsByInstance.TryGetValue(instanceID, out var value) && monsterAI.m_avoidLand != value.AvoidLand)
			{
				monsterAI.m_avoidLand = value.AvoidLand;
			}
		}

		private static void TrackOriginalDiveFlags(MonsterAI monsterAI)
		{
			if (!Object.op_Implicit((Object)(object)monsterAI))
			{
				return;
			}
			int instanceID = ((Object)monsterAI).GetInstanceID();
			if (OriginalDiveFlagsByInstance.TryGetValue(instanceID, out var value))
			{
				if (value.MonsterAI == monsterAI)
				{
					return;
				}
				if (Object.op_Implicit((Object)(object)value.MonsterAI))
				{
					RestoreOriginalDiveFlags(value);
				}
				RemoveTrackedMonsterState(instanceID);
			}
			Character character = ((BaseAI)monsterAI).m_character;
			OriginalDiveFlagsByInstance[instanceID] = new OriginalDiveFlags(monsterAI, ((BaseAI)monsterAI).m_avoidWater, monsterAI.m_avoidLand, (Object)(object)character != (Object)null && character.m_canSwim, ((Object)(object)character != (Object)null) ? new float?(character.m_swimDepth) : ((float?)null));
			if (OriginalDiveFlagsByInstance.Count > 2048)
			{
				PruneTrackedOriginalDiveFlags();
			}
		}

		private static int RestoreRemovedMonsterDiveFlags()
		{
			if (OriginalDiveFlagsByInstance.Count == 0)
			{
				return 0;
			}
			List<int> list = new List<int>();
			int num = 0;
			foreach (KeyValuePair<int, OriginalDiveFlags> item in OriginalDiveFlagsByInstance)
			{
				int key = item.Key;
				OriginalDiveFlags value = item.Value;
				MonsterAI monsterAI = value.MonsterAI;
				if (!Object.op_Implicit((Object)(object)monsterAI))
				{
					list.Add(key);
				}
				else if (!IsConfiguredMonster(monsterAI))
				{
					RestoreOriginalDiveFlags(value);
					list.Add(key);
					num++;
				}
			}
			foreach (int item2 in list)
			{
				RemoveTrackedMonsterState(item2);
			}
			return num;
		}

		private static int RestoreAllTrackedMonsterDiveFlags()
		{
			if (OriginalDiveFlagsByInstance.Count == 0)
			{
				return 0;
			}
			int num = 0;
			foreach (OriginalDiveFlags value in OriginalDiveFlagsByInstance.Values)
			{
				if (Object.op_Implicit((Object)(object)value.MonsterAI))
				{
					RestoreOriginalDiveFlags(value);
					num++;
				}
			}
			foreach (int item in new List<int>(OriginalDiveFlagsByInstance.Keys))
			{
				RemoveTrackedMonsterState(item);
			}
			return num;
		}

		private static void RestoreOriginalDiveFlags(OriginalDiveFlags originalFlags)
		{
			MonsterAI monsterAI = originalFlags.MonsterAI;
			if (!Object.op_Implicit((Object)(object)monsterAI))
			{
				return;
			}
			((BaseAI)monsterAI).m_avoidWater = originalFlags.AvoidWater;
			monsterAI.m_avoidLand = originalFlags.AvoidLand;
			Character character = ((BaseAI)monsterAI).m_character;
			if ((Object)(object)character != (Object)null)
			{
				character.m_canSwim = originalFlags.CanSwim;
				if (originalFlags.SwimDepth.HasValue)
				{
					character.m_swimDepth = originalFlags.SwimDepth.Value;
				}
			}
		}

		private static void PruneTrackedOriginalDiveFlags()
		{
			if (OriginalDiveFlagsByInstance.Count == 0)
			{
				return;
			}
			List<int> list = new List<int>();
			foreach (KeyValuePair<int, OriginalDiveFlags> item in OriginalDiveFlagsByInstance)
			{
				int key = item.Key;
				if (!Object.op_Implicit((Object)(object)item.Value.MonsterAI))
				{
					list.Add(key);
				}
			}
			foreach (int item2 in list)
			{
				RemoveTrackedMonsterState(item2);
			}
		}

		private static void RemoveTrackedMonsterState(int instanceId)
		{
			OriginalDiveFlagsByInstance.Remove(instanceId);
			InitialSpawnDepthPreservedByInstance.Remove(instanceId);
			ShallowWaterFleeingByInstance.Remove(instanceId);
		}

		private static bool IsWithinSteeringMemoryRange(Vector3 a, Vector3 b)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			float num = a.x - b.x;
			float num2 = a.z - b.z;
			return num * num + num2 * num2 <= 1f;
		}

		private static void ClearSteeringMemory()
		{
			SteeringMemory.Clear();
		}

		private static void TrimCachesIfNeeded()
		{
			if (SteeringMemory.Count > 2048)
			{
				SteeringMemory.Clear();
			}
		}

		private static float GetPassiveDesiredDepth(MonsterAI monsterAI, PassiveDepthProfile profile)
		{
			int num = Mathf.Abs(((Object)monsterAI).GetInstanceID());
			float num2 = Mathf.Sin(Mathf.Repeat(Time.time + (float)(num % 997) * 0.173f, 12f) / 12f * (float)Math.PI * 2f);
			float num3 = Mathf.Max(0f, profile.CenterDepth - profile.MinDepth);
			float num4 = Mathf.Max(0f, profile.MaxDepth - profile.CenterDepth);
			if (!(num2 >= 0f))
			{
				return profile.CenterDepth + num2 * num3;
			}
			return profile.CenterDepth + num2 * num4;
		}

		private static SwimDepthGoal CalculateSwimDepthGoal(MonsterAI monsterAI, Character character, Vector3 point, ConfiguredDiveProfile configuredDiveProfile)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			float num = Mathf.Clamp(configuredDiveProfile.ActiveMinDepth, 0f, 300f);
			float liquidLevel = character.GetLiquidLevel();
			float adjustSpeed;
			float num2;
			bool flag;
			if (IsPassiveDiveState(monsterAI))
			{
				num2 = GetPassiveDesiredDepth(monsterAI, configuredDiveProfile.PassiveDepthProfile);
				flag = true;
				adjustSpeed = 2f;
			}
			else
			{
				float num3 = liquidLevel - point.y;
				num2 = Mathf.Clamp(num3, num, 300f);
				flag = num3 < num || num3 > 300f;
				adjustSpeed = configuredDiveProfile.ActiveDepthAdjustSpeed;
			}
			float num4 = num2;
			num2 = UnderwaterDepthUtils.ClampDepthAboveBottom(character, num2, num);
			flag = flag || num2 < num4 - 0.001f;
			float clampedTargetY = liquidLevel - num2;
			return new SwimDepthGoal(num2, clampedTargetY, flag, adjustSpeed);
		}

		private static void ApplySwimDepthGoal(Character character, SwimDepthGoal goal, float dt)
		{
			if (goal.AdjustSpeed <= 0f)
			{
				character.m_swimDepth = goal.DesiredDepth;
				return;
			}
			float num = goal.AdjustSpeed * Mathf.Max(dt, 0.01f);
			character.m_swimDepth = Mathf.MoveTowards(character.m_swimDepth, goal.DesiredDepth, num);
		}

		private static UnderwaterNavigationPlan BuildUnderwaterNavigationPlan(BaseAI ai, Character character, Vector3 targetPoint, ConfiguredDiveProfile profile)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: 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_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			int instanceID = ((Object)ai).GetInstanceID();
			Vector3 position = ((Component)ai).transform.position;
			if (!profile.AvoidanceSteering)
			{
				SteeringMemory.Remove(instanceID);
				Vector3 val = targetPoint - position;
				return new UnderwaterNavigationPlan(hasRoute: true, (((Vector3)(ref val)).sqrMagnitude > 0.0001f) ? ((Vector3)(ref val)).normalized : Vector3.zero, Mathf.Min(((Vector3)(ref val)).magnitude, 6f));
			}
			float time = Time.time;
			float preferredSide = 0f;
			MonsterAI val2 = (MonsterAI)(object)((ai is MonsterAI) ? ai : null);
			bool flag = val2 == null || !((BaseAI)val2).m_serpentMovement;
			if (flag && SteeringMemory.TryGetValue(instanceID, out var value))
			{
				if (time - value.Time <= 0.1f && IsWithinSteeringMemoryRange(position, value.Position) && IsWithinSteeringMemoryRange(targetPoint, value.Target))
				{
					preferredSide = value.Side;
				}
				else
				{
					SteeringMemory.Remove(instanceID);
				}
			}
			float selectedAngle;
			UnderwaterNavigationPlan result = CalculateUnderwaterNavigationPlan(ai, character, targetPoint, preferredSide, out selectedAngle);
			float num = ((result.HasRoute && Mathf.Abs(selectedAngle) > 0.1f && Mathf.Abs(selectedAngle) <= 70f) ? Mathf.Sign(selectedAngle) : 0f);
			if (flag && num != 0f)
			{
				TrimCachesIfNeeded();
				SteeringMemory[instanceID] = new SteeringMemoryEntry(time, position, targetPoint, num);
			}
			return result;
		}

		private static UnderwaterNavigationPlan CalculateUnderwaterNavigationPlan(BaseAI ai, Character character, Vector3 targetPoint, float preferredSide, out float selectedAngle)
		{
			//IL_0008: 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_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: 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_006e: 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_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: 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)
			//IL_00d0: 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_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: 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_01c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f7: 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_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			selectedAngle = 0f;
			Vector3 val = targetPoint - ((Component)ai).transform.position;
			float magnitude = ((Vector3)(ref val)).magnitude;
			if (((Vector3)(ref val)).sqrMagnitude <= 0.0001f)
			{
				return new UnderwaterNavigationPlan(hasRoute: true, Vector3.zero, 0f);
			}
			((Vector3)(ref val)).Normalize();
			Vector3 val2 = default(Vector3);
			((Vector3)(ref val2))..ctor(val.x, 0f, val.z);
			float radius = character.GetRadius();
			float num = Utils.DistanceXZ(targetPoint, ((Component)ai).transform.position);
			float num2 = Mathf.Clamp(num, radius + 1f, 6f);
			float lookaheadDistance = Mathf.Min(magnitude, num2);
			if (num <= radius + 0.6f)
			{
				return new UnderwaterNavigationPlan(hasRoute: true, val, lookaheadDistance);
			}
			if (((Vector3)(ref val2)).sqrMagnitude <= 0.0001f)
			{
				return new UnderwaterNavigationPlan(hasRoute: true, val, lookaheadDistance);
			}
			((Vector3)(ref val2)).Normalize();
			Vector3 centerPoint = character.GetCenterPoint();
			Vector3 bestHorizontal = val2;
			bool bestHasRoute = false;
			float bestScore = float.NegativeInfinity;
			if (EvaluateSteerCandidate(ai, centerPoint, val2, SteerAngles[0], radius, num2, ref bestHorizontal, ref bestHasRoute, ref bestScore, ref selectedAngle, out var _))
			{
				return new UnderwaterNavigationPlan(hasRoute: true, val, lookaheadDistance);
			}
			bool flag = false;
			if (preferredSide != 0f)
			{
				for (int i = 1; i < SteerAngles.Length; i++)
				{
					float num3 = SteerAngles[i];
					if (IsPreferredSteerAngle(num3, preferredSide) && EvaluateSteerCandidate(ai, centerPoint, val2, num3, radius, num2, ref bestHorizontal, ref bestHasRoute, ref bestScore, ref selectedAngle, out var candidate2))
					{
						bestHorizontal = candidate2;
						bestHasRoute = true;
						selectedAngle = num3;
						flag = true;
						break;
					}
				}
			}
			if (!flag)
			{
				for (int j = 1; j < SteerAngles.Length; j++)
				{
					float num4 = SteerAngles[j];
					if ((preferredSide == 0f || !IsPreferredSteerAngle(num4, preferredSide)) && EvaluateSteerCandidate(ai, centerPoint, val2, num4, radius, num2, ref bestHorizontal, ref bestHasRoute, ref bestScore, ref selectedAngle, out var candidate3))
					{
						bestHorizontal = candidate3;
						bestHasRoute = true;
						selectedAngle = num4;
						break;
					}
				}
			}
			Vector3 val3 = default(Vector3);
			((Vector3)(ref val3))..ctor(bestHorizontal.x, val.y, bestHorizontal.z);
			Vector3 direction = ((((Vector3)(ref val3)).sqrMagnitude > 0.0001f) ? ((Vector3)(ref val3)).normalized : val);
			return new UnderwaterNavigationPlan(bestHasRoute, direction, lookaheadDistance);
		}

		private static bool IsPreferredSteerAngle(float angle, float preferredSide)
		{
			if (Mathf.Abs(angle) <= 70f)
			{
				return Mathf.Sign(angle) == preferredSide;
			}
			return false;
		}

		private static bool EvaluateSteerCandidate(BaseAI ai, Vector3 center, Vector3 horizontal, float angle, float radius, float checkDistance, ref Vector3 bestHorizontal, ref bool bestHasRoute, ref float bestScore, ref float selectedAngle, out Vector3 candidate)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: 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_0033: 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)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			candidate = Quaternion.Euler(0f, angle, 0f) * horizontal;
			if (ai.CanMove(candidate, radius, checkDistance))
			{
				return true;
			}
			float num = ai.Raycast(center, candidate, checkDistance * 2f, 0.1f);
			float num2 = num - Mathf.Abs(angle) * 0.01f;
			if (num2 <= bestScore)
			{
				return false;
			}
			bestScore = num2;
			bestHorizontal = candidate;
			bestHasRoute = num >= checkDistance * 0.9f;
			selectedAngle = angle;
			return false;
		}

		static ServerSyncModTemplatePlugin()
		{
			string configPath = Paths.ConfigPath;
			char directorySeparatorChar = Path.DirectorySeparatorChar;
			ConfigFileFullPath = configPath + directorySeparatorChar + ConfigFileName;
			ServerSyncModTemplateLogger = Logger.CreateLogSource("DiveIn");
			ConfigSync = new ConfigSync("sighsorry.DiveIn")
			{
				DisplayName = "DiveIn",
				CurrentVersion = "1.1.9",
				MinimumRequiredVersion = "1.1.9",
				ModRequired = true
			};
			_serverConfigLocked = null;
			MonsterDiveYamlFileName = "DiveIn.yaml";
			string configPath2 = Paths.ConfigPath;
			directorySeparatorChar = Path.DirectorySeparatorChar;
			MonsterDiveYamlFileFullPath = configPath2 + directorySeparatorChar + MonsterDiveYamlFileName;
			MonsterDiveYamlLock = new object();
			MonsterDiveYamlDeserializer = new DeserializerBuilder().WithNamingConvention(UnderscoredNamingConvention.Instance).WithDuplicateKeyChecking().Build();
			_monsterDiveYamlSync = null;
			_waterEquipmentBlacklist = null;
			_surfaceStaminaRegenRateMultiplier = null;
			_midwaterStaminaRegenRateMultiplier = null;
			_surfaceEitrRegenRateMultiplier = null;
			_midwaterEitrRegenRateMultiplier = null;
			_midwaterIdleStaminaDrainPerDepth = null;
			_swimStaminaDrainMultiplierPerDepth = null;
			_multiplicativeSwimStaminaModifiers = null;
			_swimStaminaDrainBaseMultiplier = null;
			_playerSwimSkillSpeedMultiplier = null;
			_fastSwimSpeedMultiplier = null;
			_fastSwimStaminaDrainMultiplier = null;
			_encumberedSwimSpeedMultiplier = null;
			_fastSwimInputMode = null;
			_playerProjectileUnderwaterTtlMultiplier = null;
			_playerProjectileUnderwaterSpeedMultiplier = null;
			_playerProjectileUnderwaterDamageMultiplier = null;
			_playerDiveAscendShortcut = null;
			_playerDiveDescendShortcut = null;
			_underwaterDarknessFactor = null;
			_underwaterVisibilityFalloff = null;
			_configuredDiveProfilesByPrefabName = new Dictionary<string, ConfiguredDiveProfile>(StringComparer.OrdinalIgnoreCase);
			OriginalDiveFlagsByInstance = new Dictionary<int, OriginalDiveFlags>();
			InitialSpawnDepthPreservedByInstance = new HashSet<int>();
			ShallowWaterFleeingByInstance = new HashSet<int>();
			SteerAngles = new float[8] { 0f, -35f, 35f, -70f, 70f, -120f, 120f, 180f };
			SteeringMemory = new Dictionary<int, SteeringMemoryEntry>();
		}
	}
	internal static class DiveLocalization
	{
		private readonly struct DiveHintTranslation
		{
			internal string FastSwimOn { get; }

			internal string FastSwimOff { get; }

			internal string Descend { get; }

			internal string Ascend { get; }

			internal DiveHintTranslation(string fastSwimOn, string fastSwimOff, string descend, string ascend)
			{
				FastSwimOn = fastSwimOn;
				FastSwimOff = fastSwimOff;
				Descend = descend;
				Ascend = ascend;
			}
		}

		internal const string FastSwimOnKey = "$divein_fast_swim_on";

		internal const string FastSwimOffKey = "$divein_fast_swim_off";

		internal const string DescendKey = "$divein_descend";

		internal const string AscendKey = "$divein_ascend";

		private const string EnglishLanguage = "english";

		private static readonly DiveHintTranslation English = new DiveHintTranslation("Fast Swim On", "Fast Swim Off", "Descend", "Ascend");

		private static readonly Dictionary<string, DiveHintTranslation> Translations = new Dictionary<string, DiveHintTranslation>(StringComparer.OrdinalIgnoreCase)
		{
			["english"] = English,
			["swedish"] = new DiveHintTranslation("Snabbsim på", "Snabbsim av", "Ner", "Upp"),
			["french"] = new DiveHintTranslation("Nage rapide ON", "Nage rapide OFF", "Descendre", "Monter"),
			["italian"] = new DiveHintTranslation("Nuoto veloce ON", "Nuoto veloce OFF", "Scendi", "Sali"),
			["german"] = new DiveHintTranslation("Schnellschwimmen an", "Schnellschwimmen aus", "Abtauchen", "Auftauchen"),
			["spanish"] = new DiveHintTranslation("Nado rápido ON", "Nado rápido OFF", "Bajar", "Subir"),
			["russian"] = new DiveHintTranslation("Быстрое плавание вкл.", "Быстрое плавание выкл.", "Вниз", "Вверх"),
			["finnish"] = new DiveHintTranslation("Nopea uinti päällä", "Nopea uinti pois", "Alas", "Ylös"),
			["danish"] = new DiveHintTranslation("Hurtig svømning til", "Hurtig svømning fra", "Ned", "Op"),
			["norwegian"] = new DiveHintTranslation("Rask svømming på", "Rask svømming av", "Ned", "Opp"),
			["turkish"] = new DiveHintTranslation("Hızlı yüzme açık", "Hızlı yüzme kapalı", "Alçal", "Yüksel"),
			["lithuanian"] = new DiveHintTranslation("Greitas plaukimas įj.", "Greitas plaukimas išj.", "Žemyn", "Aukštyn"),
			["czech"] = new DiveHintTranslation("Rychlé plavání zap.", "Rychlé plavání vyp.", "Dolů", "Nahoru"),
			["hungarian"] = new DiveHintTranslation("Gyors úszás be", "Gyors úszás ki", "Le", "Fel"),
			["slovak"] = new DiveHintTranslation("Rýchle plávanie zap.", "Rýchle plávanie vyp.", "Dole", "Hore"),
			["polish"] = new DiveHintTranslation("Szybkie pływanie wł.", "Szybkie pływanie wył.", "W dół", "W górę"),
			["dutch"] = new DiveHintTranslation("Snel zwemmen aan", "Snel zwemmen uit", "Omlaag", "Omhoog"),
			["portuguese_european"] = new DiveHintTranslation("Nado rápido ligado", "Nado rápido desligado", "Descer", "Subir"),
			["portuguese_brazilian"] = new DiveHintTranslation("Nado rápido ligado", "Nado rápido desligado", "Descer", "Subir"),
			["chinese"] = new DiveHintTranslation("快速游泳开", "快速游泳关", "下潜", "上浮"),
			["chinese_trad"] = new DiveHintTranslation("快速游泳開", "快速游泳關", "下潛", "上浮"),
			["japanese"] = new DiveHintTranslation("高速泳ぎ ON", "高速泳ぎ OFF", "潜る", "浮上"),
			["korean"] = new DiveHintTranslation("빠른 수영 켬", "빠른 수영 끔", "하강", "상승"),
			["thai"] = new DiveHintTranslation("ว\u0e48ายเร\u0e47ว เป\u0e34ด", "ว\u0e48ายเร\u0e47ว ป\u0e34ด", "ลง", "ข\u0e36\u0e49น"),
			["greek"] = new DiveHintTranslation("Γρήγορη κολύμβηση ON", "Γρήγορη κολύμβηση OFF", "Κάτω", "Πάνω"),
			["ukrainian"] = new DiveHintTranslation("Швидке плавання увімк.", "Швидке плавання вимк.", "Вниз", "Вгору"),
			["latvian"] = new DiveHintTranslation("Ātra peldēšana iesl.", "Ātra peldēšana izsl.", "Lejup", "Augšup")
		};

		internal static void Register()
		{
			if (Localization.instance != null)
			{
				Register(Localization.instance);
			}
		}

		internal static void Register(Localization localization)
		{
			if (localization != null)
			{
				DiveHintTranslation translation = GetTranslation(NormalizeLanguageName(localization.GetSelectedLanguage()));
				localization.AddWord("$divein_fast_swim_on".Substring(1), translation.FastSwimOn);
				localization.AddWord("$divein_fast_swim_off".Substring(1), translation.FastSwimOff);
				localization.AddWord("$divein_descend".Substring(1), translation.Descend);
				localization.AddWord("$divein_ascend".Substring(1), translation.Ascend);
			}
		}

		internal static string Localize(string key)
		{
			if (Localization.instance == null)
			{
				return GetEnglishText(key);
			}
			string text = Localization.instance.Localize(key);
			if (!text.Contains("$"))
			{
				return text;
			}
			return GetEnglishText(key);
		}

		private static DiveHintTranslation GetTranslation(string languageName)
		{
			if (!Translations.TryGetValue(languageName, out var value))
			{
				return English;
			}
			return value;
		}

		private static string NormalizeLanguageName(string languageName)
		{
			if (!string.IsNullOrWhiteSpace(languageName))
			{
				return languageName.Trim().Replace(" ", string.Empty).ToLowerInvariant();
			}
			return "english";
		}

		private static string GetEnglishText(string key)
		{
			return key switch
			{
				"$divein_fast_swim_on" => English.FastSwimOn, 
				"$divein_fast_swim_off" => English.FastSwimOff, 
				"$divein_descend" => English.Descend, 
				"$divein_ascend" => English.Ascend, 
				_ => key, 
			};
		}
	}
	[HarmonyPatch(typeof(Localization), "SetupLanguage")]
	internal static class LocalizationSetupLanguageDivePatch
	{
		private static void Postfix(Localization __instance)
		{
			DiveLocalization.Register(__instance);
		}
	}
	public static class KeyboardExtensions
	{
		[SpecialName]
		public sealed class <G>$8D1D3E80A18AA9715780B6CB7003B2F1
		{
			[SpecialName]
			public static class <M>$895AB635D4D087636CF1C26BA650BA11
			{
			}

			[ExtensionMarker("<M>$895AB635D4D087636CF1C26BA650BA11")]
			public bool IsKeyHeld()
			{
				throw new NotSupportedException();
			}
		}

		public static bool IsKeyHeld(this KeyboardShortcut shortcut)
		{
			//IL_0002: 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)
			if ((int)((KeyboardShortcut)(ref shortcut)).MainKey != 0 && Input.GetKey(((KeyboardShortcut)(ref shortcut)).MainKey))
			{
				return ((KeyboardShortcut)(ref shortcut)).Modifiers.All((Func<KeyCode, bool>)Input.GetKey);
			}
			return false;
		}
	}
	internal static class PlayerDiveUtils
	{
		internal static PlayerDiveController? EnsureLocalDiver()
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return null;
			}
			PlayerDiveController playerDiveController = PlayerDiveController.LocalInstance;
			if ((Object)(object)playerDiveController != (Object)null && (Object)(object)playerDiveController.Player == (Object)(object)localPlayer)
			{
				return playerDiveController;
			}
			if (!((Component)localPlayer).TryGetComponent<PlayerDiveController>(ref playerDiveController))
			{
				playerDiveController = ((Component)localPlayer).gameObject.AddComponent<PlayerDiveController>();
			}
			return playerDiveController;
		}

		internal static bool TryGetLocalDiver(Player player, out PlayerDiveController diver)
		{
			if (!IsValidLocalPlayer(player))
			{
				diver = null;
				return false;
			}
			PlayerDiveController playerDiveController = EnsureLocalDiver();
			if ((Object)(object)playerDiveController == (Object)null)
			{
				diver = null;
				return false;
			}
			diver = playerDiveController;
			return true;
		}

		internal static bool IsValidLocalPlayer(Player player)
		{
			if ((Object)(object)player != (Object)null)
			{
				return (Object)(object)player == (Object)(object)Player.m_localPlayer;
			}
			return false;
		}

		internal static bool TryGetUnderwaterLocalDiver(Player player, out PlayerDiveController diver)
		{
			diver = null;
			if (TryGetLocalDiver(player, out diver))
			{
				return diver.ShouldTreatAsSwimming();
			}
			return false;
		}
	}
	internal sealed class PlayerDiveController : MonoBehaviour
	{
		private const float HeadUnderwaterTolerance = 0.01f;

		private const float MinimumSurfaceSwimDepth = 0.1f;

		private const float SurfaceExitClearance = 0.1f;

		private const float DivingSwimDepthOffset = 1.1f;

		private const float BottomAscendDepthStep = 0.75f;

		private const float CombatMovementSuppressionDuration = 0.1f;

		private float _surfaceSwimDepth = 2f;

		private float? _surfaceExitSwimDepth;

		private bool _surfaceRotationLevelingActive;

		private bool _underwaterMovementActive;

		private bool _fastSwimEnabled;

		private bool _hasSwimSpeedOverride;

		private float _originalSwimSpeed;

		private float _activeSwimRunStaminaDrainMultiplier = 1f;

		private float _combatMovementSuppressedUntilTime;

		private int _swimmingUpdateContextDepth;

		private int _swimmingUpdateContextFrame = -1;

		internal static PlayerDiveController? LocalInstance { get; private set; }

		internal Player Player { get; private set; }

		private void Awake()
		{
			Player = ((Component)this).GetComponent<Player>();
			if ((Object)(object)Player == (Object)null)
			{
				Object.Destroy((Object)(object)this);
				return;
			}
			if ((Object)(object)Player != (Object)(object)Player.m_localPlayer)
			{
				Object.Destroy((Object)(object)this);
				return;
			}
			LocalInstance = this;
			_surfaceSwimDepth = Mathf.Max(0.1f, ((Character)Player).m_swimDepth);
		}

		private void OnDestroy()
		{
			if ((Object)(object)LocalInstance == (Object)(object)this)
			{
				LocalInstance = null;
			}
		}

		internal void DisableUnderwaterMovement()
		{
			_underwaterMovementActive = false;
			_fastSwimEnabled = false;
			_surfaceRotationLevelingActive = false;
			ResetSwimDepthToDefault();
			ResetSwimSpeedOverride();
		}

		internal void ResetSwimDepthIfNotInWater()
		{
			if (!((Character)Player).InWater())
			{
				DisableUnderwaterMovement();
			}
		}

		internal void ResetSwimDepthToDefault()
		{
			_surfaceExitSwimDepth = null;
			((Character)Player).m_swimDepth = _surfaceSwimDepth;
		}

		internal bool CanDive()
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			if (ShouldForceDive())
			{
				return true;
			}
			if (!((Character)Player).InWater() || ((Character)Player).IsOnGround() || !((Character)Player).IsSwimming())
			{
				return false;
			}
			float num = default(float);
			Vector3 val = default(Vector3);
			if (((Character)Player).GetGroundHeight(((Component)Player).transform.position, ref num, ref val) && ((Component)Player).transform.position.y - num < 1f)
			{
				return false;
			}
			return true;
		}

		internal bool IsHeadUnderwater()
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			float num = (((Object)(object)((Character)Player).m_eye != (Object)null) ? ((Character)Player).m_eye.position.y : ((Component)Player).transform.position.y);
			return ((Character)Player).GetLiquidLevel() - num > 0.01f;
		}

		internal void RefreshUnderwaterMovementState()
		{
			if (!((Character)Player).InWater() || !IsHeadUnderwater())
			{
				_underwaterMovementActive = false;
			}
			else if (IsUnderSurface())
			{
				_underwaterMovementActive = true;
			}
		}

		internal bool ShouldForceSwimming()
		{
			if (_underwaterMovementActive && ((Character)Player).InWater())
			{
				return IsHeadUnderwater();
			}
			return false;
		}

		internal bool ShouldShowDiveKeyHints()
		{
			return ShouldTreatAsSwimming();
		}

		internal bool ShouldTreatAsSwimming()
		{
			if (((Character)Player).InWater())
			{
				if (!((Character)Player).IsSwimming())
				{
					return ShouldForceSwimming();
				}
				return true;
			}
			return false;
		}

		internal bool IsFastSwimEnabled()
		{
			if (CanUseFastSwim())
			{
				return _fastSwimEnabled;
			}
			return false;
		}

		internal bool CanUseFastSwim()
		{
			if (ServerSyncModTemplatePlugin.IsSwimRunEnabled())
			{
				return !((Character)Player).IsEncumbered();
			}
			return false;
		}

		internal void UpdateFastSwimInput()
		{
			if (!ShouldShowDiveKeyHints() || !CanUseFastSwim())
			{
				_fastSwimEnabled = false;
			}
			else if (!ServerSyncModTemplatePlugin.UseFastSwimToggleInput())
			{
				_fastSwimEnabled = ZInput.GetButton("Run") || ZInput.GetButton("JoyRun");
			}
			else if (ZInput.GetButtonDown("