Decompiled source of LCDrone v0.6.28

plugins/LCDrone/LCDrone.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Dawn;
using DunGen;
using DunGen.Graph;
using Dusk;
using GameNetcodeStuff;
using HarmonyLib;
using LCDrone.Cargo;
using LCDrone.Configuration;
using LCDrone.Core;
using LCDrone.Core.Behaviors;
using LCDrone.Core.Cargo;
using LCDrone.Core.Commands;
using LCDrone.Core.Mapping;
using LCDrone.Core.Modules;
using LCDrone.Core.Navigation;
using LCDrone.Core.Save;
using LCDrone.Core.TerminalScreen;
using LCDrone.Core.World;
using LCDrone.Doors;
using LCDrone.Drone;
using LCDrone.Mapping;
using LCDrone.Navigation;
using LCDrone.NetcodePatcher;
using LCDrone.Patches;
using LCDrone.TerminalScreen;
using Microsoft.CodeAnalysis;
using PathfindingLib.API.SmartPathfinding;
using PathfindingLib.Utilities;
using TMPro;
using Unity.Collections;
using Unity.Netcode;
using Unity.Netcode.Components;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.Experimental.AI;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[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("LCDrone")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.6.28.0")]
[assembly: AssemblyInformationalVersion("0.6.28+97897d28961d118a74e3b1e9132c6158966b9ed1")]
[assembly: AssemblyProduct("LCDrone")]
[assembly: AssemblyTitle("LCDrone")]
[assembly: InternalsVisibleTo("LCDrone.Tests")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.6.28.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
[module: NetcodePatchedAssembly]
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 LCDrone
{
	[BepInPlugin("LCDrone", "LCDrone", "0.6.28")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		internal static ManualLogSource Logger = null;

		private static readonly Harmony harmony = new Harmony("LCDrone");

		internal static PluginConfig BoundConfig { get; private set; } = null;

		private void Awake()
		{
			Logger = ((BaseUnityPlugin)this).Logger;
			BoundConfig = new PluginConfig(((BaseUnityPlugin)this).Config);
			InitializeNetcode();
			ApplyPatches();
			DroneLog.Initialize();
			DroneSelection.Initialize();
			MapLabels.Initialize();
			RegisterTerminalCommands();
			Logger.LogInfo((object)"Plugin [LCDrone] is loaded!");
		}

		private static void ApplyPatches()
		{
			Type[] array = new Type[45]
			{
				typeof(RoundManagerPatches),
				typeof(RoundManagerExitIdPatches),
				typeof(StartOfRoundPatches),
				typeof(GameNetworkManagerSavePatches),
				typeof(ManualCameraRendererPatches),
				typeof(TerminalPatches),
				typeof(TerminalDisablePatches),
				typeof(HoarderBugPatches),
				typeof(DroneDamagePatches),
				typeof(ForestGiantDroneAwarenessPatches),
				typeof(ForestGiantDroneAwarenessCleanupPatches),
				typeof(MouthDogDroneCollisionPatch),
				typeof(MouthDogDroneCollisionCleanupPatch),
				typeof(MouthDogDroneNoisePatch),
				typeof(ThumperDroneChasePatch),
				typeof(ThumperDroneChaseCleanupPatch),
				typeof(ThumperDroneOwnershipCleanupPatch),
				typeof(ThumperDroneCollisionPatch),
				typeof(CoilHeadDroneVisionPatch),
				typeof(BrackenDroneVisionPatch),
				typeof(SporeLizardDroneDefensePatch),
				typeof(SporeLizardDroneDefenseCleanupPatch),
				typeof(HygrodereDronePatches),
				typeof(HygrodereDroneSlowPatch),
				typeof(HygrodereDroneCleanupPatch),
				typeof(SnareFleaDronePatches),
				typeof(BarberDroneCollisionPatch),
				typeof(BarberDroneCleanupPatch),
				typeof(NutcrackerDronePatches),
				typeof(NutcrackerDroneShotPatch),
				typeof(NutcrackerDroneCleanupPatch),
				typeof(ManticoilDronePatches),
				typeof(RoamingLocustDronePatches),
				typeof(CircuitBeeDroneHivePatch),
				typeof(CircuitBeeDroneCollisionPatch),
				typeof(FeioparDroneVisionPatch),
				typeof(FeioparDroneVisionCleanupPatch),
				typeof(FeioparDroneNoisePatch),
				typeof(KidnapperFoxDroneVisionPatch),
				typeof(KidnapperFoxDroneVisionCleanupPatch),
				typeof(CoilHeadDroneVisionCleanupPatch),
				typeof(BrackenDroneVisionCleanupPatch),
				typeof(RoamingLocustDroneCleanupPatch),
				typeof(CircuitBeeDroneHiveCleanupPatch),
				typeof(DroneShopPatches)
			};
			Type[] array2 = array;
			foreach (Type type in array2)
			{
				try
				{
					harmony.CreateClassProcessor(type).Patch();
					if (type == typeof(DroneShopPatches))
					{
						DroneShopPatches.SetHooksInstalled(installed: true);
					}
				}
				catch (Exception arg)
				{
					if (type == typeof(DroneShopPatches))
					{
						DroneShopPatches.SetHooksInstalled(installed: false);
					}
					Logger.LogError((object)$"Failed to apply the {type.Name} patches, some features won't work: {arg}");
				}
			}
		}

		private static void RegisterTerminalCommands()
		{
			try
			{
				DroneTerminalCommand.Register();
			}
			catch (Exception arg)
			{
				Logger.LogError((object)$"Failed to register the drone terminal command, the drone screen won't open: {arg}");
			}
		}

		private static void InitializeNetcode()
		{
			Type[] array;
			try
			{
				array = Assembly.GetExecutingAssembly().GetTypes();
			}
			catch (ReflectionTypeLoadException ex)
			{
				array = ex.Types.Where((Type type2) => type2 != null).ToArray();
			}
			Type[] array2 = array;
			foreach (Type type in array2)
			{
				if (type.ContainsGenericParameters)
				{
					continue;
				}
				MethodInfo[] methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (MethodInfo methodInfo in methods)
				{
					if (methodInfo.IsDefined(typeof(RuntimeInitializeOnLoadMethodAttribute), inherit: false) && !methodInfo.ContainsGenericParameters && methodInfo.GetParameters().Length == 0)
					{
						try
						{
							methodInfo.Invoke(null, null);
						}
						catch (Exception arg)
						{
							Logger.LogError((object)$"Netcode initializer {type.FullName}.{methodInfo.Name} threw an exception: {arg}");
						}
					}
				}
			}
		}

		[Conditional("DEBUG")]
		internal static void LogIfDebugBuild(string text)
		{
			Logger.LogInfo((object)text);
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "LCDrone";

		public const string PLUGIN_NAME = "LCDrone";

		public const string PLUGIN_VERSION = "0.6.28";
	}
}
namespace LCDrone.TerminalScreen
{
	internal static class DroneScreen
	{
		private const int MaxCharactersToType = 64;

		private const int ScrollFrames = 5;

		private const float ScrollBottom = 0f;

		private const float ScrollTop = 1f;

		private const float ScrolledUpThreshold = 0.05f;

		private const int HelpNodeIndex = 13;

		private const string ViewVerb = "view";

		private const string MonitorNoun = "monitor";

		private static Terminal? activeTerminal;

		private static Terminal? pendingTerminal;

		private static TerminalNode? screenNode;

		private static TerminalNode? monitorNode;

		private static int batchDepth;

		private static bool redrawPending;

		private static bool redrawPendingScrolls;

		private static FieldRef<Terminal, Coroutine?>? scrollCoroutineRef;

		private static bool scrollCoroutineRefFailed;

		internal static DroneController? CurrentDrone => DroneSelection.Current;

		internal static Terminal? AudioTerminal
		{
			get
			{
				if (!((Object)(object)activeTerminal != (Object)null) || !activeTerminal.terminalInUse)
				{
					return null;
				}
				return activeTerminal;
			}
		}

		internal static bool IsActive(Terminal terminal)
		{
			if ((Object)(object)activeTerminal != (Object)null)
			{
				return (Object)(object)activeTerminal == (Object)(object)terminal;
			}
			return false;
		}

		internal static void OnDroneCommand(Terminal terminal, TerminalNode node)
		{
			pendingTerminal = terminal;
		}

		internal static void OnNodeLoaded(Terminal terminal, TerminalNode node)
		{
			if ((Object)(object)pendingTerminal != (Object)null && (Object)(object)pendingTerminal == (Object)(object)terminal && ((Object)(object)DroneTerminalCommand.ResultNode == (Object)null || (Object)(object)node == (Object)(object)DroneTerminalCommand.ResultNode))
			{
				pendingTerminal = null;
				Enter(terminal);
			}
			else if (IsActive(terminal) && (Object)(object)terminal.currentNode != (Object)(object)screenNode)
			{
				Close("another terminal node was loaded");
			}
		}

		internal static void Leave(Terminal terminal, string reason)
		{
			bool flag = activeTerminal == terminal || pendingTerminal == terminal;
			bool flag2 = (activeTerminal != null && (Object)(object)activeTerminal == (Object)null) || (pendingTerminal != null && (Object)(object)pendingTerminal == (Object)null);
			if (flag || flag2)
			{
				Close(reason);
			}
		}

		internal static void Submit(Terminal terminal)
		{
			if (!terminal.terminalInUse)
			{
				return;
			}
			if (terminal.textAdded == 0)
			{
				Focus(terminal);
				return;
			}
			string typedText = DroneScreenText.GetTypedText(terminal.screenText.text, terminal.textAdded);
			batchDepth++;
			try
			{
				terminal.textAdded = 0;
				RequestRedraw(scroll: true);
				HandleLine(terminal, typedText);
			}
			finally
			{
				batchDepth--;
				if (batchDepth == 0 && redrawPending)
				{
					Redraw(redrawPendingScrolls);
				}
			}
			Focus(terminal);
		}

		internal static void ScrollToBottom(Terminal terminal)
		{
			ScrollTo(terminal, 0f);
		}

		private static void HandleLine(Terminal terminal, string typed)
		{
			string line;
			string[] args;
			DroneScreenInputKind droneScreenInputKind = DroneScreenInput.Classify(typed, out line, out args);
			switch (droneScreenInputKind)
			{
			case DroneScreenInputKind.Empty:
				return;
			case DroneScreenInputKind.Exit:
				Exit(terminal);
				return;
			default:
				AddLocalLine(DroneLogKind.Command, GetLocalPlayerName() + "> " + line);
				break;
			case DroneScreenInputKind.ToDrone:
			case DroneScreenInputKind.Command:
				break;
			}
			switch (droneScreenInputKind)
			{
			case DroneScreenInputKind.Help:
				AddLocalLines(DroneScreenText.FormatHelp(DroneCommandCatalog.BuiltIn));
				return;
			case DroneScreenInputKind.HelpDebug:
				AddLocalLines(DroneScreenText.FormatDebugHelp(DroneCommandCatalog.BuiltIn));
				return;
			case DroneScreenInputKind.HelpScreen:
				AddLocalLines(DroneScreenText.FormatScreenHelp());
				return;
			case DroneScreenInputKind.Drones:
				ListDrones(args);
				return;
			case DroneScreenInputKind.Select:
				Select(args);
				return;
			case DroneScreenInputKind.Release:
				Release(args);
				return;
			case DroneScreenInputKind.Monitor:
				ToggleMonitor(terminal);
				return;
			case DroneScreenInputKind.Floor:
				SelectFloor(args);
				return;
			case DroneScreenInputKind.Switch:
				SwitchMonitor(args);
				return;
			case DroneScreenInputKind.Focus:
				FocusMonitor(args);
				return;
			case DroneScreenInputKind.ToDrone:
				SendToDrone(line);
				return;
			}
			DroneController currentDrone = CurrentDrone;
			if ((Object)(object)currentDrone == (Object)null)
			{
				AddLocalLine(DroneLogKind.Command, GetLocalPlayerName() + "> " + line);
				AddLocalLine(DroneLogKind.Error, "There is no drone to send that to.");
			}
			else
			{
				currentDrone.SubmitCommand(line);
			}
		}

		private static void SendToDrone(string line)
		{
			DroneController drone;
			if (!DroneScreenInput.TryParseDronePrefix(line, out int droneId, out string command))
			{
				AddLocalLine(DroneLogKind.Command, GetLocalPlayerName() + "> " + line);
				AddLocalLine(DroneLogKind.Error, "Usage: d<n> <command> (or drone <n> <command>)");
			}
			else if (!DroneRegistry.TryGet(droneId, out drone) || (Object)(object)drone == (Object)null)
			{
				AddLocalLine(DroneLogKind.Command, GetLocalPlayerName() + "> " + line);
				AddLocalLine(DroneLogKind.Error, $"There is no drone {droneId}.");
			}
			else
			{
				drone.SubmitCommand(command);
			}
		}

		private static void ToggleMonitor(Terminal terminal)
		{
			TerminalNode val = GetMonitorNode(terminal);
			if ((Object)(object)val == (Object)null || (Object)(object)val.displayTexture == (Object)null)
			{
				AddLocalLine(DroneLogKind.Error, "Couldn't find the monitor view.");
				return;
			}
			bool flag = IsMonitorShown(terminal);
			terminal.LoadTerminalImage(val);
			if (!flag && !IsMonitorShown(terminal))
			{
				AddLocalLine(DroneLogKind.Info, "The map only works while the ship is landed.");
			}
			else
			{
				RequestRedraw(scroll: true);
			}
		}

		private static void SelectFloor(string[] args)
		{
			bool flag;
			string message;
			if (args.Length == 0)
			{
				flag = MapLabels.TryCycleFloor(out message);
			}
			else
			{
				if (args.Length != 1 || !int.TryParse(args[0], NumberStyles.None, CultureInfo.InvariantCulture, out var result))
				{
					AddLocalLine(DroneLogKind.Error, "Usage: floor [n] - cycle floors or select floor n.");
					return;
				}
				flag = MapLabels.TrySelectFloor(result, out message);
			}
			AddLocalLine(flag ? DroneLogKind.Info : DroneLogKind.Error, message);
		}

		private static void SwitchMonitor(string[] args)
		{
			StartOfRound instance = StartOfRound.Instance;
			ManualCameraRenderer val = (((Object)(object)instance != (Object)null) ? instance.mapScreen : null);
			if ((Object)(object)val == (Object)null || val.radarTargets == null || val.radarTargets.Count == 0)
			{
				AddLocalLine(DroneLogKind.Error, "The ship monitor isn't available.");
				return;
			}
			if (args.Length == 0)
			{
				MapLabels.ResetFloorSelection();
				val.SwitchRadarTargetForward(true);
				return;
			}
			string text = string.Join(" ", args);
			string[] names = val.radarTargets.Select((TransformAndName target) => target?.name).ToArray();
			int num = MonitorTargets.Find(names, text);
			if (num < 0)
			{
				AddLocalLine(DroneLogKind.Error, "There is no monitor target '" + text + "'.");
				return;
			}
			MapLabels.ResetFloorSelection();
			val.SwitchRadarTargetAndSync(num);
		}

		private static void FocusMonitor(string[] args)
		{
			if (args.Length != 0)
			{
				AddLocalLine(DroneLogKind.Error, "'focus' takes no arguments.");
				return;
			}
			DroneController currentDrone = CurrentDrone;
			if ((Object)(object)currentDrone == (Object)null)
			{
				AddLocalLine(DroneLogKind.Error, "There is no drone to focus.");
				return;
			}
			StartOfRound instance = StartOfRound.Instance;
			ManualCameraRenderer val = (((Object)(object)instance != (Object)null) ? instance.mapScreen : null);
			if ((Object)(object)val == (Object)null || val.radarTargets == null || val.radarTargets.Count == 0)
			{
				AddLocalLine(DroneLogKind.Error, "The ship monitor isn't available.");
				return;
			}
			string[] names = val.radarTargets.Select((TransformAndName target) => target?.name).ToArray();
			int num = MonitorTargets.FindDrone(names, currentDrone.DroneId);
			if (num < 0)
			{
				AddLocalLine(DroneLogKind.Error, $"Drone {currentDrone.DroneId} isn't on the ship monitor.");
				return;
			}
			MapLabels.ResetFloorSelection();
			val.SwitchRadarTargetAndSync(num);
			AddLocalLine(DroneLogKind.Info, $"Focused the ship monitor on Drone {currentDrone.DroneId}.");
		}

		private static bool IsMonitorShown(Terminal terminal)
		{
			if ((Object)(object)terminal.displayingPersistentImage != (Object)null && (Object)(object)terminal.terminalImage != (Object)null)
			{
				return ((Behaviour)terminal.terminalImage).enabled;
			}
			return false;
		}

		private static TerminalNode? GetMonitorNode(Terminal terminal)
		{
			if ((Object)(object)monitorNode != (Object)null)
			{
				return monitorNode;
			}
			TerminalKeyword[] array = (((Object)(object)terminal.terminalNodes != (Object)null) ? terminal.terminalNodes.allKeywords : null);
			if (array == null)
			{
				return null;
			}
			TerminalKeyword[] array2 = array;
			foreach (TerminalKeyword val in array2)
			{
				if ((Object)(object)val == (Object)null || !val.isVerb || val.word != "view" || val.compatibleNouns == null)
				{
					continue;
				}
				CompatibleNoun[] compatibleNouns = val.compatibleNouns;
				foreach (CompatibleNoun val2 in compatibleNouns)
				{
					if (val2 != null && (Object)(object)val2.noun != (Object)null && val2.noun.word == "monitor" && (Object)(object)val2.result != (Object)null)
					{
						monitorNode = val2.result;
						return monitorNode;
					}
				}
			}
			return null;
		}

		private static void HideMonitor(Terminal terminal)
		{
			Texture displayingPersistentImage = terminal.displayingPersistentImage;
			if (!((Object)(object)displayingPersistentImage == (Object)null))
			{
				terminal.displayingPersistentImage = null;
				if ((Object)(object)terminal.terminalImage != (Object)null && (Object)(object)terminal.terminalImage.texture == (Object)(object)displayingPersistentImage)
				{
					((Behaviour)terminal.terminalImage).enabled = false;
				}
			}
		}

		private static void ListDrones(string[] args)
		{
			if (args.Length != 0)
			{
				AddLocalLine(DroneLogKind.Error, "'drones' takes no arguments.");
				return;
			}
			DroneController[] array = (from drone in DroneRegistry.All
				where (Object)(object)drone != (Object)null
				orderby drone.DroneId
				select drone).ToArray();
			if (array.Length == 0)
			{
				AddLocalLine(DroneLogKind.Info, "No drones.");
				return;
			}
			DroneController[] array2 = array;
			foreach (DroneController droneController in array2)
			{
				AddLocalLine(DroneLogKind.Info, DroneScreenText.FormatDroneListLine(droneController.DroneId, droneController.ActiveBehaviorName, GetLockText(droneController), droneController.Health, droneController.MaxHealth, droneController.IsDisabled));
			}
		}

		private static void Select(string[] args)
		{
			if (!DroneScreenInput.TryParseDroneNumber(args, out var droneId))
			{
				AddLocalLine(DroneLogKind.Error, "Usage: select <n|d<n>>");
				return;
			}
			if (!DroneRegistry.TryGet(droneId, out DroneController drone) || (Object)(object)drone == (Object)null)
			{
				AddLocalLine(DroneLogKind.Error, $"There is no drone {droneId}.");
				return;
			}
			MapLabels.ResetFloorSelection();
			drone.RequestSelect();
		}

		private static void Release(string[] args)
		{
			if (args.Length != 0)
			{
				AddLocalLine(DroneLogKind.Error, "'release' takes no arguments.");
				return;
			}
			DroneController droneController = DroneRegistry.All.FirstOrDefault((DroneController drone) => (Object)(object)drone != (Object)null && drone.IsLockedByLocalPlayer) ?? CurrentDrone;
			if ((Object)(object)droneController == (Object)null)
			{
				AddLocalLine(DroneLogKind.Info, "You don't control a drone.");
			}
			else
			{
				droneController.RequestRelease();
			}
		}

		private static void Enter(Terminal terminal)
		{
			if ((Object)(object)screenNode == (Object)null)
			{
				screenNode = CreateScreenNode();
			}
			activeTerminal = terminal;
			terminal.currentNode = screenNode;
			terminal.textAdded = 0;
			if ((Object)(object)terminal.terminalImage != (Object)null && ((Behaviour)terminal.terminalImage).enabled)
			{
				((Behaviour)terminal.terminalImage).enabled = false;
				terminal.displayingPersistentImage = null;
			}
			Subscribe();
			Redraw(scroll: true);
		}

		private static void Exit(Terminal terminal)
		{
			Close("exit");
			TerminalNode val = (((Object)(object)terminal.terminalNodes != (Object)null && terminal.terminalNodes.specialNodes != null && terminal.terminalNodes.specialNodes.Count > 13) ? terminal.terminalNodes.specialNodes[13] : null);
			if ((Object)(object)val != (Object)null)
			{
				terminal.LoadNewNode(val);
			}
			else
			{
				Plugin.Logger.LogWarning((object)"Couldn't find the terminal's help node, showing an empty screen.");
				string text = (terminal.currentText = "\n\n\n");
				terminal.screenText.text = text;
				terminal.textAdded = 0;
			}
			ScrollTo(terminal, 1f);
		}

		private static void Close(string reason)
		{
			bool flag = activeTerminal != null;
			Terminal val = activeTerminal;
			DroneAudio.CloseTerminal();
			if ((Object)(object)val != (Object)null)
			{
				HideMonitor(val);
			}
			Unsubscribe();
			activeTerminal = null;
			pendingTerminal = null;
			redrawPending = false;
			redrawPendingScrolls = false;
		}

		private static void AddLocalLine(DroneLogKind kind, string text)
		{
			DroneLog.AddLocal(kind, text);
		}

		private static void AddLocalLines(DroneLogLine[] lines)
		{
			for (int i = 0; i < lines.Length; i++)
			{
				DroneLogLine droneLogLine = lines[i];
				AddLocalLine(droneLogLine.Kind, droneLogLine.Text);
			}
		}

		private static void RequestRedraw(bool scroll)
		{
			if (batchDepth > 0)
			{
				redrawPending = true;
				redrawPendingScrolls |= scroll;
			}
			else
			{
				Redraw(scroll);
			}
		}

		private static void Redraw(bool scroll)
		{
			redrawPending = false;
			redrawPendingScrolls = false;
			Terminal val = activeTerminal;
			if ((Object)(object)val == (Object)null)
			{
				if (val != null)
				{
					Close("the terminal is gone");
				}
			}
			else if (!((Object)(object)val.screenText == (Object)null))
			{
				DroneController currentDrone = CurrentDrone;
				string header = (((Object)(object)currentDrone == (Object)null) ? DroneScreenText.FormatHeader(0, "", "") : DroneScreenText.FormatHeader(currentDrone.DroneId, GetLockText(currentDrone), string.Format("{0} | {1}/{2} HP", currentDrone.IsDisabled ? "disabled" : DroneScreenText.FormatStatus(currentDrone.ActiveBehaviorName), currentDrone.Health, currentDrone.MaxHealth)));
				string text = DroneScreenText.Compose(header, DroneLog.GetLast(14), DroneRegistry.Count > 1, (Object)(object)currentDrone == (Object)null, IsMonitorShown(val));
				string typedText = DroneScreenText.GetTypedText(val.screenText.text, val.textAdded);
				string text2 = text + typedText;
				bool flag = (Object)(object)val.scrollBarVertical != (Object)null && val.scrollBarVertical.value > 0.05f;
				val.textAdded = typedText.Length;
				val.currentText = text2;
				val.screenText.text = text2;
				if (scroll || !flag)
				{
					ScrollToBottom(val);
				}
			}
		}

		private static string GetLockText(DroneController drone)
		{
			return DroneScreenText.FormatLock(drone.IsLocked, drone.IsLockedByLocalPlayer, drone.LockHolderName);
		}

		private static void Focus(Terminal terminal)
		{
			if ((Object)(object)terminal.screenText != (Object)null)
			{
				terminal.screenText.ActivateInputField();
				((Selectable)terminal.screenText).Select();
			}
		}

		private static void ScrollTo(Terminal terminal, float value)
		{
			if ((Object)(object)terminal.scrollBarVertical == (Object)null)
			{
				return;
			}
			terminal.scrollBarVertical.value = value;
			FieldRef<Terminal, Coroutine> val = GetScrollCoroutineRef();
			if (val != null && ((Behaviour)terminal).isActiveAndEnabled)
			{
				ref Coroutine reference = ref val.Invoke(terminal);
				if (reference != null)
				{
					((MonoBehaviour)terminal).StopCoroutine(reference);
				}
				reference = ((MonoBehaviour)terminal).StartCoroutine(KeepScrolled(terminal, value));
			}
		}

		private static IEnumerator KeepScrolled(Terminal terminal, float value)
		{
			for (int i = 0; i < 5; i++)
			{
				yield return null;
				if ((Object)(object)terminal == (Object)null || (Object)(object)terminal.scrollBarVertical == (Object)null)
				{
					break;
				}
				terminal.scrollBarVertical.value = value;
			}
		}

		private static FieldRef<Terminal, Coroutine?>? GetScrollCoroutineRef()
		{
			if (scrollCoroutineRef == null && !scrollCoroutineRefFailed)
			{
				try
				{
					scrollCoroutineRef = AccessTools.FieldRefAccess<Terminal, Coroutine>("forceScrollbarCoroutine");
				}
				catch (Exception ex)
				{
					scrollCoroutineRefFailed = true;
					Plugin.Logger.LogWarning((object)("Couldn't access the terminal's scroll coroutine, the drone screen may not stay scrolled down: " + ex.Message));
				}
			}
			return scrollCoroutineRef;
		}

		private static TerminalNode CreateScreenNode()
		{
			TerminalNode val = ScriptableObject.CreateInstance<TerminalNode>();
			((Object)val).name = "LCDroneScreen";
			((Object)val).hideFlags = (HideFlags)61;
			val.displayText = "";
			val.terminalEvent = "";
			val.creatureName = "";
			val.clearPreviousText = true;
			val.maxCharactersToType = 64;
			val.acceptAnything = false;
			val.overrideOptions = false;
			val.terminalOptions = Array.Empty<CompatibleNoun>();
			return val;
		}

		private static string GetLocalPlayerName()
		{
			GameNetworkManager instance = GameNetworkManager.Instance;
			PlayerControllerB val = (((Object)(object)instance != (Object)null) ? instance.localPlayerController : null);
			if (!((Object)(object)val != (Object)null) || string.IsNullOrWhiteSpace(val.playerUsername))
			{
				return "You";
			}
			return val.playerUsername;
		}

		private static void Subscribe()
		{
			Unsubscribe();
			DroneLog.OnLine += OnLogLine;
			DroneLog.LinesChanged += OnLinesChanged;
			DroneController.StateChanged += OnDroneStateChanged;
			DroneRegistry.Registered += OnDroneCameOrWent;
			DroneRegistry.Unregistered += OnDroneCameOrWent;
			DroneSelection.Changed += OnLinesChanged;
		}

		private static void Unsubscribe()
		{
			DroneLog.OnLine -= OnLogLine;
			DroneLog.LinesChanged -= OnLinesChanged;
			DroneController.StateChanged -= OnDroneStateChanged;
			DroneRegistry.Registered -= OnDroneCameOrWent;
			DroneRegistry.Unregistered -= OnDroneCameOrWent;
			DroneSelection.Changed -= OnLinesChanged;
		}

		private static void OnLogLine(int droneId, DroneLogKind kind, string text)
		{
			RequestRedraw(scroll: false);
		}

		private static void OnLinesChanged()
		{
			RequestRedraw(scroll: false);
		}

		private static void OnDroneCameOrWent(int droneId)
		{
			RequestRedraw(scroll: false);
		}

		private static void OnDroneStateChanged(DroneController drone)
		{
			if ((Object)(object)drone == (Object)(object)CurrentDrone)
			{
				RequestRedraw(scroll: false);
			}
		}
	}
	internal static class DroneTerminalCommand
	{
		internal const string Keyword = "drone";

		private const string Namespace = "lcdrone";

		private const string OtherHelpAnchor = "To scan for the number of items left on the current planet.\n\n";

		private const string OtherHelpText = ">DRONE\nOpen the drone control screen.\n\n";

		internal static TerminalNode? ResultNode { get; private set; }

		internal static void Register()
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Expected O, but got Unknown
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			DawnTerminalCommandInfo val = DawnLib.DefineTerminalCommand(NamespacedKey<DawnTerminalCommandInfo>.From("lcdrone", "drone_screen"), new TerminalCommandBasicInformation("LCDroneScreenCommand", "LCDrone", "Opens the drone control screen.", (ClearText)1), (Action<TerminalCommandInfoBuilder>)delegate(TerminalCommandInfoBuilder builder)
			{
				builder.SetKeywords(new List<string>(1) { "drone" });
				builder.DefineEventDrivenCommand((Action<EventDrivenCommandBuilder>)delegate(EventDrivenCommandBuilder eventBuilder)
				{
					eventBuilder.SetResultNodeDisplayText((Func<string>)(() => "The drone screen couldn't be opened, see the BepInEx log.\n\n")).SetOnTerminalEvent((Action<Terminal, TerminalNode>)DroneScreen.OnDroneCommand);
				});
			});
			DawnEventDrivenCommandInfo eventDrivenCommandInfo = val.EventDrivenCommandInfo;
			ResultNode = ((eventDrivenCommandInfo != null) ? eventDrivenCommandInfo.ResultNode : null);
			if ((Object)(object)ResultNode == (Object)null)
			{
				Plugin.Logger.LogWarning((object)"DawnLib didn't create a result node for the drone command; the drone screen opens after whatever node it loads.");
			}
			new TerminalTextModifier("To scan for the number of items left on the current planet.\n\n", (IProvider<string>)(object)new SimpleProvider<string>(">DRONE\nOpen the drone control screen.\n\n")).SetNodeFromKeyword("other").SetInsertStyle((MatchInsert)2).SetIndexStyle((MatchIndex)0);
		}
	}
}
namespace LCDrone.Patches
{
	[HarmonyPatch(typeof(EnemyAICollisionDetect), "OnTriggerStay")]
	internal static class BarberDroneCollisionPatch
	{
		private static readonly Dictionary<ClaySurgeonAI, float> lastSnipReportTimes = new Dictionary<ClaySurgeonAI, float>();

		internal static void Forget(ClaySurgeonAI barber)
		{
			lastSnipReportTimes.Remove(barber);
		}

		[HarmonyPostfix]
		private static void OnTriggerStayPostfix(EnemyAICollisionDetect __instance, Collider other)
		{
			EnemyAI mainScript = __instance.mainScript;
			ClaySurgeonAI val = (ClaySurgeonAI)(object)((mainScript is ClaySurgeonAI) ? mainScript : null);
			if (val == null || !((NetworkBehaviour)val).IsOwner || ((EnemyAI)val).isEnemyDead || ((EnemyAI)val).stunNormalizedTimer > 0f || (Object)(object)other == (Object)null)
			{
				return;
			}
			DroneController componentInParent = ((Component)other).GetComponentInParent<DroneController>();
			if (!((Object)(object)componentInParent == (Object)null) && ((Behaviour)componentInParent).isActiveAndEnabled && !componentInParent.IsDisabled && componentInParent.IsInside && !componentInParent.OnShip && componentInParent.BarberAttacksDrone)
			{
				float realtimeSinceStartup = Time.realtimeSinceStartup;
				if ((!lastSnipReportTimes.TryGetValue(val, out var value) || !(realtimeSinceStartup - value < 1f)) && !(Traverse.Create((object)val).Field<float>("timeSinceSnip").Value < 1f))
				{
					lastSnipReportTimes[val] = realtimeSinceStartup;
					Traverse.Create((object)val).Field<float>("timeSinceSnip").Value = 0f;
					val.KillPlayerServerRpc();
					componentInParent.ReportBarberCollision(val);
				}
			}
		}
	}
	[HarmonyPatch(typeof(ClaySurgeonAI), "OnDestroy")]
	internal static class BarberDroneCleanupPatch
	{
		[HarmonyPostfix]
		private static void OnDestroyPostfix(ClaySurgeonAI __instance)
		{
			BarberDroneCollisionPatch.Forget(__instance);
		}
	}
	[HarmonyPatch(typeof(RedLocustBees), "DoAIInterval")]
	internal static class CircuitBeeDroneHivePatch
	{
		private static readonly HashSet<RedLocustBees> followingDroneHive = new HashSet<RedLocustBees>();

		[HarmonyPrefix]
		private static bool DoAIIntervalPrefix(RedLocustBees __instance)
		{
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			//IL_018b: Unknown result type (might be due to invalid IL or missing references)
			//IL_019d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			if (!((NetworkBehaviour)__instance).IsOwner || ((EnemyAI)__instance).isEnemyDead)
			{
				return true;
			}
			if (((EnemyAI)__instance).daytimeEnemyLeaving || ((EnemyAI)__instance).stunNormalizedTimer > 0f || ((EnemyAI)__instance).inSpecialAnimation || StartOfRound.Instance.allPlayersDead)
			{
				StopFollowing(__instance, "vanilla movement state took over");
				return true;
			}
			if ((Object)(object)__instance.hive == (Object)null || !CargoAnchors.IsDroneCargoAnchor(__instance.hive.parentObject))
			{
				StopFollowing(__instance, "hive is no longer attached to a drone");
				return true;
			}
			Transform parentObject = __instance.hive.parentObject;
			DroneController droneController = ((parentObject != null) ? ((Component)parentObject).GetComponentInParent<DroneController>() : null);
			if ((Object)(object)droneController == (Object)null || !((Behaviour)droneController).isActiveAndEnabled || droneController.IsDisabled || droneController.IsInside || droneController.OnShip || !droneController.CircuitBeesAttackDrone)
			{
				StopFollowing(__instance, "hive carrier is not an eligible active drone");
				return true;
			}
			followingDroneHive.Add(__instance);
			if (((EnemyAI)__instance).currentBehaviourStateIndex != 2)
			{
				((EnemyAI)__instance).SwitchToBehaviourState(2);
			}
			((EnemyAI)__instance).targetPlayer = null;
			((EnemyAI)__instance).movingTowardsTargetPlayer = false;
			__instance.lastKnownHivePosition = ((Component)__instance.hive).transform.position + Vector3.up * 0.5f;
			if (__instance.searchForHive.inProgress)
			{
				((EnemyAI)__instance).StopSearch(__instance.searchForHive, true);
			}
			((EnemyAI)__instance).agent.acceleration = 16f;
			((EnemyAI)__instance).SetDestinationToPosition(__instance.lastKnownHivePosition, false);
			if (((Behaviour)((EnemyAI)__instance).agent).enabled && ((EnemyAI)__instance).agent.isOnNavMesh)
			{
				Vector3 destination = ((EnemyAI)__instance).destination;
				Traverse val = Traverse.Create((object)__instance).Field("prevDestination");
				if (destination != val.GetValue<Vector3>() && ((EnemyAI)__instance).agent.SetDestination(destination))
				{
					val.SetValue((object)destination);
				}
			}
			((EnemyAI)__instance).SyncPositionToClients();
			return false;
		}

		private static void StopFollowing(RedLocustBees bees, string reason)
		{
			followingDroneHive.Remove(bees);
		}

		internal static void Forget(RedLocustBees bees)
		{
			followingDroneHive.Remove(bees);
		}
	}
	[HarmonyPatch(typeof(EnemyAI), "OnDestroy")]
	internal static class CircuitBeeDroneHiveCleanupPatch
	{
		[HarmonyPostfix]
		private static void OnDestroyPostfix(EnemyAI __instance)
		{
			RedLocustBees val = (RedLocustBees)(object)((__instance is RedLocustBees) ? __instance : null);
			if (val != null)
			{
				CircuitBeeDroneHivePatch.Forget(val);
			}
		}
	}
	[HarmonyPatch(typeof(EnemyAICollisionDetect), "OnTriggerStay")]
	internal static class CircuitBeeDroneCollisionPatch
	{
		[HarmonyPostfix]
		private static void OnTriggerStayPostfix(EnemyAICollisionDetect __instance, Collider other)
		{
			EnemyAI mainScript = __instance.mainScript;
			RedLocustBees val = (RedLocustBees)(object)((mainScript is RedLocustBees) ? mainScript : null);
			if (val != null && ((NetworkBehaviour)val).IsOwner && !((EnemyAI)val).isEnemyDead && !(((EnemyAI)val).stunNormalizedTimer > 0f) && !((Object)(object)other == (Object)null) && !other.isTrigger)
			{
				DroneController componentInParent = ((Component)other).GetComponentInParent<DroneController>();
				if ((Object)(object)componentInParent != (Object)null)
				{
					componentInParent.ReportCircuitBeeCollision(val);
				}
			}
		}
	}
	[HarmonyPatch]
	internal static class DroneDamagePatches
	{
		[HarmonyPatch(typeof(ShotgunItem), "ShootGun")]
		[HarmonyPostfix]
		private static void ShotgunShootGunPostfix(ShotgunItem __instance, Vector3 shotgunPosition, Vector3 shotgunForward)
		{
			//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_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: 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_0044: 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_004f: 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_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: 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_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: 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)
			PlayerControllerB playerHeldBy = ((GrabbableObject)__instance).playerHeldBy;
			if ((Object)(object)playerHeldBy == (Object)null || !((NetworkBehaviour)playerHeldBy).IsOwner)
			{
				return;
			}
			Vector3 val = shotgunForward;
			if (((Vector3)(ref val)).sqrMagnitude < 0.01f)
			{
				return;
			}
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			val = Vector3.Normalize(val);
			Ray val2 = default(Ray);
			((Ray)(ref val2))..ctor(shotgunPosition - val * 10f, val);
			RaycastHit[] array = Physics.SphereCastAll(val2, 5f, 15f, 524288, (QueryTriggerInteraction)2);
			HashSet<ulong> hashSet = new HashSet<ulong>();
			RaycastHit[] array2 = array;
			for (int i = 0; i < array2.Length; i++)
			{
				RaycastHit val3 = array2[i];
				if (!((Object)(object)((RaycastHit)(ref val3)).collider == (Object)null) && !(((RaycastHit)(ref val3)).distance <= 0f) && !(((RaycastHit)(ref val3)).point == ((Ray)(ref val2)).origin))
				{
					DroneController componentInParent = ((Component)((RaycastHit)(ref val3)).collider).GetComponentInParent<DroneController>();
					if (!((Object)(object)componentInParent == (Object)null) && !componentInParent.IsDisabled && componentInParent.HasShotLineOfSight(shotgunPosition, ((RaycastHit)(ref val3)).point, instance.collidersAndRoomMaskAndDefault) && hashSet.Add(((NetworkBehaviour)componentInParent).NetworkObjectId))
					{
						float num = Vector3.Distance(shotgunPosition, ((RaycastHit)(ref val3)).point);
						int force = ((num < 3.7f) ? 5 : ((num < 6f) ? 3 : 2));
						componentInParent.ReportShotgunHit(shotgunPosition, val, force);
					}
				}
			}
		}

		[HarmonyPatch(typeof(Landmine), "SpawnExplosion")]
		[HarmonyPostfix]
		private static void LandmineSpawnExplosionPostfix(Vector3 explosionPosition, float damageRange, bool goThroughCar)
		{
			//IL_004d: 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_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: 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)
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton == (Object)null || !singleton.IsServer || (Object)(object)StartOfRound.Instance == (Object)null)
			{
				return;
			}
			RaycastHit val = default(RaycastHit);
			foreach (DroneController item in DroneRegistry.All)
			{
				if (!((Object)(object)item == (Object)null) && !item.IsDisabled)
				{
					float num = Vector3.Distance(explosionPosition, ((Component)item).transform.position);
					if (!(num >= damageRange) && !(num >= 4.5f) && (!Physics.Linecast(explosionPosition, ((Component)item).transform.position + Vector3.up * 0.3f, ref val, 1073742080, (QueryTriggerInteraction)1) || ((goThroughCar || ((Component)((RaycastHit)(ref val)).collider).gameObject.layer != 30) && !(num > 4f))))
					{
						item.DamageFromExplosion(6);
					}
				}
			}
		}
	}
	[HarmonyPatch(typeof(Terminal))]
	internal static class DroneShopPatches
	{
		private sealed class PendingPurchase
		{
			internal readonly List<DroneController> Drones;

			internal readonly int ExpectedCredits;

			internal readonly int OrderedCountBefore;

			internal readonly int OtherItemCount;

			internal PendingPurchase(List<DroneController> drones, int expectedCredits, int orderedCountBefore, int otherItemCount)
			{
				Drones = drones;
				ExpectedCredits = expectedCredits;
				OrderedCountBefore = orderedCountBefore;
				OtherItemCount = otherItemCount;
			}
		}

		private const string DroneShopItemName = "LCDrone";

		private const int RejectedPurchaseSentinel = -100;

		internal static bool HooksInstalled { get; private set; }

		internal static void SetHooksInstalled(bool installed)
		{
			HooksInstalled = installed;
		}

		[HarmonyPrefix]
		[HarmonyPatch("LoadNewNodeIfAffordable")]
		private static bool LoadNewNodeIfAffordablePrefix(Terminal __instance, TerminalNode node)
		{
			if ((Object)(object)node == (Object)null || node.isConfirmationNode || !TryGetDroneShopIndex(__instance, out var index) || node.buyItemIndex != index)
			{
				return true;
			}
			int num = Math.Max(1, __instance.playerDefinedAmount);
			if (num > 4 - DroneRegistry.Count)
			{
				LoadResult(__instance, $"You can have at most {4} drones at once.\n\n");
				return false;
			}
			if (!((NetworkBehaviour)__instance).IsServer)
			{
				return true;
			}
			int purchaseCost = GetPurchaseCost(__instance, index, num);
			if (purchaseCost == int.MaxValue)
			{
				LoadResult(__instance, "The drone shop price is invalid.\n\n");
				return false;
			}
			return CompletePurchase(__instance, node, num, purchaseCost);
		}

		[HarmonyPrefix]
		[HarmonyPatch("BuyItemsServerRpc")]
		private static bool BuyItemsServerRpcPrefix(Terminal __instance, ref int[] boughtItems, int newGroupCredits, ref int numItemsInShip, ref PendingPurchase? __state)
		{
			if (!((NetworkBehaviour)__instance).IsServer || boughtItems == null || boughtItems.Length == 0 || !TryGetDroneShopIndex(__instance, out var droneIndex))
			{
				return true;
			}
			int num = boughtItems.Count((int index) => index == droneIndex);
			if (num == 0)
			{
				return true;
			}
			string error = null;
			int numberOfItemsInDropship = __instance.numberOfItemsInDropship;
			int num2 = boughtItems.Length - num;
			if (boughtItems.Length > 12 || numItemsInShip < boughtItems.Length || numItemsInShip > 12 || num > 4 - DroneRegistry.Count || numberOfItemsInDropship < 0 || numberOfItemsInDropship > 12 || numberOfItemsInDropship + num2 > 12 || newGroupCredits < 0 || __instance.groupCredits < 0 || !TryGetMinimumOrderSpend(__instance, boughtItems, out var minimumSpend) || newGroupCredits > __instance.groupCredits - minimumSpend || !TrySpawnPurchasedDrones(num, out List<DroneController> spawned, out error))
			{
				Plugin.Logger.LogWarning((object)("Drone shop purchase was rejected: " + (error ?? "insufficient or invalid credits")));
				int num3 = Math.Max(0, Math.Min(12, __instance.numberOfItemsInDropship));
				__instance.SyncGroupCreditsClientRpc(__instance.groupCredits, -100 - num3);
				return false;
			}
			int[] array = boughtItems.Where((int index) => index != droneIndex).ToArray();
			numItemsInShip = numberOfItemsInDropship + array.Length;
			if (array.Length == 0)
			{
				__instance.groupCredits = newGroupCredits;
				__instance.SyncGroupCreditsClientRpc(newGroupCredits, numItemsInShip);
				DroneFleetSave.CommitPurchase(spawned);
				return false;
			}
			boughtItems = array;
			__state = new PendingPurchase(spawned, newGroupCredits, __instance.orderedItemsFromTerminal.Count, array.Length);
			return true;
		}

		[HarmonyPostfix]
		[HarmonyPatch("BuyItemsServerRpc")]
		private static void BuyItemsServerRpcPostfix(Terminal __instance, PendingPurchase? __state)
		{
			if (__state == null)
			{
				return;
			}
			if (__instance.groupCredits == __state.ExpectedCredits && __instance.orderedItemsFromTerminal.Count >= __state.OrderedCountBefore + __state.OtherItemCount)
			{
				DroneFleetSave.CommitPurchase(__state.Drones);
				return;
			}
			foreach (DroneController drone in __state.Drones)
			{
				drone.DespawnFromServer();
			}
		}

		[HarmonyFinalizer]
		[HarmonyPatch("BuyItemsServerRpc")]
		private static Exception? BuyItemsServerRpcFinalizer(Exception? __exception, PendingPurchase? __state)
		{
			if (__exception != null && __state != null)
			{
				foreach (DroneController drone in __state.Drones)
				{
					drone.DespawnFromServer();
				}
			}
			return __exception;
		}

		[HarmonyPostfix]
		[HarmonyPatch("SyncGroupCreditsClientRpc")]
		private static void SyncGroupCreditsClientRpcPostfix(Terminal __instance, int numItemsInShip)
		{
			if (numItemsInShip <= -100 && numItemsInShip >= -112)
			{
				__instance.numberOfItemsInDropship = -100 - numItemsInShip;
				LoadResult(__instance, "The purchase was rejected by the host. No credits were spent. Check the drone and 12-item limits, then try again.\n\n");
			}
		}

		private static bool CompletePurchase(Terminal terminal, TerminalNode receiptNode, int amount, int totalCost)
		{
			if (terminal.groupCredits < totalCost)
			{
				return true;
			}
			if (amount > 4 - DroneRegistry.Count)
			{
				LoadResult(terminal, $"You can have at most {4} drones at once.\n\n");
				return false;
			}
			if (!TrySpawnPurchasedDrones(amount, out List<DroneController> spawned, out string error))
			{
				LoadResult(terminal, "The drone could not be purchased: " + error + "\n\n");
				return false;
			}
			terminal.groupCredits -= totalCost;
			terminal.SyncGroupCreditsClientRpc(terminal.groupCredits, terminal.numberOfItemsInDropship);
			DroneFleetSave.CommitPurchase(spawned);
			terminal.LoadNewNode(receiptNode);
			return false;
		}

		private static bool TrySpawnPurchasedDrones(int amount, out List<DroneController> spawned, out string? error)
		{
			spawned = new List<DroneController>();
			if (!DroneFleetSave.Ready)
			{
				error = "The saved fleet is still loading or cannot be read. Check the host log.";
				return false;
			}
			for (int i = 0; i < amount; i++)
			{
				DroneController droneController = DroneSpawner.SpawnOnShip(out error, null, null, owned: true);
				if ((Object)(object)droneController == (Object)null)
				{
					foreach (DroneController item in spawned)
					{
						item.DespawnFromServer();
					}
					return false;
				}
				spawned.Add(droneController);
			}
			error = null;
			return true;
		}

		private static bool TryGetPriceInputs(Terminal terminal, int itemIndex, out int creditsWorth, out int salePercent)
		{
			creditsWorth = 0;
			salePercent = 0;
			if (terminal.buyableItemsList == null || itemIndex < 0 || itemIndex >= terminal.buyableItemsList.Length || terminal.itemSalesPercentages == null || itemIndex >= terminal.itemSalesPercentages.Length || (Object)(object)terminal.buyableItemsList[itemIndex] == (Object)null)
			{
				return false;
			}
			creditsWorth = terminal.buyableItemsList[itemIndex].creditsWorth;
			salePercent = terminal.itemSalesPercentages[itemIndex];
			return true;
		}

		private static int GetPurchaseCost(Terminal terminal, int itemIndex, int amount)
		{
			if (!TryGetPriceInputs(terminal, itemIndex, out var creditsWorth, out var salePercent))
			{
				return int.MaxValue;
			}
			int num = PurchasePricing.BatchCost(creditsWorth, salePercent, amount);
			if (num >= 0)
			{
				return num;
			}
			return int.MaxValue;
		}

		private static bool TryGetMinimumOrderSpend(Terminal terminal, int[] items, out long minimumSpend)
		{
			minimumSpend = 0L;
			foreach (IGrouping<int, int> item in from index in items
				group index by index)
			{
				if (!TryGetPriceInputs(terminal, item.Key, out var creditsWorth, out var salePercent))
				{
					return false;
				}
				long num = PurchasePricing.MinimumSpend(creditsWorth, salePercent, item.Count());
				if (num < 0)
				{
					return false;
				}
				minimumSpend += num;
			}
			return true;
		}

		private static bool TryGetDroneShopIndex(Terminal terminal, out int index)
		{
			index = -1;
			if (terminal.buyableItemsList == null)
			{
				return false;
			}
			for (int i = 0; i < terminal.buyableItemsList.Length; i++)
			{
				Item val = terminal.buyableItemsList[i];
				if ((Object)(object)val != (Object)null && string.Equals(val.itemName, "LCDrone", StringComparison.OrdinalIgnoreCase))
				{
					index = i;
					return true;
				}
			}
			return false;
		}

		private static void LoadResult(Terminal terminal, string text)
		{
			TerminalNode val = ScriptableObject.CreateInstance<TerminalNode>();
			val.displayText = text;
			val.clearPreviousText = true;
			terminal.LoadNewNode(val);
		}
	}
	public sealed class LCDroneShopPurchasePredicate : DuskTerminalPredicate
	{
		public override void Register(NamespacedKey namespacedKey)
		{
		}

		public override TerminalPurchaseResult CanPurchase()
		{
			if (!DroneShopPatches.HooksInstalled)
			{
				return (TerminalPurchaseResult)(object)TerminalPurchaseResult.Hidden();
			}
			return (TerminalPurchaseResult)(object)TerminalPurchaseResult.Success();
		}
	}
	[HarmonyPatch(typeof(SpringManAI), "Update")]
	internal static class CoilHeadDroneVisionPatch
	{
		private const float VisionRange = 60f;

		private const float VisionHalfAngle = 35f;

		private const float StopInterval = 0.3f;

		private static readonly HashSet<SpringManAI> stoppedForDroneVision = new HashSet<SpringManAI>();

		[HarmonyPrefix]
		private static void UpdatePrefix(SpringManAI __instance)
		{
			if (!((NetworkBehaviour)__instance).IsOwner || ((EnemyAI)__instance).isEnemyDead || ((EnemyAI)__instance).currentBehaviourStateIndex != 1)
			{
				return;
			}
			bool flag = IsVisible(__instance);
			UpdateVisibilityLog(__instance, flag);
			if (flag)
			{
				Traverse val = Traverse.Create((object)__instance);
				Traverse<bool> val2 = val.Field<bool>("stoppingMovement");
				if (!val2.Value)
				{
					__instance.SetAnimationStopServerRpc();
					val2.Value = true;
				}
				Traverse<float> val3 = val.Field<float>("stopAndGoMinimumInterval");
				if (val3.Value < 0.3f)
				{
					val3.Value = 0.3f;
				}
			}
		}

		[HarmonyPostfix]
		private static void UpdatePostfix(SpringManAI __instance)
		{
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			if (((EnemyAI)__instance).isEnemyDead)
			{
				Forget(__instance);
			}
			else
			{
				if (((EnemyAI)__instance).currentBehaviourStateIndex != 0)
				{
					return;
				}
				bool flag = IsVisible(__instance);
				if (((NetworkBehaviour)__instance).IsOwner)
				{
					UpdateVisibilityLog(__instance, flag);
				}
				if (!flag)
				{
					return;
				}
				if (((NetworkBehaviour)__instance).IsOwner && (Object)(object)((EnemyAI)__instance).agent != (Object)null)
				{
					stoppedForDroneVision.Add(__instance);
					if (((EnemyAI)__instance).agent.isOnOffMeshLink)
					{
						Traverse.Create((object)__instance).Method("StopOffMeshLinkMovement", Array.Empty<object>()).GetValue();
					}
					((EnemyAI)__instance).agent.speed = 0f;
					if (((Behaviour)((EnemyAI)__instance).agent).enabled && ((EnemyAI)__instance).agent.isOnNavMesh)
					{
						((EnemyAI)__instance).agent.velocity = Vector3.zero;
					}
				}
				if ((Object)(object)((EnemyAI)__instance).creatureAnimator != (Object)null)
				{
					((EnemyAI)__instance).creatureAnimator.SetFloat("walkSpeed", 0f);
				}
			}
		}

		private static bool IsVisible(SpringManAI coil)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: 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_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_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			if (!DroneVision.IsVisibleFromAnyDrone(((Component)coil).transform.position + Vector3.up * 0.6f, 60f, 35f, DroneVisionTarget.CoilHead))
			{
				return DroneVision.IsVisibleFromAnyDrone(((Component)coil).transform.position + Vector3.up * 3f, 60f, 35f, DroneVisionTarget.CoilHead);
			}
			return true;
		}

		private static void UpdateVisibilityLog(SpringManAI coil, bool visible)
		{
			if (visible)
			{
				stoppedForDroneVision.Add(coil);
			}
			else
			{
				stoppedForDroneVision.Remove(coil);
			}
		}

		internal static void Forget(SpringManAI coil)
		{
			stoppedForDroneVision.Remove(coil);
		}
	}
	[HarmonyPatch(typeof(EnemyAI), "OnDestroy")]
	internal static class CoilHeadDroneVisionCleanupPatch
	{
		[HarmonyPostfix]
		private static void OnDestroyPostfix(EnemyAI __instance)
		{
			SpringManAI val = (SpringManAI)(object)((__instance is SpringManAI) ? __instance : null);
			if (val != null)
			{
				CoilHeadDroneVisionPatch.Forget(val);
			}
		}
	}
	[HarmonyPatch(typeof(FlowermanAI), "Update")]
	internal static class BrackenDroneVisionPatch
	{
		private const float VisionRange = 30f;

		private const float VisionHalfAngle = 35f;

		private static readonly HashSet<FlowermanAI> evadingDroneVision = new HashSet<FlowermanAI>();

		[HarmonyPrefix]
		private static void UpdatePrefix(FlowermanAI __instance)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			if (((NetworkBehaviour)__instance).IsOwner && !((EnemyAI)__instance).isEnemyDead && ((EnemyAI)__instance).currentBehaviourStateIndex == 0)
			{
				if (!DroneVision.IsVisibleFromAnyDrone(((Component)__instance).transform.position + Vector3.up * 0.5f, 30f, 35f, DroneVisionTarget.Bracken))
				{
					evadingDroneVision.Remove(__instance);
					return;
				}
				__instance.evadeStealthTimer = 0f;
				evadingDroneVision.Add(__instance);
				((EnemyAI)__instance).SwitchToBehaviourState(1);
			}
		}

		internal static void Forget(FlowermanAI bracken)
		{
			evadingDroneVision.Remove(bracken);
		}
	}
	[HarmonyPatch(typeof(EnemyAI), "OnDestroy")]
	internal static class BrackenDroneVisionCleanupPatch
	{
		[HarmonyPostfix]
		private static void OnDestroyPostfix(EnemyAI __instance)
		{
			FlowermanAI val = (FlowermanAI)(object)((__instance is FlowermanAI) ? __instance : null);
			if (val != null)
			{
				BrackenDroneVisionPatch.Forget(val);
			}
		}
	}
	internal static class EnemyDroneDebugLog
	{
		[Conditional("DEBUG")]
		internal static void Log(EnemyAI enemy, DroneController drone, string message)
		{
		}

		[Conditional("DEBUG")]
		internal static void Log(EnemyAI enemy, string message)
		{
		}
	}
	[HarmonyPatch(typeof(PumaAI), "DetectNoise", new Type[]
	{
		typeof(Vector3),
		typeof(float),
		typeof(int),
		typeof(int)
	})]
	internal static class FeioparDroneNoisePatch
	{
		[HarmonyPrefix]
		private static bool DetectNoisePrefix(PumaAI __instance)
		{
			bool result = ((NetworkBehaviour)__instance).IsOwner || !DroneNoise.IsPlaying;
			_ = DroneNoise.IsPlaying;
			return result;
		}
	}
	[HarmonyPatch(typeof(PumaAI), "Update")]
	internal static class FeioparDroneVisionPatch
	{
		private const float VisionRange = 30f;

		private const float VisionHalfAngle = 35f;

		private static readonly HashSet<PumaAI> startledWhileVisible = new HashSet<PumaAI>();

		[HarmonyPostfix]
		private static void UpdatePostfix(PumaAI __instance)
		{
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: 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_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			if (!((NetworkBehaviour)__instance).IsSpawned || ((EnemyAI)__instance).isEnemyDead)
			{
				Forget(__instance);
				return;
			}
			if (((EnemyAI)__instance).currentBehaviourStateIndex != 1)
			{
				startledWhileVisible.Remove(__instance);
				return;
			}
			Vector3 targetPosition = (((Object)(object)((EnemyAI)__instance).eye != (Object)null) ? ((EnemyAI)__instance).eye.position : (((Component)__instance).transform.position + Vector3.up * 1.5f));
			if (!DroneVision.TryGetVisibleDronePosition(targetPosition, 30f, 35f, DroneVisionTarget.Feiopar, out var dronePosition))
			{
				startledWhileVisible.Remove(__instance);
			}
			else if (startledWhileVisible.Add(__instance) && ((NetworkBehaviour)__instance).IsOwner)
			{
				__instance.PingAttention(15, 0.6f, dronePosition + Vector3.up * 0.7f, true, true);
			}
			else if (!((NetworkBehaviour)__instance).IsOwner)
			{
				startledWhileVisible.Remove(__instance);
			}
		}

		internal static void Forget(PumaAI feiopar)
		{
			startledWhileVisible.Remove(feiopar);
		}
	}
	[HarmonyPatch(typeof(EnemyAI), "OnDestroy")]
	internal static class FeioparDroneVisionCleanupPatch
	{
		[HarmonyPostfix]
		private static void OnDestroyPostfix(EnemyAI __instance)
		{
			PumaAI val = (PumaAI)(object)((__instance is PumaAI) ? __instance : null);
			if (val != null)
			{
				FeioparDroneVisionPatch.Forget(val);
			}
		}
	}
	[HarmonyPatch(typeof(ForestGiantAI), "DoAIInterval")]
	internal static class ForestGiantDroneAwarenessPatches
	{
		private const int DroneNoticeRange = 45;

		private static readonly HashSet<ForestGiantAI> giantsThatNoticedDrone = new HashSet<ForestGiantAI>();

		private static readonly HashSet<ForestGiantAI> giantsLoggedInitialCheck = new HashSet<ForestGiantAI>();

		private static readonly HashSet<ForestGiantAI> giantsLoggedNonIdleState = new HashSet<ForestGiantAI>();

		private static readonly HashSet<ForestGiantAI> giantsWithVisibleDrone = new HashSet<ForestGiantAI>();

		private static readonly HashSet<ForestGiantAI> giantsLoggedExistingInvestigation = new HashSet<ForestGiantAI>();

		private static readonly HashSet<ForestGiantAI> giantsLoggedNoVisibleDrone = new HashSet<ForestGiantAI>();

		[HarmonyPostfix]
		private static void DoAIIntervalPostfix(ForestGiantAI __instance)
		{
			//IL_00bd: 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_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			if (!((NetworkBehaviour)__instance).IsOwner || ((EnemyAI)__instance).isEnemyDead)
			{
				return;
			}
			giantsLoggedInitialCheck.Add(__instance);
			if (((EnemyAI)__instance).currentBehaviourStateIndex != 0)
			{
				giantsLoggedNonIdleState.Add(__instance);
				giantsWithVisibleDrone.Remove(__instance);
				giantsThatNoticedDrone.Remove(__instance);
				giantsLoggedExistingInvestigation.Remove(__instance);
				return;
			}
			giantsLoggedNonIdleState.Remove(__instance);
			DroneController droneController = null;
			int num = 0;
			float num2 = float.PositiveInfinity;
			foreach (DroneController item in DroneRegistry.All)
			{
				if (!((Object)(object)item == (Object)null) && ((Behaviour)item).isActiveAndEnabled && !item.IsDisabled && !item.OnShip && item.ForestGiantInvestigatesDrone)
				{
					num++;
					Vector3 val = ((Component)item).transform.position - ((Component)__instance).transform.position;
					float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
					if (sqrMagnitude < num2 && ((EnemyAI)__instance).CheckLineOfSightForPosition(((Component)item).transform.position, 45f, 45, -1f, (Transform)null))
					{
						droneController = item;
						num2 = sqrMagnitude;
					}
				}
			}
			if ((Object)(object)droneController == (Object)null)
			{
				if (!giantsWithVisibleDrone.Remove(__instance))
				{
					giantsLoggedNoVisibleDrone.Add(__instance);
				}
				giantsThatNoticedDrone.Remove(__instance);
				giantsLoggedExistingInvestigation.Remove(__instance);
				return;
			}
			if (giantsWithVisibleDrone.Add(__instance))
			{
				giantsLoggedNoVisibleDrone.Remove(__instance);
			}
			if (__instance.investigating)
			{
				giantsLoggedExistingInvestigation.Add(__instance);
				return;
			}
			giantsLoggedExistingInvestigation.Remove(__instance);
			if (giantsThatNoticedDrone.Add(__instance))
			{
				__instance.investigatePosition = RoundManager.Instance.GetNavMeshPosition(((Component)droneController).transform.position, default(NavMeshHit), 5f, ((EnemyAI)__instance).agentMask);
				__instance.investigating = true;
			}
		}

		internal static void Forget(ForestGiantAI giant)
		{
			giantsThatNoticedDrone.Remove(giant);
			giantsLoggedInitialCheck.Remove(giant);
			giantsLoggedNonIdleState.Remove(giant);
			giantsWithVisibleDrone.Remove(giant);
			giantsLoggedExistingInvestigation.Remove(giant);
			giantsLoggedNoVisibleDrone.Remove(giant);
		}
	}
	[HarmonyPatch(typeof(EnemyAI), "OnDestroy")]
	internal static class ForestGiantDroneAwarenessCleanupPatches
	{
		[HarmonyPostfix]
		private static void OnDestroyPostfix(EnemyAI __instance)
		{
			ForestGiantAI val = (ForestGiantAI)(object)((__instance is ForestGiantAI) ? __instance : null);
			if (val != null)
			{
				ForestGiantDroneAwarenessPatches.Forget(val);
			}
		}
	}
	[HarmonyPatch(typeof(GameNetworkManager))]
	internal static class GameNetworkManagerSavePatches
	{
		[HarmonyPrefix]
		[HarmonyPatch("ResetSavedGameValues")]
		private static void ResetSavedGameValuesPrefix()
		{
			DroneFleetSave.ClearForRunReset();
		}

		[HarmonyPostfix]
		[HarmonyPatch("SaveGame")]
		private static void SaveGamePostfix(GameNetworkManager __instance)
		{
			DroneFleetSave.FlushOnVanillaSave(__instance);
		}
	}
	[HarmonyPatch(typeof(HoarderBugAI))]
	internal static class HoarderBugPatches
	{
		[HarmonyPostfix]
		[HarmonyPatch("RefreshGrabbableObjectsInMapList")]
		private static void RefreshGrabbableObjectsInMapListPostfix()
		{
			for (int num = HoarderBugAI.grabbableObjectsInMap.Count - 1; num >= 0; num--)
			{
				GameObject val = HoarderBugAI.grabbableObjectsInMap[num];
				GrabbableObject val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<GrabbableObject>() : null);
				if ((Object)(object)val2 == (Object)null || CargoAnchors.IsDroneCargoAnchor(val2.parentObject))
				{
					HoarderBugAI.grabbableObjectsInMap.RemoveAt(num);
				}
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch("GrabTargetItemIfClose")]
		private static bool GrabTargetItemIfClosePrefix(HoarderBugAI __instance, ref bool __result)
		{
			GrabbableObject targetItem = __instance.targetItem;
			if ((Object)(object)targetItem == (Object)null || !CargoAnchors.IsDroneCargoAnchor(targetItem.parentObject))
			{
				return true;
			}
			__instance.targetItem = null;
			__result = false;
			return false;
		}

		[HarmonyPrefix]
		[HarmonyPatch("GrabItem", new Type[] { typeof(NetworkObject) })]
		private static bool GrabItemPrefix(HoarderBugAI __instance, NetworkObject item)
		{
			GrabbableObject val = (((Object)(object)item != (Object)null) ? ((Component)item).GetComponent<GrabbableObject>() : null);
			if ((Object)(object)val == (Object)null || !CargoAnchors.IsDroneCargoAnchor(val.parentObject))
			{
				return true;
			}
			if (Traverse.Create((object)__instance).Field<bool>("sendingGrabOrDropRPC").Value)
			{
				return true;
			}
			if ((Object)(object)__instance.targetItem == (Object)(object)val)
			{
				__instance.targetItem = null;
			}
			if (((NetworkBehaviour)__instance).IsOwner && ((EnemyAI)__instance).currentBehaviourStateIndex == 1 && __instance.heldItem == null)
			{
				((EnemyAI)__instance).SwitchToBehaviourStateOnLocalClient(0);
			}
			return false;
		}

		[HarmonyPostfix]
		[HarmonyPatch("GrabItem", new Type[] { typeof(NetworkObject) })]
		private static void GrabItemPostfix(NetworkObject item)
		{
			GrabbableObject val = (((Object)(object)item != (Object)null) ? ((Component)item).GetComponent<GrabbableObject>() : null);
			if ((Object)(object)val != (Object)null)
			{
				val.isHeldByEnemy = true;
				HoarderBugAI.grabbableObjectsInMap.Remove(((Component)val).gameObject);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch("DropItem", new Type[]
		{
			typeof(NetworkObject),
			typeof(Vector3),
			typeof(bool)
		})]
		private static void DropItemPostfix(NetworkObject item)
		{
			GrabbableObject val = (((Object)(object)item != (Object)null) ? ((Component)item).GetComponent<GrabbableObject>() : null);
			if ((Object)(object)val != (Object)null)
			{
				val.isHeldByEnemy = false;
			}
		}
	}
	[HarmonyPatch(typeof(BlobAI), "DoAIInterval")]
	internal static class HygrodereDronePatches
	{
		private const float FollowRange = 4f;

		private static readonly Dictionary<BlobAI, DroneController> followedDrones = new Dictionary<BlobAI, DroneController>();

		[HarmonyPostfix]
		private static void DoAIIntervalPostfix(BlobAI __instance)
		{
			//IL_002a: 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_003a: 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_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			if (!((NetworkBehaviour)__instance).IsOwner || ((EnemyAI)__instance).isEnemyDead)
			{
				return;
			}
			int num;
			if ((Object)(object)((EnemyAI)__instance).targetPlayer != (Object)null)
			{
				Vector3 val = ((Component)((EnemyAI)__instance).targetPlayer).transform.position - ((Component)__instance).transform.position;
				num = ((((Vector3)(ref val)).sqrMagnitude < 16f) ? 1 : 0);
			}
			else
			{
				num = 0;
			}
			bool flag = (byte)num != 0;
			if (((EnemyAI)__instance).stunNormalizedTimer > 0f || flag || StartOfRound.Instance.allPlayersDead)
			{
				ClearFollowTarget(__instance, "yielding to vanilla player/stun behavior");
				return;
			}
			DroneController droneController = FindNearestDrone(__instance, 4f);
			if ((Object)(object)droneController == (Object)null || (Object)(object)RoundManager.Instance == (Object)null)
			{
				ClearFollowTarget(__instance, "no eligible drone within follow range");
				return;
			}
			if (!followedDrones.TryGetValue(__instance, out DroneController value) || (Object)(object)value != (Object)(object)droneController)
			{
				followedDrones[__instance] = droneController;
			}
			((EnemyAI)__instance).StopSearch(__instance.searchForPlayers, true);
			((EnemyAI)__instance).movingTowardsTargetPlayer = false;
			Vector3 navMeshPosition = RoundManager.Instance.GetNavMeshPosition(((Component)droneController).transform.position, default(NavMeshHit), 5f, ((EnemyAI)__instance).agentMask);
			((EnemyAI)__instance).SetDestinationToPosition(navMeshPosition, false);
		}

		private static DroneController? FindNearestDrone(BlobAI blob, float range)
		{
			//IL_0051: 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_0061: 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)
			DroneController result = null;
			float num = range * range;
			foreach (DroneController item in DroneRegistry.All)
			{
				if (!((Object)(object)item == (Object)null) && ((Behaviour)item).isActiveAndEnabled && !item.IsDisabled && item.IsInside && !item.OnShip && item.HygrodereFollowsDrone)
				{
					Vector3 val = ((Component)item).transform.position - ((Component)blob).transform.position;
					float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
					if (!(sqrMagnitude >= num))
					{
						result = item;
						num = sqrMagnitude;
					}
				}
			}
			return result;
		}

		internal static void Forget(BlobAI blob)
		{
			ClearFollowTarget(blob, "Hygrodere removed");
			foreach (DroneController item in DroneRegistry.All)
			{
				if ((Object)(object)item != (Object)null)
				{
					item.SetHygrodereSlow(blob, slowed: false);
				}
			}
		}

		private static void ClearFollowTarget(BlobAI blob, string reason)
		{
			if (followedDrones.TryGetValue(blob, out DroneController _))
			{
				followedDrones.Remove(blob);
			}
		}
	}
	[HarmonyPatch(typeof(BlobAI), "Update")]
	internal static class HygrodereDroneSlowPatch
	{
		private const float SlowRange = 4f;

		[HarmonyPostfix]
		private static void UpdatePostfix(BlobAI __instance)
		{
			//IL_006e: 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)
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton == (Object)null || !singleton.IsServer)
			{
				return;
			}
			foreach (DroneController item in DroneRegistry.All)
			{
				int num;
				if (!((EnemyAI)__instance).isEnemyDead && (Object)(object)item != (Object)null && ((Behaviour)item).isActiveAndEnabled && !item.IsDisabled && item.IsInside && !item.OnShip && item.HygrodereFollowsDrone)
				{
					Vector3 val = ((Component)item).transform.position - ((Component)__instance).transform.position;
					num = ((((Vector3)(ref val)).sqrMagnitude < 16f) ? 1 : 0);
				}
				else
				{
					num = 0;
				}
				bool slowed = (byte)num != 0;
				if ((Object)(object)item != (Object)null)
				{
					item.SetHygrodereSlow(__instance, slowed);
				}
			}
		}
	}
	[HarmonyPatch(typeof(EnemyAI), "OnDestroy")]
	internal static class HygrodereDroneCleanupPatch
	{
		[HarmonyPostfix]
		private static void OnDestroyPostfix(EnemyAI __instance)
		{
			BlobAI val = (BlobAI)(object)((__instance is BlobAI) ? __instance : null);
			if (val != null)
			{
				HygrodereDronePatches.Forget(val);
			}
		}
	}
	[HarmonyPatch(typeof(BushWolfEnemy), "Update")]
	internal static class KidnapperFoxDroneVisionPatch
	{
		private sealed class ReactionState
		{
			internal Vector3 DronePosition;

			internal float LookTimer;

			internal bool HasReactedWhileVisible;

			internal bool IsLooking;

			internal bool PreviousAgentUpdateRotation;

			internal float PreviousAgentSpeed;

			internal bool PendingSpeedRestore;
		}

		private const float VisionRange = 30f;

		private const float VisionHalfAngle = 35f;

		private const float LookDuration = 0.55f;

		private const float RetreatDistance = 4.6f;

		private const float RetreatSpeed = 5f;

		private static readonly FieldRef<BushWolfEnemy, float> matingCallTimerRef = AccessTools.FieldRefAccess<BushWolfEnemy, float>("matingCallTimer");

		private static readonly Dictionary<BushWolfEnemy, ReactionState> reactions = new Dictionary<BushWolfEnemy, ReactionState>();

		[HarmonyPostfix]
		private static void UpdatePostfix(BushWolfEnemy __instance)
		{
			//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0229: Unknown result type (might be due to invalid IL or missing references)
			//IL_022b: Unknown result type (might be due to invalid IL or missing references)
			//IL_027e: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c9: Unknown result type (might be due to invalid IL or missing references)
			if (!((NetworkBehaviour)__instance).IsSpawned || ((EnemyAI)__instance).isEnemyDead)
			{
				Forget(__instance);
				return;
			}
			StartOfRound instance = StartOfRound.Instance;
			reactions.TryGetValue(__instance, out ReactionState value);
			bool flag = ((EnemyAI)__instance).stunNormalizedTimer > 0f;
			bool flag2 = matingCallTimerRef.Invoke(__instance) >= 0f;
			if (value != null && value.PendingSpeedRestore && !flag && !flag2 && ((EnemyAI)__instance).currentBehaviourStateIndex == 0 && __instance.isHiding && (Object)(object)((EnemyAI)__instance).agent != (Object)null)
			{
				((EnemyAI)__instance).agent.speed = value.PreviousAgentSpeed;
				value.PendingSpeedRestore = false;
			}
			if (!((NetworkBehaviour)__instance).IsOwner || (Object)(object)instance == (Object)null || instance.livingPlayers == 0 || ((EnemyAI)__instance).currentBehaviourStateIndex != 0 || !__instance.isHiding || flag || (Object)(object)__instance.staringAtPlayer != (Object)null || flag2 || (Object)(object)((EnemyAI)__instance).agent == (Object)null)
			{
				if (value != null && value.IsLooking)
				{
					RestoreAgentRotation(__instance, value);
					value.IsLooking = false;
					if (flag || flag2)
					{
						value.PendingSpeedRestore = true;
					}
					else if ((Object)(object)((EnemyAI)__instance).agent != (Object)null && ((EnemyAI)__instance).agent.speed <= 0f && (Object)(object)__instance.staringAtPlayer == (Object)null && ((EnemyAI)__instance).currentBehaviourStateIndex != 2)
					{
						((EnemyAI)__instance).agent.speed = value.PreviousAgentSpeed;
					}
				}
				if (value != null && (((EnemyAI)__instance).currentBehaviourStateIndex != 0 || !__instance.isHiding))
				{
					value.PendingSpeedRestore = false;
				}
				if (value == null || !value.PendingSpeedRestore)
				{
					reactions.Remove(__instance);
				}
				return;
			}
			NavMeshAgent agent = ((EnemyAI)__instance).agent;
			Vector3 targetPosition = (((Object)(object)((EnemyAI)__instance).eye != (Object)null) ? ((EnemyAI)__instance).eye.position : (((Component)__instance).transform.position + Vector3.up * 0.75f));
			Vector3 dronePosition;
			bool flag3 = DroneVision.TryGetVisibleDronePosition(targetPosition, 30f, 35f, DroneVisionTarget.KidnapperFox, out dronePosition);
			if (!flag3)
			{
				if (value != null)
				{
					value.HasReactedWhileVisible = false;
					if (!value.IsLooking)
					{
						reactions.Remove(__instance);
					}
				}
			}
			else if (value == null)
			{
				value = new ReactionState();
				reactions.Add(__instance, value);
			}
			if (value == null)
			{
				return;
			}
			if (flag3)
			{
				value.DronePosition = dronePosition;
				if (!value.HasReactedWhileVisible && !value.IsLooking)
				{
					value.IsLooking = true;
					value.LookTimer = 0.55f;
					value.PreviousAgentUpdateRotation = agent.updateRotation;
					value.PreviousAgentSpeed = agent.speed;
				}
				value.HasReactedWhileVisible = true;
			}
			if (!value.IsLooking)
			{
				return;
			}
			LookToward(__instance, value.DronePosition);
			agent.speed = 0f;
			value.LookTimer -= Time.deltaTime;
			if (value.LookTimer <= 0f)
			{
				value.IsLooking = false;
				agent.updateRotation = value.PreviousAgentUpdateRotation;
				if (!RetreatFromDrone(__instance, value.DronePosition))
				{
					agent.speed = value.PreviousAgentSpeed;
				}
			}
		}

		private static void RestoreAgentRotation(BushWolfEnemy fox, ReactionState state)
		{
			NavMeshAgent agent = ((EnemyAI)fox).agent;
			if (state.IsLooking && (Object)(object)agent != (Object)null)
			{
				agent.updateRotation = state.PreviousAgentUpdateRotation;
			}
		}

		private static void LookToward(BushWolfEnemy fox, Vector3 dronePosition)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: 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_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			NavMeshAgent agent = ((EnemyAI)fox).agent;
			if (!((Object)(object)agent == (Object)null))
			{
				Vector3 val = dronePosition;
				val.y = ((Component)fox).transform.position.y;
				Vector3 val2 = val - ((Component)fox).transform.position;
				val2.y = 0f;
				if (!(((Vector3)(ref val2)).sqrMagnitude <= 0.001f))
				{
					agent.updateRotation = false;
					((Component)fox).transform.rotation = Quaternion.Lerp(((Component)fox).transform.rotation, Quaternion.LookRotation(val2, Vector3.up), 4f * Time.deltaTime);
				}
			}
		}

		private static bool RetreatFromDrone(BushWolfEnemy fox, Vector3 dronePosition)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			NavMeshAgent agent = ((EnemyAI)fox).agent;
			RoundManager instance = RoundManager.Instance;
			StartOfRound instance2 = StartOfRound.Instance;
			if ((Object)(object)agent == (Object)null || (Object)(object)instance == (Object)null || (Object)(object)instance2 == (Object)null)
			{
				return false;
			}
			Vector3 val = ((Component)fox).transform.position - dronePosition;
			val.y = 0f;
			if (((Vector3)(ref val)).sqrMagnitude <= 0.001f)
			{
				val = ((Component)fox).transform.forward;
				val.y = 0f;
			}
			if (((Vector3)(ref val)).sqrMagnitude <= 0.001f)
			{
				return false;
			}
			Ray val2 = default(Ray);
			((Ray)(ref val2))..ctor(((Component)fox).transform.position + Vector3.up * 0.5f, ((Vector3)(ref val)).normalized);
			float num = 4.6f;
			RaycastHit val3 = default(RaycastHit);
			if (Physics.Raycast(val2, ref val3, 4.6f, instance2.collidersAndRoomMaskAndDefault, (QueryTriggerInteraction)1))
			{
				num = Mathf.Clamp(((RaycastHit)(ref val3)).distance - 1f, 0f, ((RaycastHit)(ref val3)).distance);
			}
			Vector3 navMeshPosition = instance.GetNavMeshPosition(((Ray)(ref val2)).GetPoint(num), default(NavMeshHit), 5f, -1);
			if (!instance.GotNavMeshPositionResult)
			{
				return false;
			}
			if (Vector3.Distance(navMeshPosition, ((Component)fox).transform.position) < 0.5f)
			{
				return false;
			}
			((EnemyAI)fox).SetDestinationToPosition(navMeshPosition, false);
			agent.speed = 5f * fox.speedMultiplier;
			return true;
		}

		internal static void Forget(BushWolfEnemy fox)
		{
			reactions.Remove(fox);
		}
	}
	[HarmonyPatch(typeof(EnemyAI), "OnDestroy")]
	internal static class KidnapperFoxDroneVisionCleanupPatch
	{
		[HarmonyPostfix]
		private static void OnDestroyPostfix(EnemyAI __instance)
		{
			BushWolfEnemy val = (BushWolfEnemy)(object)((__instance is BushWolfEnemy) ? __instance : null);
			if (val != null)
			{
				KidnapperFoxDroneVisionPatch.Forget(val);
			}
		}
	}
	[HarmonyPatch(typeof(DoublewingAI), "DoAIInterval")]
	internal static class ManticoilDronePatches
	{
		[HarmonyPostfix]
		private static void DoAIIntervalPostfix(DoublewingAI __instance)
		{
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			if (!((NetworkBehaviour)__instance).IsOwner || ((EnemyAI)__instance).isEnemyDead || ((EnemyAI)__instance).daytimeEnemyLeaving || StartOfRound.Instance.allPlayersDead || ((EnemyAI)__instance).stunNormalizedTimer > 0f)
			{
				return;
			}
			bool value = Traverse.Create((object)__instance).Field<bool>("oddInterval").Value;
			if (((EnemyAI)__instance).currentBehaviourStateIndex == 0 && value && !Traverse.Create((object)__instance).Field<bool>("alertingBird").Value)
			{
				DroneController droneController = FindVisibleDrone(__instance, 8, 4f);
				if ((Object)(object)droneController != (Object)null)
				{
					Traverse.Create((object)__instance).Field<bool>("alertingBird").Value = true;
					__instance.AlertBirdServerRpc();
				}
			}
			else
			{
				if (((EnemyAI)__instance).currentBehaviourStateIndex != 1 || Traverse.Create((object)__instance).Field<float>("avoidingPlayer").Value > 0f)
				{
					return;
				}
				DroneController droneController2 = FindVisibleDrone(__instance, 10, 8f);
				if (!((Object)(object)droneController2 == (Object)null))
				{
					Transform val = ((EnemyAI)__instance).ChooseFarthestNodeFromPosition(((Component)droneController2).transform.position, false, Random.Range(0, ((EnemyAI)__instance).allAINodes.Length / 2), false, 50, -1);
					if ((Object)(object)val != (Object)null && ((EnemyAI)__instance).SetDestinationToPosition(val.position, false))
					{
						Traverse.Create((object)__instance).Field<float>("avoidingPlayer").Value = Random.Range(10f, 20f);
						((EnemyAI)__instance).StopSearch(__instance.roamGlide, true);
					}
				}
			}
		}

		private static DroneController? FindVisibleDrone(DoublewingAI bird, int range, float proximityAwareness)
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			DroneController result = null;
			float num = float.MaxValue;
			foreach (DroneController item in DroneRegistry.All)
			{
				if (!((Object)(object)item == (Object)null) && ((Behaviour)item).isActiveAndEnabled && !item.IsDisabled && !item.IsInside && !item.OnShip && item.ManticoilEvadesFromDrone)
				{
					Vector3 val = ((Component)item).transform.position + Vector3.up * 0.3f;
					float num2 = Vector3.Distance(((EnemyAI)bird).eye.position, val);
					if (!(num2 >= (float)range) && ((EnemyAI)bird).CheckLineOfSightForPosition(val, 80f, range, proximityAwareness, (Transform)null) && !(num2 >= num))
					{
						result = item;
						num = num2;
					}
				}
			}
			return result;
		}
	}
	[HarmonyPatch(typeof(ManualCameraRenderer))]
	internal static class ManualCameraRendererPatches
	{
		private static bool loggedError;

		private static bool loggedFloorError;

		internal static void RefreshMapCameraFloor()
		{
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				StartOfRound instance = StartOfRound.Instance;
				ManualCameraRenderer val = (((Object)(object)instance != (Object)null) ? instance.mapScreen : null);
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val.mapCamera == (Object)null) && val.radarTargets != null && val.targetTransformIndex >= 0 && val.targetTransformIndex < val.radarTargets.Count)
				{
					Transform val2 = val.radarTargets[val.targetTransformIndex]?.transform;
					if ((Object)(object)val2 != (Object)null)
					{
						AccessTools.Method(typeof(ManualCameraRenderer), "MapCameraFocusOnPosition", (Type[])null, (Type[])null)?.Invoke(val, new object[1] { val2.position });
					}
				}
			}
			catch (Exception arg)
			{
				if (!loggedFloorError)
				{
					loggedFloorError = true;
					Plugin.Logger.LogError((object)$"Failed to refresh the monitor map floor (logged once): {arg}");
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch("MapCameraFocusOnPosition")]
		private static void MapCameraFocusOnPositionPostfix(ManualCameraRenderer __instance, Vector3 pos)
		{
			//IL_000e: 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)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!((Object)(object)__instance.mapCamera == (Object)null) && MapLabels.TryGetMonitorCameraYOffset(pos, out var offset))
				{
					Transform transform = ((Component)__instance.mapCamera).transform;
					transform.position += Vector3.up * offset;
				}
			}
			catch (Exception arg)
			{
				if (!loggedFloorError)
				{
					loggedFloorError = true;
					Plugin.Logger.LogError((object)$"Failed to switch the monitor map floor (logged once): {arg}");
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch("LateUpdate")]
		private static void LateUpdatePostfix(ManualCameraRenderer __instance)
		{
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!__instance.enableHeadMountedCam || (Object)(object)__instance.headMountedCam == (Object)null || (Object)(object)__instance.targetedPlayer != (Object)null)
				{
					return;
				}
				List<TransformAndName> radarTargets = __instance.radarTargets;
				int targetTransformIndex = __instance.targetTransformIndex;
				if (radarTargets != null && targetTransformIndex >= 0 && targetTransformIndex < radarTargets.Count)
				{
					Transform val = radarTargets[targetTransformIndex]?.transform;
					DroneController droneController = default(DroneController);
					if (!((Object)(object)val == (Object)null) && ((Component)val).TryGetComponent<DroneController>(ref droneController))
					{
						Transform transform = ((Component)__instance.headMountedCam).transform;
						DroneVision.GetCameraPose(val, out var position, out var rotation);
						transform.position = position;
						transform.rotation = rotation;
					}
				}
			}
			catch (Exception arg)
			{
				if (!loggedError)
				{
					loggedError = true;
					Plugin.Logger.LogError((object)$"Failed to place the monitor camera on the drone (logged once): {arg}");
				}
			}
		}
	}
	[HarmonyPatch(typeof(EnemyAICollisionDetect), "OnTriggerStay")]
	internal static class MouthDogDroneCollisionPatch
	{
		private const float ReportInterval = 0.3f;

		private static readonly Dictionary<(MouthDogAI Dog, DroneController Drone), float> lastReports = new Dictionary<(MouthDogAI, DroneController), float>();

		[HarmonyPostfix]
		private static void OnTriggerStayPostfix(EnemyAICollisionDetect __instance, Collider other)
		{
			EnemyAI mainScript = __instance.mainScript;
			MouthDogAI val = (MouthDogAI)(object)((mainScript is MouthDogAI) ? mainScript : null);
			if (val == null || !((NetworkBehaviour)val).IsOwner || ((EnemyAI)val).isEnemyDead || ((EnemyAI)val).currentBehaviourStateIndex != 3 || (Object)(object)other == (Object)null)
			{
				return;
			}
			DroneController componentInParent = ((Component)other).GetComponentInParent<DroneController>();
			if (!((Object)(object)componentInParent == (Object)null) && ((Behaviour)componentInParent).isActiveAndEnabled && !componentInParent.IsDisabled && !componentInParent.OnShip && componentInParent.MouthDogAttacksDrone)
			{
				float realtimeSinceStartup = Time.realtimeSinceStartup;
				(MouthDogAI, DroneController) key = (val, componentInParent);
				if (!lastReports.TryGetValue(key, out var value) || !(realtimeSinceStartup - value < 0.3f))
				{
					lastReports[key] = realtimeSinceStartup;
					componentInParent.ReportMouthDogCollision(val);
				}
			}
		}

		internal static void Forget(MouthDogAI dog)
		{
			foreach (var item in new List<(MouthDogAI, DroneController)>(lastReports.Keys))
			{
				if ((Object)(object)item.Item1 == (Object)(object)dog)
				{
					lastReports.Remove(item);
				}
			}
		}
	}
	[HarmonyPatch(typeof(MouthDogAI), "DetectNoise", new Type[]
	{
		typeof(Vector3),
		typeof(float),
		typeof(int),
		typeof(int)
	})]
	internal static class MouthDogDroneNoisePatch
	{
		[HarmonyPrefix]
		private static bool DetectNoisePrefix(MouthDogAI __instance)
		{
			bool result = ((NetworkBehaviour)__instance).IsOwner || !DroneNoise.IsPlaying;
			_ = DroneNoise.IsPlaying;
			return result;
		}
	}
	[HarmonyPatch(typeof(EnemyAI), "OnDestroy")]
	internal static class MouthDogDroneCollisionCleanupPatch
	{
		[HarmonyPostfix]
		private static void OnDestroyPostfix(EnemyAI __instance)
		{
			MouthDogAI val = (MouthDogAI)(object)((__instance is MouthDogAI) ? __instance : null);
			if (val != null)
			{
				MouthDogDroneCollisionPatch.Forget(val);
			}
		}
	}
	[HarmonyPatch(typeof(NutcrackerEnemyAI), "Update")]
	internal static class NutcrackerDronePatches
	{
		private struct MotionSample
		{
			internal Vector3 Position;

			internal float SampledAt;

			internal float MovingUntil;
		}

		private const float SightRange = 60f;

		private const float SightHalfAngle = 70f;

		private const float RepeatShotCooldown = 6f;

		private const float TargetTimeout = 6f;

		private static readonly Dictionary<NutcrackerEnemyAI, DroneController> droneTargets = new Dictionary<NutcrackerEnemyAI, DroneController>();

		private static readonly Dictionary<NutcrackerEnemyAI, float> targetStartTimes = new Dictionary<NutcrackerEnemyAI, float>();

		private static readonly Dictionary<NutcrackerEnemyAI, float> lastShotEndTimes = new Dictionary<NutcrackerEnemyAI, float>();

		private static readonly Dictionary<NutcrackerEnemyAI, float> lastSightReportTimes = new Dictionary<NutcrackerEnemyAI, float>();

		private static readonly HashSet<NutcrackerEnemyAI> aimRequestsStarted = new HashSet<NutcrackerEnemyAI>();

		private static readonly HashSet<NutcrackerEnemyAI> resumeInspectionAfterShot = new HashSet<NutcrackerEnemyAI>();

		private static readonly Dictionary<DroneController, MotionSample> motionSamples = new Dictionary<DroneController, MotionSample>();

		private static readonly List<DroneController> removedMotionSamples = new List<DroneController>();

		[HarmonyPostfix]
		private static void UpdatePostfix(NutcrackerEnemyAI __instance)
		{
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_019d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_020f: Unknown result type (might be due to invalid IL or missing references)
			if (!((NetworkBehaviour)__instance).IsSpawned || ((EnemyAI)__instance).isEnemyDead)
			{
				Forget(__instance);
				return;
			}
			UpdateDroneMotion();
			if (droneTargets.TryGetValue(__instance, out DroneController value))
			{
				float value2;
				float num = (targetStartTimes.TryGetValue(__instance, out value2) ? value2 : Time.realtimeSinceStartup);
				bool value3 = Traverse.Create((object)__instance).Field<bool>("aimingGun").Value;
				if (!IsEligible(value) || Time.realtimeSinceStartup - num > 6f)
				{
					CancelTarget(__instance, value);
					return;
				}
				if (((EnemyAI)__instance).currentBehaviourStateIndex != 1 && !value3)
				{
					CancelTarget(__instance, value);
					return;
				}
				Traverse.Create((object)__instance).Method("SetTargetDegreesToPosition", new object[1] { ((Component)value).transform.position }).GetValue();
				float value4 = Traverse.Create((object)__instance).Field<float>("timeSinceFiringGun").Value;
				float value5 = Traverse.Create((object)__instance).Field<float>("timeSinceHittingPlayer").Value;
				if (((NetworkBehaviour)__instance).IsOwner && !aimRequestsStarted.Contains(__instance) && ((EnemyAI)__instance).currentBehaviourStateIndex == 1 && ((EnemyAI)__instance).stunNormalizedTimer <= 0f && !Traverse.Create((object)__instance).Field<bool>("reloadingGun").Value && !value3 && (Object)(object)__instance.gun != (Object)null && __instance.gun.shellsLoaded > 0 && value4 > 0.75f && value5 > 1f && Vector3.Angle(__instance.gun.shotgunRayPoint.forward, ((Component)value).transform.position + Vector3.up * 0.4f - __instance.gun.shotgunRayPoint.position) < 30f)
				{
					aimRequestsStarted.Add(__instance);
					Traverse.Create((object)__instance).Field<float>("timeSinceFiringGun").Value = 0f;
					((EnemyAI)__instance).agent.speed = 0f;
					__instance.AimGunServerRpc(((Component)__instance).transform.position);
				}
			}
			else if (resumeInspectionAfterShot.Contains(__instance) && ((EnemyAI)__instance).currentBehaviourStateIndex == 1 && !Traverse.Create((object)__instance).Field<bool>("aimingGun").Value && !Traverse.Create((object)__instance).Field<bool>("isInspecting").Value)
			{
				resumeInspectionAfterShot.Remove(__instance);
				Traverse.Create((object)__instance).Method("StartInspectionTurn", Array.Empty<object>()).GetValue();
			}
			else
			{
				if (!((NetworkBehaviour)__instance).IsOwner || ((EnemyAI)__instance).currentBehaviourStateIndex != 1 || ((EnemyAI)__instance).stunNormalizedTimer > 0f || !Traverse.Create((object)__instance).Field<bool>("isInspecting").Value || (Object)(object)__instance.gun == (Object)null || __instance.gun.shellsLoaded <= 0)
				{
					return;
				}
				DroneController droneController = FindNearestVisibleMovingDrone(__instance);
				if (!((Object)(object)droneController == (Object)null))
				{
					float realtimeSinceStartup = Time.realtimeSinceStartup;
					if (!lastSightReportTimes.TryGetValue(__instance, out var value6) || !(realtimeSinceStartup - value6 < 2f))
					{
						lastSightReportTimes[__instance] = realtimeSinceStartup;
						droneController.ReportNutcrackerSight(__instance);
					}
				}
			}
		}

		internal static bool TryRegisterServerTarget(NutcrackerEnemyAI nutcracker, DroneController drone)
		{
			if (droneTargets.ContainsKey(nutcracker) || (lastShotEndTimes.TryGetValue(nutcracker, out var value) && Time.realtimeSinceStartup - value < 6f))
			{
				return false;
			}
			droneTargets[nutcracker] = drone;
			targetStartTimes[nutcracker] = Time.realtimeSinceStartup;
			aimRequestsStarted.Remove(nutcracker);
			return true;
		}

		internal static bool CanServerSeeDrone(NutcrackerEnemyAI nutcracker, DroneController drone)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: 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_009d: Unknown result type (might be due to invalid IL or missing references)
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)((EnemyAI)nutcracker).eye == (Object)null || !IsEligible(drone) || !motionSamples.TryGetValue(drone, out var value) || value.MovingUntil < Time.realtimeSinceStartup)
			{
				return false;
			}
			Vector3 val = ((Component)drone).transform.position + Vector3.up * 0.4f;
			Vector3 val2 = val - ((EnemyAI)nutcracker).eye.position;
			if (((Vector3)(ref val2)).sqrMagnitude < 3600f && Vector3.Angle(((EnemyAI)nutcracker).eye.forward, val2) < 70f)
			{
				return HasClearLineOfSight(nutcracker, drone, val, instance.collidersAndRoomMaskAndDefault);
			}
			return false;
		}

		internal static DroneController? GetTargetForShot(NutcrackerEnemyAI nutcracker)
		{
			if (!droneTargets.TryGetValue(nutcracker, out DroneController value))
			{
				return null;
			}
			return value;
		}