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)
{
}
}
}