Decompiled source of PandoraEnemyPatch v1.0.14

BepInEx\plugins\BeneathTwo-PandoraEnemyPatch\PandoraEnemyPatch.dll

Decompiled 16 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Logging;
using CodeRebirthLib.Util.INetworkSerializables;
using GameNetcodeStuff;
using Microsoft.CodeAnalysis;
using MonoMod.RuntimeDetour;
using PandoraEnemy;
using PandoraEnemy.Content.Enemies;
using PandoraEnemyPatch.Attacks;
using PandoraEnemyPatch.Chasing;
using PandoraEnemyPatch.Hooks;
using PandoraEnemyPatch.Logging;
using PandoraEnemyPatch.Networking;
using PandoraEnemyPatch.Runtime;
using PandoraEnemyPatch.Swapping;
using PandoraEnemyPatch.Targeting;
using PandoraEnemyPatch.UI;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.AI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: IgnoresAccessChecksTo("com.github.xuuxiaolan.pandoraenemy")]
[assembly: AssemblyCompany("PandoraEnemyPatch")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.14.0")]
[assembly: AssemblyInformationalVersion("1.0.14+c4c2a7ce235c3aea691e90e0cbb65ac5a59a0e50")]
[assembly: AssemblyProduct("PandoraEnemyPatch")]
[assembly: AssemblyTitle("PandoraEnemyPatch")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.14.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace PandoraEnemyPatch
{
	[BepInPlugin("PandoraEnemyPatch", "PandoraEnemyPatch", "1.0.14")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		private static bool _registered;

		internal static Plugin Instance { get; private set; }

		internal static ManualLogSource Logger { get; private set; }

		private void Awake()
		{
			Instance = this;
			Logger = ((BaseUnityPlugin)this).Logger;
			NetworkEventLog.EnsureInitialized();
			if (!_registered)
			{
				PandoraHookRegistrar.Register();
				_registered = true;
			}
			NetworkEventLog.Info("PandoraEnemyPatch v1.0.14 has loaded!");
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "PandoraEnemyPatch";

		public const string PLUGIN_NAME = "PandoraEnemyPatch";

		public const string PLUGIN_VERSION = "1.0.14";
	}
}
namespace PandoraEnemyPatch.UI
{
	internal static class GroundPoundEscapeCue
	{
		private const string LogCategory = "GroundPoundCue";

		private const string EscapeHeader = "";

		private const string EscapeTipText = "JUMP! BREAK FREE!";

		private const int InitialPulseTextIndex = 0;

		private const int FirstFollowUpPulseIndex = 1;

		private const int FallbackPulseIndex = 0;

		private static readonly string[] PulseTexts = new string[3] { "JUMP!", "BREAK FREE!", "JUMP!" };

		private static readonly HashSet<int> ActiveEnemyIds = new HashSet<int>();

		private static readonly Dictionary<int, int> PulseIndexByEnemyId = new Dictionary<int, int>();

		public static void ShowForLocalPlayer(PandoraEnemyAI self, PlayerControllerB? player)
		{
			PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController;
			if ((Object)(object)player == (Object)null || (Object)(object)val == (Object)null || (Object)(object)player != (Object)(object)val)
			{
				Log(self, "skipped cue because target/local player did not match.", relayToOtherClients: false);
				return;
			}
			if ((Object)(object)HUDManager.Instance == (Object)null)
			{
				Log(self, "skipped cue because HUDManager.Instance was null.", relayToOtherClients: false);
				return;
			}
			int instanceID = ((Object)self).GetInstanceID();
			ActiveEnemyIds.Add(instanceID);
			PulseIndexByEnemyId[instanceID] = 1;
			Log(self, $"showing escape cue for {val.playerUsername} ({val.playerClientId}).", relayToOtherClients: false);
			HUDManager.Instance.DisplayTip("", "JUMP! BREAK FREE!", true, false, "LC_Tip1");
			HUDManager.Instance.DisplayStatusEffect(PulseTexts[0]);
		}

		public static void Cancel(PandoraEnemyAI self)
		{
			StopTracking(self);
			Log(self, "cancelled active ground-pound cue sequence.", relayToOtherClients: false);
		}

		public static void PulseFromHeartbeat(HeartbeatAudioHandler heartbeat)
		{
			PandoraEnemyAI val = ((Component)heartbeat).GetComponent<PandoraEnemyAI>() ?? ((Component)heartbeat).GetComponentInParent<PandoraEnemyAI>();
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			int instanceID = ((Object)val).GetInstanceID();
			if (!ActiveEnemyIds.Contains(instanceID))
			{
				return;
			}
			if (!IsCueStillValid(val))
			{
				StopTracking(val);
				return;
			}
			if ((Object)(object)HUDManager.Instance == (Object)null)
			{
				Log(val, "skipped heartbeat pulse because HUDManager.Instance was null.", relayToOtherClients: false);
				return;
			}
			if (!PulseIndexByEnemyId.TryGetValue(instanceID, out var value))
			{
				value = 0;
			}
			string text = PulseTexts[value];
			Log(val, "pulsing heartbeat-synced escape cue '" + text + "'.", relayToOtherClients: false);
			HUDManager.Instance.DisplayStatusEffect(text);
			PulseIndexByEnemyId[instanceID] = (value + 1) % PulseTexts.Length;
		}

		public static bool IsActiveFor(PandoraEnemyAI self)
		{
			return ActiveEnemyIds.Contains(((Object)self).GetInstanceID());
		}

		private static bool IsCueStillValid(PandoraEnemyAI self)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Invalid comparison between Unknown and I4
			if ((int)self._nextAttack > 0)
			{
				Log(self, "stopped heartbeat pulse because Pandora is no longer performing GroundPound.", relayToOtherClients: false);
				return false;
			}
			PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController;
			if ((Object)(object)val == (Object)null || val.isPlayerDead || !val.disableMoveInput)
			{
				Log(self, "stopped heartbeat pulse because the player was already released or invalid.", relayToOtherClients: false);
				return false;
			}
			return true;
		}

		private static void StopTracking(PandoraEnemyAI self)
		{
			int instanceID = ((Object)self).GetInstanceID();
			ActiveEnemyIds.Remove(instanceID);
			PulseIndexByEnemyId.Remove(instanceID);
		}

		private static void Log(PandoraEnemyAI self, string message, bool relayToOtherClients)
		{
			NetworkEventLog.Write("[GroundPoundCue] [" + EnemyLogLabel.For(self) + "] " + message, relayToOtherClients);
		}
	}
	internal static class GroundPoundHeartbeatIntensity
	{
		private const float DangerHeartbeatMaxInterval = 1.2f;

		private const float DangerHeartbeatMinInterval = 0.35f;

		private const float DangerHeartbeatMinVolume = 0.85f;

		private const float DangerHeartbeatMaxVolume = 1.3f;

		public static void ApplyIfActive(HeartbeatAudioHandler heartbeat, AudioSource source)
		{
			PandoraEnemyAI val = ResolvePandora(heartbeat);
			if ((Object)(object)val == (Object)null || !GroundPoundEscapeCue.IsActiveFor(val))
			{
				return;
			}
			if (!TryGetPinnedLocalPlayer(val, out PlayerControllerB localPlayer))
			{
				GroundPoundEscapeCue.Cancel(val);
				return;
			}
			PandoraAdditionalPlayerData orCreate = PandoraAdditionalPlayerData.GetOrCreate(localPlayer);
			if ((Object)(object)orCreate.inAnimationWithPandora != (Object)(object)val)
			{
				GroundPoundEscapeCue.Cancel(val);
				return;
			}
			float num = Mathf.Clamp01(orCreate.OverrideSinkingValue);
			float num2 = Mathf.SmoothStep(0f, 1f, num);
			heartbeat.Focus();
			heartbeat.SetInterval(Mathf.Lerp(1.2f, 0.35f, num2));
			source.volume = Mathf.Max(source.volume, Mathf.Lerp(0.85f, 1.3f, num2));
			source.pitch = Mathf.Lerp(1f, 1.1f, num2);
		}

		private static bool TryGetPinnedLocalPlayer(PandoraEnemyAI self, out PlayerControllerB localPlayer)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Invalid comparison between Unknown and I4
			localPlayer = GameNetworkManager.Instance?.localPlayerController;
			return (Object)(object)localPlayer != (Object)null && !localPlayer.isPlayerDead && localPlayer.disableMoveInput && (int)self._nextAttack == 0;
		}

		private static PandoraEnemyAI? ResolvePandora(HeartbeatAudioHandler heartbeat)
		{
			return ((Component)heartbeat).GetComponent<PandoraEnemyAI>() ?? ((Component)heartbeat).GetComponentInParent<PandoraEnemyAI>();
		}
	}
}
namespace PandoraEnemyPatch.Targeting
{
	internal readonly struct TargetObservation
	{
		public PlayerControllerB Player { get; }

		public Vector3 TargetPoint { get; }

		public float Distance { get; }

		public TargetObservation(PlayerControllerB player, Vector3 targetPoint, float distance)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			Player = player;
			TargetPoint = targetPoint;
			Distance = distance;
		}
	}
	internal sealed class PandoraTargetingService
	{
		private const float TargetAnchorHeight = 1.6f;

		private const int PlayerSightMask = 60;

		private const float PlayerSightProximity = -1f;

		internal const float SharedVisibilityRange = 40f;

		public bool TryGetClosestVisibleTarget(PandoraEnemyAI self, out PlayerControllerB? player)
		{
			player = null;
			TargetObservation? targetObservation = null;
			PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts;
			foreach (PlayerControllerB player2 in allPlayerScripts)
			{
				if (TryObserve(self, player2, out var observation) && (!targetObservation.HasValue || observation.Distance < targetObservation.Value.Distance))
				{
					targetObservation = observation;
				}
			}
			if (!targetObservation.HasValue)
			{
				return false;
			}
			player = targetObservation.Value.Player;
			return true;
		}

		public bool CanAnyEligiblePlayerSeePandora(PandoraEnemyAI self)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts;
			foreach (PlayerControllerB val in allPlayerScripts)
			{
				if (IsEligible(val) && val.HasLineOfSightToPosition(((EnemyAI)self).eye.position, 40f, 60, -1f))
				{
					return true;
				}
			}
			return false;
		}

		internal static bool TryObserve(PandoraEnemyAI self, PlayerControllerB? player, out TargetObservation observation)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: 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)
			observation = default(TargetObservation);
			if (!IsEligible(player))
			{
				return false;
			}
			Vector3 targetPoint = GetTargetPoint(player);
			if (!HasLineOfSight(self, targetPoint, out var distance))
			{
				return false;
			}
			observation = new TargetObservation(player, targetPoint, distance);
			return true;
		}

		private static bool IsEligible(PlayerControllerB? player)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			return (Object)(object)player != (Object)null && (Object)player != (Object)null && !player.isPlayerDead && player.isPlayerControlled;
		}

		private static Vector3 GetTargetPoint(PlayerControllerB player)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: 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_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player.gameplayCamera != (Object)null)
			{
				return ((Component)player.gameplayCamera).transform.position;
			}
			return ((Component)player).transform.position + Vector3.up * 1.6f;
		}

		private static bool HasLineOfSight(PandoraEnemyAI self, Vector3 targetPoint, out float distance)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			Vector3 position = ((EnemyAI)self).eye.position;
			distance = Vector3.Distance(position, targetPoint);
			if (distance > 40f)
			{
				return false;
			}
			Vector3 val = targetPoint - position;
			Vector3 normalized = ((Vector3)(ref val)).normalized;
			return !Physics.Raycast(position, normalized, distance, StartOfRound.Instance.collidersAndRoomMaskAndDefault, (QueryTriggerInteraction)1);
		}
	}
}
namespace PandoraEnemyPatch.Swapping
{
	internal sealed class SwapPlanner
	{
		internal readonly struct SwapResolution
		{
			public bool Succeeded { get; }

			public SwapTeleportPlan? Plan { get; }

			public IReadOnlyList<SwapTeleportStep> FallbackSteps { get; }

			public string FailureReason { get; }

			public SwapResolution(SwapTeleportPlan plan)
			{
				Succeeded = true;
				Plan = plan;
				FallbackSteps = Array.Empty<SwapTeleportStep>();
				FailureReason = string.Empty;
			}

			public SwapResolution(string failureReason, IReadOnlyList<SwapTeleportStep> fallbackSteps)
			{
				Succeeded = false;
				Plan = null;
				FallbackSteps = fallbackSteps;
				FailureReason = failureReason;
			}
		}

		private const string LogCategory = "Swap";

		public bool CanBuildAnySwap(PandoraEnemyAI self)
		{
			List<PlayerControllerB> eligibleSeenPlayers = GetEligibleSeenPlayers(self);
			for (int i = 0; i < eligibleSeenPlayers.Count; i++)
			{
				for (int j = i + 1; j < eligibleSeenPlayers.Count; j++)
				{
					if (CanSwap(eligibleSeenPlayers[i], eligibleSeenPlayers[j], self._minTeleportSeparation))
					{
						return true;
					}
				}
			}
			return false;
		}

		public SwapResolution Resolve(PandoraEnemyAI self, IReadOnlyList<PlayerControllerB> trappedPlayers)
		{
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_023e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
			List<PlayerControllerB> list = (from player in trappedPlayers.Where(IsEligible).Distinct()
				orderby player.actualClientId
				select player).ToList();
			if (list.Count == 0)
			{
				return CreateFailedResolution(self, list, "no trapped players were still valid when the swap resolved");
			}
			List<PlayerControllerB> eligibleSeenPlayers = GetEligibleSeenPlayers(self);
			Log(self, "planner snapshot trapped=" + FormatPlayers(list) + " seenEligible=" + FormatPlayers(eligibleSeenPlayers));
			if (eligibleSeenPlayers.Count < 2)
			{
				return CreateFailedResolution(self, list, $"not enough eligible players were seen to build a swap. eligible={eligibleSeenPlayers.Count}");
			}
			HashSet<ulong> hashSet = new HashSet<ulong>();
			List<SwapTeleportStep> list2 = new List<SwapTeleportStep>(list.Count * 2);
			List<PlannedSwapPair> list3 = new List<PlannedSwapPair>(list.Count);
			foreach (PlayerControllerB item in list)
			{
				if (!hashSet.Contains(item.actualClientId))
				{
					PlayerControllerB partner = FindPartner(item, eligibleSeenPlayers, hashSet, self, self._minTeleportSeparation);
					if ((Object)(object)partner == (Object)null)
					{
						return CreateFailedResolution(self, list, BuildNoPartnerReason(item, eligibleSeenPlayers, hashSet, self._minTeleportSeparation));
					}
					hashSet.Add(item.actualClientId);
					hashSet.Add(partner.actualClientId);
					list2.Add(new SwapTeleportStep(item, ((Component)partner).transform.position, partner.targetYRot));
					list3.Add(new PlannedSwapPair(item, partner));
					if (!list.Any((PlayerControllerB player) => player.actualClientId == partner.actualClientId))
					{
						list2.Add(new SwapTeleportStep(partner, ((Component)item).transform.position, item.targetYRot));
						list3.Add(new PlannedSwapPair(partner, item));
					}
					else
					{
						list2.Add(new SwapTeleportStep(partner, ((Component)item).transform.position, item.targetYRot));
					}
				}
			}
			return new SwapResolution(new SwapTeleportPlan(list2, list3));
		}

		public void Execute(PandoraEnemyAI self, SwapTeleportPlan plan)
		{
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			Log(self, $"executing plan with {plan.Pairs.Count} swap assignment(s)");
			foreach (PlannedSwapPair pair in plan.Pairs)
			{
				Log(self, $"{pair.Source.playerUsername} ({pair.Source.playerClientId}) -> " + $"{pair.DestinationSource.playerUsername} ({pair.DestinationSource.playerClientId})");
			}
			foreach (SwapTeleportStep step in plan.Steps)
			{
				PlayerControllerReference val = PlayerControllerReference.op_Implicit(step.Player);
				self.TeleportPlayerServerRPC(val, step.Destination, step.YRot);
			}
		}

		public void Execute(PandoraEnemyAI self, SwapResolution resolution)
		{
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			if (!resolution.Succeeded)
			{
				Log(self, "cancelled swap attack because " + resolution.FailureReason + ".");
				{
					foreach (SwapTeleportStep fallbackStep in resolution.FallbackSteps)
					{
						Log(self, $"falling back to random teleport for {fallbackStep.Player.playerUsername} ({fallbackStep.Player.playerClientId}).");
						PlayerControllerReference val = PlayerControllerReference.op_Implicit(fallbackStep.Player);
						self.TeleportPlayerServerRPC(val, fallbackStep.Destination, fallbackStep.YRot);
					}
					return;
				}
			}
			Execute(self, resolution.Plan);
		}

		private List<PlayerControllerB> GetEligibleSeenPlayers(PandoraEnemyAI self)
		{
			return (from player in self._playersSeenOverLifetime.Where(IsEligible).Distinct()
				orderby player.actualClientId
				select player).ToList();
		}

		private PlayerControllerB? FindPartner(PlayerControllerB trappedPlayer, IReadOnlyList<PlayerControllerB> eligiblePlayers, HashSet<ulong> reservedClientIds, PandoraEnemyAI self, float minSeparation)
		{
			HashSet<ulong> reservedClientIds2 = reservedClientIds;
			PlayerControllerB trappedPlayer2 = trappedPlayer;
			List<PlayerControllerB> list = eligiblePlayers.Where((PlayerControllerB candidate) => !reservedClientIds2.Contains(candidate.actualClientId) && CanSwap(trappedPlayer2, candidate, minSeparation)).ToList();
			if (list.Count == 0)
			{
				return null;
			}
			return list[((PandoraBaseEnemyAI)self)._enemyRandom.Next(list.Count)];
		}

		private static string BuildNoPartnerReason(PlayerControllerB trappedPlayer, IReadOnlyList<PlayerControllerB> eligiblePlayers, HashSet<ulong> reservedClientIds, float minSeparation)
		{
			HashSet<ulong> reservedClientIds2 = reservedClientIds;
			PlayerControllerB trappedPlayer2 = trappedPlayer;
			List<PlayerControllerB> list = eligiblePlayers.Where((PlayerControllerB candidate) => !reservedClientIds2.Contains(candidate.actualClientId) && candidate != trappedPlayer2).ToList();
			if (list.Count == 0)
			{
				return "no partner remained for " + FormatPlayer(trappedPlayer2) + " after other assignments were reserved";
			}
			float num = list.Select((PlayerControllerB candidate) => Vector3.Distance(((Component)trappedPlayer2).transform.position, ((Component)candidate).transform.position)).Min();
			return $"no partner for {FormatPlayer(trappedPlayer2)} satisfied the minimum separation of {minSeparation:0.##}. " + $"nearest available distance was {num:0.##}";
		}

		private static SwapResolution CreateFailedResolution(PandoraEnemyAI self, IReadOnlyList<PlayerControllerB> trapped, string failureReason)
		{
			return new SwapResolution(failureReason, BuildRandomTeleportFallbacks(self, trapped));
		}

		private static IReadOnlyList<SwapTeleportStep> BuildRandomTeleportFallbacks(PandoraEnemyAI self, IReadOnlyList<PlayerControllerB> trapped)
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			List<SwapTeleportStep> list = new List<SwapTeleportStep>(trapped.Count);
			foreach (PlayerControllerB item in trapped)
			{
				if (IsEligible(item) && TryPickFarTeleport(self, item, out var destination, out var yRot))
				{
					list.Add(new SwapTeleportStep(item, destination, yRot));
				}
			}
			return list;
		}

		private static bool TryPickFarTeleport(PandoraEnemyAI self, PlayerControllerB player, out Vector3 destination, out float yRot)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: 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_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			destination = default(Vector3);
			yRot = player.targetYRot;
			Vector3 position = ((Component)player).transform.position;
			for (int i = 0; i < self._teleportAttempts; i++)
			{
				Vector3 position2 = RoundManager.Instance.insideAINodes[((PandoraBaseEnemyAI)self)._enemyRandom.Next(0, RoundManager.Instance.insideAINodes.Length)].transform.position;
				Vector3 randomNavMeshPositionInBoxPredictable = RoundManager.Instance.GetRandomNavMeshPositionInBoxPredictable(position2, 10f, default(NavMeshHit), ((PandoraBaseEnemyAI)self)._enemyRandom, -1, 1f);
				if (!(Vector3.Distance(randomNavMeshPositionInBoxPredictable, position) < self._minTeleportSeparation) && !(Vector3.Distance(randomNavMeshPositionInBoxPredictable, ((Component)self).transform.position) < 10f))
				{
					destination = randomNavMeshPositionInBoxPredictable;
					return true;
				}
			}
			destination = RoundManager.Instance.GetRandomNavMeshPositionInRadiusSpherical(position, 60f, default(NavMeshHit));
			return true;
		}

		private static bool IsEligible(PlayerControllerB? player)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			return (Object)(object)player != (Object)null && (Object)player != (Object)null && !player.isPlayerDead && player.isPlayerControlled;
		}

		private static bool CanSwap(PlayerControllerB source, PlayerControllerB candidate, float minSeparation)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			return IsEligible(source) && IsEligible(candidate) && source != candidate && Vector3.Distance(((Component)source).transform.position, ((Component)candidate).transform.position) >= minSeparation;
		}

		private static string FormatPlayers(IReadOnlyList<PlayerControllerB> players)
		{
			if (players.Count == 0)
			{
				return "[]";
			}
			return "[" + string.Join(", ", players.Select((PlayerControllerB player) => FormatPlayer(player))) + "]";
		}

		private static string FormatPlayer(PlayerControllerB player)
		{
			return $"{player.playerUsername} ({player.playerClientId})";
		}

		private static void Log(PandoraEnemyAI self, string message, bool relayToOtherClients = true)
		{
			NetworkEventLog.Write("[Swap] [" + EnemyLogLabel.For(self) + "] " + message, relayToOtherClients);
		}
	}
	internal readonly struct SwapTeleportStep
	{
		public PlayerControllerB Player { get; }

		public Vector3 Destination { get; }

		public float YRot { get; }

		public SwapTeleportStep(PlayerControllerB player, Vector3 destination, float yRot)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			Player = player;
			Destination = destination;
			YRot = yRot;
		}
	}
	internal readonly struct PlannedSwapPair
	{
		public PlayerControllerB Source { get; }

		public PlayerControllerB DestinationSource { get; }

		public PlannedSwapPair(PlayerControllerB source, PlayerControllerB destinationSource)
		{
			Source = source;
			DestinationSource = destinationSource;
		}
	}
	internal sealed class SwapTeleportPlan
	{
		public IReadOnlyList<SwapTeleportStep> Steps { get; }

		public IReadOnlyList<PlannedSwapPair> Pairs { get; }

		public SwapTeleportPlan(IReadOnlyList<SwapTeleportStep> steps, IReadOnlyList<PlannedSwapPair> pairs)
		{
			Steps = steps;
			Pairs = pairs;
		}
	}
}
namespace PandoraEnemyPatch.Runtime
{
	internal sealed class PandoraAiIntervalCoordinator
	{
		private static readonly FieldInfo StartHeartbeatDistanceField = typeof(PandoraEnemyAI).GetField("_startHeartbeatDistance", BindingFlags.Instance | BindingFlags.NonPublic);

		public void Execute(PandoraEnemyAI self)
		{
			//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_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Expected I4, but got Unknown
			if (!StartOfRound.Instance.allPlayersDead && !((EnemyAI)self).isEnemyDead)
			{
				SyncHeartbeatRange(self);
				UpdateHostAnimation(self);
				self._timeSinceLastAttack += ((EnemyAI)self).AIIntervalTime;
				TrackNewlySeenPlayers(self);
				PandoraState value = self._currentState.Value;
				PandoraState val = value;
				switch ((int)val)
				{
				case 0:
					self.DoIdle();
					break;
				case 1:
					self.DoAttacking();
					break;
				case 2:
					self.DoChasing();
					break;
				case 3:
					self.DoDeath();
					break;
				}
			}
		}

		private static void SyncHeartbeatRange(PandoraEnemyAI self)
		{
			float num2 = ((StartHeartbeatDistanceField.GetValue(self) is float num) ? num : 0f);
			if (!Mathf.Approximately(num2, 40f))
			{
				StartHeartbeatDistanceField.SetValue(self, 40f);
			}
		}

		private static void UpdateHostAnimation(PandoraEnemyAI self)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			if (((NetworkBehaviour)self).IsHost)
			{
				Animator creatureAnimator = ((EnemyAI)self).creatureAnimator;
				Vector3 velocity = ((EnemyAI)self).agent.velocity;
				creatureAnimator.SetFloat(PandoraEnemyAI.RunSpeedAnimation, ((Vector3)(ref velocity)).magnitude / 3f);
			}
		}

		private static void TrackNewlySeenPlayers(PandoraEnemyAI self)
		{
			PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts;
			foreach (PlayerControllerB val in allPlayerScripts)
			{
				if (!((Object)(object)val == (Object)null) && !val.isPlayerDead && val.isPlayerControlled && !self._playersSeenOverLifetime.Contains(val) && self.CanSeePlayer(val))
				{
					self._playersSeenOverLifetime.Add(val);
				}
			}
		}
	}
	internal static class PandoraAttackRecovery
	{
		private const string GroundPoundLogCategory = "GroundPound";

		public static List<PlayerControllerB> CapturePlayers(PandoraEnemyAI self)
		{
			List<PlayerControllerB> list = new List<PlayerControllerB>();
			if ((Object)(object)((EnemyAI)self).targetPlayer != (Object)null)
			{
				list.Add(((EnemyAI)self).targetPlayer);
			}
			foreach (PlayerControllerB item in self._playersTrappedThisAttack)
			{
				if (!list.Contains(item))
				{
					list.Add(item);
				}
			}
			PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController;
			if ((Object)(object)val != (Object)null)
			{
				PandoraAdditionalPlayerData orCreate = PandoraAdditionalPlayerData.GetOrCreate(val);
				if ((Object)(object)orCreate.inAnimationWithPandora == (Object)(object)self && !list.Contains(val))
				{
					list.Add(val);
				}
			}
			return list;
		}

		public static void ReleasePlayers(PandoraEnemyAI self, IReadOnlyList<PlayerControllerB> players, NextAttack completedAttack)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			if ((int)completedAttack == 0)
			{
				GroundPoundEscapeCue.Cancel(self);
			}
			foreach (PlayerControllerB player in players)
			{
				if (!((Object)(object)player == (Object)null) && !((Object)player == (Object)null))
				{
					player.disableMoveInput = false;
					PandoraAdditionalPlayerData orCreate = PandoraAdditionalPlayerData.GetOrCreate(player);
					if ((Object)(object)orCreate.inAnimationWithPandora == (Object)(object)self)
					{
						orCreate.OverrideSinkingValue = 0f;
						orCreate.inAnimationWithPandora = null;
					}
				}
			}
		}

		public static void ReleaseGroundPoundPlayer(PandoraEnemyAI self, PlayerControllerB? player)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			if (!((Object)(object)player == (Object)null) && !((Object)player == (Object)null))
			{
				GroundPoundEscapeCue.Cancel(self);
				player.disableMoveInput = false;
				NetworkEventLog.Write(string.Format("[{0}] [{1}] released {2} ({3}) after sinking resolved.", "GroundPound", EnemyLogLabel.For(self), player.playerUsername, player.playerClientId), relayToOtherClients: false);
			}
		}
	}
	internal static class PandoraStareTimerTuning
	{
		private const float OriginalStareDecayPerSecond = 1f;

		private const float DesiredStareDecayPerSecond = 0.5f;

		private const float CompensationPerSecond = 0.5f;

		private const float StareCheckRange = 30f;

		private const int StareCheckMask = 60;

		private const float StareCheckProximity = -1f;

		private static readonly FieldInfo StareTimerField = typeof(PandoraEnemyAI).GetField("_stareTimer", BindingFlags.Instance | BindingFlags.NonPublic);

		public static void Apply(PandoraEnemyAI self)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController;
			if (!((Object)(object)val == (Object)null) && !val.isPlayerDead && !val.HasLineOfSightToPosition(((EnemyAI)self).eye.position, 30f, 60, -1f))
			{
				float currentStareTimer = GetCurrentStareTimer(self);
				if (!(currentStareTimer <= 0f))
				{
					float num = currentStareTimer + 0.5f * Time.deltaTime;
					StareTimerField.SetValue(self, num);
				}
			}
		}

		internal static float GetCurrentStareTimer(PandoraEnemyAI self)
		{
			return (float)(StareTimerField.GetValue(self) ?? ((object)0f));
		}
	}
}
namespace PandoraEnemyPatch.Networking
{
	internal readonly struct ResolvedPlayerReference
	{
		public object RawReference { get; }

		public PlayerControllerB? Player { get; }

		public bool HasPlayer => (Object)(object)Player != (Object)null;

		public ResolvedPlayerReference(object rawReference, PlayerControllerB? player)
		{
			RawReference = rawReference;
			Player = player;
		}

		public string FormatLogDetails()
		{
			return "refType=" + (RawReference?.GetType().FullName ?? "<null>") + ", player=" + (((Object)(object)Player != (Object)null) ? Player.playerUsername : "<null>") + ", clientId=" + (((Object)(object)Player != (Object)null) ? Player.actualClientId.ToString() : "<null>");
		}

		public static ResolvedPlayerReference From(object rawReference)
		{
			PlayerControllerReference val = (PlayerControllerReference)((rawReference is PlayerControllerReference) ? rawReference : null);
			if (val != null)
			{
				PlayerControllerB player = PlayerControllerReference.op_Implicit(val);
				return new ResolvedPlayerReference(rawReference, player);
			}
			PlayerControllerB val2 = (PlayerControllerB)((rawReference is PlayerControllerB) ? rawReference : null);
			if (val2 != null)
			{
				return new ResolvedPlayerReference(rawReference, val2);
			}
			return new ResolvedPlayerReference(rawReference, null);
		}
	}
}
namespace PandoraEnemyPatch.Logging
{
	internal static class EnemyLogLabel
	{
		public static string For(PandoraEnemyAI self)
		{
			NetworkObject networkObject = ((NetworkBehaviour)self).NetworkObject;
			if ((Object)(object)networkObject != (Object)null)
			{
				return $"{((EnemyAI)self).enemyType.enemyName}(net::{networkObject.NetworkObjectId})";
			}
			return ((EnemyAI)self).enemyType.enemyName + "(unspawned)";
		}
	}
	internal static class NetworkEventLog
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static Action<NetworkManager> <0>__OnNetworkManagerInstantiated;

			public static Action<NetworkManager> <1>__OnNetworkManagerDestroying;

			public static Action <2>__OnNetworkStarted;

			public static Action<bool> <3>__OnNetworkStopped;

			public static HandleNamedMessageDelegate <4>__ReceiveRelayedLog;
		}

		private const string RelayMessageName = "PandoraEnemyPatch.NetworkEventLog";

		private const int RelayMessageBufferCapacity = 2048;

		private static bool _initialized;

		private static NetworkManager? _attachedNetworkManager;

		private static CustomMessagingManager? _registeredManager;

		public static void Write(string message, bool relayToOtherClients = true)
		{
			Plugin.Logger.LogDebug((object)message);
			if (relayToOtherClients)
			{
				TrySendToOtherClients(message);
			}
		}

		public static void Info(string message, bool relayToOtherClients = true)
		{
			Plugin.Logger.LogInfo((object)message);
			if (relayToOtherClients)
			{
				TrySendToOtherClients(message);
			}
		}

		public static void EnsureInitialized()
		{
			if (!_initialized)
			{
				_initialized = true;
				NetworkManager.OnInstantiated += OnNetworkManagerInstantiated;
				NetworkManager.OnDestroying += OnNetworkManagerDestroying;
				if ((Object)(object)NetworkManager.Singleton != (Object)null)
				{
					AttachToNetworkManager(NetworkManager.Singleton);
				}
			}
		}

		private static void TrySendToOtherClients(string message)
		{
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton == (Object)null || !singleton.IsListening || !singleton.IsServer)
			{
				return;
			}
			RegisterHandlerIfNeeded();
			List<ulong> list = singleton.ConnectedClientsIds.Where((ulong clientId) => clientId != 0).ToList();
			if (list.Count == 0)
			{
				return;
			}
			FastBufferWriter val = default(FastBufferWriter);
			((FastBufferWriter)(ref val))..ctor(2048, (Allocator)2, -1);
			try
			{
				((FastBufferWriter)(ref val)).WriteValueSafe(message, false);
				singleton.CustomMessagingManager.SendNamedMessage("PandoraEnemyPatch.NetworkEventLog", (IReadOnlyList<ulong>)list, val, (NetworkDelivery)3);
			}
			finally
			{
				((IDisposable)(FastBufferWriter)(ref val)).Dispose();
			}
		}

		private static void AttachToNetworkManager(NetworkManager manager)
		{
			if (_attachedNetworkManager != manager)
			{
				DetachFromNetworkManager(_attachedNetworkManager);
				_attachedNetworkManager = manager;
				manager.OnServerStarted += OnNetworkStarted;
				manager.OnClientStarted += OnNetworkStarted;
				manager.OnServerStopped += OnNetworkStopped;
				manager.OnClientStopped += OnNetworkStopped;
				if (manager.IsListening)
				{
					RegisterHandlerIfNeeded();
				}
			}
		}

		private static void DetachFromNetworkManager(NetworkManager? manager)
		{
			if (!((Object)(object)manager == (Object)null))
			{
				manager.OnServerStarted -= OnNetworkStarted;
				manager.OnClientStarted -= OnNetworkStarted;
				manager.OnServerStopped -= OnNetworkStopped;
				manager.OnClientStopped -= OnNetworkStopped;
				UnregisterHandler();
				if (_attachedNetworkManager == manager)
				{
					_attachedNetworkManager = null;
				}
			}
		}

		private static void RegisterHandlerIfNeeded()
		{
			//IL_0053: 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_005e: Expected O, but got Unknown
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton == (Object)null || !singleton.IsListening)
			{
				return;
			}
			CustomMessagingManager customMessagingManager = singleton.CustomMessagingManager;
			if (_registeredManager != customMessagingManager)
			{
				UnregisterHandler();
				object obj = <>O.<4>__ReceiveRelayedLog;
				if (obj == null)
				{
					HandleNamedMessageDelegate val = ReceiveRelayedLog;
					<>O.<4>__ReceiveRelayedLog = val;
					obj = (object)val;
				}
				customMessagingManager.RegisterNamedMessageHandler("PandoraEnemyPatch.NetworkEventLog", (HandleNamedMessageDelegate)obj);
				_registeredManager = customMessagingManager;
			}
		}

		private static void UnregisterHandler()
		{
			if (_registeredManager != null)
			{
				_registeredManager.UnregisterNamedMessageHandler("PandoraEnemyPatch.NetworkEventLog");
				_registeredManager = null;
			}
		}

		private static void OnNetworkManagerInstantiated(NetworkManager manager)
		{
			AttachToNetworkManager(manager);
		}

		private static void OnNetworkManagerDestroying(NetworkManager manager)
		{
			DetachFromNetworkManager(manager);
		}

		private static void OnNetworkStarted()
		{
			RegisterHandlerIfNeeded();
		}

		private static void OnNetworkStopped(bool _)
		{
			UnregisterHandler();
		}

		private static void ReceiveRelayedLog(ulong senderClientId, FastBufferReader payload)
		{
			string arg = default(string);
			((FastBufferReader)(ref payload)).ReadValueSafe(ref arg, false);
			Plugin.Logger.LogDebug((object)$"[Remote:{senderClientId}] {arg}");
		}
	}
}
namespace PandoraEnemyPatch.Hooks
{
	internal static class PandoraHookRegistrar
	{
		private delegate void EnemyAIIntervalInvoker(EnemyAI self);

		private delegate bool CanSeePlayerOrig(PandoraEnemyAI self, PlayerControllerB player);

		private delegate bool CanSeePlayerHook(CanSeePlayerOrig orig, PandoraEnemyAI self, PlayerControllerB player);

		private delegate void PandoraEnemyAIOrig(PandoraEnemyAI self);

		private delegate void PandoraEnemyAIHook(PandoraEnemyAIOrig orig, PandoraEnemyAI self);

		private delegate void TeleportPlayerClientRPCOrig(PandoraEnemyAI self, PlayerControllerReference playerRef, Vector3 position, float yRot);

		private delegate void TeleportPlayerClientRPCHook(TeleportPlayerClientRPCOrig orig, PandoraEnemyAI self, PlayerControllerReference playerRef, Vector3 position, float yRot);

		private delegate void SetTeleportTrappedClientRPCOrig(PandoraEnemyAI self, PlayerControllerReference playerRef, bool trapped);

		private delegate void SetTeleportTrappedClientRPCHook(SetTeleportTrappedClientRPCOrig orig, PandoraEnemyAI self, PlayerControllerReference playerRef, bool trapped);

		private delegate void BeginSinkingClientRPCOrig(PandoraEnemyAI self, PlayerControllerReference playerRef);

		private delegate void BeginSinkingClientRPCHook(BeginSinkingClientRPCOrig orig, PandoraEnemyAI self, PlayerControllerReference playerRef);

		private delegate IEnumerator DoGroundPoundSinkingOrig(PandoraEnemyAI self, PlayerControllerB player, float duration, float waitDuration);

		private delegate IEnumerator DoGroundPoundSinkingHook(DoGroundPoundSinkingOrig orig, PandoraEnemyAI self, PlayerControllerB player, float duration, float waitDuration);

		private delegate bool TeleportAndSwapIsValidOrig(PandoraEnemyAI self);

		private delegate bool TeleportAndSwapIsValidHook(TeleportAndSwapIsValidOrig orig, PandoraEnemyAI self);

		private delegate void PerformSwapTeleportOrig(PandoraEnemyAI self, List<PlayerControllerB> players);

		private delegate void PerformSwapTeleportHook(PerformSwapTeleportOrig orig, PandoraEnemyAI self, List<PlayerControllerB> players);

		private delegate void KillEnemyOrig(PandoraEnemyAI self, bool destroy);

		private delegate void KillEnemyHook(KillEnemyOrig orig, PandoraEnemyAI self, bool destroy);

		private delegate void HeartbeatAudioHandlerOrig(HeartbeatAudioHandler self);

		private delegate void HeartbeatAudioHandlerHook(HeartbeatAudioHandlerOrig orig, HeartbeatAudioHandler self);

		private delegate void PandoraEnemyAIUpdateOrig(PandoraEnemyAI self);

		private delegate void PandoraEnemyAIUpdateHook(PandoraEnemyAIUpdateOrig orig, PandoraEnemyAI self);

		private readonly struct RpcLogContext
		{
			public PlayerControllerReference PlayerReference { get; }

			public ResolvedPlayerReference Resolved { get; }

			public bool ShouldLogExecution { get; }

			public RpcLogContext(PlayerControllerReference playerReference, ResolvedPlayerReference resolved, bool shouldLogExecution)
			{
				PlayerReference = playerReference;
				Resolved = resolved;
				ShouldLogExecution = shouldLogExecution;
			}

			public string FormatPlayerReferenceDetails()
			{
				return Resolved.FormatLogDetails();
			}
		}

		[CompilerGenerated]
		private sealed class <PandoraEnemyAI_DoGroundPoundSinking>d__48 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public DoGroundPoundSinkingOrig orig;

			public PandoraEnemyAI self;

			public PlayerControllerB player;

			public float duration;

			public float waitDuration;

			private IEnumerator <routine>5__1;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <PandoraEnemyAI_DoGroundPoundSinking>d__48(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<routine>5__1 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<routine>5__1 = orig(self, player, duration, waitDuration);
					break;
				case 1:
					<>1__state = -1;
					break;
				}
				if (<routine>5__1.MoveNext())
				{
					<>2__current = <routine>5__1.Current;
					<>1__state = 1;
					return true;
				}
				PandoraAttackRecovery.ReleaseGroundPoundPlayer(self, player);
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private const int AvailableAttackCapacity = 3;

		private const int ClientRpcExecutionStage = 1;

		private const string HooksLogCategory = "Hooks";

		private const string VisionLogCategory = "Vision";

		private const string RpcLogCategory = "RPC";

		private static readonly FieldInfo RpcExecStageField = typeof(NetworkBehaviour).GetField("__rpc_exec_stage", BindingFlags.Instance | BindingFlags.NonPublic);

		private static readonly EnemyAIIntervalInvoker InvokeEnemyAIBaseDoAIInterval = CreateEnemyAIBaseDoAIIntervalInvoker();

		private static readonly List<Hook> ActiveHooks = new List<Hook>();

		private static bool _registered;

		private static readonly PandoraTargetingService TargetingService = new PandoraTargetingService();

		private static readonly PandoraChaseController ChaseController = new PandoraChaseController(TargetingService);

		private static readonly PandoraAiIntervalCoordinator AiIntervalCoordinator = new PandoraAiIntervalCoordinator();

		private static readonly SwapPlanner SwapPlanner = new SwapPlanner();

		private static readonly PandoraAttackSelector AttackSelector = new PandoraAttackSelector();

		private static bool _loggedCanSeeHook;

		internal static void Register()
		{
			if (!_registered)
			{
				RegisterTargetingAndChaseHooks();
				RegisterAttackHooks();
				RegisterRpcHooks();
				RegisterGroundPoundHooks();
				RegisterHeartbeatHooks();
				_registered = true;
				Log("Hooks", "Registered Pandora hooks");
			}
		}

		private static bool PandoraEnemyAI_CanSeePlayer(CanSeePlayerOrig orig, PandoraEnemyAI self, PlayerControllerB player)
		{
			if (!_loggedCanSeeHook)
			{
				_loggedCanSeeHook = true;
				Log("Vision", "PandoraEnemyAI.CanSeePlayer hook is running.");
			}
			TargetObservation observation;
			return PandoraTargetingService.TryObserve(self, player, out observation);
		}

		private static void PandoraEnemyAI_DoAIInterval(PandoraEnemyAIOrig orig, PandoraEnemyAI self)
		{
			InvokeEnemyAIBaseDoAIInterval((EnemyAI)(object)self);
			AiIntervalCoordinator.Execute(self);
		}

		private static void PandoraEnemyAI_DoChasing(PandoraEnemyAIOrig orig, PandoraEnemyAI self)
		{
			ChaseController.Execute(self);
		}

		private static void PandoraEnemyAI_DoIdle(PandoraEnemyAIOrig orig, PandoraEnemyAI self)
		{
			ChaseController.ExecuteIdle(self);
		}

		private static void PandoraEnemyAI_ChooseNextAttack(PandoraEnemyAIOrig orig, PandoraEnemyAI self)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			List<NextAttack> list = new List<NextAttack>(3)
			{
				(NextAttack)1,
				(NextAttack)0
			};
			if (self.TeleportAndSwapIsValid())
			{
				list.Add((NextAttack)2);
			}
			self._nextAttack = AttackSelector.ChooseNextAttack(self, list);
		}

		private static void PandoraEnemyAI_Update(PandoraEnemyAIUpdateOrig orig, PandoraEnemyAI self)
		{
			orig(self);
			PandoraStareTimerTuning.Apply(self);
		}

		private static void PandoraEnemyAI_TeleportPlayerClientRPC(TeleportPlayerClientRPCOrig orig, PandoraEnemyAI self, PlayerControllerReference playerRef, Vector3 position, float yRot)
		{
			//IL_0016: 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)
			RpcLogContext context = CreateRpcLogContext(self, playerRef);
			LogRpcIfNeeded(context, $"TeleportPlayerClientRPC {context.FormatPlayerReferenceDetails()}, pos={position}, yRot={yRot}");
			orig(self, playerRef, position, yRot);
		}

		private static void PandoraEnemyAI_SetTeleportTrappedClientRPC(SetTeleportTrappedClientRPCOrig orig, PandoraEnemyAI self, PlayerControllerReference playerRef, bool trapped)
		{
			RpcLogContext context = CreateRpcLogContext(self, playerRef);
			LogRpcIfNeeded(context, $"SetTeleportTrappedClientRPC {context.FormatPlayerReferenceDetails()}, trapped={trapped}");
			orig(self, playerRef, trapped);
		}

		private static void PandoraEnemyAI_BeginSinkingClientRPC(BeginSinkingClientRPCOrig orig, PandoraEnemyAI self, PlayerControllerReference playerRef)
		{
			RpcLogContext context = CreateRpcLogContext(self, playerRef);
			LogRpcIfNeeded(context, "BeginSinkingClientRPC " + context.FormatPlayerReferenceDetails());
			orig(self, playerRef);
			if (context.ShouldLogExecution)
			{
				GroundPoundEscapeCue.ShowForLocalPlayer(self, context.Resolved.Player);
			}
		}

		[IteratorStateMachine(typeof(<PandoraEnemyAI_DoGroundPoundSinking>d__48))]
		private static IEnumerator PandoraEnemyAI_DoGroundPoundSinking(DoGroundPoundSinkingOrig orig, PandoraEnemyAI self, PlayerControllerB player, float duration, float waitDuration)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <PandoraEnemyAI_DoGroundPoundSinking>d__48(0)
			{
				orig = orig,
				self = self,
				player = player,
				duration = duration,
				waitDuration = waitDuration
			};
		}

		private static bool PandoraEnemyAI_TeleportAndSwapIsValid(TeleportAndSwapIsValidOrig orig, PandoraEnemyAI self)
		{
			return SwapPlanner.CanBuildAnySwap(self);
		}

		private static void PandoraEnemyAI_PerformSwapTeleport(PerformSwapTeleportOrig orig, PandoraEnemyAI self, List<PlayerControllerB> players)
		{
			SwapPlanner.SwapResolution resolution = SwapPlanner.Resolve(self, players);
			SwapPlanner.Execute(self, resolution);
		}

		private static void PandoraEnemyAI_FinishAttack(PandoraEnemyAIOrig orig, PandoraEnemyAI self)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			List<PlayerControllerB> players = PandoraAttackRecovery.CapturePlayers(self);
			NextAttack nextAttack = self._nextAttack;
			orig(self);
			PandoraAttackRecovery.ReleasePlayers(self, players, nextAttack);
		}

		private static void PandoraEnemyAI_KillEnemy(KillEnemyOrig orig, PandoraEnemyAI self, bool destroy)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			List<PlayerControllerB> players = PandoraAttackRecovery.CapturePlayers(self);
			orig(self, destroy);
			PandoraAttackRecovery.ReleasePlayers(self, players, self._nextAttack);
		}

		private static bool ShouldLogClientRpcExecution(NetworkBehaviour behaviour)
		{
			object value = RpcExecStageField.GetValue(behaviour);
			if (value == null)
			{
				return true;
			}
			return (int)value == 1;
		}

		private static RpcLogContext CreateRpcLogContext(PandoraEnemyAI self, PlayerControllerReference playerRef)
		{
			return new RpcLogContext(playerRef, ResolvedPlayerReference.From(playerRef), ShouldLogClientRpcExecution((NetworkBehaviour)(object)self));
		}

		private static void LogRpcIfNeeded(RpcLogContext context, string message)
		{
			if (context.ShouldLogExecution)
			{
				Log("RPC", message);
			}
		}

		private static EnemyAIIntervalInvoker CreateEnemyAIBaseDoAIIntervalInvoker()
		{
			MethodInfo method = typeof(EnemyAI).GetMethod("DoAIInterval", BindingFlags.Instance | BindingFlags.Public);
			DynamicMethod dynamicMethod = new DynamicMethod("PandoraEnemyPatch_InvokeEnemyAIBaseDoAIInterval", typeof(void), new Type[1] { typeof(EnemyAI) }, typeof(PandoraHookRegistrar).Module, skipVisibility: true);
			ILGenerator iLGenerator = dynamicMethod.GetILGenerator();
			iLGenerator.Emit(OpCodes.Ldarg_0);
			iLGenerator.Emit(OpCodes.Call, method);
			iLGenerator.Emit(OpCodes.Ret);
			return (EnemyAIIntervalInvoker)dynamicMethod.CreateDelegate(typeof(EnemyAIIntervalInvoker));
		}

		private static void HeartbeatAudioHandler_Update(HeartbeatAudioHandlerOrig orig, HeartbeatAudioHandler self)
		{
			AudioSource component = ((Component)self).GetComponent<AudioSource>();
			float currentTime = self._currentTime;
			orig(self);
			if (!((Object)(object)component == (Object)null))
			{
				GroundPoundHeartbeatIntensity.ApplyIfActive(self, component);
				float currentTime2 = self._currentTime;
				if (currentTime2 < currentTime)
				{
					GroundPoundEscapeCue.PulseFromHeartbeat(self);
				}
			}
		}

		private static void RegisterTargetingAndChaseHooks()
		{
			RegisterPandoraHook("CanSeePlayer", new Type[1] { typeof(PlayerControllerB) }, new CanSeePlayerHook(PandoraEnemyAI_CanSeePlayer));
			RegisterPandoraHook("Update", Type.EmptyTypes, new PandoraEnemyAIUpdateHook(PandoraEnemyAI_Update));
			RegisterPandoraHook("DoAIInterval", Type.EmptyTypes, new PandoraEnemyAIHook(PandoraEnemyAI_DoAIInterval));
			RegisterPandoraHook("DoIdle", Type.EmptyTypes, new PandoraEnemyAIHook(PandoraEnemyAI_DoIdle));
			RegisterPandoraHook("DoChasing", Type.EmptyTypes, new PandoraEnemyAIHook(PandoraEnemyAI_DoChasing));
		}

		private static void RegisterAttackHooks()
		{
			RegisterPandoraHook("ChooseNextAttack", Type.EmptyTypes, new PandoraEnemyAIHook(PandoraEnemyAI_ChooseNextAttack));
			RegisterPandoraHook("FinishAttack", Type.EmptyTypes, new PandoraEnemyAIHook(PandoraEnemyAI_FinishAttack));
			RegisterPandoraHook("TeleportAndSwapIsValid", Type.EmptyTypes, new TeleportAndSwapIsValidHook(PandoraEnemyAI_TeleportAndSwapIsValid));
			RegisterPandoraHook("PerformSwapTeleport", new Type[1] { typeof(List<PlayerControllerB>) }, new PerformSwapTeleportHook(PandoraEnemyAI_PerformSwapTeleport));
			RegisterPandoraHook("KillEnemy", new Type[1] { typeof(bool) }, new KillEnemyHook(PandoraEnemyAI_KillEnemy));
		}

		private static void RegisterRpcHooks()
		{
			RegisterPandoraHook("TeleportPlayerClientRPC", new Type[3]
			{
				typeof(PlayerControllerReference),
				typeof(Vector3),
				typeof(float)
			}, new TeleportPlayerClientRPCHook(PandoraEnemyAI_TeleportPlayerClientRPC));
			RegisterPandoraHook("SetTeleportTrappedClientRPC", new Type[2]
			{
				typeof(PlayerControllerReference),
				typeof(bool)
			}, new SetTeleportTrappedClientRPCHook(PandoraEnemyAI_SetTeleportTrappedClientRPC));
			RegisterPandoraHook("BeginSinkingClientRPC", new Type[1] { typeof(PlayerControllerReference) }, new BeginSinkingClientRPCHook(PandoraEnemyAI_BeginSinkingClientRPC));
		}

		private static void RegisterGroundPoundHooks()
		{
			RegisterPandoraHook("DoGroundPoundSinking", new Type[3]
			{
				typeof(PlayerControllerB),
				typeof(float),
				typeof(float)
			}, new DoGroundPoundSinkingHook(PandoraEnemyAI_DoGroundPoundSinking));
		}

		private static void RegisterHeartbeatHooks()
		{
			RegisterHook(typeof(HeartbeatAudioHandler), "Update", Type.EmptyTypes, new HeartbeatAudioHandlerHook(HeartbeatAudioHandler_Update));
		}

		private static void RegisterPandoraHook(string methodName, Type[] parameterTypes, Delegate hookDelegate)
		{
			RegisterHook(typeof(PandoraEnemyAI), methodName, parameterTypes, hookDelegate);
		}

		private static void RegisterHook(Type declaringType, string methodName, Type[] parameterTypes, Delegate hookDelegate)
		{
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Expected O, but got Unknown
			MethodInfo method = declaringType.GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, parameterTypes, null);
			if (method == null)
			{
				string text = string.Join(", ", parameterTypes.Select((Type type) => type.Name));
				throw new MissingMethodException(declaringType.FullName, methodName + "(" + text + ")");
			}
			ActiveHooks.Add(new Hook((MethodBase)method, hookDelegate));
		}

		private static void Log(string category, string message)
		{
			NetworkEventLog.Write("[" + category + "] " + message);
		}

		private static void Log(PandoraEnemyAI self, string category, string message, bool relayToOtherClients = false)
		{
			NetworkEventLog.Write("[" + category + "] [" + EnemyLogLabel.For(self) + "] " + message, relayToOtherClients);
		}
	}
}
namespace PandoraEnemyPatch.Chasing
{
	internal sealed class PandoraChaseController
	{
		private const string LogCategory = "Chase";

		private const double RareLostTargetDisappearChance = 0.2;

		private readonly PandoraTargetingService _targetingService;

		private const float MinGiveUpSeconds = 25f;

		private const float MaxGiveUpSeconds = 35f;

		private const float AttackCooldownSeconds = 30f;

		private const float GroundPoundAttackRange = 2f;

		private const float TeleportAttackRange = 4f;

		private int? _lastLoggedTargetClientId;

		private bool _hasRolledCurrentMutualSightLoss;

		public PandoraChaseController(PandoraTargetingService targetingService)
		{
			_targetingService = targetingService;
		}

		public void Execute(PandoraEnemyAI self)
		{
			//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
			self._giveUpTimer -= ((EnemyAI)self).AIIntervalTime;
			if (HasGivenUp(self))
			{
				Log(self, $"giving up chase after timer expired (remaining={self._giveUpTimer:0.##}).");
				ReturnToIdle(self);
				return;
			}
			PlayerControllerB target;
			bool flag = TryRefreshTarget(self, out target);
			bool flag2 = _targetingService.CanAnyEligiblePlayerSeePandora(self);
			if (flag || flag2)
			{
				_hasRolledCurrentMutualSightLoss = false;
			}
			if (!flag && !flag2 && !_hasRolledCurrentMutualSightLoss)
			{
				_hasRolledCurrentMutualSightLoss = true;
				Log(self, $"running vanish check after mutual sight loss (remaining={self._giveUpTimer:0.##}, chancePercent={FormatPercent(0.2)}, pandoraCanSeePlayer={flag}, playerCanSeePandora={flag2}).");
				if (ShouldDisappearAfterLosingAllTargets(self, out var roll))
				{
					Log(self, "vanishing after mutual sight loss (rollPercent=" + FormatPercent(roll) + ", chancePercent=" + FormatPercent(0.2) + ").");
					ReturnToIdle(self);
					return;
				}
				Log(self, $"kept chasing after mutual sight loss (rollPercent={FormatPercent(roll)}, chancePercent={FormatPercent(0.2)}, remaining={self._giveUpTimer:0.##}).");
			}
			if ((Object)(object)target == (Object)null)
			{
				if (flag)
				{
				}
				return;
			}
			if (PandoraTargetingService.TryObserve(self, target, out var _))
			{
				self._giveUpTimer = NextGiveUpTime(self);
			}
			((PandoraBaseEnemyAI)self).smartAgentNavigator.DoPathingToDestination(((Component)target).transform.position);
			if (CanBeginAttack(self, target))
			{
				BeginAttack(self, target);
			}
		}

		public void ExecuteIdle(PandoraEnemyAI self)
		{
			self._giveUpTimer -= ((EnemyAI)self).AIIntervalTime;
			PlayerControllerB val = default(PlayerControllerB);
			if (self._giveUpTimer < 0f)
			{
				self.TeleportAndResetSearchRoutine();
				self._giveUpTimer = NextGiveUpTime(self);
			}
			else if (self.TryGetClosestChaseTarget(ref val) && !((Object)(object)val == (Object)null))
			{
				((EnemyAI)self).targetPlayer = val;
				self._currentState.Value = (PandoraState)2;
				self._timeSinceLastAttack = 30f;
				self.ChooseNextAttack();
				((PandoraBaseEnemyAI)self).smartAgentNavigator.StopSearchRoutine();
				((PandoraBaseEnemyAI)self).smartAgentNavigator.StopAgent();
				self._giveUpTimer = NextGiveUpTime(self);
				Log(self, $"target -> {val.playerUsername} ({val.playerClientId})");
			}
		}

		private bool TryRefreshTarget(PandoraEnemyAI self, out PlayerControllerB? target)
		{
			PlayerControllerB targetPlayer = ((EnemyAI)self).targetPlayer;
			if (_targetingService.TryGetClosestVisibleTarget(self, out PlayerControllerB player) && (Object)(object)player != (Object)null)
			{
				if (targetPlayer != player)
				{
					((EnemyAI)self).targetPlayer = player;
					self._giveUpTimer = NextGiveUpTime(self);
					LogTargetChange(self, player);
				}
				target = player;
				return true;
			}
			if ((Object)(object)targetPlayer != (Object)null && !targetPlayer.isPlayerDead && targetPlayer.isPlayerControlled && targetPlayer.isInsideFactory)
			{
				target = targetPlayer;
				return false;
			}
			((EnemyAI)self).targetPlayer = null;
			LogTargetChange(self, null);
			target = null;
			return false;
		}

		private void LogTargetChange(PandoraEnemyAI self, PlayerControllerB? newTarget)
		{
			int? num = (((Object)(object)newTarget != (Object)null) ? new int?((int)newTarget.playerClientId) : null);
			if (_lastLoggedTargetClientId != num)
			{
				_lastLoggedTargetClientId = num;
				if ((Object)(object)newTarget == (Object)null)
				{
					Log(self, "target cleared");
				}
				else
				{
					Log(self, $"target -> {newTarget.playerUsername} ({newTarget.playerClientId})");
				}
			}
		}

		private static bool HasGivenUp(PandoraEnemyAI self)
		{
			return self._giveUpTimer < 0f;
		}

		private static bool ShouldDisappearAfterLosingAllTargets(PandoraEnemyAI self, out double roll)
		{
			roll = ((PandoraBaseEnemyAI)self)._enemyRandom.NextDouble();
			return roll < 0.2;
		}

		private static string FormatPercent(double value)
		{
			return $"{value * 100.0:0.00}%";
		}

		private static void ReturnToIdle(PandoraEnemyAI self)
		{
			self._currentState.Value = (PandoraState)0;
			self.TeleportAndResetSearchRoutine();
			self._giveUpTimer = NextGiveUpTime(self);
		}

		private static float NextGiveUpTime(PandoraEnemyAI self)
		{
			return 25f + (float)((PandoraBaseEnemyAI)self)._enemyRandom.NextDouble() * 10f;
		}

		private static bool CanBeginAttack(PandoraEnemyAI self, PlayerControllerB target)
		{
			//IL_0007: 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_001e: Unknown result type (might be due to invalid IL or missing references)
			float num = Vector3.Distance(((Component)self).transform.position, ((Component)target).transform.position);
			float num2 = (((int)self._nextAttack == 0) ? 2f : 4f);
			return num < num2 && self._timeSinceLastAttack >= 30f;
		}

		private static void BeginAttack(PandoraEnemyAI self, PlayerControllerB target)
		{
			//IL_0044: 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_0071: Invalid comparison between Unknown and I4
			self._currentState.Value = (PandoraState)1;
			self._playersTrappedThisAttack.Clear();
			((PandoraBaseEnemyAI)self).smartAgentNavigator.StopAgent();
			((PandoraBaseEnemyAI)self).creatureNetworkAnimator.SetTrigger(Animator.StringToHash("armsRaised"), true);
			Log(self, $"starting attack '{self._nextAttack}' on {target.playerUsername} ({target.playerClientId})");
			if ((int)self._nextAttack == 0)
			{
				self.BeginSinkingClientRPC(PlayerControllerReference.op_Implicit(target));
				return;
			}
			List<PlayerControllerB> playersNearby = self.GetPlayersNearby(10f);
			foreach (PlayerControllerB item in playersNearby)
			{
				if (!self._playersTrappedThisAttack.Contains(item))
				{
					self._playersTrappedThisAttack.Add(item);
				}
			}
			self.TrapPlayersForTeleportAttack(playersNearby);
		}

		private static void Log(PandoraEnemyAI self, string message)
		{
			NetworkEventLog.Write("[Chase] [" + EnemyLogLabel.For(self) + "] " + message);
		}
	}
}
namespace PandoraEnemyPatch.Attacks
{
	internal sealed class PandoraAttackSelector
	{
		private sealed class AttackHistory
		{
			private const int RecentWindowSize = 3;

			private const int BaseAttackWeight = 10;

			private const int RecentMatchPenalty = 2;

			private const int RepeatStreakPenalty = 3;

			private const int MinimumAttackWeight = 1;

			private readonly Queue<NextAttack> _recentAttacks = new Queue<NextAttack>();

			public int GetWeight(NextAttack attack)
			{
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				//IL_001b: Unknown result type (might be due to invalid IL or missing references)
				//IL_001e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0020: Unknown result type (might be due to invalid IL or missing references)
				//IL_005f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0064: 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_0069: Unknown result type (might be due to invalid IL or missing references)
				int num = 0;
				int num2 = 0;
				foreach (NextAttack recentAttack in _recentAttacks)
				{
					if (recentAttack == attack)
					{
						num++;
					}
				}
				foreach (NextAttack item in _recentAttacks.Reverse())
				{
					if (item != attack)
					{
						break;
					}
					num2++;
				}
				int num3 = 10;
				num3 -= num * 2;
				num3 -= num2 * 3;
				return (num3 < 1) ? 1 : num3;
			}

			public void Record(NextAttack attack)
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				_recentAttacks.Enqueue(attack);
				while (_recentAttacks.Count > 3)
				{
					_recentAttacks.Dequeue();
				}
			}
		}

		private const string LogCategory = "Attack";

		private readonly Dictionary<int, AttackHistory> _historyByEnemyId = new Dictionary<int, AttackHistory>();

		public NextAttack ChooseNextAttack(PandoraEnemyAI self, IReadOnlyList<NextAttack> availableAttacks)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: 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_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_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: 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_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			AttackHistory history = GetHistory(self);
			List<(NextAttack, int)> list = new List<(NextAttack, int)>(availableAttacks.Count);
			int num = 0;
			foreach (NextAttack availableAttack in availableAttacks)
			{
				int weight = history.GetWeight(availableAttack);
				list.Add((availableAttack, weight));
				num += weight;
			}
			int num2 = ((PandoraBaseEnemyAI)self)._enemyRandom.Next(0, num);
			NextAttack val = list[0].Item1;
			foreach (var (val2, num3) in list)
			{
				if (num2 < num3)
				{
					val = val2;
					break;
				}
				num2 -= num3;
			}
			history.Record(val);
			Log(self, "available=[" + string.Join(", ", availableAttacks) + "] weights=[" + string.Join(", ", list.Select<(NextAttack, int), string>(((NextAttack Attack, int Weight) candidate) => $"{candidate.Attack}:{candidate.Weight}")) + "] " + $"chosen={val}");
			return val;
		}

		private static void Log(PandoraEnemyAI self, string message)
		{
			NetworkEventLog.Write("[Attack] [" + EnemyLogLabel.For(self) + "] " + message);
		}

		private AttackHistory GetHistory(PandoraEnemyAI self)
		{
			int instanceID = ((Object)self).GetInstanceID();
			if (_historyByEnemyId.TryGetValue(instanceID, out AttackHistory value))
			{
				return value;
			}
			value = new AttackHistory();
			_historyByEnemyId[instanceID] = value;
			return value;
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}