Decompiled source of Dive In v1.2.10

DiveIn.dll

Decompiled 2 hours 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.Bootstrap;
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.2.10")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.2.10.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.2.10")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[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
		}

		public enum SwimHudMode
		{
			Full,
			FastSwimOnly,
			Off
		}

		public enum WaterEquipmentMode
		{
			Blacklist,
			Whitelist
		}

		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_0013: Unknown result type (might be due to invalid IL or missing references)
				if (!TryGetConfiguredWaterDiveMonster(__instance, out MonsterAI _, out Character character, out ConfiguredDiveProfile configuredDiveProfile))
				{
					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_0012: Unknown result type (might be due to invalid IL or missing references)
				//IL_001c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0058: 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)
				//IL_0069: Unknown result type (might be due to invalid IL or missing references)
				//IL_0075: Unknown result type (might be due to invalid IL or missing references)
				//IL_0091: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
				//IL_0129: Unknown result type (might be due to invalid IL or missing references)
				//IL_0111: Unknown result type (might be due to invalid IL or missing references)
				if (!TryGetConfiguredWaterDiveMonster(__instance, out MonsterAI monsterAI, out Character character, out ConfiguredDiveProfile configuredDiveProfile))
				{
					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)
				{
					GameAccess.MoveTowardsSwoop(__instance, 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.2.10";

		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 float _nextPovContractCheck;

		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<WaterEquipmentMode> _waterEquipmentMode;

		internal static ConfigEntry<string> _waterEquipmentBlacklist;

		internal static ConfigEntry<string> _waterEquipmentWhitelist;

		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<SwimHudMode> _swimHudMode;

		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 (KeyboardShortcut Shortcut, string Text)? _ascendKeyHint;

		private static (KeyboardShortcut Shortcut, string Text)? _descendKeyHint;

		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();
				PathOfValheimanCompat.Initialize();
				_harmony.PatchAll(Assembly.GetExecutingAssembly());
				PathOfValheimanCompat.CompleteInitialization();
				PathOfValheimanCompat.Maintain();
				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 Update()
		{
			if (!_isShuttingDown && PathOfValheimanCompat.Installed && !(Time.unscaledTime < _nextPovContractCheck))
			{
				_nextPovContractCheck = Time.unscaledTime + 1f;
				TryCleanup(PathOfValheimanCompat.Maintain, "Path of Valheiman contract/cleanup");
			}
		}

		private void Cleanup(bool saveConfig)
		{
			if (_isShuttingDown)
			{
				return;
			}
			_isShuttingDown = true;
			TryCleanup(PathOfValheimanCompat.Reset, "Path of Valheiman integration");
			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(FastSwimHud.Clear, "fast swim HUD");
			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();
						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_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Expected O, but got Unknown
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Expected O, but got Unknown
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Expected O, but got Unknown
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Expected O, but got Unknown
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Expected O, but got Unknown
			//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f6: Expected O, but got Unknown
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0245: Expected O, but got Unknown
			//IL_0289: Unknown result type (might be due to invalid IL or missing references)
			//IL_0294: Expected O, but got Unknown
			//IL_02c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d1: Expected O, but got Unknown
			//IL_0315: Unknown result type (might be due to invalid IL or missing references)
			//IL_0320: Expected O, but got Unknown
			//IL_0364: Unknown result type (might be due to invalid IL or missing references)
			//IL_036f: Expected O, but got Unknown
			//IL_03b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_03be: Expected O, but got Unknown
			//IL_0402: Unknown result type (might be due to invalid IL or missing references)
			//IL_040d: Expected O, but got Unknown
			//IL_0451: Unknown result type (might be due to invalid IL or missing references)
			//IL_045c: Expected O, but got Unknown
			//IL_04a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ab: Expected O, but got Unknown
			//IL_04ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_04fa: Expected O, but got Unknown
			//IL_053e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0549: Expected O, but got Unknown
			//IL_0560: Unknown result type (might be due to invalid IL or missing references)
			//IL_058a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0595: Expected O, but got Unknown
			//IL_05af: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_05e4: Expected O, but got Unknown
			//IL_0616: Unknown result type (might be due to invalid IL or missing references)
			//IL_0621: Expected O, but got Unknown
			//IL_065f: Unknown result type (might be due to invalid IL or missing references)
			//IL_066a: Expected O, but got Unknown
			//IL_06bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_06c7: Expected O, but got Unknown
			//IL_070b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0716: Expected O, but got Unknown
			_waterEquipmentMode = config("2 - Player Diving", "Water Equipment Mode", WaterEquipmentMode.Blacklist, new ConfigDescription("Select which list controls swimming equipment exceptions. Blacklist: all except listed items. Whitelist: only listed items. Switching modes preserves both lists. With supported Path of Valheiman, this list replaces its water equipment restrictions; Combat in the Depths is not required. Other equipment requirements still apply.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 101
				}
			}));
			_waterEquipmentBlacklist = config("2 - Player Diving", "Water Equipment Blacklist", "", new ConfigDescription("Blacklist mode only. Comma-separated item prefab names that keep their swimming restriction. Empty: no items restricted by this list. A restricted hand item can stow both hands; armor and accessories do not hide unrelated hand equipment. Example: BowFineWood,ShieldBronzeBuckler.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 100
				}
			}));
			_waterEquipmentWhitelist = config("2 - Player Diving", "Water Equipment Whitelist", "", new ConfigDescription("Whitelist mode only. Comma-separated item prefab names eligible for DiveIn's swimming equipment exception. Empty: no equipment exceptions. Example: Hammer,Hoe.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 99
				}
			}));
			_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 status and key hint; Full HUD mode still shows ascent and descent controls. 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("Lifetime multiplier applied to the initial player-owned projectile when fired underwater. Spawned child projectiles keep their own lifetime without another DiveIn multiplier. 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("Velocity multiplier applied to the initial player-owned projectile when fired underwater. Spawned child projectiles keep the velocity from the parent spawn logic without another DiveIn multiplier. 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("Damage multiplier applied to the initial player-owned projectile when fired underwater. Inherited hit data keeps this multiplier; spawned child projectiles do not apply it again. 1 keeps vanilla damage; 0 removes the initial projectile's 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);
			string text = TakePersistedSwimHudMode(((BaseUnityPlugin)this).Config);
			_swimHudMode = config("2 - Player Diving", "Swim HUD Mode", SwimHudMode.Full, new ConfigDescription("Client-side swimming HUD near the stamina bar, independent of the game's Key Hints setting. Full shows ascent/descent controls and Fast Swim On/Off; FastSwimOnly shows only the status. Off hides this HUD and restores the regular dive key hints if Key Hints are enabled. Unavailable Fast Swim status is hidden while Full mode keeps the dive controls. Swimming behavior is unchanged.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 107
				}
			}), synchronizedSetting: false);
			if (text != null)
			{
				((ConfigEntryBase)_swimHudMode).SetSerializedValue(text);
			}
			_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 string? TakePersistedSwimHudMode(ConfigFile configFile)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			ConfigDefinition val = new ConfigDefinition("2 - Player Diving", "Swim HUD Mode");
			ConfigDefinition val2 = new ConfigDefinition("2 - Player Diving", "Show Fast Swim HUD");
			bool saveOnConfigSet = configFile.SaveOnConfigSet;
			configFile.SaveOnConfigSet = false;
			try
			{
				string value = configFile.Bind<string>(val, (string)null, (ConfigDescription)null).Value;
				string value2 = configFile.Bind<string>(val2, (string)null, (ConfigDescription)null).Value;
				configFile.Remove(val);
				configFile.Remove(val2);
				if (value != null)
				{
					return value;
				}
				Toggle result;
				return (!Enum.TryParse<Toggle>(value2, ignoreCase: true, out result)) ? null : (result switch
				{
					Toggle.On => "Full", 
					Toggle.Off => "Off", 
					_ => null, 
				});
			}
			finally
			{
				configFile.SaveOnConfigSet = saveOnConfigSet;
			}
		}

		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>())), ref _ascendKeyHint);
		}

		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>())), ref _descendKeyHint);
		}

		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 unsafe static string FormatShortcutForKeyHint(KeyboardShortcut shortcut, ref (KeyboardShortcut Shortcut, string Text)? cache)
		{
			//IL_0035: 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)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			if (cache.HasValue)
			{
				object obj = cache.Value.Shortcut;
				if (((object)(*(KeyboardShortcut*)(&shortcut))/*cast due to .constrained prefix*/).Equals(obj))
				{
					return cache.Value.Text;
				}
			}
			if ((int)((KeyboardShortcut)(ref shortcut)).MainKey == 0)
			{
				cache = (shortcut, "None");
				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));
			string text = string.Join(" + ", list);
			cache = (shortcut, text);
			return text;
		}

		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 TryGetConfiguredWaterDiveMonster(BaseAI ai, out MonsterAI monsterAI, out Character character, out ConfiguredDiveProfile configuredDiveProfile)
		{
			MonsterAI val = (MonsterAI)(object)((ai is MonsterAI) ? ai : null);
			if (val != null && Object.op_Implicit((Object)(object)val))
			{
				Character val2 = GameAccess.AICharacter.Invoke((BaseAI)(object)val);
				if (ShouldUseWaterDiveMode(val2) && TryGetConfiguredDiveProfile(val, out configuredDiveProfile))
				{
					monsterAI = val;
					character = val2;
					return true;
				}
			}
			monsterAI = null;
			character = null;
			configuredDiveProfile = default(ConfiguredDiveProfile);
			return false;
		}

		private static bool ShouldUseWaterDiveMode(Character character)
		{
			if ((Object)(object)character == (Object)null)
			{
				return false;
			}
			if (character.InWater())
			{
				return GameAccess.LiquidDepth(character) > 0.05f;
			}
			return false;
		}

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

		private static ShallowWaterFleeRequest GetShallowWaterFleeRequest(MonsterAI monsterAI, ConfiguredDiveProfile profile)
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			if (!ShouldFleeFromShallowWater(monsterAI, profile))
			{
				return default(ShallowWaterFleeRequest);
			}
			return new ShallowWaterFleeRequest(((Object)(object)((BaseAI)monsterAI).GetTargetCreature() != (Object)null) ? ((Component)((BaseAI)monsterAI).GetTargetCreature()).transform.position : (((Object)(object)monsterAI.GetStaticTarget() != (Object)null) ? monsterAI.GetStaticTarget().GetCenter() : GameAccess.LastTargetPosition.Invoke(monsterAI)));
		}

		private static bool HasVanillaMonsterAIPriority(MonsterAI monsterAI)
		{
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Invalid comparison between Unknown and I4
			//IL_0095: 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)
			if (((BaseAI)monsterAI).IsSleeping())
			{
				return true;
			}
			if ((Object)(object)GameAccess.AITameable.Invoke((BaseAI)(object)monsterAI) != (Object)null && (Object)(object)GameAccess.AITameable.Invoke((BaseAI)(object)monsterAI).m_saddle != (Object)null && GameAccess.AITameable.Invoke((BaseAI)(object)monsterAI).m_saddle.HaveValidUser())
			{
				return true;
			}
			if (monsterAI.DespawnInDay() && EnvMan.IsDay())
			{
				return true;
			}
			Character targetCreature = ((BaseAI)monsterAI).GetTargetCreature();
			Player val = (Player)(object)((targetCreature is Player) ? targetCreature : null);
			if (val != null && val.InCrownMode() && (int)GameAccess.AICharacter.Invoke((BaseAI)(object)monsterAI).GetFaction() != 8 && Vector3.Distance(((Component)targetCreature).transform.position, ((Component)monsterAI).transform.position) - targetCreature.GetRadius() < monsterAI.m_crownFearRange)
			{
				return true;
			}
			if (monsterAI.IsEventCreature())
			{
				return !RandEventSystem.HaveActiveEvent();
			}
			return false;
		}

		private static void ApplyShallowWaterFlee(MonsterAI monsterAI, float dt, ShallowWaterFleeRequest fleeRequest)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			GameAccess.LastTargetPosition.Invoke(monsterAI) = fleeRequest.FleeFrom;
			GameAccess.TargetCreature.Invoke(monsterAI) = null;
			GameAccess.TargetStatic.Invoke(monsterAI) = null;
			GameAccess.TargetTimer.Invoke(monsterAI) = Mathf.Max(GameAccess.TargetTimer.Invoke(monsterAI), 3f);
			GameAccess.Flee((BaseAI)(object)monsterAI, dt, fleeRequest.FleeFrom);
		}

		private static bool ShouldFleeFromShallowWater(MonsterAI monsterAI, ConfiguredDiveProfile profile)
		{
			if ((Object)(object)monsterAI == (Object)null || (Object)(object)GameAccess.AICharacter.Invoke((BaseAI)(object)monsterAI) == (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)GameAccess.AIView.Invoke((BaseAI)(object)monsterAI) == (Object)null || !GameAccess.AIView.Invoke((BaseAI)(object)monsterAI).IsOwner())
			{
				ShallowWaterFleeingByInstance.Remove(((Object)monsterAI).GetInstanceID());
				return false;
			}
			int instanceID = ((Object)monsterAI).GetInstanceID();
			bool flag = ShallowWaterFleeingByInstance.Contains(instanceID);
			if (!ShouldUseWaterDiveMode(GameAccess.AICharacter.Invoke((BaseAI)(object)monsterAI)))
			{
				ShallowWaterFleeingByInstance.Remove(instanceID);
				return false;
			}
			if (!flag && IsPassiveDiveState(monsterAI))
			{
				return false;
			}
			if (!TryGetTerrainWaterDepth(GameAccess.AICharacter.Invoke((BaseAI)(object)monsterAI), 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);
			GameAccess.TargetTimer.Invoke(monsterAI) = Mathf.Max(GameAccess.TargetTimer.Invoke(monsterAI), 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 val = GameAccess.AICharacter.Invoke((BaseAI)(object)monsterAI);
			if ((Object)(object)val != (Object)null && !val.m_canSwim)
			{
				val.m_canSwim = true;
			}
		}

		private static void PreserveInitialUnderwaterSpawnDepth(MonsterAI monsterAI, ConfiguredDiveProfile profile)
		{
			if ((Object)(object)monsterAI == (Object)null || (Object)(object)GameAccess.AICharacter.Invoke((BaseAI)(object)monsterAI) == (Object)null)
			{
				return;
			}
			int instanceID = ((Object)monsterAI).GetInstanceID();
			if (profile.PreserveSpawnDepth && !InitialSpawnDepthPreservedByInstance.Contains(instanceID))
			{
				InitialSpawnDepthPreservedByInstance.Add(instanceID);
				Character val = GameAccess.AICharacter.Invoke((BaseAI)(object)monsterAI);
				if (TryGetCurrentWaterDepth(val, out var currentWaterDepth) && currentWaterDepth > val.m_swimDepth)
				{
					val.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(GameAccess.AICharacter.Invoke((BaseAI)(object)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 val = GameAccess.AICharacter.Invoke((BaseAI)(object)monsterAI);
			OriginalDiveFlagsByInstance[instanceID] = new OriginalDiveFlags(monsterAI, ((BaseAI)monsterAI).m_avoidWater, monsterAI.m_avoidLand, (Object)(object)val != (Object)null && val.m_canSwim, ((Object)(object)val != (Object)null) ? new float?(val.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 val = GameAccess.AICharacter.Invoke((BaseAI)(object)monsterAI);
			if ((Object)(object)val != (Object)null)
			{
				val.m_canSwim = originalFlags.CanSwim;
				if (originalFlags.SwimDepth.HasValue)
				{
					val.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_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: 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)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			candidate = Quaternion.Euler(0f, angle, 0f) * horizontal;
			if (GameAccess.CanMove(ai, 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.2.10",
				MinimumRequiredVersion = "1.2.10",
				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;
			_waterEquipmentMode = null;
			_waterEquipmentBlacklist = null;
			_waterEquipmentWhitelist = 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;
			_swimHudMode = 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 GameAccess
	{
		internal delegate bool GroundHeightDelegate(Character character, Vector3 position, out float height, out Vector3 normal);

		internal delegate void PushbackDelegate(Character character, ref Vector3 velocity);

		internal static readonly FieldRef<Character, Rigidbody> Body = AccessTools.FieldRefAccess<Character, Rigidbody>("m_body");

		internal static readonly FieldRef<Character, ZNetView> CharacterView = AccessTools.FieldRefAccess<Character, ZNetView>("m_nview");

		internal static readonly FieldRef<Character, float> LastGroundTouch = AccessTools.FieldRefAccess<Character, float>("m_lastGroundTouch");

		internal static readonly FieldRef<Character, float> SwimTimer = AccessTools.FieldRefAccess<Character, float>("m_swimTimer");

		internal static readonly FieldRef<Character, bool> WallRunning = AccessTools.FieldRefAccess<Character, bool>("m_wallRunning");

		internal static readonly FieldRef<Character, Vector3> CurrentVelocity = AccessTools.FieldRefAccess<Character, Vector3>("m_currentVel");

		internal static readonly FieldRef<Player, float> Stamina = AccessTools.FieldRefAccess<Player, float>("m_stamina");

		internal static readonly FieldRef<Player, float> StaminaRegenTimer = AccessTools.FieldRefAccess<Player, float>("m_staminaRegenTimer");

		internal static readonly FieldRef<Player, float> Eitr = AccessTools.FieldRefAccess<Player, float>("m_eitr");

		internal static readonly FieldRef<Humanoid, ItemData> HiddenRightItem = AccessTools.FieldRefAccess<Humanoid, ItemData>("m_hiddenRightItem");

		internal static readonly FieldRef<Humanoid, ItemData> HiddenLeftItem = AccessTools.FieldRefAccess<Humanoid, ItemData>("m_hiddenLeftItem");

		internal static readonly FieldRef<BaseAI, Character> AICharacter = AccessTools.FieldRefAccess<BaseAI, Character>("m_character");

		internal static readonly FieldRef<BaseAI, ZNetView> AIView = AccessTools.FieldRefAccess<BaseAI, ZNetView>("m_nview");

		internal static readonly FieldRef<BaseAI, Tameable> AITameable = AccessTools.FieldRefAccess<BaseAI, Tameable>("m_tamable");

		internal static readonly FieldRef<MonsterAI, Character?> TargetCreature = AccessTools.FieldRefAccess<MonsterAI, Character>("m_targetCreature");

		internal static readonly FieldRef<MonsterAI, StaticTarget?> TargetStatic = AccessTools.FieldRefAccess<MonsterAI, StaticTarget>("m_targetStatic");

		internal static readonly FieldRef<MonsterAI, Vector3> LastTargetPosition = AccessTools.FieldRefAccess<MonsterAI, Vector3>("m_lastKnownTargetPos");

		internal static readonly FieldRef<MonsterAI, float> TargetTimer = AccessTools.FieldRefAccess<MonsterAI, float>("m_updateTargetTimer");

		internal static readonly FieldRef<Projectile, Character> ProjectileOwner = AccessTools.FieldRefAccess<Projectile, Character>("m_owner");

		internal static readonly FieldRef<Projectile, ZNetView> ProjectileView = AccessTools.FieldRefAccess<Projectile, ZNetView>("m_nview");

		internal static readonly FieldRef<Projectile, Vector3> ProjectileVelocity = AccessTools.FieldRefAccess<Projectile, Vector3>("m_vel");

		internal static readonly FieldRef<Projectile, HitData> OriginalHitData = AccessTools.FieldRefAccess<Projectile, HitData>("m_originalHitData");

		internal static readonly FieldRef<GameCamera, Camera> Camera = AccessTools.FieldRefAccess<GameCamera, Camera>("m_camera");

		internal static readonly FieldRef<GameCamera, bool> WaterClipping = AccessTools.FieldRefAccess<GameCamera, bool>("m_waterClipping");

		internal static readonly FieldRef<KeyHints, bool> KeyHintsEnabled = AccessTools.FieldRefAccess<KeyHints, bool>("m_keyHintsEnabled");

		internal static readonly FieldRef<WaterVolume, float[]> WaterDepth = AccessTools.FieldRefAccess<WaterVolume, float[]>("m_normalizedDepth");

		internal static readonly Func<Character, float> LiquidDepth = AccessTools.MethodDelegate<Func<Character, float>>(AccessTools.DeclaredMethod(typeof(Character), "InLiquidDepth", Type.EmptyTypes, (Type[])null), (object)null, true);

		internal static readonly GroundHeightDelegate GroundHeight = AccessTools.MethodDelegate<GroundHeightDelegate>(AccessTools.DeclaredMethod(typeof(Character), "GetGroundHeight", new Type[3]
		{
			typeof(Vector3),
			typeof(float).MakeByRefType(),
			typeof(Vector3).MakeByRefType()
		}, (Type[])null), (object)null, true);

		internal static readonly Action<Humanoid, bool, bool> ShowHandItems = AccessTools.MethodDelegate<Action<Humanoid, bool, bool>>(AccessTools.DeclaredMethod(typeof(Humanoid), "ShowHandItems", new Type[2]
		{
			typeof(bool),
			typeof(bool)
		}, (Type[])null), (object)null, true);

		internal static readonly Func<Player, bool> TakeInput = AccessTools.MethodDelegate<Func<Player, bool>>(AccessTools.DeclaredMethod(typeof(Player), "TakeInput", Type.EmptyTypes, (Type[])null), (object)null, true);

		internal static readonly Func<Player, bool> AlwaysRotateCamera = AccessTools.MethodDelegate<Func<Player, bool>>(AccessTools.DeclaredMethod(typeof(Player), "AlwaysRotateCamera", Type.EmptyTypes, (Type[])null), (object)null, true);

		internal static readonly Func<Humanoid, float> AttackSpeedMovement = AccessTools.MethodDelegate<Func<Humanoid, float>>(AccessTools.DeclaredMethod(typeof(Humanoid), "GetAttackSpeedFactorMovement", Type.EmptyTypes, (Type[])null), (object)null, true);

		internal static readonly Func<Humanoid, float> AttackSpeedRotation = AccessTools.MethodDelegate<Func<Humanoid, float>>(AccessTools.DeclaredMethod(typeof(Humanoid), "GetAttackSpeedFactorRotation", Type.EmptyTypes, (Type[])null), (object)null, true);

		internal static readonly PushbackDelegate AddPushbackForce = AccessTools.MethodDelegate<PushbackDelegate>(AccessTools.DeclaredMethod(typeof(Character), "AddPushbackForce", new Type[1] { typeof(Vector3).MakeByRefType() }, (Type[])null), (object)null, true);

		internal static readonly Func<BaseAI, Vector3, float, float, bool> CanMove = AccessTools.MethodDelegate<Func<BaseAI, Vector3, float, float, bool>>(AccessTools.DeclaredMethod(typeof(BaseAI), "CanMove", new Type[3]
		{
			typeof(Vector3),
			typeof(float),
			typeof(float)
		}, (Type[])null), (