Decompiled source of Dive In v1.1.0

DiveIn.dll

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

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("DiveIn")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("sighsorry")]
[assembly: AssemblyProduct("DiveIn")]
[assembly: AssemblyCopyright("Copyright ©  2021")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("4358610B-F3F4-4843-B7AF-98B7BC60DCDE")]
[assembly: AssemblyFileVersion("1.1.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class ExtensionMarkerAttribute : Attribute
	{
		private readonly string <Name>k__BackingField;

		public string Name => <Name>k__BackingField;

		public ExtensionMarkerAttribute(string name)
		{
			<Name>k__BackingField = name;
		}
	}
}
namespace ServerSyncModTemplate
{
	[BepInPlugin("sighsorry.DiveIn", "DiveIn", "1.1.0")]
	[BepInIncompatibility("Searica.Valheim.UnderTheSea")]
	[BepInIncompatibility("blacks7ar.VikingsDoSwim")]
	public class ServerSyncModTemplatePlugin : BaseUnityPlugin
	{
		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? ActiveDepthAdjustSpeed { get; set; }

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

		}

		public enum Toggle
		{
			On = 1,
			Off = 0
		}

		private class ConfigurationManagerAttributes
		{
			[UsedImplicitly]
			public int? Order;

			[UsedImplicitly]
			public bool? Browsable;

			[UsedImplicitly]
			public string? Category;

			[UsedImplicitly]
			public Action<ConfigEntryBase>? CustomDrawer;
		}

		private class AcceptableShortcuts : AcceptableValueBase
		{
			public 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 ActiveDepthAdjustSpeed;

			public ConfiguredDiveProfile(string groupName, PassiveDepthProfile passiveDepthProfile, float activeDepthAdjustSpeed)
			{
				GroupName = groupName;
				PassiveDepthProfile = passiveDepthProfile;
				ActiveDepthAdjustSpeed = activeDepthAdjustSpeed;
			}
		}

		private readonly struct OriginalDiveFlags
		{
			public readonly MonsterAI MonsterAI;

			public readonly bool AvoidWater;

			public readonly bool AvoidLand;

			public readonly bool CanSwim;

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

		private readonly struct MovePlanCacheEntry
		{
			public readonly float Time;

			public readonly Vector3Int PositionBucket;

			public readonly Vector3Int TargetBucket;

			public readonly bool HasRoute;

			public readonly Vector3 Direction;

			public MovePlanCacheEntry(float time, Vector3Int positionBucket, Vector3Int targetBucket, bool hasRoute, Vector3 direction)
			{
				//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)
				//IL_001e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0020: Unknown result type (might be due to invalid IL or missing references)
				Time = time;
				PositionBucket = positionBucket;
				TargetBucket = targetBucket;
				HasRoute = hasRoute;
				Direction = direction;
			}
		}

		private readonly struct UnderwaterNavigationPlan
		{
			public readonly bool HasRoute;

			public readonly Vector3 Direction;

			public UnderwaterNavigationPlan(bool hasRoute, Vector3 direction)
			{
				//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;
			}
		}

		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(MonsterAI), "Awake")]
		private static class MonsterAIAwakePatch
		{
			private static void Postfix(MonsterAI __instance)
			{
				if (IsConfiguredMonster(__instance))
				{
					EnsureDiveFlags(__instance);
				}
			}
		}

		[HarmonyPatch(typeof(MonsterAI), "UpdateAI")]
		private static class MonsterAIUpdateAIPatch
		{
			private static void Prefix(MonsterAI __instance)
			{
				if (IsConfiguredMonster(__instance))
				{
					EnsureDiveFlags(__instance);
				}
			}
		}

		[HarmonyPatch(typeof(BaseAI), "HavePath")]
		private static class BaseAIHavePathPatch
		{
			private static bool Prefix(BaseAI __instance, Vector3 target, ref bool __result)
			{
				//IL_0029: Unknown result type (might be due to invalid IL or missing references)
				if (!TryGetConfiguredMonster(__instance, out MonsterAI monsterAI) || !ShouldUseWaterDiveMode(monsterAI))
				{
					return true;
				}
				Character character = ((BaseAI)monsterAI).m_character;
				if ((Object)(object)character == (Object)null)
				{
					return true;
				}
				__result = BuildUnderwaterNavigationPlan(__instance, character, target).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_0028: 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_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_00d8: Unknown result type (might be due to invalid IL or missing references)
				//IL_00df: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
				//IL_0106: Unknown result type (might be due to invalid IL or missing references)
				//IL_010b: 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)
				if (!TryGetConfiguredMonster(__instance, out MonsterAI monsterAI) || !ShouldUseWaterDiveMode(monsterAI))
				{
					return true;
				}
				Character character = ((BaseAI)monsterAI).m_character;
				if ((Object)(object)character == (Object)null)
				{
					return true;
				}
				SwimDepthGoal goal = CalculateSwimDepthGoal(monsterAI, character, point);
				UnderwaterNavigationPlan underwaterNavigationPlan = BuildUnderwaterNavigationPlan(__instance, character, point);
				ApplySwimDepthGoal(character, goal, dt);
				float num = Mathf.Max(dist, run ? 1f : 0.5f);
				float num2 = Utils.DistanceXZ(point, ((Component)__instance).transform.position);
				float num3 = Mathf.Abs(point.y - ((Component)__instance).transform.position.y);
				float num4 = Mathf.Abs(goal.ClampedTargetY - ((Component)__instance).transform.position.y);
				bool flag = num3 < 0.75f || (goal.RequestedOutsideRange && num4 < 0.35f);
				if (num2 < num && flag)
				{
					__instance.StopMoving();
					__result = true;
					return false;
				}
				Vector3 val = point - ((Component)__instance).transform.position;
				if (((Vector3)(ref val)).sqrMagnitude <= 0.0001f)
				{
					__instance.StopMoving();
					__result = true;
					return false;
				}
				Vector3 direction = underwaterNavigationPlan.Direction;
				if (((Vector3)(ref direction)).sqrMagnitude <= 0.0001f)
				{
					__instance.StopMoving();
					__result = true;
					return false;
				}
				__instance.MoveTowards(underwaterNavigationPlan.Direction, run);
				__result = false;
				return false;
			}
		}

		internal const string ModName = "DiveIn";

		internal const string ModVersion = "1.1.0";

		internal const string Author = "sighsorry";

		private const string ModGUID = "sighsorry.DiveIn";

		private static readonly string MonsterDiveYamlFileName = "DiveIn.yaml";

		private static readonly string MonsterDiveYamlFileFullPath;

		private static readonly object MonsterDiveYamlLock;

		private static readonly IDeserializer MonsterDiveYamlDeserializer;

		private FileSystemWatcher _monsterDiveYamlWatcher;

		private DateTime _lastMonsterDiveYamlReloadTime;

		private static CustomSyncedValue<string> _monsterDiveYamlSync;

		internal const float DefaultUnderwaterDarknessFactor = 0.5f;

		internal const float DefaultUnderwaterVisibilityFalloff = 0.25f;

		internal const float DefaultUnderwaterCameraMinWaterDistance = -5000f;

		internal static ConfigEntry<string> _waterEquipmentBlacklist;

		internal static ConfigEntry<float> _surfaceStaminaRegenRateMultiplier;

		internal static ConfigEntry<float> _midwaterStaminaRegenRateMultiplier;

		internal static ConfigEntry<float> _surfaceEitrRegenRateMultiplier;

		internal static ConfigEntry<float> _midwaterEitrRegenRateMultiplier;

		internal static ConfigEntry<float> _midwaterIdleStaminaDrainPerDepth;

		internal static ConfigEntry<float> _swimStaminaDrainMultiplierPerDepth;

		internal static ConfigEntry<Toggle> _multiplicativeSwimStaminaModifiers;

		internal static ConfigEntry<float> _swimStaminaDrainBaseMultiplier;

		internal static ConfigEntry<float> _playerSwimSkillSpeedMultiplier;

		internal static ConfigEntry<float> _fastSwimSpeedMultiplier;

		internal static ConfigEntry<float> _fastSwimStaminaDrainMultiplier;

		internal static ConfigEntry<KeyboardShortcut> _playerDiveAscendShortcut;

		internal static ConfigEntry<KeyboardShortcut> _playerDiveDescendShortcut;

		internal static ConfigEntry<float> _underwaterDarknessFactor;

		internal static ConfigEntry<float> _underwaterVisibilityFalloff;

		private static readonly object WaterEquipmentBlacklistLock;

		private static string _lastWaterEquipmentBlacklistRaw;

		private static HashSet<string> _waterEquipmentBlacklistSet;

		private static readonly string ConfigFileName;

		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 DateTime _lastConfigReloadTime;

		private const long RELOAD_DELAY = 10000000L;

		private static ConfigEntry<Toggle> _serverConfigLocked;

		private static IReadOnlyDictionary<string, ConfiguredDiveProfile> _configuredDiveProfilesByPrefabName;

		private static readonly Dictionary<int, OriginalDiveFlags> OriginalDiveFlagsByInstance;

		private const int MaxCacheEntries = 2048;

		private const float PassiveWavePeriodSeconds = 12f;

		private const float ActiveSwimDepthMin = 0.25f;

		private const float ActiveSwimDepthMax = 300f;

		private const float SwimDepthAdjustSpeed = 2f;

		private const float MovePlanCacheSeconds = 0.1f;

		private const float MovePlanCacheCellSize = 0.1f;

		private const int AvoidanceSampleCount = 8;

		private static readonly float[] SteerAngles;

		private static readonly Dictionary<int, MovePlanCacheEntry> MovePlanCache;

		private void InitializeMonsterDiveYaml()
		{
			_monsterDiveYamlSync = new CustomSyncedValue<string>(ConfigSync, "MonsterDiveYaml", string.Empty);
			_monsterDiveYamlSync.ValueChanged += OnMonsterDiveYamlValueChanged;
			ConfigSync.SourceOfTruthChanged += OnMonsterDiveSourceOfTruthChanged;
			if (ConfigSync.IsSourceOfTruth)
			{
				LoadMonsterDiveYamlFromDisk(forceWriteDefaultIfMissing: true, syncToPeers: true, "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.IncludeSubdirectories = true;
			_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)
		{
			DateTime now = DateTime.Now;
			if (now.Ticks - _lastMonsterDiveYamlReloadTime.Ticks < 10000000)
			{
				return;
			}
			_lastMonsterDiveYamlReloadTime = now;
			lock (_reloadLock)
			{
				if (!ConfigSync.IsSourceOfTruth)
				{
					ServerSyncModTemplateLogger.LogInfo((object)"Ignoring local monster dive YAML reload because remote synced values are active.");
					return;
				}
				try
				{
					LoadMonsterDiveYamlFromDisk(forceWriteDefaultIfMissing: true, syncToPeers: true, "yaml reload");
					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(forceWriteDefaultIfMissing: true, syncToPeers: true, "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 void LoadMonsterDiveYamlFromDisk(bool forceWriteDefaultIfMissing, bool syncToPeers, string reason)
		{
			lock (MonsterDiveYamlLock)
			{
				if (!File.Exists(MonsterDiveYamlFileFullPath))
				{
					if (!forceWriteDefaultIfMissing)
					{
						return;
					}
					string contents = BuildDefaultMonsterDiveYaml();
					Directory.CreateDirectory(Paths.ConfigPath);
					File.WriteAllText(MonsterDiveYamlFileFullPath, contents);
				}
				string text = File.ReadAllText(MonsterDiveYamlFileFullPath);
				if (ApplyMonsterDiveYaml(text, reason) && syncToPeers && _monsterDiveYamlSync.Value != text)
				{
					_monsterDiveYamlSync.Value = text;
				}
			}
		}

		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;
			}
			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;
				PassiveDepthProfile passiveDepthProfile = NormalizePassiveDepthProfile(key, value.PassiveMinDepth, value.PassiveCenterDepth, value.PassiveMaxDepth);
				float activeDepthAdjustSpeed = NormalizeActiveDepthAdjustSpeed(key, value.ActiveDepthAdjustSpeed);
				AddYamlGroupEntries(configuredDiveProfile: new ConfiguredDiveProfile(key, passiveDepthProfile, activeDepthAdjustSpeed), 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;
			int num = RestoreRemovedMonsterDiveFlags();
			ClearRuntimeCaches();
			ServerSyncModTemplateLogger.LogInfo((object)$"Loaded monster dive YAML ({reason}). passiveGroups={definedGroups.Count}, prefabs={dictionary.Count}, active[min={0.25f:F2}, max={300f:F2}, defaultAdjust={2f:F2}], restoredRemovedInstances={num}.");
			return true;
		}

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

		private static PassiveDepthProfile NormalizePassiveDepthProfile(string groupName, float minDepth, float centerDepth, float maxDepth)
		{
			float num = minDepth;
			float num2 = centerDepth;
			float num3 = maxDepth;
			float num4 = Mathf.Max(0f, num);
			float num5 = Mathf.Max(0f, num3);
			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) + "."));
			}
			return new PassiveDepthProfile(num7, num4, num5);
		}

		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, 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, new string[30]
			{
				"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_WhiteShark", "SA_HumboldtSquid",
				"SA_LeatherbackSeaTurtle", "SA_RightWhale", "SA_WhaleShark", "SA_BlueShark", "SA_HammerHeadShark", "SA_TigerShark", "SA_BlueTurtle", "SA_GreenTurtle", "SA_RedTurtle", "SA_YellowTurtle"
			});
			stringBuilder.AppendLine();
			AppendDefaultGroup(stringBuilder, "mods_midwater", 0f, 15f, 30f, 2f, includeGroupHeaderComment: false, includeFieldComments: false, 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, new string[2] { "Animal_Tuna_RtD", "Animal_Squid_RtD" });
			stringBuilder.AppendLine();
			AppendDefaultGroup(stringBuilder, "mods_bottom", 20f, 30f, 40f, 2f, includeGroupHeaderComment: false, includeFieldComments: false, 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, 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 ? " # How quickly this group adjusts swim depth while alerted or chasing a target." : string.Empty);
			string text6 = (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_depth_adjust_speed: " + FormatYamlFloat(activeDepthAdjustSpeed) + text5);
			if (examplePrefabs != null)
			{
				string[] array = examplePrefabs.Where((string prefab) => !string.IsNullOrWhiteSpace(prefab)).ToArray();
				if (array.Length != 0)
				{
					builder.AppendLine("  prefabs:" + text6);
					string[] array2 = array;
					foreach (string text7 in array2)
					{
						builder.AppendLine("    - " + text7);
					}
					return;
				}
			}
			builder.AppendLine("  prefabs: []" + text6);
		}

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

		private void InitializePlayerDiveConfig()
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected O, but got Unknown
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Expected O, but got Unknown
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Expected O, but got Unknown
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Expected O, but got Unknown
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Expected O, but got Unknown
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c7: Expected O, but got Unknown
			//IL_020b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0216: Expected O, but got Unknown
			//IL_0248: Unknown result type (might be due to invalid IL or missing references)
			//IL_0253: Expected O, but got Unknown
			//IL_0297: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a2: Expected O, but got Unknown
			//IL_02e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f1: Expected O, but got Unknown
			//IL_0335: Unknown result type (might be due to invalid IL or missing references)
			//IL_0340: Expected O, but got Unknown
			//IL_0384: Unknown result type (might be due to invalid IL or missing references)
			//IL_038f: Expected O, but got Unknown
			//IL_03a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_03db: Expected O, but got Unknown
			//IL_03f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_041f: Unknown result type (might be due to invalid IL or missing references)
			//IL_042a: Expected O, but got Unknown
			//IL_046e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0479: Expected O, but got Unknown
			//IL_04bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c8: Expected O, but got Unknown
			_waterEquipmentBlacklist = config("2 - Player Diving", "Water Equipment Blacklist", "", new ConfigDescription("Comma-separated item prefab names that remain restricted in water. Everything not listed is allowed in water by default. Example: BowFineWood,ShieldBronzeBuckler.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 100
				}
			}));
			_surfaceStaminaRegenRateMultiplier = config("3 - Regen Rate", "Surface Stamina Regen Rate", 0.5f, new ConfigDescription("Multiplier applied to vanilla stamina regeneration while swimming on the surface with your head above water. 0 matches vanilla swimming behavior, 1 matches normal non-swimming stamina regeneration timing and rate.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 110
				}
			}));
			_midwaterStaminaRegenRateMultiplier = config("3 - Regen Rate", "Midwater Stamina Regen Rate", 0f, new ConfigDescription("Multiplier applied to vanilla stamina regeneration while your head is underwater. 0 makes stamina recover only after surfacing.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 109
				}
			}));
			_surfaceEitrRegenRateMultiplier = config("3 - Regen Rate", "Surface Eitr Regen Rate", 0.7f, new ConfigDescription("Multiplier applied to vanilla eitr regeneration while swimming on the surface with your head above water. 0 disables eitr regeneration while surface swimming, 1 keeps vanilla eitr regeneration.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 108
				}
			}));
			_midwaterEitrRegenRateMultiplier = config("3 - Regen Rate", "Midwater Eitr Regen Rate", 0.3f, new ConfigDescription("Multiplier applied to vanilla eitr regeneration while your head is underwater. 0 makes eitr recover only after surfacing.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 107
				}
			}));
			_midwaterIdleStaminaDrainPerDepth = config("4 - Stamina Drain", "Midwater Idle Stamina Drain Per Depth", 0.02f, new ConfigDescription("Idle stamina drained per second per 1m of current liquid depth while your head is underwater. 0 disables idle underwater stamina drain. Example: 0.1 drains 3 stamina per second at 30m depth.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 109
				}
			}));
			_swimStaminaDrainMultiplierPerDepth = config("4 - Stamina Drain", "Swim Stamina Drain Multiplier Per Depth", 2.5f, new ConfigDescription("Additional moving swim stamina drain percent per 1m of current liquid depth. 1 means 30% extra at 30m; 2.5 means 75% extra at 30m. Applied multiplicatively with base and Fast Swim stamina drain.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 108
				}
			}));
			_multiplicativeSwimStaminaModifiers = config("4 - Stamina Drain", "Multiplicative Swim Stamina Modifiers", Toggle.On, new ConfigDescription("If on, status-effect swim stamina use modifiers stack multiplicatively during actual swim stamina consumption. Off is vanilla. Example: -50% and -60% leaves 20% cost instead of 0%. Tooltips keep vanilla display behavior.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 110
				}
			}));
			_swimStaminaDrainBaseMultiplier = config("4 - Stamina Drain", "Swim Stamina Drain Base Multiplier", 1f, new ConfigDescription("Multiplier applied to vanilla moving swim stamina drain before depth and Fast Swim multipliers. 1 keeps vanilla cost, 0.5 halves it, 2 doubles it.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 2f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 107
				}
			}));
			_fastSwimSpeedMultiplier = config("5 - Swim Speed", "Fast Swim Speed Multiplier", 2f, new ConfigDescription("Swim speed multiplier while Fast Swim is toggled on with the vanilla run key. 1 disables Fast Swim and hides its key hint. Swim skill separately increases base swim speed.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 3f), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 109
				}
			}));
			_fastSwimStaminaDrainMultiplier = config("5 - Swim Speed", "Fast Swim Stamina Drain Multiplier", 2f, new ConfigDescription("Moving swim stamina drain multiplier while Fast Swim is toggled on. 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
				}
			}));
			_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);
			_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 bool IsUnderwaterVisualStylingEnabled()
		{
			return true;
		}

		internal static float GetUnderwaterCameraMinWaterDistance()
		{
			return -5000f;
		}

		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 UseMultiplicativeSwimStaminaModifiers()
		{
			ConfigEntry<Toggle> multiplicativeSwimStaminaModifiers = _multiplicativeSwimStaminaModifiers;
			if (multiplicativeSwimStaminaModifiers == null)
			{
				return false;
			}
			return multiplicativeSwimStaminaModifiers.Value == Toggle.On;
		}

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

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

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

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

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

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

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

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

		private 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)(ref key)).ToString(), 
				}, 
			};
		}

		internal static bool IsWaterRestrictedItem(ItemData? item)
		{
			if (item == null || (Object)(object)item.m_dropPrefab == (Object)null)
			{
				return false;
			}
			RefreshWaterEquipmentBlacklistIfNeeded();
			string prefabName = Utils.GetPrefabName(item.m_dropPrefab);
			if (!string.IsNullOrEmpty(prefabName))
			{
				return _waterEquipmentBlacklistSet.Contains(prefabName);
			}
			return false;
		}

		internal static bool HumanoidHasWaterRestrictedEquipment(Humanoid? humanoid)
		{
			if ((Object)(object)humanoid == (Object)null)
			{
				return false;
			}
			if (!IsWaterRestrictedItem(humanoid.m_rightItem) && !IsWaterRestrictedItem(humanoid.m_hiddenRightItem) && !IsWaterRestrictedItem(humanoid.m_leftItem) && !IsWaterRestrictedItem(humanoid.m_hiddenLeftItem) && !IsWaterRestrictedItem(humanoid.m_chestItem) && !IsWaterRestrictedItem(humanoid.m_legItem) && !IsWaterRestrictedItem(humanoid.m_helmetItem) && !IsWaterRestrictedItem(humanoid.m_shoulderItem) && !IsWaterRestrictedItem(humanoid.m_utilityItem))
			{
				return IsWaterRestrictedItem(humanoid.m_trinketItem);
			}
			return true;
		}

		private static void RefreshWaterEquipmentBlacklistIfNeeded(bool force = false)
		{
			string text = _waterEquipmentBlacklist?.Value ?? string.Empty;
			if (!force && string.Equals(text, _lastWaterEquipmentBlacklistRaw, StringComparison.Ordinal))
			{
				return;
			}
			lock (WaterEquipmentBlacklistLock)
			{
				if (force || !string.Equals(text, _lastWaterEquipmentBlacklistRaw, StringComparison.Ordinal))
				{
					_waterEquipmentBlacklistSet = (from entry in text.Split(new char[5] { ',', ';', '\n', '\r', '\t' }, StringSplitOptions.RemoveEmptyEntries)
						select entry.Trim() into entry
						where entry.Length > 0
						select entry).ToHashSet<string>(StringComparer.OrdinalIgnoreCase);
					_lastWaterEquipmentBlacklistRaw = text;
				}
			}
		}

		public void Awake()
		{
			bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			_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();
			InitializeMonsterDiveModule();
			_harmony.PatchAll(Assembly.GetExecutingAssembly());
			DiveLocalization.Register();
			SetupWatcher();
			StartMonsterDiveModule();
			((BaseUnityPlugin)this).Config.Save();
			if (saveOnConfigSet)
			{
				((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet;
			}
		}

		private void OnDestroy()
		{
			_harmony.UnpatchSelf();
			UnderwaterVisualState.ResetAll();
			DisposeMonsterDiveModule();
			SaveWithRespectToConfigSet();
			DisposeConfigWatcher();
		}

		private void SetupWatcher()
		{
			_watcher = new FileSystemWatcher(Paths.ConfigPath, ConfigFileName);
			_watcher.Changed += ReadConfigValues;
			_watcher.Created += ReadConfigValues;
			_watcher.Renamed += ReadConfigValues;
			_watcher.IncludeSubdirectories = true;
			_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)
		{
			DateTime now = DateTime.Now;
			if (now.Ticks - _lastConfigReloadTime.Ticks < 10000000)
			{
				return;
			}
			_lastConfigReloadTime = now;
			lock (_reloadLock)
			{
				if (!File.Exists(ConfigFileFullPath))
				{
					ServerSyncModTemplateLogger.LogWarning((object)"Config file does not exist. Skipping reload.");
					return;
				}
				try
				{
					SaveWithRespectToConfigSet(reload: true);
					UnderwaterVisualState.ResetAll();
					ReloadMonsterDiveModule();
					ServerSyncModTemplateLogger.LogInfo((object)"Configuration reload complete.");
				}
				catch (Exception ex)
				{
					ServerSyncModTemplateLogger.LogError((object)("Error reloading configuration: " + ex.Message));
				}
			}
		}

		private void SaveWithRespectToConfigSet(bool reload = false)
		{
			bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			if (reload)
			{
				((BaseUnityPlugin)this).Config.Reload();
			}
			((BaseUnityPlugin)this).Config.Save();
			if (saveOnConfigSet)
			{
				((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 InitializeMonsterDiveModule()
		{
			InitializeMonsterDiveYaml();
			ClearRuntimeCaches();
		}

		private void StartMonsterDiveModule()
		{
			SetupMonsterDiveYamlWatcher();
		}

		private void DisposeMonsterDiveModule()
		{
			int num = RestoreAllTrackedMonsterDiveFlags();
			if (num > 0)
			{
				ServerSyncModTemplateLogger.LogInfo((object)$"Restored original dive flags for {num} monster instances.");
			}
			ClearRuntimeCaches();
			DisposeMonsterDiveYamlWatcher();
		}

		private static void ReloadMonsterDiveModule()
		{
			ClearRuntimeCaches();
		}

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

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

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

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

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

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

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

		private static void TrackOriginalDiveFlags(MonsterAI monsterAI)
		{
			if (!Object.op_Implicit((Object)(object)monsterAI))
			{
				return;
			}
			int instanceID = ((Object)monsterAI).GetInstanceID();
			if (!OriginalDiveFlagsByInstance.ContainsKey(instanceID))
			{
				Character character = ((BaseAI)monsterAI).m_character;
				OriginalDiveFlagsByInstance[instanceID] = new OriginalDiveFlags(monsterAI, ((BaseAI)monsterAI).m_avoidWater, monsterAI.m_avoidLand, (Object)(object)character != (Object)null && character.m_canSwim);
				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)
			{
				OriginalDiveFlagsByInstance.Remove(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++;
				}
			}
			OriginalDiveFlagsByInstance.Clear();
			return num;
		}

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

		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)
			{
				OriginalDiveFlagsByInstance.Remove(item2);
			}
		}

		private static Vector3Int ToCacheBucket(Vector3 value)
		{
			//IL_0000: 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_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			return new Vector3Int(Mathf.RoundToInt(value.x / 0.1f), Mathf.RoundToInt(value.y / 0.1f), Mathf.RoundToInt(value.z / 0.1f));
		}

		private static void ClearRuntimeCaches()
		{
			MovePlanCache.Clear();
		}

		private static void TrimCachesIfNeeded()
		{
			if (MovePlanCache.Count > 2048)
			{
				MovePlanCache.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)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			TryGetConfiguredDiveProfile(monsterAI, out var configuredDiveProfile);
			float liquidLevel = character.GetLiquidLevel();
			float adjustSpeed;
			float num;
			bool flag;
			if (IsPassiveDiveState(monsterAI))
			{
				num = GetPassiveDesiredDepth(monsterAI, configuredDiveProfile.PassiveDepthProfile);
				flag = true;
				adjustSpeed = 2f;
			}
			else
			{
				float num2 = liquidLevel - point.y;
				num = Mathf.Clamp(num2, 0.25f, 300f);
				flag = num2 < 0.25f || num2 > 300f;
				adjustSpeed = configuredDiveProfile.ActiveDepthAdjustSpeed;
			}
			float num3 = num;
			num = ClampSwimDepthForBottomContact(character, num);
			flag = flag || num < num3 - 0.001f;
			float clampedTargetY = liquidLevel - num;
			return new SwimDepthGoal(num, 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 float ClampSwimDepthForBottomContact(Character character, float desiredDepth)
		{
			return UnderwaterDepthUtils.ClampDepthAboveBottom(character, desiredDepth, 0.25f);
		}

		private static UnderwaterNavigationPlan BuildUnderwaterNavigationPlan(BaseAI ai, Character character, Vector3 targetPoint)
		{
			//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_0017: 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_0019: 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_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: 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_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			int instanceID = ((Object)ai).GetInstanceID();
			Vector3Int val = ToCacheBucket(((Component)ai).transform.position);
			Vector3Int val2 = ToCacheBucket(targetPoint);
			float time = Time.time;
			if (MovePlanCache.TryGetValue(instanceID, out var value) && time - value.Time <= 0.1f && value.PositionBucket == val && value.TargetBucket == val2)
			{
				return new UnderwaterNavigationPlan(value.HasRoute, value.Direction);
			}
			UnderwaterNavigationPlan result = CalculateUnderwaterNavigationPlan(ai, character, targetPoint);
			TrimCachesIfNeeded();
			MovePlanCache[instanceID] = new MovePlanCacheEntry(time, val, val2, result.HasRoute, result.Direction);
			return result;
		}

		private static UnderwaterNavigationPlan CalculateUnderwaterNavigationPlan(BaseAI ai, Character character, Vector3 targetPoint)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: 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_0096: 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_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_019d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = targetPoint - ((Component)ai).transform.position;
			if (((Vector3)(ref val)).sqrMagnitude <= 0.0001f)
			{
				return new UnderwaterNavigationPlan(hasRoute: true, Vector3.zero);
			}
			((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);
			if (num <= radius + 0.6f)
			{
				return new UnderwaterNavigationPlan(hasRoute: true, val);
			}
			if (((Vector3)(ref val2)).sqrMagnitude <= 0.0001f)
			{
				return new UnderwaterNavigationPlan(hasRoute: true, val);
			}
			((Vector3)(ref val2)).Normalize();
			Vector3 centerPoint = character.GetCenterPoint();
			int num3 = Mathf.Clamp(8, 3, SteerAngles.Length);
			Vector3 val3 = val2;
			bool hasRoute = false;
			float num4 = float.NegativeInfinity;
			for (int i = 0; i < num3; i++)
			{
				float num5 = SteerAngles[i];
				Vector3 val4 = Quaternion.Euler(0f, num5, 0f) * val2;
				float num6;
				bool flag;
				if (ai.CanMove(val4, radius, num2))
				{
					num6 = 1000f - Mathf.Abs(num5);
					flag = true;
				}
				else
				{
					float num7 = ai.Raycast(centerPoint, val4, num2 * 2f, 0.1f);
					num6 = num7 - Mathf.Abs(num5) * 0.01f;
					flag = num7 >= num2 * 0.9f;
				}
				if (num6 > num4)
				{
					num4 = num6;
					val3 = val4;
					hasRoute = flag;
				}
			}
			Vector3 val5 = default(Vector3);
			((Vector3)(ref val5))..ctor(val3.x, val.y, val3.z);
			Vector3 direction = ((((Vector3)(ref val5)).sqrMagnitude > 0.0001f) ? ((Vector3)(ref val5)).normalized : val);
			return new UnderwaterNavigationPlan(hasRoute, direction);
		}

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

			internal string FastSwimOff { get; }

			internal string Descend { get; }

			internal string Ascend { get; }

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

		internal const string FastSwimOnKey = "$divein_fast_swim_on";

		internal const string FastSwimOffKey = "$divein_fast_swim_off";

		internal const string DescendKey = "$divein_descend";

		internal const string AscendKey = "$divein_ascend";

		private const string FastSwimOnWord = "divein_fast_swim_on";

		private const string FastSwimOffWord = "divein_fast_swim_off";

		private const string DescendWord = "divein_descend";

		private const string AscendWord = "divein_ascend";

		private const string EnglishLanguage = "english";

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

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

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

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

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

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

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

		private static string GetEnglishText(string key)
		{
			return key switch
			{
				"$divein_fast_swim_on" => English.FastSwimOn, 
				"$divein_fast_swim_off" => English.FastSwimOff, 
				"$divein_descend" => English.Descend, 
				"$divein_ascend" => English.Ascend, 
				_ => key, 
			};
		}
	}
	[HarmonyPatch(typeof(Localization), "SetupLanguage")]
	internal static class LocalizationSetupLanguageDivePatch
	{
		private static void Postfix(Localization __instance)
		{
			DiveLocalization.Register(__instance);
		}
	}
	internal static class PlayerDiveUtils
	{
		internal static PlayerDiveController? EnsureLocalDiver()
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return null;
			}
			PlayerDiveController playerDiveController = PlayerDiveController.LocalInstance;
			if ((Object)(object)playerDiveController != (Object)null && (Object)(object)playerDiveController.Player == (Object)(object)localPlayer)
			{
				return playerDiveController;
			}
			if (!((Component)localPlayer).TryGetComponent<PlayerDiveController>(ref playerDiveController))
			{
				playerDiveController = ((Component)localPlayer).gameObject.AddComponent<PlayerDiveController>();
			}
			return playerDiveController;
		}

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

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

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

		private const float MinimumSurfaceSwimDepth = 0.1f;

		private const float DivingSwimDepthOffset = 1.1f;

		private const float BottomAscendDepthStep = 0.75f;

		private const float CombatMovementSuppressionDuration = 0.1f;

		private float _surfaceSwimDepth = 2f;

		private bool _underwaterMovementActive;

		private bool _fastSwimEnabled;

		private bool _hasSwimSpeedOverride;

		private float _originalSwimSpeed;

		private float _activeSwimRunStaminaDrainMultiplier = 1f;

		private float _combatMovementSuppressedUntilTime;

		private int _swimmingUpdateContextDepth;

		private int _swimmingUpdateContextFrame = -1;

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

		internal Player Player { get; private set; }

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

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

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

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

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

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

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

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

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

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

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

		internal bool IsFastSwimEnabled()
		{
			if (ServerSyncModTemplatePlugin.IsSwimRunEnabled())
			{
				return _fastSwimEnabled;
			}
			return false;
		}

		internal void UpdateFastSwimToggle()
		{
			if (!ShouldShowDiveKeyHints() || !ServerSyncModTemplatePlugin.IsSwimRunEnabled())
			{
				_fastSwimEnabled = false;
			}
			else if (ZInput.GetButtonDown("Run") || ZInput.GetButtonDown("JoyRun"))
			{
				_fastSwimEnabled = !_fastSwimEnabled;
			}
		}

		internal void SuppressMovementForCombat()
		{
			_combatMovementSuppressedUntilTime = Mathf.Max(_combatMovementSuppressedUntilTime, Time.time + 0.1f);
		}

		internal bool IsMovementSuppressedForCombat()
		{
			return Time.time <= _combatMovementSuppressedUntilTime;
		}

		internal bool ShouldForceDive()
		{
			if (ShouldForceSwimming())
			{
				return !((Character)Player).IsOnGround();
			}
			return false;
		}

		internal void PrepareForcedSwimming()
		{
			ClampSwimDepthForBottomContact();
			((Character)Player).m_body.WakeUp();
			((Character)Player).m_lastGroundTouch = 0.3f;
			((Character)Player).m_swimTimer = 0f;
		}

		internal bool IsUnderSurface()
		{
			return ((Character)Player).m_swimDepth > _surfaceSwimDepth + 0.01f;
		}

		internal bool IsDiving()
		{
			return ((Character)Player).m_swimDepth > _surfaceSwimDepth + 1.1f;
		}

		internal bool IsSurfacing()
		{
			if (!IsDiving())
			{
				return IsUnderSurface();
			}
			return false;
		}

		internal bool IsIdleInWater()
		{
			//IL_000e: 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)
			if (ShouldTreatAsSwimming())
			{
				Vector3 velocity = ((Character)Player).GetVelocity();
				return ((Vector3)(ref velocity)).magnitude < 1f;
			}
			return false;
		}

		internal void BeginSwimmingUpdateContext()
		{
			_swimmingUpdateContextDepth++;
			_swimmingUpdateContextFrame = Time.frameCount;
		}

		internal void EndSwimmingUpdateContext()
		{
			if (_swimmingUpdateContextDepth > 0)
			{
				_swimmingUpdateContextDepth--;
			}
			if (_swimmingUpdateContextDepth == 0)
			{
				_swimmingUpdateContextFrame = -1;
			}
		}

		internal bool IsInSwimmingUpdateContext()
		{
			if (_swimmingUpdateContextDepth > 0)
			{
				return _swimmingUpdateContextFrame == Time.frameCount;
			}
			return false;
		}

		internal void RegenWaterStamina(float dt)
		{
			float num = (IsHeadUnderwater() ? ServerSyncModTemplatePlugin._midwaterStaminaRegenRateMultiplier.Value : ServerSyncModTemplatePlugin._surfaceStaminaRegenRateMultiplier.Value);
			if (!(num <= 0f))
			{
				float maxStamina = ((Character)Player).GetMaxStamina();
				float num2 = 1f;
				if (((Character)Player).IsBlocking())
				{
					num2 *= 0.8f;
				}
				if (((Character)Player).InAttack() || ((Character)Player).InDodge() || ((Character)Player).m_wallRunning || ((Character)Player).IsEncumbered())
				{
					num2 = 0f;
				}
				float num3 = (Player.m_staminaRegen + (1f - Player.m_stamina / maxStamina) * Player.m_staminaRegen * Player.m_staminaRegenTimeMultiplier) * num2;
				float num4 = 1f;
				((Character)Player).m_seman.ModifyStaminaRegen(ref num4);
				num3 *= num4;
				num3 *= num;
				if (Player.m_stamina < maxStamina && Player.m_staminaRegenTimer <= 0f)
				{
					Player.m_stamina = Mathf.Min(maxStamina, Player.m_stamina + num3 * dt * Game.m_staminaRegenRate);
				}
			}
		}

		internal void ApplyIdleMidwaterStaminaDrain(float dt)
		{
			float num = Mathf.Max(0f, ServerSyncModTemplatePlugin._midwaterIdleStaminaDrainPerDepth.Value);
			if (!(num <= 0f) && IsHeadUnderwater() && IsIdleInWater())
			{
				float num2 = Mathf.Max(0f, ((Character)Player).InLiquidDepth()) * num;
				if (!(num2 <= 0f))
				{
					((Character)Player).UseStamina(num2 * dt);
				}
			}
		}

		internal void AdjustMovingSwimStaminaDrain(float staminaBeforeVanillaSwim)
		{
			if (!SwimResourceAdjustments.TryGetDrain(staminaBeforeVanillaSwim, Player.m_stamina, out var drain))
			{
				return;
			}
			float movingSwimStaminaDrainMultiplier = GetMovingSwimStaminaDrainMultiplier();
			if (!Mathf.Approximately(movingSwimStaminaDrainMultiplier, 1f))
			{
				float scaledDrainValue = SwimResourceAdjustments.GetScaledDrainValue(staminaBeforeVanillaSwim, ((Character)Player).GetMaxStamina(), drain, movingSwimStaminaDrainMultiplier);
				if (scaledDrainValue < Player.m_stamina)
				{
					((Character)Player).UseStamina(Player.m_stamina - scaledDrainValue);
				}
				else
				{
					Player.m_stamina = scaledDrainValue;
				}
			}
		}

		internal void UpdateSwimSpeed()
		{
			ResetSwimSpeedOverride();
			float swimSkillSpeedMultiplier = GetSwimSkillSpeedMultiplier();
			float swimRunSpeedMultiplier = GetSwimRunSpeedMultiplier();
			_activeSwimRunStaminaDrainMultiplier = GetSwimRunStaminaDrainMultiplier();
			float num = swimSkillSpeedMultiplier * swimRunSpeedMultiplier;
			if (!Mathf.Approximately(num, 1f))
			{
				_originalSwimSpeed = ((Character)Player).m_swimSpeed;
				_hasSwimSpeedOverride = true;
				Player player = Player;
				((Character)player).m_swimSpeed = ((Character)player).m_swimSpeed * num;
			}
		}

		internal void ResetSwimSpeedOverride()
		{
			if (!_hasSwimSpeedOverride)
			{
				_activeSwimRunStaminaDrainMultiplier = 1f;
				return;
			}
			((Character)Player).m_swimSpeed = _originalSwimSpeed;
			_hasSwimSpeedOverride = false;
			_activeSwimRunStaminaDrainMultiplier = 1f;
		}

		private float GetSwimSkillSpeedMultiplier()
		{
			float skillFactor = Player.m_skills.GetSkillFactor((SkillType)103);
			float num = Mathf.Max(1f, ServerSyncModTemplatePlugin._playerSwimSkillSpeedMultiplier.Value);
			return Mathf.Lerp(1f, num, skillFactor);
		}

		private float GetSwimRunSpeedMultiplier()
		{
			if (!IsFastSwimEnabled() || !((Character)Player).HaveStamina(0f))
			{
				return 1f;
			}
			return Mathf.Max(1f, ServerSyncModTemplatePlugin._fastSwimSpeedMultiplier.Value);
		}

		private float GetSwimRunStaminaDrainMultiplier()
		{
			if (!IsFastSwimEnabled() || !((Character)Player).HaveStamina(0f))
			{
				return 1f;
			}
			return Mathf.Max(1f, ServerSyncModTemplatePlugin._fastSwimStaminaDrainMultiplier.Value);
		}

		internal void Dive(float dt, bool ascend, out Vector3? defaultMoveDir)
		{
			//IL_0007: 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_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			defaultMoveDir = ((Character)Player).m_moveDir;
			((Character)Player).m_moveDir = GetDiveDirection(ascend);
			if (ascend)
			{
				EnsureAscendTargetFromBottom();
			}
			Vector3 val = CalculateSwimVelocity();
			float num = ((Character)Player).m_swimDepth - val.y * dt;
			((Character)Player).m_swimDepth = Mathf.Max(num, _surfaceSwimDepth);
		}

		private void EnsureAscendTargetFromBottom()
		{
			float num = ((Character)Player).InLiquidDepth();
			if (!(num <= _surfaceSwimDepth) && UnderwaterDepthUtils.IsAtUnderwaterBottom((Character)(object)Player))
			{
				float num2 = Mathf.Max(_surfaceSwimDepth, num - 0.75f);
				if (((Character)Player).m_swimDepth > num2)
				{
					((Character)Player).m_swimDepth = num2;
				}
				((Character)Player).m_body.WakeUp();
			}
		}

		private void ClampSwimDepthForBottomContact()
		{
			((Character)Player).m_swimDepth = UnderwaterDepthUtils.ClampDepthAboveBottom((Character)(object)Player, ((Character)Player).m_swimDepth, _surfaceSwimDepth);
		}

		private Vector3 GetDiveDirection(bool ascend)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: 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_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = (ascend ? Vector3.up : Vector3.down);
			Vector3 val2 = ((Character)Player).m_moveDir;
			if (((Vector3)(ref val2)).magnitude < 0.1f)
			{
				float scale = ((ascend && IsSurfacing()) ? 0.6f : 0.05f);
				val2 = GetHorizontalLookDirection(scale);
			}
			Vector3 val3 = val2 + val;
			return ((Vector3)(ref val3)).normalized;
		}

		private Vector3 GetHorizontalLookDirection(float scale)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			Vector3 lookDir = ((Character)Player).m_lookDir;
			lookDir.y = 0f;
			((Vector3)(ref lookDir)).Normalize();
			return lookDir * scale;
		}

		private Vector3 CalculateSwimVelocity()
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			float num = ((Character)Player).m_swimSpeed * ((Character)Player).GetAttackSpeedFactorMovement();
			if (((Character)Player).InMinorActionSlowdown())
			{
				num = 0f;
			}
			((Character)Player).m_seman.ApplyStatusEffectSpeedMods(ref num, ((Character)Player).m_moveDir);
			Vector3 val = ((Character)Player).m_moveDir * num;
			val = Vector3.Lerp(((Character)Player).m_currentVel, val, ((Character)Player).m_swimAcceleration);
			((Character)Player).AddPushbackForce(ref val);
			return val;
		}

		private float GetMovingSwimStaminaDrainMultiplier()
		{
			return Mathf.Clamp(ServerSyncModTemplatePlugin._swimStaminaDrainBaseMultiplier.Value, 0.1f, 2f) * GetDepthSwimStaminaDrainMultiplier() * Mathf.Max(1f, _activeSwimRunStaminaDrainMultiplier);
		}

		private float GetDepthSwimStaminaDrainMultiplier()
		{
			float num = Mathf.Max(0f, ServerSyncModTemplatePlugin._swimStaminaDrainMultiplierPerDepth.Value);
			float num2 = Mathf.Max(0f, ((Character)Player).InLiquidDepth());
			return 1f + num2 * num / 100f;
		}
	}
	[HarmonyPatch]
	internal static class PlayerDivePatches
	{
		private readonly struct SwimmingUpdateState
		{
			internal PlayerDiveController? Diver { get; }

			internal Vector3? OriginalMoveDir { get; }

			internal SwimmingUpdateState(PlayerDiveController? diver, Vector3? originalMoveDir)
			{
				Diver = diver;
				OriginalMoveDir = originalMoveDir;
			}
		}

		private readonly struct ResourceUpdateState
		{
			internal PlayerDiveController? Diver { get; }

			internal float Eitr { get; }

			internal ResourceUpdateState(PlayerDiveController? diver, float eitr)
			{
				Diver = diver;
				Eitr = eitr;
			}
		}

		private readonly struct SwimmingStaminaState
		{
			internal PlayerDiveController? Diver { get; }

			internal float StaminaBeforeVanillaSwim { get; }

			internal bool IsMoving { get; }

			internal bool EnteredSwimStaminaModifierContext { get; }

			internal SwimmingStaminaState(PlayerDiveController? diver, float staminaBeforeVanillaSwim, bool isMoving, bool enteredSwimStaminaModifierContext)
			{
				Diver = diver;
				StaminaBeforeVanillaSwim = staminaBeforeVanillaSwim;
				IsMoving = isMoving;
				EnteredSwimStaminaModifierContext = enteredSwimStaminaModifierContext;
			}
		}

		[ThreadStatic]
		private static int _swimStaminaModifierContextDepth;

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Player), "Awake")]
		private static void PlayerAwakePostfix(Player __instance)
		{
			if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				PlayerDiveUtils.EnsureLocalDiver();
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Character), "UpdateMotion")]
		private static void CharacterUpdateMotionPrefix(Character __instance)
		{
			if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			PlayerDiveController playerDiveController = PlayerDiveUtils.EnsureLocalDiver();
			if (!((Object)(object)playerDiveController == (Object)null))
			{
				playerDiveController.ResetSwimDepthIfNotInWater();
				playerDiveController.RefreshUnderwaterMovementState();
				playerDiveController.UpdateFastSwimToggle();
				if (playerDiveController.ShouldForceSwimming())
				{
					playerDiveController.PrepareForcedSwimming();
				}
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Character), "UpdateSwimming")]
		private static void CharacterUpdateSwimmingPrefix(Character __instance, float dt, out SwimmingUpdateState __state)
		{
			if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer)
			{
				__state = new SwimmingUpdateState(null, null);
				return;
			}
			PlayerDiveController playerDiveController = PlayerDiveUtils.EnsureLocalDiver();
			if ((Object)(object)playerDiveController == (Object)null)
			{
				__state = new SwimmingUpdateState(null, null);
				return;
			}
			playerDiveController.BeginSwimmingUpdateContext();
			__state = new SwimmingUpdateState(playerDiveController, null);
			playerDiveController.UpdateSwimSpeed();
			bool flag = playerDiveController.IsMovementSuppressedForCombat();
			if (!flag && ServerSyncModTemplatePlugin.IsDiveAscendInputHeld() && playerDiveController.IsUnderSurface())
			{
				playerDiveController.Dive(dt, ascend: true, out var defaultMoveDir);
				__state = new SwimmingUpdateState(playerDiveController, defaultMoveDir);
			}
			else if (!flag && ServerSyncModTemplatePlugin.IsDiveDescendInputHeld() && playerDiveController.CanDive())
			{
				playerDiveController.Dive(dt, ascend: false, out var defaultMoveDir2);
				__state = new SwimmingUpdateState(playerDiveController, defaultMoveDir2);
			}
			else if (__instance.IsOnGround() || !playerDiveController.IsDiving())
			{
				playerDiveController.ResetSwimDepthToDefault();
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Character), "UpdateSwimming")]
		private static void CharacterUpdateSwimmingPostfix(Character __instance, ref SwimmingUpdateState __state)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			__state.Diver?.ResetSwimSpeedOverride();
			__state.Diver?.EndSwimmingUpdateContext();
			if (__state.OriginalMoveDir.HasValue)
			{
				__instance.m_moveDir = __state.OriginalMoveDir.Value;
				__state = default(SwimmingUpdateState);
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Character), "UpdateRotation")]
		private static void CharacterUpdateRotationPrefix(Character __instance, out Quaternion? __state)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer)
			{
				__state = null;
				return;
			}
			PlayerDiveController playerDiveController = PlayerDiveUtils.EnsureLocalDiver();
			if ((Object)(object)playerDiveController != (Object)null && playerDiveController.IsInSwimmingUpdateContext())
			{
				__state = ((Component)__instance).transform.rotation;
			}
			else
			{
				__state = null;
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Character), "UpdateRotation")]
		private static void CharacterUpdateRotationPostfix(Character __instance, float turnSpeed, float dt, ref Quaternion? __state)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			if (__state.HasValue && !((Object)(object)__instance == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && !(((Component)__instance).transform.rotation != __state.Value))
			{
				PlayerDiveController playerDiveController = PlayerDiveUtils.EnsureLocalDiver();
				if (!((Object)(object)playerDiveController == (Object)null) && playerDiveController.IsInSwimmingUpdateContext() && playerDiveController.IsUnderSurface())
				{
					Player player = playerDiveController.Player;
					Quaternion val = ((((Character)player).AlwaysRotateCamera() || ((Character)player).m_moveDir == Vector3.zero) ? ((Character)player).m_lookYaw : Quaternion.LookRotation(((Character)player).m_moveDir));
					float num = turnSpeed * ((Character)player).GetAttackSpeedFactorRotation();
					((Component)player).transform.rotation = Quaternion.RotateTowards(((Component)player).transform.rotation, val, num * dt);
				}
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Player), "OnSwimming")]
		private static void PlayerOnSwimmingPrefix(Player __instance, Vector3 targetVel, float dt, out SwimmingStaminaState __state)
		{
			__state = default(SwimmingStaminaState);
			if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			PlayerDiveController playerDiveController = PlayerDiveUtils.EnsureLocalDiver();
			if (!((Object)(object)playerDiveController == (Object)null))
			{
				playerDiveController.RegenWaterStamina(dt);
				playerDiveController.ApplyIdleMidwaterStaminaDrain(dt);
				bool flag = ((Vector3)(ref targetVel)).magnitude >= 0.1f;
				if (flag)
				{
					BeginSwimStaminaModifierContext();
				}
				__state = new SwimmingStaminaState(playerDiveController, __instance.m_stamina, flag, flag);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Player), "OnSwimming")]
		private static void PlayerOnSwimmingPostfix(Player __instance, ref SwimmingStaminaState __state)
		{
			if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && !((Object)(object)__state.Diver == (Object)null) && __state.IsMoving)
			{
				__state.Diver.AdjustMovingSwimStaminaDrain(__state.StaminaBeforeVanillaSwim);
			}
		}

		[HarmonyFinalizer]
		[HarmonyPatch(typeof(Player), "OnSwimming")]
		private static void PlayerOnSwimmingFinalizer(ref SwimmingStaminaState __state)
		{
			if (__state.EnteredSwimStaminaModifierContext)
			{
				EndSwimStaminaModifierContext();
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(SEMan), "ModifySwimStaminaUsage")]
		private static bool SEManModifySwimStaminaUsagePrefix(SEMan __instance, float baseStaminaUse, ref float staminaUse, bool minZero)
		{
			if (!ShouldUseMultiplicativeSwimStaminaModifiers(baseStaminaUse, staminaUse, minZero))
			{
				return true;
			}
			float num = 1f;
			foreach (StatusEffect statusEffect in __instance.m_statusEffects)
			{
				if (!((Object)(object)statusEffect == (Object)null))
				{
					float num2 = baseStaminaUse;
					statusEffect.ModifySwimStaminaUsage(baseStaminaUse, ref num2);
					if (!float.IsNaN(num2) && !float.IsInfinity(num2))
					{
						num *= Mathf.Max(0f, num2 / baseStaminaUse);
					}
				}
			}
			staminaUse = Mathf.Max(0f, baseStaminaUse * num);
			return false;
		}

		private static bool ShouldUseMultiplicativeSwimStaminaModifiers(float baseStaminaUse, float staminaUse, bool minZero)
		{
			if (ServerSyncModTemplatePlugin.UseMultiplicativeSwimStaminaModifiers() && IsInSwimStaminaModifierContext() && minZero && baseStaminaUse > 0f)
			{
				return Mathf.Approximately(staminaUse, baseStaminaUse);
			}
			return false;
		}

		private static void BeginSwimStaminaModifierContext()
		{
			_swimStaminaModifierContextDepth++;
		}

		private static void EndSwimStaminaModifierContext()
		{
			if (_swimStaminaModifierContextDepth > 0)
			{
				_swimStaminaModifierContextDepth--;
			}
		}

		private static bool IsInSwimStaminaModifierContext()
		{
			return _swimStaminaModifierContextDepth > 0;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Player), "UpdateStats", new Type[] { typeof(float) })]
		private static void PlayerUpdateStatsPrefix(Player __instance, out ResourceUpdateState __state)
		{
			__state = default(ResourceUpdateState);
			if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && PlayerDiveUtils.TryGetLocalDiver(__instance, out PlayerDiveController diver) && diver.ShouldTreatAsSwimming())
			{
				__state = new ResourceUpdateState(diver, __instance.m_eitr);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Player), "UpdateStats", new Type[] { typeof(float) })]
		private static void PlayerUpdateStatsPostfix(Player __instance, ref ResourceUpdateState __state)
		{
			if ((Object)(object)__state.Diver == (Object)null || !SwimResourceAdjustments.TryGetGain(__state.Eitr, __instance.m_eitr, out var gain))
			{
				return;
			}
			float num = (__state.Diver.IsHeadUnderwater() ? ServerSyncModTemplatePlugin._midwaterEitrRegenRateMultiplier.Value : ServerSyncModTemplatePlugin._surfaceEitrRegenRateMultiplier.Value);
			if (!(num >= 1f))
			{
				__instance.m_eitr = SwimResourceAdjustments.GetScaledGainValue