Decompiled source of Bestiary Redux v1.0.0

BestiaryRedux.dll

Decompiled 3 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using BestiaryRedux.Compat;
using BestiaryRedux.Core;
using BestiaryRedux.Enemies.HoarderBug;
using BestiaryRedux.Enemies.HoarderBug.Nest;
using BestiaryRedux.Enemies.Leviathan;
using BestiaryRedux.ItemUse;
using BestiaryRedux.Net;
using Dawn;
using GameNetcodeStuff;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using On;
using On.GameNetcodeStuff;
using Unity.Collections;
using Unity.Jobs;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.Rendering;
using UnityEngine.Rendering.HighDefinition;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Moroxide")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+faaf570efa8467eb5d63232035660187b9683874")]
[assembly: AssemblyProduct("BestiaryRedux")]
[assembly: AssemblyTitle("BestiaryRedux")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace BestiaryRedux
{
	[BepInPlugin("com.github.moroxiderblx.bestiaryredux", "BestiaryRedux", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		internal static Plugin Instance;

		private void Awake()
		{
			Instance = this;
			Log.Init(((BaseUnityPlugin)this).Logger);
			OldNames.CopyOldFiles(((BaseUnityPlugin)this).Config);
			Runner.Create();
			ConfigLayout.Begin(((BaseUnityPlugin)this).Config);
			ConfigLayout.MoveSection("2. Hoarding Bug", "3. Hoarding Bug");
			ConfigLayout.MoveSection("2.1 Hoarding Bug Items", "3.1 Hoarding Bug Items");
			ConfigLayout.MoveSection("3. Hoarding Bug Nest", "4. Hoarding Bug Nest");
			ConfigLayout.MoveSection("4. Earth Leviathan", "2. Earth Leviathan");
			ModConfig.Load();
			LoadConfig("Earth Leviathan", LeviathanConfig.Load);
			LoadConfig("Hoarding Bug", HoarderBugConfig.Load);
			ConfigLayout.Finish();
			LobbyCompatibility.Register();
			Bundle.Load();
			NestChunks.Register();
			ModuleRegistry.Add(() => new NetworkModule());
			ModuleRegistry.Add(() => new ItemUseModule());
			ModuleRegistry.Add(() => new WeaponDamage());
			ModuleRegistry.Add(() => new HoarderBugItemUse());
			ModuleRegistry.Add(() => new HoarderBugHoldAtNest());
			ModuleRegistry.Add(() => new NestModule());
			ModuleRegistry.Add(() => new LeviathanModule());
			ModuleRegistry.Refresh();
			((BaseUnityPlugin)this).Config.SettingChanged += delegate
			{
				if (!ConfigLayout.Busy)
				{
					Runner.EndOfFrame("Config refresh", delegate
					{
						bool flag = ConfigLayout.Refresh();
						ModuleRegistry.Refresh();
						NetworkModule.OnConfigChanged();
						if (flag)
						{
							LethalConfig.ReloadOpenPage();
						}
					});
				}
			};
			Runner.EndOfFrame("Symbiosis check", delegate
			{
				Symbiosis.Check();
				ModuleRegistry.Refresh();
			});
			Log.Info("com.github.moroxiderblx.bestiaryredux [1.0.0] loaded");
		}

		private static void LoadConfig(string enemy, Action load)
		{
			try
			{
				load();
			}
			catch (Exception arg)
			{
				Log.Error($"Config: the {enemy} section could not be loaded: {arg}");
			}
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "com.github.moroxiderblx.bestiaryredux";

		public const string PLUGIN_NAME = "BestiaryRedux";

		public const string PLUGIN_VERSION = "1.0.0";
	}
}
namespace BestiaryRedux.Net
{
	internal static class NetChannel
	{
		internal delegate void Handler(ulong sender, FastBufferReader reader);

		internal delegate void Writer(ref FastBufferWriter writer);

		private const string Prefix = "BestiaryRedux.";

		private static readonly Dictionary<string, Handler> handlers = new Dictionary<string, Handler>();

		private static CustomMessagingManager? Messaging
		{
			get
			{
				NetworkManager singleton = NetworkManager.Singleton;
				if (singleton == null)
				{
					return null;
				}
				return singleton.CustomMessagingManager;
			}
		}

		internal static bool InSession
		{
			get
			{
				if ((Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsListening)
				{
					return Messaging != null;
				}
				return false;
			}
		}

		internal static bool IsHost
		{
			get
			{
				if (InSession)
				{
					return NetworkManager.Singleton.IsServer;
				}
				return false;
			}
		}

		internal static ulong LocalClientId
		{
			get
			{
				NetworkManager singleton = NetworkManager.Singleton;
				if (singleton == null)
				{
					return 0uL;
				}
				return singleton.LocalClientId;
			}
		}

		internal static ulong HostClientId => 0uL;

		internal static void On(string name, Handler handler)
		{
			handlers["BestiaryRedux." + name] = handler;
			if (InSession)
			{
				Register("BestiaryRedux." + name, handler);
			}
		}

		internal static void RegisterAll()
		{
			if (!InSession)
			{
				return;
			}
			foreach (KeyValuePair<string, Handler> handler in handlers)
			{
				Register(handler.Key, handler.Value);
			}
		}

		private static void Register(string fullName, Handler handler)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected O, but got Unknown
			Messaging.RegisterNamedMessageHandler(fullName, (HandleNamedMessageDelegate)delegate(ulong sender, FastBufferReader reader)
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				try
				{
					handler(sender, reader);
				}
				catch (Exception arg)
				{
					Log.Error($"Network: message {fullName} from client {sender} failed: {arg}");
				}
			});
		}

		internal static void SendToHost(string name, Writer write)
		{
			if (InSession && !IsHost)
			{
				Send(name, write, delegate(CustomMessagingManager m, string full, FastBufferWriter w)
				{
					//IL_0007: Unknown result type (might be due to invalid IL or missing references)
					m.SendNamedMessage(full, HostClientId, w, (NetworkDelivery)3);
				});
			}
		}

		internal static void SendTo(string name, IReadOnlyList<ulong> clientIds, Writer write, bool large = false)
		{
			if (IsHost && clientIds.Count != 0)
			{
				Send(name, write, delegate(CustomMessagingManager m, string full, FastBufferWriter w)
				{
					//IL_0008: Unknown result type (might be due to invalid IL or missing references)
					m.SendNamedMessage(full, clientIds, w, (NetworkDelivery)(large ? 4 : 3));
				});
			}
		}

		private static void Send(string name, Writer write, Action<CustomMessagingManager, string, FastBufferWriter> send)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			FastBufferWriter writer = default(FastBufferWriter);
			((FastBufferWriter)(ref writer))..ctor(256, (Allocator)2, 65536);
			try
			{
				write(ref writer);
				send(Messaging, "BestiaryRedux." + name, writer);
			}
			catch (Exception arg)
			{
				Log.Error(string.Format("Network: could not send {0}: {1}", "BestiaryRedux." + name, arg));
			}
			finally
			{
				((FastBufferWriter)(ref writer)).Dispose();
			}
		}
	}
	internal class NetworkModule : BestiaryRedux.Core.Module
	{
		private const string HelloMessage = "Hello";

		private const string HostConfigMessage = "HostConfig";

		private const float ReplyWait = 15f;

		private static readonly string Version = "1.0.0";

		private static readonly Dictionary<ulong, string> modded = new Dictionary<ulong, string>();

		private static readonly Dictionary<ulong, string> problems = new Dictionary<ulong, string>();

		private static string? hostVersion;

		private static int session;

		private static string? pausedReason;

		private static NetworkModule? instance;

		internal static IEnumerable<ulong> ModdedClients => modded.Keys;

		internal static int Round { get; private set; }

		internal static string? ClientPause => pausedReason;

		public override string Name => "Network";

		public override bool ShouldRun => true;

		internal static event Action<ulong>? PlayerReady;

		internal NetworkModule()
		{
			instance = this;
		}

		protected override void RegisterHooks()
		{
			NetChannel.On("Hello", OnHello);
			NetChannel.On("HostConfig", OnHostConfig);
			Hook(delegate
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Expected O, but got Unknown
				StartOfRound.Start += new hook_Start(StartOfRound_Start);
			}, delegate
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Expected O, but got Unknown
				StartOfRound.Start -= new hook_Start(StartOfRound_Start);
			});
			Hook(delegate
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Expected O, but got Unknown
				StartOfRound.StartGame += new hook_StartGame(StartOfRound_StartGame);
			}, delegate
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Expected O, but got Unknown
				StartOfRound.StartGame -= new hook_StartGame(StartOfRound_StartGame);
			});
			Hook(delegate
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Expected O, but got Unknown
				StartOfRound.OnClientConnect += new hook_OnClientConnect(StartOfRound_OnClientConnect);
			}, delegate
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Expected O, but got Unknown
				StartOfRound.OnClientConnect -= new hook_OnClientConnect(StartOfRound_OnClientConnect);
			});
			Hook(delegate
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Expected O, but got Unknown
				StartOfRound.OnClientDisconnect += new hook_OnClientDisconnect(StartOfRound_OnClientDisconnect);
			}, delegate
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Expected O, but got Unknown
				StartOfRound.OnClientDisconnect -= new hook_OnClientDisconnect(StartOfRound_OnClientDisconnect);
			});
			Hook(delegate
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Expected O, but got Unknown
				StartOfRound.OnDestroy += new hook_OnDestroy(StartOfRound_OnDestroy);
			}, delegate
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Expected O, but got Unknown
				StartOfRound.OnDestroy -= new hook_OnDestroy(StartOfRound_OnDestroy);
			});
		}

		private static void Reset()
		{
			session++;
			Round++;
			modded.Clear();
			problems.Clear();
			pausedReason = null;
			hostVersion = null;
			SharedSettings.Clear();
		}

		private void StartOfRound_Start(orig_Start orig, StartOfRound self)
		{
			orig.Invoke(self);
			Run(delegate
			{
				Reset();
				NetChannel.RegisterAll();
				if (NetChannel.IsHost)
				{
					modded[NetChannel.LocalClientId] = Version;
					Log.Debug("Network: lobby started as host (v" + Version + ")");
					LogPlayers();
				}
				else
				{
					Log.Debug("Network: joined a lobby, sending handshake to the host (v" + Version + ")");
					NetChannel.SendToHost("Hello", delegate(ref FastBufferWriter w)
					{
						((FastBufferWriter)(ref w)).WriteValueSafe(Version, false);
					});
					int lobby = session;
					Runner.After(15f, delegate
					{
						Run(delegate
						{
							if (lobby == session && hostVersion == null && NetChannel.InSession)
							{
								Log.Warning("Network: the host doesn't have BestiaryRedux (no reply). Its changes won't happen in this lobby.");
								Tip("The host doesn't have BestiaryRedux", "Its changes won't happen in this lobby.");
							}
						});
					});
				}
			});
		}

		private void StartOfRound_StartGame(orig_StartGame orig, StartOfRound self)
		{
			orig.Invoke(self);
			Run(delegate
			{
				if (NetChannel.IsHost)
				{
					Round++;
					pausedReason = CurrentProblem();
					if (pausedReason != null)
					{
						Log.Debug("Network: new round; modules that need every player stay paused: " + pausedReason);
					}
				}
			});
		}

		private void StartOfRound_OnClientConnect(orig_OnClientConnect orig, StartOfRound self, ulong clientId)
		{
			orig.Invoke(self, clientId);
			Run(delegate
			{
				if (NetChannel.IsHost && clientId != NetChannel.LocalClientId)
				{
					int lobby = session;
					Runner.After(15f, delegate
					{
						Run(delegate
						{
							if (lobby == session && !modded.ContainsKey(clientId) && NetChannel.IsHost && NetworkManager.Singleton.ConnectedClientsIds.Contains(clientId))
							{
								Flag(clientId, "doesn't have BestiaryRedux");
							}
						});
					});
				}
			});
		}

		private void StartOfRound_OnClientDisconnect(orig_OnClientDisconnect orig, StartOfRound self, ulong clientId)
		{
			string name = Run(() => PlayerName(clientId), $"client {clientId}");
			orig.Invoke(self, clientId);
			Run(delegate
			{
				if (NetChannel.IsHost && (modded.Remove(clientId) | problems.Remove(clientId)))
				{
					Log.Debug("Network: " + name + " left");
					LogPlayers();
				}
			});
		}

		private void StartOfRound_OnDestroy(orig_OnDestroy orig, StartOfRound self)
		{
			Run(delegate
			{
				Reset();
				Runner.EndOfFrame("Module refresh", ModuleRegistry.Refresh);
			});
			orig.Invoke(self);
		}

		private void OnHello(ulong sender, FastBufferReader reader)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			if (!NetChannel.IsHost)
			{
				return;
			}
			Run(delegate
			{
				string text = default(string);
				((FastBufferReader)(ref reader)).ReadValueSafe(ref text, false);
				modded[sender] = text;
				Log.Debug("Network: handshake from " + PlayerName(sender) + " (v" + text + ")");
				problems.Remove(sender);
				SendHostConfig(new ulong[1] { sender });
				NetworkModule.PlayerReady?.Invoke(sender);
				if (text != Version)
				{
					Flag(sender, "has BestiaryRedux v" + text + " instead of v" + Version);
				}
				LogPlayers();
			});
		}

		private void OnHostConfig(ulong sender, FastBufferReader reader)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			if (NetChannel.IsHost || sender != NetChannel.HostClientId)
			{
				return;
			}
			Run(delegate
			{
				//IL_001e: Unknown result type (might be due to invalid IL or missing references)
				string text = default(string);
				((FastBufferReader)(ref reader)).ReadValueSafe(ref text, false);
				bool flag = hostVersion == null;
				hostVersion = text;
				string text2 = SharedSettings.Read(reader);
				if (flag && text != Version)
				{
					Log.Warning("Network: the host has BestiaryRedux v" + text + ", you have v" + Version + ". Everyone needs the same version; some of its changes are paused.");
					Tip("BestiaryRedux version mismatch", "Host has v" + text + ", you have v" + Version + ". Use the same version.");
				}
				Log.Debug("Network: settings received from the host (v" + text + "): " + text2);
				Runner.EndOfFrame("Module refresh", ModuleRegistry.Refresh);
			});
		}

		private static void Flag(ulong clientId, string problem)
		{
			string text = PlayerName(clientId);
			string text2 = ChatName(clientId);
			problems[clientId] = text + " " + problem;
			if (pausedReason == null)
			{
				pausedReason = problems[clientId];
			}
			Log.Warning("Network: " + text + " " + problem + ". Every player needs BestiaryRedux v" + Version + "; changes that need every player are paused this round.");
			Tip(PlainName(clientId) + " " + problem, "Everyone needs v" + Version + ". Some changes are paused.");
			Chat("BestiaryRedux: " + text2 + " " + problem + ". Everyone needs v" + Version + ".");
			LogPlayers();
		}

		private static string? CurrentProblem()
		{
			HashSet<ulong> connected = new HashSet<ulong>(NetworkManager.Singleton.ConnectedClientsIds);
			return (from kv in problems
				where connected.Contains(kv.Key)
				select kv.Value).FirstOrDefault();
		}

		private static void SendHostConfig(IReadOnlyList<ulong> clients)
		{
			NetChannel.SendTo("HostConfig", clients, delegate(ref FastBufferWriter w)
			{
				((FastBufferWriter)(ref w)).WriteValueSafe(Version, false);
				SharedSettings.Write(ref w);
			});
			Log.Debug("Network: settings sent to " + string.Join(", ", clients.Select(PlayerName)) + ": " + SharedSettings.Snapshot());
		}

		internal static void OnConfigChanged()
		{
			instance?.Run(delegate
			{
				if (NetChannel.IsHost && SharedSettings.TakeChange())
				{
					List<ulong> list = modded.Keys.Where((ulong id) => id != NetChannel.LocalClientId).ToList();
					if (list.Count == 0)
					{
						Log.Debug("Network: host settings changed (" + SharedSettings.Snapshot() + "); no other players with the mod to send them to");
					}
					else
					{
						SendHostConfig(list);
					}
				}
			});
		}

		private static void Tip(string header, string body)
		{
			if (!((Object)(object)HUDManager.Instance == (Object)null))
			{
				HUDManager.Instance.DisplayTip(header, body, true, false, "LC_Tip1");
				Log.Debug("Network: tip shown: " + header + " / " + body);
			}
		}

		private static void Chat(string text)
		{
			try
			{
				HUDManager obj = HUDManager.Instance;
				if (obj != null)
				{
					obj.AddTextToChatOnServer(text, -1);
				}
			}
			catch (Exception ex)
			{
				Log.Warning("Network: chat line not sent: " + ex.Message);
			}
		}

		private static PlayerControllerB? Player(ulong clientId)
		{
			return StartOfRound.Instance?.allPlayerScripts?.FirstOrDefault((Func<PlayerControllerB, bool>)((PlayerControllerB x) => (Object)(object)x != (Object)null && x.actualClientId == clientId && (x.isPlayerControlled || x.isPlayerDead)));
		}

		private static string PlayerName(ulong clientId)
		{
			PlayerControllerB val = Player(clientId);
			if (!((Object)(object)val != (Object)null))
			{
				return $"client {clientId}";
			}
			return $"{val.playerUsername} (client {clientId})";
		}

		private static string PlainName(ulong clientId)
		{
			return Player(clientId)?.playerUsername ?? "A player";
		}

		private static string ChatName(ulong clientId)
		{
			PlayerControllerB[] array = StartOfRound.Instance?.allPlayerScripts;
			int num = ((array == null) ? (-1) : Array.FindIndex(array, (PlayerControllerB x) => (Object)(object)x != (Object)null && x.actualClientId == clientId && (x.isPlayerControlled || x.isPlayerDead)));
			if (num < 0)
			{
				return "A player";
			}
			return $"[playerNum{num}]";
		}

		private static void LogPlayers()
		{
			if (ModConfig.DebugLogging && NetChannel.IsHost)
			{
				string value;
				IEnumerable<string> values = NetworkManager.Singleton.ConnectedClientsIds.Select((ulong id) => PlayerName(id) + ((id == NetChannel.LocalClientId) ? ", host" : "") + (modded.TryGetValue(id, out value) ? (", v" + value) : ", no mod or no reply yet"));
				Log.Debug("Network: players: " + string.Join("; ", values) + ((pausedReason != null) ? " (modules that need every player paused this round)" : ""));
			}
		}
	}
	internal static class SharedSettings
	{
		private static readonly List<ConfigLayout.Setting> synced = new List<ConfigLayout.Setting>();

		private static readonly Dictionary<string, object> fromHost = new Dictionary<string, object>();

		private static string lastSeen = "";

		private static string Id(ConfigLayout.Setting s)
		{
			return s.Definition.Section + "/" + s.Definition.Key;
		}

		internal static void Add(ConfigLayout.Setting s)
		{
			synced.Add(s);
		}

		internal static T Get<T>(ConfigLayout.Setting<T> s)
		{
			if (!NetChannel.IsHost && fromHost.TryGetValue(Id(s), out object value) && value is T)
			{
				return (T)value;
			}
			return s.Value;
		}

		internal static void Clear()
		{
			fromHost.Clear();
			lastSeen = Snapshot();
		}

		internal static bool TakeChange()
		{
			string text = Snapshot();
			if (text == lastSeen)
			{
				return false;
			}
			lastSeen = text;
			return true;
		}

		internal static string Snapshot()
		{
			return string.Join(", ", synced.Select((ConfigLayout.Setting s) => s.Definition.Key + "=" + s.Serialized));
		}

		internal static void Write(ref FastBufferWriter w)
		{
			//IL_0010: 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)
			int count = synced.Count;
			((FastBufferWriter)(ref w)).WriteValueSafe<int>(ref count, default(ForPrimitives));
			foreach (ConfigLayout.Setting item in synced)
			{
				((FastBufferWriter)(ref w)).WriteValueSafe(Id(item), false);
				((FastBufferWriter)(ref w)).WriteValueSafe(item.Serialized, false);
			}
		}

		internal static string Read(FastBufferReader r)
		{
			//IL_0010: 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)
			fromHost.Clear();
			int num = default(int);
			((FastBufferReader)(ref r)).ReadValueSafe<int>(ref num, default(ForPrimitives));
			List<string> list = new List<string>();
			string id = default(string);
			string text = default(string);
			for (int i = 0; i < num; i++)
			{
				((FastBufferReader)(ref r)).ReadValueSafe(ref id, false);
				((FastBufferReader)(ref r)).ReadValueSafe(ref text, false);
				list.Add(id.Substring(id.IndexOf('/') + 1) + "=" + text);
				ConfigLayout.Setting setting = synced.FirstOrDefault((ConfigLayout.Setting x) => Id(x) == id);
				if (setting != null)
				{
					try
					{
						fromHost[id] = TomlTypeConverter.ConvertToValue(text, setting.SettingType);
					}
					catch
					{
						Log.Warning("Network: host value \"" + text + "\" for " + id + " could not be read; using your own.");
					}
				}
			}
			return string.Join(", ", list);
		}
	}
}
namespace BestiaryRedux.ItemUse
{
	internal static class ActiveUseTracker
	{
		private class ActiveUse
		{
			public EnemyAI? By;

			public string Module = "";

			public string What = "";

			public Action TurnOff = delegate
			{
			};
		}

		private static readonly Dictionary<GrabbableObject, ActiveUse> active = new Dictionary<GrabbableObject, ActiveUse>();

		internal static void Started(GrabbableObject item, EnemyAI by, string module, string what, Action turnOff)
		{
			active[item] = new ActiveUse
			{
				By = by,
				Module = module,
				What = what,
				TurnOff = turnOff
			};
			Log.Debug("Item use: " + Name(item) + " " + what + " (by " + (by?.enemyType?.enemyName ?? "an enemy") + ")");
		}

		internal static bool StartedByUs(GrabbableObject item)
		{
			if ((Object)(object)item != (Object)null)
			{
				return active.ContainsKey(item);
			}
			return false;
		}

		internal static void Stop(GrabbableObject item, string reason)
		{
			if (!((Object)(object)item == (Object)null) && active.TryGetValue(item, out ActiveUse value))
			{
				active.Remove(item);
				ItemErrorGuard.Run(item, "turning off", value.TurnOff, checkBlock: false);
				Log.Debug("Item use: " + Name(item) + " switched off (" + reason + ")");
			}
		}

		internal static void StopAllBy(EnemyAI enemy, string reason)
		{
			StopWhere((ActiveUse u) => u.By == enemy, reason);
		}

		internal static void StopAllFrom(string module, string reason)
		{
			StopWhere((ActiveUse u) => u.Module == module, reason);
		}

		internal static void StopAll(string reason)
		{
			StopWhere((ActiveUse _) => true, reason);
		}

		internal static void Forget(GrabbableObject item)
		{
			active.Remove(item);
		}

		internal static void Clear()
		{
			active.Clear();
		}

		private static void StopWhere(Func<ActiveUse, bool> match, string reason)
		{
			foreach (GrabbableObject item in active.Keys.Where((GrabbableObject k) => (Object)(object)k == (Object)null).ToList())
			{
				active.Remove(item);
			}
			foreach (GrabbableObject item2 in (from kv in active
				where match(kv.Value)
				select kv.Key).ToList())
			{
				Stop(item2, reason);
			}
		}

		private static string Name(GrabbableObject item)
		{
			return item.itemProperties?.itemName ?? ((Object)item).name;
		}
	}
	internal static class CodeScan
	{
		private const int MaxDepth = 3;

		private static readonly Dictionary<short, OpCode> opcodes = (from f in typeof(OpCodes).GetFields(BindingFlags.Static | BindingFlags.Public)
			select f.GetValue(null)).OfType<OpCode>().ToDictionary((OpCode o) => o.Value);

		internal static string? NeedsPlayer(MethodInfo method, Type itemType)
		{
			return Scan(method, itemType, 0, new HashSet<MethodBase>());
		}

		private static string? Scan(MethodBase m, Type itemType, int depth, HashSet<MethodBase> visited)
		{
			if (!visited.Add(m))
			{
				return null;
			}
			byte[] array;
			try
			{
				array = m.GetMethodBody()?.GetILAsByteArray();
			}
			catch
			{
				return null;
			}
			if (array == null)
			{
				return null;
			}
			Type[] typeArgs = ((m.DeclaringType != null && m.DeclaringType.IsGenericType) ? m.DeclaringType.GetGenericArguments() : Type.EmptyTypes);
			Type[] methodArgs = (m.IsGenericMethod ? m.GetGenericArguments() : Type.EmptyTypes);
			int num2;
			for (int i = 0; i < array.Length; i += num2)
			{
				short num = array[i++];
				if (num == 254 && i < array.Length)
				{
					num = (short)(0xFE00 | array[i++]);
				}
				if (!opcodes.TryGetValue(num, out var value))
				{
					return null;
				}
				num2 = OperandSize(value.OperandType, array, i);
				if (value.OperandType != OperandType.InlineField && value.OperandType != OperandType.InlineMethod && value.OperandType != OperandType.InlineTok)
				{
					continue;
				}
				MemberInfo memberInfo = Resolve(m.Module, BitConverter.ToInt32(array, i), typeArgs, methodArgs);
				string text = NeedsPlayer(memberInfo);
				if (text != null)
				{
					return text + " in " + m.DeclaringType?.Name + "." + m.Name;
				}
				if (depth >= 3 || !(memberInfo is MethodBase callee))
				{
					continue;
				}
				foreach (MethodBase item in Followed(callee, itemType))
				{
					string text2 = Scan(item, itemType, depth + 1, visited);
					if (text2 != null)
					{
						return text2;
					}
				}
			}
			return null;
		}

		private static string? NeedsPlayer(MemberInfo? member)
		{
			if (member is FieldInfo fieldInfo)
			{
				if (fieldInfo.Name == "playerHeldBy" && typeof(GrabbableObject).IsAssignableFrom(fieldInfo.DeclaringType))
				{
					return "playerHeldBy";
				}
				if (fieldInfo.DeclaringType == typeof(GameNetworkManager) && fieldInfo.Name == "localPlayerController")
				{
					return "the local player";
				}
				if (fieldInfo.DeclaringType == typeof(PlayerControllerB))
				{
					return "player." + fieldInfo.Name;
				}
			}
			else if (member is MethodBase methodBase && methodBase.DeclaringType == typeof(PlayerControllerB))
			{
				return "player." + methodBase.Name;
			}
			return null;
		}

		private static IEnumerable<MethodBase> Followed(MethodBase callee, Type itemType)
		{
			Type t = callee.DeclaringType;
			if (t == null)
			{
				yield break;
			}
			if (IsItemClass(t, itemType))
			{
				if (callee is MethodInfo { IsVirtual: not false } methodInfo && t != itemType)
				{
					MethodInfo method = itemType.GetMethod(methodInfo.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, (from p in methodInfo.GetParameters()
						select p.ParameterType).ToArray(), null);
					if (method != null)
					{
						yield return method;
						yield break;
					}
				}
				yield return callee;
			}
			else
			{
				if (!IsNestedInItemClass(t, itemType))
				{
					yield break;
				}
				yield return callee;
				if (callee is ConstructorInfo)
				{
					MethodInfo method2 = t.GetMethod("MoveNext", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (method2 != null)
					{
						yield return method2;
					}
				}
			}
		}

		private static bool IsItemClass(Type t, Type itemType)
		{
			if (t != typeof(GrabbableObject) && typeof(GrabbableObject).IsAssignableFrom(t))
			{
				return t.IsAssignableFrom(itemType);
			}
			return false;
		}

		private static bool IsNestedInItemClass(Type t, Type itemType)
		{
			Type declaringType = t.DeclaringType;
			while (declaringType != null)
			{
				if (IsItemClass(declaringType, itemType))
				{
					return true;
				}
				declaringType = declaringType.DeclaringType;
			}
			return false;
		}

		private static MemberInfo? Resolve(System.Reflection.Module module, int token, Type[] typeArgs, Type[] methodArgs)
		{
			try
			{
				return module.ResolveMember(token, typeArgs, methodArgs);
			}
			catch
			{
				return null;
			}
		}

		private static int OperandSize(OperandType type, byte[] il, int at)
		{
			switch (type)
			{
			case OperandType.InlineNone:
				return 0;
			case OperandType.ShortInlineBrTarget:
			case OperandType.ShortInlineI:
			case OperandType.ShortInlineVar:
				return 1;
			case OperandType.InlineVar:
				return 2;
			case OperandType.InlineI8:
			case OperandType.InlineR:
				return 8;
			case OperandType.InlineSwitch:
				return 4 + 4 * BitConverter.ToInt32(il, at);
			default:
				return 4;
			}
		}
	}
	public enum ItemClass
	{
		Safe,
		WeaponShotgun,
		WeaponMelee,
		WeaponThrowable,
		Workaround,
		Never,
		Risky,
		NoActivation
	}
	internal static class ItemCatalog
	{
		internal class Entry
		{
			public string Name = "";

			public Type Type;

			public ItemClass Class;

			public string Reason = "";

			public bool Modded;

			public ThrowableKind Throwable;

			public Type? Base;

			public bool Generic;

			public bool UsableByDefault
			{
				get
				{
					if (Class != ItemClass.Safe && Class != ItemClass.WeaponShotgun && Class != ItemClass.WeaponMelee && Class != ItemClass.WeaponThrowable)
					{
						return Class == ItemClass.Workaround;
					}
					return true;
				}
			}
		}

		private static readonly Assembly GameAssembly = typeof(GrabbableObject).Assembly;

		private static readonly Dictionary<Type, (ItemClass cls, string reason)> vanilla = new Dictionary<Type, (ItemClass, string)>
		{
			[typeof(ShotgunItem)] = (ItemClass.WeaponShotgun, "fired with the enemy's aim, never its own use"),
			[typeof(Shovel)] = (ItemClass.WeaponMelee, "own hit in front of the enemy, never its own use"),
			[typeof(KnifeItem)] = (ItemClass.WeaponMelee, "own hit in front of the enemy, never its own use"),
			[typeof(FlashlightItem)] = (ItemClass.Workaround, "toggled; bulb shown on other players' screens"),
			[typeof(NoisemakerProp)] = (ItemClass.Safe, "plays its sound"),
			[typeof(BoomboxItem)] = (ItemClass.Safe, "music on/off, stopped on drop"),
			[typeof(RadarBoosterItem)] = (ItemClass.Safe, "on/off, stopped on drop"),
			[typeof(RemoteProp)] = (ItemClass.Safe, "toggles the ship lights"),
			[typeof(JetpackItem)] = (ItemClass.Never, "needs a player"),
			[typeof(PatcherTool)] = (ItemClass.Never, "needs a player"),
			[typeof(SprayPaintItem)] = (ItemClass.Never, "needs a player"),
			[typeof(KeyItem)] = (ItemClass.Never, "needs a player"),
			[typeof(LockPicker)] = (ItemClass.Never, "needs a player"),
			[typeof(ExtensionLadderItem)] = (ItemClass.Never, "needs a player"),
			[typeof(TetraChemicalItem)] = (ItemClass.Never, "needs a player"),
			[typeof(HauntedMaskItem)] = (ItemClass.Never, "needs a player"),
			[typeof(GiftBoxItem)] = (ItemClass.Never, "needs a player"),
			[typeof(BeltBagItem)] = (ItemClass.Never, "opens the bag for the local player"),
			[typeof(WalkieTalkie)] = (ItemClass.Never, "needs a player"),
			[typeof(CaveDwellerPhysicsProp)] = (ItemClass.Never, "needs a player"),
			[typeof(MapDevice)] = (ItemClass.Never, "needs a player")
		};

		private static readonly Dictionary<(Item, Type), Entry> entries = new Dictionary<(Item, Type), Entry>();

		internal static string Display(ItemClass c)
		{
			return c switch
			{
				ItemClass.WeaponShotgun => "Weapon (shotgun)", 
				ItemClass.WeaponMelee => "Weapon (melee)", 
				ItemClass.WeaponThrowable => "Weapon (throwable)", 
				ItemClass.NoActivation => "No activation", 
				_ => c.ToString(), 
			};
		}

		internal static void Clear()
		{
			entries.Clear();
		}

		internal static Entry Get(GrabbableObject item)
		{
			return Get(item.itemProperties, item);
		}

		internal static Entry Get(Item item, GrabbableObject sample)
		{
			Type type = ((object)sample).GetType();
			if (entries.TryGetValue((item, type), out Entry value))
			{
				return value;
			}
			value = Classify(item, type, sample);
			entries[(item, type)] = value;
			return value;
		}

		internal static void DumpToLog()
		{
			if (!ModConfig.DebugLogging)
			{
				return;
			}
			List<Item> list = StartOfRound.Instance?.allItemsList?.itemsList;
			if (list == null)
			{
				return;
			}
			int num = 0;
			foreach (Item item in from x in list
				where (Object)(object)x != (Object)null
				orderby x.itemName
				select x)
			{
				GrabbableObject val = (((Object)(object)item.spawnPrefab != (Object)null) ? item.spawnPrefab.GetComponent<GrabbableObject>() : null);
				if ((Object)(object)val == (Object)null)
				{
					Log.Debug("Item catalog: \"" + item.itemName + "\" - No activation: no item script");
					continue;
				}
				Entry entry = Get(item, val);
				string text = (entry.Modded ? (entry.Type.Name + ", " + entry.Type.Assembly.GetName().Name) : entry.Type.Name);
				string text2 = (ItemErrorGuard.IsBlocked(entry.Name) ? " [auto-blocked]" : "");
				Log.Debug("Item catalog: \"" + entry.Name + "\" (" + text + ") - " + Display(entry.Class) + ": " + entry.Reason + text2);
				num++;
			}
			Log.Debug($"Item catalog: {num} items");
		}

		private static Entry Classify(Item item, Type type, GrabbableObject sample)
		{
			Entry entry = new Entry
			{
				Name = item.itemName,
				Type = type,
				Modded = (type.Assembly != GameAssembly)
			};
			try
			{
				StunGrenadeItem val = (StunGrenadeItem)(object)((sample is StunGrenadeItem) ? sample : null);
				if (val != null)
				{
					ClassifyThrowable(entry, val);
				}
				else
				{
					Entry entry2 = entry;
					Entry entry3 = entry;
					if (entry.Modded)
					{
						(entry2.Class, entry3.Reason) = ClassifyModded(entry, type);
					}
					else
					{
						(entry2.Class, entry3.Reason) = ClassifyVanilla(type);
					}
				}
			}
			catch (Exception ex)
			{
				Entry entry3 = entry;
				string reason = "could not be checked (" + ex.GetType().Name + ")";
				entry.Class = ItemClass.Risky;
				entry3.Reason = reason;
			}
			return entry;
		}

		private static void ClassifyThrowable(Entry e, StunGrenadeItem g)
		{
			ThrowableKind throwableKind = (e.Throwable = Throwables.Kind(g));
			e.Class = ((throwableKind == ThrowableKind.Unclear) ? ItemClass.Never : ItemClass.WeaponThrowable);
			string text = Throwables.Describe(g) + ((throwableKind == ThrowableKind.Unclear) ? "; not used (unknown variant)" : "");
			if (!e.Modded)
			{
				e.Reason = text;
				return;
			}
			string[] array = ItemClassRules.ChangedBelow(e.Type, typeof(StunGrenadeItem), Throwables.BehaviourMethods);
			e.Base = typeof(StunGrenadeItem);
			e.Reason = ((throwableKind == ThrowableKind.Unclear) ? ("based on StunGrenadeItem: " + text) : ("based on StunGrenadeItem, behaves like the " + Throwables.LikeName(throwableKind) + ": " + text + ((array.Length != 0) ? ("; its own " + string.Join(", ", array) + " (watched by the error guard)") : "")));
		}

		private static (ItemClass, string) ClassifyVanilla(Type type)
		{
			Type type2 = ItemClassRules.NearestKnown(type, typeof(GrabbableObject), vanilla.ContainsKey);
			if (type2 != null)
			{
				return vanilla[type2];
			}
			if (!(ItemClassRules.ActivationFrom(type, typeof(GrabbableObject)) == null))
			{
				return (ItemClass.Never, "vanilla item not checked for enemy use");
			}
			return (ItemClass.NoActivation, "nothing happens when used");
		}

		private static (ItemClass, string) ClassifyModded(Entry e, Type type)
		{
			ItemClassRules.Result result = ItemClassRules.Classify(type, typeof(GrabbableObject), vanilla.ContainsKey, (Type t) => t.Assembly == GameAssembly);
			switch (result.Route)
			{
			case ItemClassRules.Route.LikeVanilla:
			{
				e.Base = result.Base;
				(ItemClass cls, string reason) tuple2 = vanilla[result.Base];
				ItemClass item2 = tuple2.cls;
				string item3 = tuple2.reason;
				bool flag = ItemClassRules.ChangedBelow(type, result.Base, new string[1] { "ItemActivate" }).Length != 0;
				return (item2, "based on " + result.Base.Name + ": " + item3 + (flag ? "; its own ItemActivate" : ""));
			}
			case ItemClassRules.Route.VanillaRule:
				e.Base = result.Base;
				var (item, text2) = ClassifyVanilla(result.Base);
				return (item, "based on " + result.Base.Name + ": " + text2);
			case ItemClassRules.Route.Nothing:
				return (ItemClass.NoActivation, "nothing happens when used");
			default:
			{
				MethodInfo method = type.GetMethod("ItemActivate", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2]
				{
					typeof(bool),
					typeof(bool)
				}, null);
				string text = CodeScan.NeedsPlayer(method, type);
				if (text != null)
				{
					return (ItemClass.Risky, "code check: uses " + text);
				}
				e.Generic = true;
				return (ItemClass.Safe, "its own activation, code check found nothing that needs a player; generic use");
			}
			}
		}
	}
	internal static class ItemErrorGuard
	{
		private class Watch
		{
			public GrabbableObject? Item;

			public string ItemName = "";

			public string[] TypeNames = Array.Empty<string>();

			public float Until;
		}

		private sealed class ConfigList : IBlockList
		{
			public bool IsBlocked(string itemName)
			{
				return ItemErrorGuard.IsBlocked(itemName);
			}

			public void Add(string itemName)
			{
				AddBlocked(itemName);
			}
		}

		[CompilerGenerated]
		private static class <>O
		{
			public static LogCallback <0>__OnUnityLog;
		}

		private const float WatchSeconds = 5f;

		private static readonly List<Watch> watches = new List<Watch>();

		private static HashSet<string> blocked = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private static string blockedFrom = "";

		private static bool listening;

		private static bool blocking;

		private static readonly ConfigList list = new ConfigList();

		internal static void Init()
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Expected O, but got Unknown
			if (!listening)
			{
				object obj = <>O.<0>__OnUnityLog;
				if (obj == null)
				{
					LogCallback val = OnUnityLog;
					<>O.<0>__OnUnityLog = val;
					obj = (object)val;
				}
				Application.logMessageReceived += (LogCallback)obj;
				listening = true;
			}
		}

		internal static bool IsBlocked(string itemName)
		{
			string autoBlockedItems = ModConfig.AutoBlockedItems;
			if (autoBlockedItems != blockedFrom)
			{
				blockedFrom = autoBlockedItems;
				blocked = new HashSet<string>(Split(autoBlockedItems), StringComparer.OrdinalIgnoreCase);
			}
			return blocked.Contains(itemName.Trim());
		}

		internal static bool Run(GrabbableObject item, string what, Action use, bool checkBlock = true)
		{
			string itemName = item.itemProperties?.itemName ?? ((Object)item).name;
			bool flag = ItemUseGuard.Run(list, itemName, checkBlock, use, delegate(string n, Exception e)
			{
				Blocked(item, n, what + ": " + e.GetType().Name + ": " + e.Message, e);
			}, delegate(string n, Exception e)
			{
				Log.Debug("Item use: \"" + n + "\" (blocked) failed again (" + what + ": " + e.GetType().Name + ": " + e.Message + ")");
			});
			if (flag)
			{
				watches.RemoveAll((Watch w) => w.Until < Time.realtimeSinceStartup);
				string[] typeNames = (from t in ItemClassRules.Line(((object)item).GetType(), typeof(GrabbableObject))
					select t.FullName ?? t.Name).ToArray();
				watches.Add(new Watch
				{
					Item = item,
					ItemName = itemName,
					TypeNames = typeNames,
					Until = Time.realtimeSinceStartup + 5f
				});
			}
			return flag;
		}

		private static void OnUnityLog(string message, string stackTrace, LogType type)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Invalid comparison between Unknown and I4
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			if (blocking || watches.Count == 0 || ((int)type != 4 && (int)type != 0))
			{
				return;
			}
			float now = Time.realtimeSinceStartup;
			watches.RemoveAll((Watch w) => w.Until < now);
			string frame = ThrowingFrame(stackTrace);
			if (frame == null)
			{
				return;
			}
			Watch hit = watches.FirstOrDefault((Watch w) => w.TypeNames.Any((string t) => StartsWithType(frame, t)));
			if (hit != null)
			{
				watches.RemoveAll((Watch w) => w.ItemName == hit.ItemName);
				if (!IsBlocked(hit.ItemName))
				{
					AddBlocked(hit.ItemName);
					Blocked(hit.Item, hit.ItemName, "error shortly after use: " + message, null);
				}
			}
		}

		internal static string? ThrowingFrame(string stackTrace)
		{
			string[] array = stackTrace.Split('\n');
			foreach (string text in array)
			{
				string text2 = text.Trim();
				if (text2.StartsWith("at "))
				{
					text2 = text2.Substring(3);
				}
				if (text2.StartsWith("(wrapper dynamic-method) "))
				{
					text2 = text2.Substring("(wrapper dynamic-method) ".Length);
				}
				if (text2.Length != 0 && !text2.StartsWith("UnityEngine.") && !text2.StartsWith("Unity.") && !text2.StartsWith("System."))
				{
					return text2;
				}
			}
			return null;
		}

		internal static bool StartsWithType(string frame, string typeName)
		{
			if (!frame.StartsWith(typeName + ".") && !frame.StartsWith(typeName + ":"))
			{
				return frame.StartsWith(typeName + "+");
			}
			return true;
		}

		private static void AddBlocked(string itemName)
		{
			blocking = true;
			try
			{
				List<string> list = Split(ModConfig.AutoBlockedItems).ToList();
				list.Add(itemName.Trim());
				ModConfig.SetAutoBlockedItems(string.Join(", ", list));
			}
			finally
			{
				blocking = false;
			}
		}

		private static void Blocked(GrabbableObject? item, string itemName, string why, Exception? e)
		{
			Log.Warning("Item use: \"" + itemName + "\"" + (((Object)(object)item != (Object)null) ? (" (" + ((object)item).GetType().Name + ")") : "") + " is now auto-blocked for enemies after an error (" + why + "). Clear it from \"Auto-blocked items\" in 1. General to try it again.");
			if (e != null)
			{
				Log.Debug($"Item use: {itemName} error: {e}");
			}
			if ((Object)(object)item != (Object)null && NetChannel.IsHost)
			{
				blocking = true;
				try
				{
					ActiveUseTracker.Stop(item, "auto-blocked");
				}
				finally
				{
					blocking = false;
				}
			}
		}

		private static IEnumerable<string> Split(string list)
		{
			return from s in list.Split(',')
				select s.Trim() into s
				where s.Length > 0
				select s;
		}
	}
	internal static class ItemUseMessage
	{
		internal delegate void ActionHandler(EnemyAI? enemy, GrabbableObject item, int value);

		private const string MessageName = "ItemUse";

		private static readonly Dictionary<string, ActionHandler> actions = new Dictionary<string, ActionHandler>();

		internal static void Init()
		{
			NetChannel.On("ItemUse", OnMessage);
		}

		internal static void On(string action, ActionHandler handler)
		{
			actions[action] = handler;
		}

		internal static bool Send(EnemyAI? enemy, GrabbableObject item, string action, int value = 0, bool turnOff = false)
		{
			if (!NetChannel.IsHost || (Object)(object)item == (Object)null)
			{
				return false;
			}
			if ((Object)(object)enemy == (Object)null)
			{
				enemy = null;
			}
			if (!RunLocal(enemy, item, action, value, !turnOff))
			{
				Log.Debug("Item use: \"" + action + "\" didn't run on the host, not sent");
				return false;
			}
			List<ulong> clientIds = NetworkModule.ModdedClients.Where((ulong id) => id != NetChannel.LocalClientId).ToList();
			ulong enemyId = (((Object)(object)enemy != (Object)null) ? ((NetworkBehaviour)enemy).NetworkObjectId : 0);
			ulong itemId = ((NetworkBehaviour)item).NetworkObjectId;
			NetChannel.SendTo("ItemUse", clientIds, delegate(ref FastBufferWriter w)
			{
				//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_001e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0024: Unknown result type (might be due to invalid IL or missing references)
				//IL_0040: 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)
				((FastBufferWriter)(ref w)).WriteValueSafe<ulong>(ref enemyId, default(ForPrimitives));
				((FastBufferWriter)(ref w)).WriteValueSafe<ulong>(ref itemId, default(ForPrimitives));
				((FastBufferWriter)(ref w)).WriteValueSafe(action, false);
				((FastBufferWriter)(ref w)).WriteValueSafe<int>(ref value, default(ForPrimitives));
			});
			return true;
		}

		private static void OnMessage(ulong sender, FastBufferReader reader)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: 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_004c: Unknown result type (might be due to invalid IL or missing references)
			if (!NetChannel.IsHost && sender == NetChannel.HostClientId)
			{
				ulong num = default(ulong);
				((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref num, default(ForPrimitives));
				ulong num2 = default(ulong);
				((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref num2, default(ForPrimitives));
				string text = default(string);
				((FastBufferReader)(ref reader)).ReadValueSafe(ref text, false);
				int value = default(int);
				((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref value, default(ForPrimitives));
				EnemyAI enemy = ((num != 0L) ? Find<EnemyAI>(num) : null);
				GrabbableObject val = Find<GrabbableObject>(num2);
				if ((Object)(object)val == (Object)null)
				{
					Log.Debug($"Item use: \"{text}\" received for an item that's gone here (item {num2})");
				}
				else
				{
					RunLocal(enemy, val, text, value, checkBlock: false);
				}
			}
		}

		private static bool RunLocal(EnemyAI? enemy, GrabbableObject item, string action, int value, bool checkBlock)
		{
			if (!actions.TryGetValue(action, out ActionHandler handler))
			{
				Log.Debug("Item use: no handler for \"" + action + "\" on this machine");
				return false;
			}
			return ItemErrorGuard.Run(item, action, delegate
			{
				handler(enemy, item, value);
			}, checkBlock);
		}

		private static T? Find<T>(ulong networkObjectId) where T : class
		{
			NetworkManager singleton = NetworkManager.Singleton;
			Dictionary<ulong, NetworkObject> dictionary = ((singleton == null) ? null : singleton.SpawnManager?.SpawnedObjects);
			if (dictionary == null || !dictionary.TryGetValue(networkObjectId, out var value) || (Object)(object)value == (Object)null)
			{
				return null;
			}
			return ((Component)value).GetComponent<T>();
		}
	}
	internal class ItemUseModule : BestiaryRedux.Core.Module
	{
		private const float DumpDelay = 2f;

		public override string Name => "ItemUse";

		public override bool ShouldRun => true;

		protected override void RegisterHooks()
		{
			ItemErrorGuard.Init();
			ItemUseMessage.Init();
			Hook(delegate
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Expected O, but got Unknown
				StartOfRound.Start += new hook_Start(StartOfRound_Start);
			}, delegate
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Expected O, but got Unknown
				StartOfRound.Start -= new hook_Start(StartOfRound_Start);
			});
			Hook(delegate
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Expected O, but got Unknown
				StartOfRound.OnDestroy += new hook_OnDestroy(StartOfRound_OnDestroy);
			}, delegate
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Expected O, but got Unknown
				StartOfRound.OnDestroy -= new hook_OnDestroy(StartOfRound_OnDestroy);
			});
			Hook(delegate
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Expected O, but got Unknown
				StartOfRound.ShipLeave += new hook_ShipLeave(StartOfRound_ShipLeave);
			}, delegate
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Expected O, but got Unknown
				StartOfRound.ShipLeave -= new hook_ShipLeave(StartOfRound_ShipLeave);
			});
		}

		private void StartOfRound_ShipLeave(orig_ShipLeave orig, StartOfRound self)
		{
			orig.Invoke(self);
			Run(delegate
			{
				if (NetChannel.IsHost)
				{
					ActiveUseTracker.StopAll("the ship is leaving");
				}
			});
		}

		private void StartOfRound_Start(orig_Start orig, StartOfRound self)
		{
			orig.Invoke(self);
			Run(delegate
			{
				ItemCatalog.Clear();
				Runner.After(2f, delegate
				{
					Run(ItemCatalog.DumpToLog);
				});
			});
		}

		private void StartOfRound_OnDestroy(orig_OnDestroy orig, StartOfRound self)
		{
			Run(delegate
			{
				ActiveUseTracker.Clear();
				ItemCatalog.Clear();
			});
			orig.Invoke(self);
		}

		internal static bool CanEverUse(GrabbableObject item, out string reason)
		{
			reason = "";
			if ((Object)(object)item == (Object)null || (Object)(object)item.itemProperties == (Object)null)
			{
				reason = "no item";
				return false;
			}
			ItemCatalog.Entry entry = ItemCatalog.Get(item);
			if (ItemErrorGuard.IsBlocked(entry.Name))
			{
				reason = "auto-blocked";
				return false;
			}
			if (!entry.UsableByDefault)
			{
				reason = ItemCatalog.Display(entry.Class);
				return false;
			}
			return true;
		}

		internal static bool BatteryEmpty(GrabbableObject item)
		{
			if ((Object)(object)item.itemProperties != (Object)null && item.itemProperties.requiresBattery)
			{
				if (item.insertedBattery != null)
				{
					return item.insertedBattery.empty;
				}
				return true;
			}
			return false;
		}

		internal static bool CanUse(GrabbableObject item, out string reason)
		{
			if (!CanEverUse(item, out reason))
			{
				return false;
			}
			if (item.isHeld || item.isPocketed)
			{
				reason = "held by a player";
				return false;
			}
			if (item.deactivated)
			{
				reason = "deactivated";
				return false;
			}
			BugNest? current = BugNest.Current;
			if (current != null && current.IsStored(item))
			{
				reason = "stored in the nest";
				return false;
			}
			if (item.isBeingUsed && !ActiveUseTracker.StartedByUs(item))
			{
				reason = "already in use";
				return false;
			}
			return true;
		}
	}
	public enum ThrowableKind
	{
		None,
		PinGrenade,
		ShortFuseGrenade,
		LandingExplosive,
		Unclear
	}
	internal static class Throwables
	{
		internal const float ShortFuse = 1f;

		internal static readonly string[] BehaviourMethods = new string[6] { "ItemActivate", "Update", "Start", "EquipItem", "OnHitGround", "FallWithCurve" };

		internal static ThrowableKind Kind(StunGrenadeItem g)
		{
			if (g.explodeOnCollision && g.dontRequirePullingPin)
			{
				return ThrowableKind.LandingExplosive;
			}
			if (!g.explodeOnCollision && !g.dontRequirePullingPin)
			{
				if (!(g.TimeToExplode < 1f))
				{
					return ThrowableKind.PinGrenade;
				}
				return ThrowableKind.ShortFuseGrenade;
			}
			return ThrowableKind.Unclear;
		}

		internal static string LikeName(ThrowableKind kind)
		{
			return kind switch
			{
				ThrowableKind.PinGrenade => "Stun grenade", 
				ThrowableKind.ShortFuseGrenade => "Homemade flashbang", 
				ThrowableKind.LandingExplosive => "Easter egg", 
				_ => "?", 
			};
		}

		internal static string Describe(StunGrenadeItem g)
		{
			string text = (g.spawnDamagingShockwave ? "damaging blast" : "stun flash");
			switch (Kind(g))
			{
			case ThrowableKind.PinGrenade:
			case ThrowableKind.ShortFuseGrenade:
				return string.Format("pin, fuse {0:0.##} s{1}, {2}{3}", g.TimeToExplode, (g.TimeToExplode < 1f) ? " (too short to throw)" : "", text, g.DestroyGrenade ? ", used up" : "");
			case ThrowableKind.LandingExplosive:
				return $"goes off on landing after a throw, chance {g.chanceToExplode:0.##}%, {text}";
			default:
				return $"unusual settings (explode on landing {g.explodeOnCollision}, no pin {g.dontRequirePullingPin})";
			}
		}
	}
	internal class WeaponDamage : BestiaryRedux.Core.Module
	{
		internal readonly struct Rules
		{
			public readonly bool HurtPlayers;

			public readonly bool HurtEnemies;

			public readonly string Why;

			public bool AllowsAll
			{
				get
				{
					if (HurtPlayers)
					{
						return HurtEnemies;
					}
					return false;
				}
			}

			public Rules(bool hurtPlayers, bool hurtEnemies, string why)
			{
				HurtPlayers = hurtPlayers;
				HurtEnemies = hurtEnemies;
				Why = why;
			}
		}

		private static readonly List<Func<EnemyAI, Rules?>> providers = new List<Func<EnemyAI, Rules?>>();

		private static readonly Stack<Rules> scopes = new Stack<Rules>();

		private static WeaponDamage? instance;

		public override string Name => "WeaponDamage";

		public override bool ShouldRun => true;

		internal static bool Ready
		{
			get
			{
				if (instance != null)
				{
					return instance.State == ModuleState.On;
				}
				return false;
			}
		}

		private static Rules? Current
		{
			get
			{
				if (scopes.Count <= 0)
				{
					return null;
				}
				return scopes.Peek();
			}
		}

		internal WeaponDamage()
		{
			instance = this;
		}

		internal static void AddRules(Func<EnemyAI, Rules?> provider)
		{
			providers.Add(provider);
		}

		internal static Rules? RulesFor(EnemyAI? holder)
		{
			if ((Object)(object)holder == (Object)null)
			{
				return null;
			}
			foreach (Func<EnemyAI, Rules?> provider in providers)
			{
				Rules? result = provider(holder);
				if (result.HasValue)
				{
					return result;
				}
			}
			return null;
		}

		internal static void WithRules(Rules rules, Action body)
		{
			scopes.Push(rules);
			try
			{
				body();
			}
			finally
			{
				scopes.Pop();
			}
		}

		protected override void RegisterHooks()
		{
			Hook(delegate
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Expected O, but got Unknown
				ShotgunItem.ShootGun += new hook_ShootGun(ShotgunItem_ShootGun);
			}, delegate
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Expected O, but got Unknown
				ShotgunItem.ShootGun -= new hook_ShootGun(ShotgunItem_ShootGun);
			});
			Hook(delegate
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Expected O, but got Unknown
				PlayerControllerB.DamagePlayer += new hook_DamagePlayer(PlayerControllerB_DamagePlayer);
			}, delegate
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Expected O, but got Unknown
				PlayerControllerB.DamagePlayer -= new hook_DamagePlayer(PlayerControllerB_DamagePlayer);
			});
			Hook(delegate
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Expected O, but got Unknown
				EnemyAI.HitEnemyOnLocalClient += new hook_HitEnemyOnLocalClient(EnemyAI_HitEnemyOnLocalClient);
			}, delegate
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Expected O, but got Unknown
				EnemyAI.HitEnemyOnLocalClient -= new hook_HitEnemyOnLocalClient(EnemyAI_HitEnemyOnLocalClient);
			});
			Hook(delegate
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Expected O, but got Unknown
				EnemyAI.SetEnemyStunned += new hook_SetEnemyStunned(EnemyAI_SetEnemyStunned);
			}, delegate
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Expected O, but got Unknown
				EnemyAI.SetEnemyStunned -= new hook_SetEnemyStunned(EnemyAI_SetEnemyStunned);
			});
		}

		private void ShotgunItem_ShootGun(orig_ShootGun orig, ShotgunItem self, Vector3 shotgunPosition, Vector3 shotgunForward)
		{
			//IL_0015: 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_001c: 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_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			Rules? rules = Run(() => RulesFor(self.heldByEnemy), null);
			if (!rules.HasValue || rules.Value.AllowsAll)
			{
				orig.Invoke(self, shotgunPosition, shotgunForward);
				return;
			}
			WithRules(rules.Value, delegate
			{
				//IL_000d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0013: Unknown result type (might be due to invalid IL or missing references)
				orig.Invoke(self, shotgunPosition, shotgunForward);
			});
		}

		private void PlayerControllerB_DamagePlayer(orig_DamagePlayer orig, PlayerControllerB self, int damageNumber, bool hasDamageSFX, bool callRPC, CauseOfDeath causeOfDeath, int deathAnimation, bool fallDamage, Vector3 force)
		{
			//IL_0062: 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)
			Rules? rules = Current;
			if (rules.HasValue && !rules.Value.HurtPlayers)
			{
				Run(delegate
				{
					Log.Debug($"Weapon damage: {damageNumber} damage to {self.playerUsername} skipped ({rules.Value.Why})");
				});
			}
			else
			{
				orig.Invoke(self, damageNumber, hasDamageSFX, callRPC, causeOfDeath, deathAnimation, fallDamage, force);
			}
		}

		private void EnemyAI_SetEnemyStunned(orig_SetEnemyStunned orig, EnemyAI self, bool setToStunned, float setToStunTime, PlayerControllerB setStunnedByPlayer)
		{
			Rules? rules = Current;
			if (rules.HasValue && !rules.Value.HurtEnemies && setToStunned)
			{
				Run(delegate
				{
					Log.Debug("Weapon damage: stun on " + self.enemyType?.enemyName + " skipped (" + rules.Value.Why + ")");
				});
			}
			else
			{
				orig.Invoke(self, setToStunned, setToStunTime, setStunnedByPlayer);
			}
		}

		private void EnemyAI_HitEnemyOnLocalClient(orig_HitEnemyOnLocalClient orig, EnemyAI self, int force, Vector3 hitDirection, PlayerControllerB playerWhoHit, bool playHitSFX, int hitID)
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			Rules? rules = Current;
			if (rules.HasValue && !rules.Value.HurtEnemies)
			{
				Run(delegate
				{
					Log.Debug("Weapon damage: hit on " + self.enemyType?.enemyName + " skipped (" + rules.Value.Why + ")");
				});
			}
			else
			{
				orig.Invoke(self, force, hitDirection, playerWhoHit, playHitSFX, hitID);
			}
		}
	}
}
namespace BestiaryRedux.Enemies
{
	public static class CreatureBrains
	{
		public readonly struct Needs
		{
			public readonly string Feature;

			public readonly string[] Methods;

			public Needs(string feature, params string[] methods)
			{
				Feature = feature;
				Methods = methods;
			}
		}

		private static readonly Dictionary<(Type, Type, string), string?> cache = new Dictionary<(Type, Type, string), string>();

		public static bool IsSubclass(Type type, Type vanilla)
		{
			if (type != vanilla)
			{
				return vanilla.IsAssignableFrom(type);
			}
			return false;
		}

		public static string? Replaced(Type type, Type vanilla, string method)
		{
			if (cache.TryGetValue((type, vanilla, method), out string value))
			{
				return value;
			}
			string text = null;
			Type type2 = type;
			while (text == null && type2 != null && type2 != vanilla && vanilla.IsAssignableFrom(type2))
			{
				MethodInfo method2 = type2.GetMethod(method, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
				if (!(method2 == null) && !CallsBelow(method2, type2))
				{
					text = type2.Name + "." + method;
				}
				type2 = type2.BaseType;
			}
			cache[(type, vanilla, method)] = text;
			return text;
		}

		public static List<(string Feature, string Why)> Skipped(Type type, Type vanilla, IEnumerable<Needs> features)
		{
			List<(string, string)> list = new List<(string, string)>();
			if (!IsSubclass(type, vanilla))
			{
				return list;
			}
			foreach (Needs feature in features)
			{
				string[] array = (from m in feature.Methods
					select Replaced(type, vanilla, m) into r
					where r != null
					select (r)).ToArray();
				if (array.Length != 0)
				{
					list.Add((feature.Feature, "it replaces " + string.Join(", ", array) + " without calling the vanilla one"));
				}
			}
			return list;
		}

		private static bool CallsBelow(MethodInfo m, Type declaring)
		{
			byte[] array;
			try
			{
				array = m.GetMethodBody()?.GetILAsByteArray();
			}
			catch (Exception)
			{
				return false;
			}
			if (array == null)
			{
				return false;
			}
			for (int i = 0; i + 4 < array.Length; i++)
			{
				if (array[i] != 40)
				{
					continue;
				}
				int metadataToken = BitConverter.ToInt32(array, i + 1);
				try
				{
					MethodBase methodBase = m.Module.ResolveMethod(metadataToken, declaring.IsGenericType ? declaring.GetGenericArguments() : null, null);
					if (methodBase != null && methodBase.Name == m.Name && methodBase.DeclaringType != null && methodBase.DeclaringType != declaring && methodBase.DeclaringType.IsAssignableFrom(declaring) && methodBase.GetParameters().Length == 0)
					{
						return true;
					}
				}
				catch (Exception)
				{
				}
			}
			return false;
		}
	}
}
namespace BestiaryRedux.Enemies.Leviathan
{
	internal enum AttackKind
	{
		Leap,
		FromBelow,
		Charge
	}
	internal sealed class AttackPlan
	{
		public AttackKind Kind;

		public LeviathanPath Path;

		public float Offset;

		public Vector3 Exit;

		public Vector3 Target;

		public Vector3 Dive;

		public float WarnUntil;

		public float BreakOut;

		public float DiveAt;

		public float WarnSpeed;

		public float Hold;

		public float DiveFrom;

		public float ArcLength;

		public float Launch;

		public float RunUp;

		public string Shape = "";

		public float Height;

		public Vector3? Peak;

		public readonly List<Vector3> Preview = new List<Vector3>();

		internal void Rejoined()
		{
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			WarnUntil = Path.Map(WarnUntil);
			BreakOut = Path.Map(BreakOut);
			DiveAt = Path.Map(DiveAt);
			if (Hold > 0f)
			{
				Hold = Path.Map(Hold);
			}
			if (DiveFrom > 0f)
			{
				DiveFrom = Path.Map(DiveFrom);
			}
			Preview.Clear();
			for (float num = 0f; num <= DiveAt + 20f && num <= Path.Length; num += 4f)
			{
				Preview.Add(Path.At(num).Position);
			}
		}
	}
	internal static class LeviathanAttack
	{
		private sealed class LeapArc
		{
			public List<Vector3> Points;

			public Vector3 Land;

			public Vector3 Tangent;

			public float A1;

			public float A2;

			public float Height;

			public float Length;

			public string Mode = "";
		}

		private sealed class Points
		{
			public readonly List<Vector3> List = new List<Vector3>();

			public float Length;

			public Vector3 Last => List[List.Count - 1];

			public void Add(Vector3 p)
			{
				//IL_003f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0028: Unknown result type (might be due to invalid IL or missing references)
				//IL_002d: Unknown result type (might be due to invalid IL or missing references)
				if (List.Count > 0)
				{
					Length += Vector3.Distance(List[List.Count - 1], p);
				}
				List.Add(p);
			}

			public Points Copy()
			{
				Points points = new Points
				{
					Length = Length
				};
				points.List.AddRange(List);
				return points;
			}

			public void Curve(Vector3 b, Vector3 c, Vector3 d, int steps = 60)
			{
				//IL_0001: 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_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_000d: Unknown result type (might be due to invalid IL or missing references)
				//IL_000e: 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)
				Vector3 last = Last;
				for (int i = 1; i <= steps; i++)
				{
					Add(Bezier(last, b, c, d, (float)i / (float)steps));
				}
			}

			public void Deep(Vector3 to, float depth, float ease = 0f)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				foreach (bool item in DeepSteps(to, depth, ease))
				{
				}
			}

			public IEnumerable<bool> DeepSteps(Vector3 to, float depth, float ease = 0f, int every = 16)
			{
				//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)
				Vector3 from = Last;
				float length = Flat(from, to);
				List<float> ys = new List<float>();
				List<Vector3> xz = new List<Vector3>();
				float lastY = from.y + depth;
				for (float s = 2f; s <= length; s += 2f)
				{
					Vector3 p = Vector3.Lerp(new Vector3(from.x, 0f, from.z), new Vector3(to.x, 0f, to.z), s / length);
					if (LeviathanPath.GroundAt(new Vector3(p.x, lastY, p.z), out var y))
					{
						lastY = y;
					}
					if (ys.Count % every == every - 1)
					{
						yield return false;
					}
					float num = lastY - depth;
					if (s < ease)
					{
						num = Mathf.Lerp(from.y, num, Mathf.SmoothStep(0f, 1f, s / ease));
					}
					ys.Add(num);
					xz.Add(p);
				}
				for (int i = 0; i < ys.Count; i++)
				{
					float num2 = 0f;
					int num3 = 0;
					for (int j = Mathf.Max(0, i - 3); j <= Mathf.Min(ys.Count - 1, i + 3); j++)
					{
						num2 += ys[j];
						num3++;
					}
					Add(new Vector3(xz[i].x, ((float)i * 2f < ease) ? ys[i] : (num2 / (float)num3), xz[i].z));
				}
			}
		}

		[StructLayout(LayoutKind.Auto)]
		[CompilerGenerated]
		private struct <>c__DisplayClass41_0
		{
			public Dictionary<string, int> misses;
		}

		private const float BelowRise = 20f;

		private const float HoldBack = 12f;

		private const float HoldOff = 10f;

		internal static readonly AttackPlan Fits = new AttackPlan();

		private const float LaunchStep = 15f;

		private const float HighAbove = 8f;

		private const float CatchAbove = 0.5f;

		private static readonly Collider[] overlap = (Collider[])(object)new Collider[16];

		internal const string Dips = "joined to its heading, the head would go back under the ground before the crest";

		internal const string TargetOn = "the target stands on";

		private const float ChargeReach = 0.6f;

		internal static (float min, float max) Range(AttackKind kind, float radius = 0f)
		{
			(float, float) tuple = kind switch
			{
				AttackKind.Leap => (LeviathanTuning.LeapMin.Current, LeviathanTuning.LeapMax.Current), 
				AttackKind.FromBelow => (LeviathanTuning.FromBelowMin.Current, LeviathanTuning.FromBelowMax.Current), 
				_ => (LeviathanTuning.ChargeMin.Current, LeviathanTuning.ChargeMax.Current), 
			};
			float item = tuple.Item1;
			float item2 = tuple.Item2;
			float num = RunUp(kind, radius);
			return (min: item + num, max: Mathf.Max(item + 10f, item2) + num);
		}

		internal static float Ideal(AttackKind kind, float radius = 0f)
		{
			var (num, num2) = Range(kind);
			return Mathf.Clamp(kind switch
			{
				AttackKind.FromBelow => LeviathanTuning.FromBelowIdeal.Current, 
				AttackKind.Leap => LeviathanTuning.LeapIdeal.Current, 
				_ => LeviathanTuning.ChargeIdeal.Current, 
			}, num, num2) + RunUp(kind, radius);
		}

		internal static bool InRange(AttackKind kind, float d, float radius = 0f)
		{
			if (d >= Range(kind, radius).min)
			{
				return d <= Range(kind, radius).max;
			}
			return false;
		}

		internal static float RunUp(AttackKind kind, float radius)
		{
			if (!(radius <= 0f))
			{
				switch (kind)
				{
				case AttackKind.FromBelow:
					break;
				case AttackKind.Leap:
					return LeapAhead(0.5f * radius + 25f);
				default:
				{
					Slide(0f, LeviathanTuning.ChargeRide.Current * radius + radius + 25f, LeviathanTuning.ChargeRiseAngle.Current, out var length);
					return length;
				}
				}
			}
			return 0f;
		}

		internal static float LeapAhead(float depth)
		{
			float current = LeviathanTuning.LeapExit.Current;
			if (depth <= 1f)
			{
				return current;
			}
			float num = Mathf.Clamp(LeviathanTuning.LeapExitAngle.Current, 30f, 90f) * (MathF.PI / 180f);
			float num2 = Mathf.Max(25f, 1.25f * LeviathanTuning.MinTurnAttack.Current);
			return Mathf.Max(current, num2 * Mathf.Sin(num) + Mathf.Max(0f, depth - num2 * (1f - Mathf.Cos(num))) / Mathf.Max(0.05f, Mathf.Tan(num)));
		}

		internal static bool AtSurface(Vector3 head, float radius, out float ground)
		{
			//IL_0000: 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)
			if (LeviathanPath.GroundAt(head, out ground))
			{
				return head.y > ground - 0.25f * radius;
			}
			return false;
		}

		internal static string Name(AttackKind kind)
		{
			if (kind != AttackKind.FromBelow)
			{
				return kind.ToString();
			}
			return "From below";
		}

		private static float Flat(Vector3 a, Vector3 b)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			Vector2 val = new Vector2(a.x - b.x, a.z - b.z);
			return ((Vector2)(ref val)).magnitude;
		}

		private static Vector3 FlatDir(Vector3 from, Vector3 to)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = to - from;
			val.y = 0f;
			if (!(((Vector3)(ref val)).sqrMagnitude < 0.0001f))
			{
				return ((Vector3)(ref val)).normalized;
			}
			return Vector3.forward;
		}

		internal static AttackPlan? Plan(AttackKind kind, Vector3 start, Vector3 target, LeviathanBrain worm, Random rng, out string why, bool fitOnly = false, float? joinAt = null)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			PlanJob planJob = new PlanJob(kind, start, target, worm, rng, fitOnly, joinAt);
			planJob.RunAll();
			why = planJob.Why;
			return planJob.Result;
		}

		internal static IEnumerable<bool> Steps(PlanJob job)
		{
			return job.Kind switch
			{
				AttackKind.Leap => Leap(job), 
				AttackKind.FromBelow => FromBelow(job), 
				_ => Charge(job), 
			};
		}

		private static string? Spot(Vector3 p, LeviathanBrain worm, string what)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			if (!LeviathanPath.GroundAt(p, out var y))
			{
				return what + " is off the map";
			}
			if (worm.Zones.Inside(new Vector3(p.x, y, p.z), worm.Radius, out string zone))
			{
				return what + " is in a safe zone (" + zone + ")";
			}
			return null;
		}

		private static string? ArcFits(List<Vector3> arc, LeviathanBrain worm)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: 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_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			for (int i = 1; i <= 9; i++)
			{
				Vector3 val = arc[arc.Count * i / 10 - 1];
				if (!LeviathanPath.GroundAt(val, out var y))
				{
					return "its arc goes off the map";
				}
				if (val.y < y)
				{
					num++;
				}
				if (val.y - y < 2f * worm.Radius)
				{
					string text = Spot(val, worm, "its arc");
					if (text != null)
					{
						return text;
					}
				}
			}
			if (num <= Mathf.RoundToInt(LeviathanTuning.ArcHump.Current))
			{
				return null;
			}
			return $"the ground is in the way of its arc ({num} of 9 points under it)";
		}

		private static float DiveIn(Points pts, Vector3 dir, Vector3 tangent, float a2, float after, float under, out Vector3 end)
		{
			//IL_0001: 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_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_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: 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_003f: 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_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: 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_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: 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_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			Vector3 last = pts.Last;
			float num = Mathf.Max(0.26f, Mathf.Sin(a2));
			Vector3 at = last + dir * 40f;
			float y;
			float num2 = Mathf.Max(10f, last.y - ((LeviathanPath.GroundAt(at, out y) ? y : last.y) - after));
			float num3 = Mathf.Max(40f, 1.3f * num2 * Mathf.Cos(a2) / num + 25f);
			at = last + dir * num3;
			at.y = (LeviathanPath.GroundAt(at, out var y2) ? y2 : (last.y - num2)) - after;
			Vector3 b = last + tangent * (0.55f * (last.y - at.y) / num);
			Vector3 c = at - dir * (0.4f * num3);
			float num4 = -1f;
			for (int i = 1; i <= 40; i++)
			{
				pts.Add(Bezier(last, b, c, at, (float)i / 40f));
				if (num4 < 0f && LeviathanPath.GroundAt(pts.Last, out var y3) && pts.Last.y < y3 - under)
				{
					num4 = pts.Length;
				}
			}
			end = at;
			if (!(num4 < 0f))
			{
				return num4;
			}
			return pts.Length;
		}

		private static Vector3 Top(List<Vector3> arc)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: 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_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = arc[0];
			foreach (Vector3 item in arc)
			{
				if (item.y > val.y)
				{
					val = item;
				}
			}
			return val;
		}

		private static void LogRaised(PlanJob job, AttackKind kind, float height, float length, float a1, float a2)
		{
			var (num, num2, _, _) = Shape(kind);
			if (a1 * 57.29578f > num + 0.5f || a2 * 57.29578f > num2 + 0.5f)
			{
				job.Note($"{job.Worm.Label}: {Name(kind)}: its arc ({height:0} m high, {length:0} m long) is too steep for the set angles; it leaves at {a1 * 57.29578f:0}° (set {num:0}°) and comes in at {a2 * 57.29578f:0}° (set {num2:0}°)");
			}
		}

		private static void Recovery(List<Vector3> corners, Vector3 dive, Vector3 dir, LeviathanBrain worm)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: 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_0013: 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_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: 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_0068: 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_007b: 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_0030: 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_003c: 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_004a: 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_0061: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = dive + dir * 60f;
			corners.Add(val);
			Vector3 val2 = val;
			Vector3 val3 = dir;
			if (Flat(val, worm.Territory) > 60f)
			{
				val3 = FlatDir(val, worm.Territory);
				((Vector3)(ref val2))..ctor(worm.Territory.x, val.y, worm.Territory.z);
				corners.Add(val2);
			}
			corners.Add(val2 + val3 * (worm.Body.Length + 200f));
		}

		private static void Finish(AttackPlan plan)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			for (float num = 0f; num <= plan.DiveAt + 20f && num <= plan.Path.Length; num += 4f)
			{
				plan.Preview.Add(plan.Path.At(num).Position);
			}
		}

		private static IEnumerable<bool> Leap(PlanJob job)
		{
			Vector3 start = job.Start;
			Vector3 target = job.Target;
			LeviathanBrain worm = job.Worm;
			float r = worm.Radius;
			(float min, float max) tuple = Range(AttackKind.Leap);
			float item = tuple.min;
			float item2 = tuple.max;
			float current = LeviathanTuning.LeapSlack.Current;
			Vector3 dir = FlatDir(start, target);
			float gH;
			bool fromHead = AtSurface(start, r, out gH);
			Vector3 at = start + dir * LeviathanTuning.LeapExit.Current;
			float y;
			float depth = (LeviathanPath.GroundAt(at, out y) ? y : start.y) - 0.5f * r - start.y;
			float num = (fromHead ? 0f : LeapAhead(depth));
			float toTarget = Flat(start, target);
			float num2 = toTarget - num;
			if (num2 < item - current || num2 > item2 + current)
			{
				job.Why = $"the target is {num2:0} m from where it would break out ({num:0} m ahead; {item - current:0}-{item2 + current:0} m)";
				yield break;
			}
			string why;
			if ((why = Spot(target, worm, "the target's spot")) != null)
			{
				job.Why = why;
				yield break;
			}
			List<string> misses = new List<string>();
			List<float> list = new List<float> { num2 };
			if (!fromHead)
			{
				for (float num3 = num2 - 15f; num3 >= item - current; num3 -= 15f)
				{
					list.Add(num3);
				}
			}
			foreach (float d1 in list)
			{
				yield return false;
				float ahead = toTarget - d1;
				Vector3 exit = start + dir * ahead;
				if (!LeviathanPath.GroundAt(exit, out var y2))
				{
					misses.Add("its exit is off the map");
					continue;
				}
				exit.y = y2;
				string text = Spot(exit, worm, "the exit point") ?? LeviathanSurfaces.AttackOut(fromHead ? start : exit, "its break-out");
				if (text != null)
				{
					misses.Add($"{d1:0} m: {text}");
					continue;
				}
				Vector3 p0 = (Vector3)(fromHead ? start : new Vector3(exit.x, y2 - 0.5f * r, exit.z));
				Found<LeapArc> found = new Found<LeapArc>();
				foreach (bool item3 in LeapArcs(p0, exit, dir, d1, target, worm, misses, found))
				{
					yield return item3;
				}
				LeapArc value = found.Value;
				if (value == null)
				{
					continue;
				}
				foreach (bool item4 in LeapPlan(job, exit, p0, dir, ahead, d1, depth, fromHead, gH, value))
				{
					yield return item4;
				}
				if (job.Result == null)
				{
					misses.Add($"{d1:0} m: {job.Why}");
					continue;
				}
				yield break;
			}
			job.Why = string.Join("; ", misses.Distinct().Take(6));
			job.Note(worm.Label + ": Leap: no arc fits (" + job.Why + ")");
		}

		private static IEnumerable<bool> LeapArcs(Vector3 p0, Vector3 exit, Vector3 dir, float d1, Vector3 target, LeviathanBrain worm, List<string> misses, Found<LeapArc> into)
		{
			//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_0016: 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_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			float radius = worm.Radius;
			float catchY = target.y + 0.5f * radius;
			float maxHeight = LeviathanTuning.LeapMaxHeight.Current;
			Vector3 near = target + dir * LeviathanTuning.LeapPast.Current;
			float k;
			float[] array;
			if (!LeviathanPath.GroundAt(near, out var gNear) || !(target.y > Mathf.Max(exit.y, gNear) + 8f))
			{
				float full = Mathf.Clamp(d1 * LeviathanTuning.LeapHeightPerMetre.Current, LeviathanTuning.LeapHeightMin.Current, Mathf.Max(LeviathanTuning.LeapHeightMin.Current, LeviathanTuning.LeapHeightMax.Current));
				string sizes = "";
				array = LeviathanTuning.Scales(LeviathanTuning.LeapFallback1, LeviathanTuning.LeapFallback2);
				for (int i = 0; i < array.Length; i++)
				{
					k = array[i];
					yield return false;
					string why;
					LeapArc leapArc = Through(p0, dir, near, Mathf.Max((p0.y + gNear) / 2f + full * k, Mathf.Max(p0.y, gNear) + 10f), worm, null, out why);
					if (leapArc != null)
					{
						leapArc.Height = full * k;
						leapArc.Mode = ((k >= 0.999f) ? "landing on the target, the full size" : $"landing on the target, fallback {k * 100f:0} % (the full {full:0} m: {sizes})");
						into.Value = leapArc;
						yield break;
					}
					sizes = sizes + ((sizes.Length > 0) ? "; " : "") + $"{full * k:0} m: {why}";
					misses.Add(Prefix($"landing on the target {full * k:0} m high: {why}"));
				}
			}
			float num = catchY - p0.y;
			if (num > maxHeight)
			{
				misses.Add(Prefix($"no Leap arc reaches the player under Leap max height (its crest {num:0} m over the break-out, max {maxHeight:0} m)"));
			}
			else if (num >= 10f)
			{
				float num2 = 1f + Mathf.Clamp01(LeviathanTuning.LeapSymmetry.Current);
				float full = exit.y;
				Vector3 land = target;
				for (int j = 0; j < 4; j++)
				{
					float num3 = d1 * Mathf.Max(10f, catchY - full) * num2 / num;
					land = target + dir * num3;
					if (!LeviathanPath.GroundAt(land, out full))
					{
						break;
					}
				}
				yield return false;
				string why2;
				LeapArc leapArc2 = Through(p0, dir, new Vector3(land.x, full, land.z), catchY, worm, target, out why2);
				if (leapArc2 != null)
				{
					leapArc2.Height = catchY - p0.y;
					leapArc2.Mode = $"its crest through the target ({target.y - Mathf.Max(exit.y, gNear):0} m over the ground)";
					into.Value = leapArc2;
					yield break;
				}
				misses.Add(Prefix("its crest through the target: " + why2));
			}
			array = new float[4] { 20f, 40f, 60f, 80f };
			foreach (float full in array)
			{
				yield return false;
				Vector3 land = target + dir * (LeviathanTuning.LeapPast.Current + full);
				if (!LeviathanPath.GroundAt(land, out k))
				{
					continue;
				}
				land.y = k;
				float lo = Mathf.Max(Mathf.Max(p0.y, k) + 10f, catchY);
				float hi = p0.y + maxHeight;
				if (lo > hi)
				{
					misses.Add(Prefix($"landing {full:0} m beyond: no Leap arc reaches the player under Leap max height"));
					continue;
				}
				string why3 = null;
				for (int l = 0; l < 14; l++)
				{
					if (!(hi - lo > 0.5f))
					{
						break;
					}
					float num4 = (lo + hi) / 2f;
					Vector3 tangent;
					float a;
					float a2;
					List<Vector3> list = EruptionArc(p0, dir, num4 - p0.y, num4 - k, Flat(p0, land), 120, AttackKind.Leap, out tangent, out a, out a2);
					if (list == null)
					{
						lo = num4;
						continue;
					}
					float num5 = HeightAt(list, p0, dir, d1);
					if (num5 < catchY)
					{
						lo = num4;
					}
					else
					{
						hi = num4;
					}
					if (l % 4 == 3)
					{
						yield return false;
					}
				}
				LeapArc leapArc3 = Through(p0, dir, land, hi, worm, target, out why3);
				if (leapArc3 != null)
				{
					leapArc3.Height = hi - p0.y;
					leapArc3.Mode = $"passing the target on its way down, landing {full:0} m beyond";
					into.Value = leapArc3;
					break;
				}
				misses.Add(Prefix($"passing the target, landing {full:0} m beyond: {why3}"));
			}
			string Prefix(string mode)
			{
				return $"{d1:0} m, {mode}";
			}
		}

		private static LeapArc? Through(Vector3 p0, Vector3 dir, Vector3 land, float top, LeviathanBrain worm, Vector3? target, out string? why)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: 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_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			float radius = worm.Radius;
			if (!LeviathanPath.GroundAt(land, out var y))
			{
				why = "its landing is off the map";
				return null;
			}
			land.y = y;
			if ((why = Spot(land, worm, "its landing")) != null || (why = LeviathanSurfaces.AttackOut(land, "its landing")) != null)
			{
				return null;
			}
			if (top - p0.y > LeviathanTuning.LeapMaxHeight.Current)
			{
				why = $"no Leap arc reaches the player under Leap max height ({top - p0.y:0} m, max {LeviathanTuning.LeapMaxHeight.Current:0} m)";
				return null;
			}
			float length = Flat(p0, land);
			Vector3 tangent;
			float a;
			float a2;
			List<Vector3> list = EruptionArc(p0, dir, top - p0.y, top - y, length, 120, AttackKind.Leap, out tangent, out a, out a2);
			if (list == null)
			{
				why = "its landing is too far below the exit for the arc's angles";
				return null;
			}
			if ((why = ArcFits(list, worm)) != null)
			{
				return null;
			}
			if (target.HasValue)
			{
				float num = Closest(list, target.Value) - radius;
				if (num > 0f)
				{
					why = $"it passes {num:0.0} m from the target";
					return null;
				}
			}
			if ((why = ThroughStructure(list, p0, land, target, radius)) != null)
			{
				return null;
			}
			for (float num2 = 0f; num2 <= 40f; num2 += 8f)
			{
				if ((why = Spot(land + dir * num2, worm, "its dive")) != null)
				{
					return null;
				}
			}
			why = null;
			return new LeapArc
			{
				Points = list,
				Land = land,
				Tangent = tangent,
				A1 = a,
				A2 = a2,
				Length = length
			};
		}

		private static float HeightAt(List<Vector3> arc, Vector3 p0, Vector3 dir, float x)
		{
			//IL_0006: 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_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: 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_002b: 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_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: 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_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			float num = 0f;
			Vector3 val = p0;
			foreach (Vector3 item in arc)
			{
				float num2 = Vector3.Dot(new Vector3(item.x - p0.x, 0f, item.z - p0.z), dir);
				if (num2 >= x)
				{
					return Mathf.Lerp(val.y, item.y, Mathf.InverseLerp(num, num2, x));
				}
				num = num2;
				val = item;
			}
			return val.y;
		}

		private static float Closest(List<Vector3> arc, Vector3 feet)
		{
			//IL_001c: 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_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_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			float num = float.MaxValue;
			float current = LeviathanTuning.StandingHeight.Current;
			Vector3 val = default(Vector3);
			foreach (Vector3 item in arc)
			{
				((Vector3)(ref val))..ctor(feet.x, Mathf.Clamp(item.y, feet.y, feet.y + current), feet.z);
				num = Mathf.Min(num, Vector3.Distance(item, val));
			}
			return num;
		}

		internal static string? ThroughStructure(List<Vector3> arc, Vector3 exit, Vector3 land, Vector3? target, float r)
		{
			//IL_001d: 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_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: 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_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: 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)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: 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)
			int structureMask = LeviathanBrain.StructureMask;
			if (structureMask == 0)
			{
				return null;
			}
			float num = 0.4f * r;
			for (int i = 0; i < arc.Count; i += 2)
			{
				Vector3 val = arc[i];
				if (Flat(val, exit) < 1.5f * r || Flat(val, land) < 1.5f * r || (target.HasValue && Vector3.Distance(val, target.Value + Vector3.up * 0.9f) < r + 2f))
				{
					continue;
				}
				LeviathanCost.Rays++;
				int num2 = Physics.OverlapSphereNonAlloc(val, num, overlap, structureMask, (QueryTriggerInteraction)1);
				for (int j = 0; j < num2; j++)
				{
					Collider val2 = overlap[j];
					float y;
					if ((Object)(object)val2 != (Object)null && ManMade(val2))
					{
						return $"its arc would go through \"{((Object)val2).name}\" ({LeviathanSurfaces.Of(val2, val)}) {val.y - (LeviathanPath.GroundAt(val, out y) ? y : val.y):0} m up";
					}
				}
			}
			return null;
		}

		internal static bool ManMade(Collider c)
		{
			if (!(c is TerrainCollider) && !LeviathanPath.IsNaturalGround(c) && !((Component)c).CompareTag("Tree"))
			{
				return !LeviathanPath.Small(c);
			}
			return false;
		}

		private static IEnumerable<bool> LeapPlan(PlanJob job, Vector3 exit, Vector3 p0, Vector3 dir, float ahead, float d1, float depth, bool fromHead, float gH, LeapArc arc)
		{
			//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_0016: 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_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			Vector3 start = job.Start;
			Vector3 target = job.Target;
			LeviathanBrain worm = job.Worm;
			bool fitOnly = job.FitOnly;
			float r = worm.Radius;
			float after = r + Mathf.Max(LeviathanTuning.RecoveryDepth.Current, LeviathanTuning.LeapDiveDepth.Current + 5f);
			float under = r + LeviathanTuning.LeapDiveDepth.Current;
			if (fitOnly && !fromHead)
			{
				job.Succeed(Fits);
				yield break;
			}
			Points pts = ne