Decompiled source of BRUTALISTICKMP v0.3.7

Mods/Mods/BrutalistickMP.dll

Decompiled a week ago
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BrutalistickMP;
using BrutalistickMP.Core;
using HarmonyLib;
using Il2Cpp;
using Il2CppActiveRagdoll;
using Il2CppHexabodyVR.PlayerController;
using Il2CppHurricaneVR.Framework.Core;
using Il2CppHurricaneVR.Framework.Core.Grabbers;
using Il2CppHurricaneVR.Framework.Weapons;
using Il2CppHurricaneVR.Framework.Weapons.Guns;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using Il2CppSystem.Reflection;
using MelonLoader;
using MelonLoader.Preferences;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(MultiplayerMod), "BrutalistickMP", "0.3.5", "BrutalistickMP contributors", null)]
[assembly: MelonGame("Kubunautilus", "BrutalistickVR")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("BrutalistickMP")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("BrutalistickMP")]
[assembly: AssemblyTitle("BrutalistickMP")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

		public NullableAttribute(byte[] flags)
		{
			NullableFlags = flags;
		}
	}
}
namespace BrutalistickMP
{
	internal static class DevAutoTest
	{
		private static float _menuSince = -1f;

		private static bool _missionStarted;

		private static float _levelSince = -1f;

		private static float _nextShot;

		private static float _itemStepAt = -1f;

		private static int _itemStep;

		private static HVRGunBase? _testGun;

		private static bool _clientGrabbed;

		private static float _levelAt = -1f;

		private static bool _pvpDone;

		private static bool _spawnDone;

		private static bool _sliceDone;

		private static bool _grabDone;

		private static bool _releaseDone;

		private static HVRHandGrabber? _grabHand;

		private static GameObject? _sliceCountRoot;

		private static bool _clientSliceCounted;

		private static float _sliceCountAt = -1f;

		public static bool Enabled => DevOptions.AutoTest.Length > 0;

		private static bool IsHost
		{
			get
			{
				string autoTest = DevOptions.AutoTest;
				if (autoTest == "host" || autoTest == "solo")
				{
					return true;
				}
				return false;
			}
		}

		public static MissionInfo TestMission => new MissionInfo
		{
			SceneName = "Farmland",
			WaveLength = 6,
			EnemyType = DevOptions.EnemyType,
			WaveType = 0,
			WaveDifficulty = 1
		};

		public static void OnSteamReady(Session session)
		{
			if (IsHost)
			{
				session.StartHosting();
			}
		}

		public static void OnScene(string scene)
		{
			bool flag = ((scene == "MainMenuScene" || scene == "MainHub") ? true : false);
			_menuSince = (flag ? Time.unscaledTime : (-1f));
			_levelSince = ((scene == TestMission.SceneName) ? Time.unscaledTime : (-1f));
		}

		public static void AdjustPose(ref PoseData pose)
		{
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			if (DevOptions.AutoTest != "client-bait")
			{
				return;
			}
			foreach (StickHealth item in Object.FindObjectsOfType<StickHealth>())
			{
				GameObject gameObject = ((Component)((Component)item).transform.root).gameObject;
				if (!EnemySync.IsPuppet(gameObject) || item.isDead)
				{
					continue;
				}
				ColliderBridge val = null;
				foreach (ColliderBridge componentsInChild in gameObject.GetComponentsInChildren<ColliderBridge>())
				{
					if (componentsInChild.damageRegion == "head")
					{
						val = componentsInChild;
					}
				}
				if (!((Object)(object)val == (Object)null))
				{
					Vector3 vector = ((Component)val).transform.position.ToN() + new Vector3(1.2f, -0.6f, 0f) - pose.PelvisPos;
					pose.HeadPos += vector;
					pose.ChestPos += vector;
					pose.PelvisPos += vector;
					pose.LeftHandPos += vector;
					pose.RightHandPos += vector;
					pose.FeetPos += vector;
					break;
				}
			}
		}

		private static void ClientShoot(Session session)
		{
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			if (session.Role != Role.Client || Time.unscaledTime < _nextShot)
			{
				return;
			}
			_nextShot = Time.unscaledTime + 5f;
			foreach (StickHealth item in Object.FindObjectsOfType<StickHealth>())
			{
				GameObject gameObject = ((Component)((Component)item).transform.root).gameObject;
				if (!EnemySync.IsPuppet(gameObject) || item.isDead)
				{
					continue;
				}
				ColliderBridge val = null;
				foreach (ColliderBridge componentsInChild in gameObject.GetComponentsInChildren<ColliderBridge>())
				{
					if (componentsInChild.damageRegion == "head")
					{
						val = componentsInChild;
					}
				}
				if (!((Object)(object)val == (Object)null))
				{
					Transform transform = ((Component)val).transform;
					MelonLogger.Msg($"[autotest] shooting '{((Object)gameObject).name}' in the head at {transform.position}");
					BulletForce.Current = 20f;
					val.HitByBullet(40f, "9mm", transform.position, transform.forward, (transform.forward.ToN() * -1f).ToU(), 0.5f);
					break;
				}
			}
		}

		private static HVRHandGrabber? RightHand()
		{
			foreach (HVRHandGrabber item in Object.FindObjectsOfType<HVRHandGrabber>())
			{
				if (!item.IsLeftHand && ((Behaviour)item).isActiveAndEnabled)
				{
					return item;
				}
			}
			return null;
		}

		private static void HostItemTest(Session session)
		{
			//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
			if (_itemStepAt < 0f || Time.unscaledTime < _itemStepAt)
			{
				return;
			}
			HVRHandGrabber val = RightHand();
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			switch (_itemStep)
			{
			case 0:
			{
				foreach (HVRGunBase item in Object.FindObjectsOfType<HVRGunBase>())
				{
					if (((Object)item).name.StartsWith("9mmPistol"))
					{
						_testGun = item;
					}
				}
				HVRGrabbable val3 = (((Object)(object)_testGun != (Object)null) ? ((Component)_testGun).GetComponentInParent<HVRGrabbable>() : null);
				if ((Object)(object)val3 == (Object)null)
				{
					MelonLogger.Msg("[autotest] no pistol on the weapon wall");
					_itemStepAt = -1f;
					break;
				}
				bool value = ((HVRGrabberBase)val).TryGrab(val3, true);
				MelonLogger.Msg($"[autotest] grabbing '{((Object)val3).name}': {value}, holding {(((Object)(object)((HVRGrabberBase)val).GrabbedTarget != (Object)null) ? ((Object)((HVRGrabberBase)val).GrabbedTarget).name : "nothing")}");
				_itemStep = 1;
				_itemStepAt = Time.unscaledTime + 3f;
				break;
			}
			case 1:
			case 2:
			case 3:
			case 4:
				_itemStep++;
				_itemStepAt = Time.unscaledTime + 2f;
				if ((Object)(object)_testGun != (Object)null)
				{
					Transform val2 = (((Object)(object)_testGun.BulletOrigin != (Object)null) ? _testGun.BulletOrigin : ((Component)_testGun).transform);
					MelonLogger.Msg("[autotest] firing the pistol");
					_testGun.FireBullet(val2.forward, -1);
				}
				break;
			case 5:
				MelonLogger.Msg("[autotest] dropping the pistol");
				((HVRGrabberBase)val).ForceRelease();
				_itemStepAt = -1f;
				break;
			}
		}

		private static void ClientItemTest()
		{
			if (!_clientGrabbed)
			{
				HVRGrabbable val = ItemSync.FirstGrabbableCopy();
				HVRHandGrabber val2 = RightHand();
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val2 == (Object)null))
				{
					_clientGrabbed = true;
					bool value = ((HVRGrabberBase)val2).TryGrab(val, true);
					MelonLogger.Msg($"[autotest] picking up our copy of '{((Object)val).name}': {value}");
				}
			}
		}

		private static void ClientExtraTests(Session session)
		{
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			if (session.Role != Role.Client || _levelAt < 0f)
			{
				return;
			}
			CountSlicePieces();
			float num = Time.unscaledTime - _levelAt;
			if (_sliceCountAt < 0f && !_clientSliceCounted && EnemySync.SeenSlice)
			{
				_clientSliceCounted = true;
				_sliceCountAt = Time.unscaledTime + 3f;
			}
			if (!_pvpDone && num > 12f)
			{
				Vector3? val = PvpSync.AnyChest();
				if (val.HasValue)
				{
					Vector3 valueOrDefault = val.GetValueOrDefault();
					Transform head = PlayerRig.Head;
					if (head != null)
					{
						_pvpDone = true;
						Vector3 vector = head.position.ToN() + new Vector3(0f, 0.3f, 0f);
						Vector3 vector2 = valueOrDefault.ToN() - vector;
						bool flag = PvpSync.Shoot((valueOrDefault.ToN() - Vector3.Normalize(vector2) * 0.6f).ToU(), vector2.ToU(), 30f);
						MelonLogger.Msg("[autotest] shooting the host's body: " + (flag ? "hit" : "missed"));
					}
				}
			}
			if (!_spawnDone && num > 16f)
			{
				Transform head2 = PlayerRig.Head;
				if (head2 != null)
				{
					_spawnDone = true;
					Vector3 position = head2.position.ToN() + new Vector3(3f, -1.5f, 0f);
					PacketWriter w = session.Begin(MsgType.EnemySpawnRequest);
					EnemySpawnRequestMsg enemySpawnRequestMsg = default(EnemySpawnRequestMsg);
					enemySpawnRequestMsg.Prefab = "Handgun";
					enemySpawnRequestMsg.Position = position;
					enemySpawnRequestMsg.Yaw = 90f;
					enemySpawnRequestMsg.Write(w);
					session.SendToHost(reliable: true);
					MelonLogger.Msg("[autotest] asking the host to spawn a 'Handgun' enemy");
				}
			}
			if (!_grabDone && num > 22f)
			{
				foreach (StickHealth item in Object.FindObjectsOfType<StickHealth>())
				{
					GameObject gameObject = ((Component)((Component)item).transform.root).gameObject;
					if (!EnemySync.IsPuppet(gameObject) || item.isDead)
					{
						continue;
					}
					HVRGrabbable val2 = null;
					foreach (HVRGrabbable componentsInChild in gameObject.GetComponentsInChildren<HVRGrabbable>())
					{
						if (((Object)componentsInChild).name == "UpperChest" || ((Object)componentsInChild).name == "Head")
						{
							val2 = componentsInChild;
						}
					}
					_grabHand = RightHand();
					if (!((Object)(object)val2 == (Object)null) && !((Object)(object)_grabHand == (Object)null))
					{
						_grabDone = true;
						bool value = ((HVRGrabberBase)_grabHand).TryGrab(val2, true);
						MelonLogger.Msg($"[autotest] grabbing host enemy '{((Object)gameObject).name}' by '{((Object)val2).name}': {value}");
						break;
					}
				}
			}
			if (_grabDone && !_releaseDone && num > 30f)
			{
				_releaseDone = true;
				HVRHandGrabber? grabHand = _grabHand;
				if (grabHand != null)
				{
					((HVRGrabberBase)grabHand).ForceRelease();
				}
				MelonLogger.Msg("[autotest] letting go of the enemy");
			}
		}

		private static void CountSlicePieces()
		{
			if (_sliceCountAt < 0f || Time.unscaledTime < _sliceCountAt)
			{
				return;
			}
			_sliceCountAt = -1f;
			int num = 0;
			foreach (Rigidbody item in Object.FindObjectsOfType<Rigidbody>())
			{
				if (!item.isKinematic && ((Object)item).name.Contains("Elbow"))
				{
					num++;
				}
			}
			MelonLogger.Msg($"[autotest] moving 'Elbow' pieces in the level after the slice: {num}");
		}

		private static void HostExtraTests(Session session)
		{
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			CountSlicePieces();
			if (session.Role != Role.Host || _levelAt < 0f || _sliceDone || Time.unscaledTime - _levelAt < 20f)
			{
				return;
			}
			foreach (SliceOnInputCustom item in Object.FindObjectsOfType<SliceOnInputCustom>())
			{
				GameObject gameObject = ((Component)((Component)item).transform.root).gameObject;
				if (EnemySync.IsHostEnemy(gameObject) && !(((Object)item).name != "Elbow_R") && item.canSlice)
				{
					_sliceDone = true;
					Transform transform = ((Component)item).transform;
					int length = gameObject.GetComponentsInChildren<Transform>().Length;
					MelonLogger.Msg($"[autotest] slicing '{((Object)gameObject).name}' through '{((Object)transform).name}'");
					try
					{
						item.InitiateSlicing(new Plane(transform.up, transform.position), transform.position, Vector3.zero, EnemySync.SliceDummy, new Il2CppReferenceArray<Collider>(0L), (Rigidbody)null, 0.3f, (Rigidbody)null);
					}
					catch (Exception ex)
					{
						MelonLogger.Msg("[autotest] slicing failed: " + ex.Message);
					}
					MelonLogger.Msg($"[autotest] '{((Object)gameObject).name}' had {length} parts");
					_sliceCountRoot = gameObject;
					_sliceCountAt = Time.unscaledTime + 3f;
					break;
				}
			}
		}

		public static void Update(Session session)
		{
			if (_levelAt < 0f && MultiplayerMod.CurrentScene == TestMission.SceneName)
			{
				_levelAt = Time.unscaledTime;
			}
			if (DevOptions.AutoTest.StartsWith("client"))
			{
				ClientShoot(session);
				ClientItemTest();
				ClientExtraTests(session);
			}
			if (IsHost)
			{
				HostItemTest(session);
				HostExtraTests(session);
			}
			if (Enabled && IsHost && _levelSince >= 0f && Time.unscaledTime - _levelSince > 6f)
			{
				_levelSince = -1f;
				foreach (EnemyWaveSpawner item in Object.FindObjectsOfType<EnemyWaveSpawner>())
				{
					MelonLogger.Msg("[autotest] starting the wave");
					item.spawnerActive = true;
				}
				_itemStep = 0;
				_itemStepAt = Time.unscaledTime + 4f;
			}
			if (!Enabled || !IsHost || _missionStarted || _menuSince < 0f || (!(DevOptions.AutoTest == "solo") && session.Peers.Count <= 0) || Time.unscaledTime - _menuSince < 8f)
			{
				return;
			}
			_menuSince = -1f;
			if (MultiplayerMod.CurrentScene != "MainHub")
			{
				MelonLogger.Msg("[autotest] going to the hub");
				Il2CppArrayBase<MenuUIManager> val = Object.FindObjectsOfType<MenuUIManager>(true);
				if (val.Length > 0)
				{
					val[0].SelectScene("MainHub");
				}
			}
			else
			{
				_missionStarted = true;
				MelonLogger.Msg("[autotest] starting test mission");
				MissionSync.StartAsHost(TestMission);
			}
		}
	}
	internal static class DevOptions
	{
		public static int LocalSlot { get; }

		public static string AutoTest { get; }

		public static int EnemyType { get; }

		public static bool Probe { get; }

		static DevOptions()
		{
			AutoTest = "";
			string[] commandLineArgs = Environment.GetCommandLineArgs();
			foreach (string text in commandLineArgs)
			{
				int result2;
				if (text.StartsWith("--bmp-local=") && int.TryParse(text.AsSpan("--bmp-local=".Length), out var result))
				{
					LocalSlot = result;
				}
				else if (text.StartsWith("--bmp-autotest="))
				{
					AutoTest = text.Substring("--bmp-autotest=".Length);
				}
				else if (text.StartsWith("--bmp-enemytype=") && int.TryParse(text.AsSpan("--bmp-enemytype=".Length), out result2))
				{
					EnemyType = result2;
				}
				else if (text == "--bmp-probe")
				{
					Probe = true;
				}
			}
		}
	}
	internal static class DevProbe
	{
		private static readonly HashSet<string> DumpedPrefabs = new HashSet<string>();

		private static float _nextStatus;

		private static StickHealth? _hitTarget;

		private static float _hitAt;

		private static float _watchUntil;

		private static bool _hitDone;

		private static float _itemSurveyAt = -1f;

		private static void Log(string msg)
		{
			MelonLogger.Msg("[probe] " + msg);
		}

		public static void OnScene(string scene)
		{
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			if (!DevOptions.Probe)
			{
				return;
			}
			_hitTarget = null;
			_hitDone = false;
			foreach (EnemyWaveSpawner item in Object.FindObjectsOfType<EnemyWaveSpawner>(true))
			{
				Log($"spawner '{Path(((Component)item).transform)}' active={((Behaviour)item).isActiveAndEnabled} waveType={item.waveType} difficulty={item.waveDifficulty} waveEnemyCount={item.waveEnemyCount} breach={item.breachEnemyCount} aliveAtOnce={item.enemiesAliveAtOnce} maxDead={item.maxDeadBodies} ranged={Names(item.rangedPrefabs)} melee={Names(item.meleePrefabs)} zombie={Names(item.zombiePrefabs)} dummy={(((Object)(object)item.dummyEnemyPrefab != (Object)null) ? ((Object)item.dummyEnemyPrefab).name : "-")}");
			}
			Il2CppArrayBase<StickHealth> val = Object.FindObjectsOfType<StickHealth>(true);
			Log($"scene {scene}: {val.Length} StickHealth objects already in the scene");
			foreach (StickHealth item2 in ((IEnumerable<StickHealth>)val).Take(5))
			{
				Log($"  pre-placed stick '{Path(((Component)item2).transform)}' active={((Component)item2).gameObject.activeInHierarchy}");
			}
			foreach (PlayerDamageBridge item3 in Object.FindObjectsOfType<PlayerDamageBridge>(true))
			{
				Collider component = ((Component)item3).GetComponent<Collider>();
				Log($"player damage bridge '{Path(((Component)item3).transform)}' layer={LayerMask.LayerToName(((Component)item3).gameObject.layer)} collider={(((Object)(object)component != (Object)null) ? ((MemberInfo)((Object)component).GetIl2CppType()).Name : "-")} mult={item3.dmgMultiplier}");
			}
			PlayerHealthManager val2 = ((IEnumerable<PlayerHealthManager>)Object.FindObjectsOfType<PlayerHealthManager>(true)).FirstOrDefault();
			if ((Object)(object)val2 != (Object)null)
			{
				Log($"player health {val2.health}/{val2.startHealth} on '{Path(((Component)val2).transform)}'");
			}
			_itemSurveyAt = Time.unscaledTime + 10f;
		}

		private static void ItemSurvey()
		{
			//IL_074f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_08e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_08ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_056e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0573: Unknown result type (might be due to invalid IL or missing references)
			//IL_0644: Unknown result type (might be due to invalid IL or missing references)
			//IL_0663: Unknown result type (might be due to invalid IL or missing references)
			GameObject playerHexaPelvisStatic = PlayerStaticReferences.playerHexaPelvisStatic;
			if ((Object)(object)playerHexaPelvisStatic != (Object)null)
			{
				Log("player body under '" + Path(playerHexaPelvisStatic.transform) + "':");
				foreach (Renderer componentsInChild in playerHexaPelvisStatic.GetComponentsInChildren<Renderer>(true))
				{
					Transform transform = ((Component)componentsInChild).transform;
					MeshFilter component = ((Component)componentsInChild).GetComponent<MeshFilter>();
					object obj2;
					if (!((Object)(object)component != (Object)null) || !((Object)(object)component.sharedMesh != (Object)null))
					{
						SkinnedMeshRenderer obj = ((Il2CppObjectBase)componentsInChild).TryCast<SkinnedMeshRenderer>();
						if (obj == null)
						{
							obj2 = null;
						}
						else
						{
							Mesh sharedMesh = obj.sharedMesh;
							obj2 = ((sharedMesh != null) ? ((Object)sharedMesh).name : null);
						}
						if (obj2 == null)
						{
							obj2 = "-";
						}
					}
					else
					{
						obj2 = ((Object)component.sharedMesh).name;
					}
					string value = (string)obj2;
					string value2 = string.Join(",", ((IEnumerable<Material>)componentsInChild.sharedMaterials).Select((Material m) => (!((Object)(object)m != (Object)null)) ? "null" : ((Object)m).name));
					Log($"  {((MemberInfo)((Object)componentsInChild).GetIl2CppType()).Name} '{Path(transform)}' on={componentsInChild.enabled && ((Component)componentsInChild).gameObject.activeInHierarchy} layer={LayerMask.LayerToName(((Component)transform).gameObject.layer)} mesh={value} mats=[{value2}] pos={transform.position} scale={transform.lossyScale}");
				}
				Transform root = playerHexaPelvisStatic.transform.root;
				int length = ((Component)root).GetComponentsInChildren<SkinnedMeshRenderer>(true).Length;
				Log($"  rig '{((Object)root).name}' has {((Component)root).GetComponentsInChildren<Renderer>(true).Length} renderers ({length} skinned)");
				Dictionary<string, (int, int)> dictionary = new Dictionary<string, (int, int)>();
				foreach (Renderer componentsInChild2 in ((Component)root).GetComponentsInChildren<Renderer>(true))
				{
					string[] array = Path(((Component)componentsInChild2).transform).Split('/');
					string key = string.Join("/", array.Take(Math.Min(4, array.Length - 1)));
					dictionary.TryGetValue(key, out var value3);
					dictionary[key] = (value3.Item1 + 1, value3.Item2 + ((componentsInChild2.enabled && ((Component)componentsInChild2).gameObject.activeInHierarchy) ? 1 : 0));
					SkinnedMeshRenderer val = ((Il2CppObjectBase)componentsInChild2).TryCast<SkinnedMeshRenderer>();
					if (val != null)
					{
						DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(42, 5);
						defaultInterpolatedStringHandler.AppendLiteral("  skinned '");
						defaultInterpolatedStringHandler.AppendFormatted(Path(((Component)componentsInChild2).transform));
						defaultInterpolatedStringHandler.AppendLiteral("' shown=");
						defaultInterpolatedStringHandler.AppendFormatted(componentsInChild2.enabled && ((Component)componentsInChild2).gameObject.activeInHierarchy);
						defaultInterpolatedStringHandler.AppendLiteral(" mesh=");
						Mesh sharedMesh2 = val.sharedMesh;
						defaultInterpolatedStringHandler.AppendFormatted((sharedMesh2 != null) ? ((Object)sharedMesh2).name : null);
						defaultInterpolatedStringHandler.AppendLiteral(" bones=");
						defaultInterpolatedStringHandler.AppendFormatted(((Il2CppArrayBase<Transform>)(object)val.bones)?.Length);
						defaultInterpolatedStringHandler.AppendLiteral(" rootBone=");
						defaultInterpolatedStringHandler.AppendFormatted(((Object)(object)val.rootBone != (Object)null) ? Path(val.rootBone) : "-");
						Log(defaultInterpolatedStringHandler.ToStringAndClear());
					}
				}
				foreach (var (value4, tuple2) in dictionary)
				{
					Log($"  renderers under '{value4}': {tuple2.Item1} ({tuple2.Item2} shown)");
				}
				StringBuilder stringBuilder = new StringBuilder("visible body parts:\n");
				foreach (MeshRenderer componentsInChild3 in ((Component)root).GetComponentsInChildren<MeshRenderer>(false))
				{
					if (!((Renderer)componentsInChild3).enabled)
					{
						continue;
					}
					string text2 = Path(((Component)componentsInChild3).transform);
					if (text2.Contains("PhysicalUpperbody") || text2.Contains("CameraRig") || text2.Contains("HandModel"))
					{
						MeshFilter component2 = ((Component)componentsInChild3).GetComponent<MeshFilter>();
						Bounds bounds = ((Renderer)componentsInChild3).bounds;
						StringBuilder stringBuilder2 = stringBuilder;
						StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(25, 5, stringBuilder2);
						handler.AppendLiteral("  ");
						handler.AppendFormatted(text2.Substring(((Object)root).name.Length));
						handler.AppendLiteral(" mesh=");
						handler.AppendFormatted(((Object)(object)component2 != (Object)null && (Object)(object)component2.sharedMesh != (Object)null) ? ((Object)component2.sharedMesh).name : "-");
						handler.AppendLiteral(" mats=");
						handler.AppendFormatted(string.Join(",", ((IEnumerable<Material>)((Renderer)componentsInChild3).sharedMaterials).Select((Material m) => (!((Object)(object)m != (Object)null)) ? "null" : ((Object)m).name)));
						handler.AppendLiteral(" size=");
						handler.AppendFormatted<Vector3>(((Bounds)(ref bounds)).size);
						handler.AppendLiteral(" at=");
						handler.AppendFormatted<Vector3>(((Component)componentsInChild3).transform.position);
						handler.AppendLiteral("\n");
						stringBuilder2.Append(ref handler);
					}
				}
				Log(stringBuilder.ToString());
			}
			List<SpawnableItemInfo> spawnableItems = SpawnGunManager.spawnableItems;
			Log($"items: catalog has {spawnableItems?.Count ?? (-1)} entries");
			if (spawnableItems != null)
			{
				for (int num = 0; num < spawnableItems.Count; num += Math.Max(1, spawnableItems.Count / 12))
				{
					SpawnableItemInfo val2 = spawnableItems[num];
					Log($"  catalog[{num}] '{val2.spawnableName}' {val2.spawnableType} prefab='{(((Object)(object)val2.itemPrefab != (Object)null) ? ((Object)val2.itemPrefab).name : "-")}' modded={val2.isModded}");
				}
			}
			foreach (HVRHandGrabber item in Object.FindObjectsOfType<HVRHandGrabber>(true))
			{
				Log($"  hand '{Path(((Component)item).transform)}' left={item.IsLeftHand} active={((Behaviour)item).isActiveAndEnabled} holding={(((Object)(object)((HVRGrabberBase)item).GrabbedTarget != (Object)null) ? ((Object)((HVRGrabberBase)item).GrabbedTarget).name : "-")}");
			}
			Transform head = PlayerRig.Head;
			int num2 = 0;
			foreach (HVRGrabbable item2 in Object.FindObjectsOfType<HVRGrabbable>())
			{
				if ((Object)(object)head == (Object)null || num2 >= 25)
				{
					break;
				}
				if (!(Vector3.Distance(((Component)item2).transform.position, head.position) > 2.5f) && !((Object)((Component)item2).transform.root).name.Contains("Hexa"))
				{
					num2++;
					Rigidbody rigidbody = item2.Rigidbody;
					Collider componentInChildren = ((Component)item2).GetComponentInChildren<Collider>();
					Log($"  near player: '{Path(((Component)item2).transform)}' rb={(((Object)(object)rigidbody != (Object)null) ? $"kin={rigidbody.isKinematic}" : "-")} layer={LayerMask.LayerToName(((Component)item2).gameObject.layer)} col={(((Object)(object)componentInChildren != (Object)null) ? LayerMask.LayerToName(((Component)componentInChildren).gameObject.layer) : "-")} socketed={item2.IsSocketed}");
				}
			}
			foreach (HVRGunBase item3 in ((IEnumerable<HVRGunBase>)Object.FindObjectsOfType<HVRGunBase>()).Take(5))
			{
				Log($"  gun '{Path(((Component)item3).transform)}' bullet={(((Object)(object)item3.BulletPrefab != (Object)null) ? ((Object)item3.BulletPrefab).name : "-")} speed={item3.BulletSpeed} origin={(((Object)(object)item3.BulletOrigin != (Object)null) ? ((Object)item3.BulletOrigin).name : "-")} sounds={(Object)(object)((Component)item3).GetComponent<HVRGunSounds>() != (Object)null}");
			}
		}

		public static void Update()
		{
			if (!DevOptions.Probe)
			{
				return;
			}
			float unscaledTime = Time.unscaledTime;
			if (_itemSurveyAt > 0f && unscaledTime >= _itemSurveyAt)
			{
				_itemSurveyAt = -1f;
				try
				{
					ItemSurvey();
				}
				catch (Exception ex)
				{
					Log("item survey failed: " + ex.Message);
				}
			}
			if (unscaledTime >= _nextStatus)
			{
				_nextStatus = unscaledTime + 3f;
				foreach (EnemyWaveSpawner item in Object.FindObjectsOfType<EnemyWaveSpawner>())
				{
					List<StickHealth> stickHealthScripts = item.stickHealthScripts;
					KillCounterScript instanceOfThis = KillCounterScript.instanceOfThis;
					PlayerHealthManager val = ((IEnumerable<PlayerHealthManager>)Object.FindObjectsOfType<PlayerHealthManager>()).FirstOrDefault();
					MissionComplete val2 = ((IEnumerable<MissionComplete>)Object.FindObjectsOfType<MissionComplete>()).FirstOrDefault();
					Log($"wave[{MultiplayerMod.Session?.Role}]: hp={(((Object)(object)val != (Object)null) ? val.health : (-1f)):F0} complete={(Object)(object)val2 != (Object)null && val2.isMissionComplete} active={item.spawnerActive} current={item.waveEnemyCurrentCount}/{item.waveEnemyCount} alive={item.enemiesCurrentlyAlive} list={stickHealthScripts?.Count ?? (-1)} breach={item.breachEnemyCurrentCount}/{item.breachEnemyCount} dead={item.breachDeadBodiesCount} kills={(((Object)(object)instanceOfThis != (Object)null) ? instanceOfThis.killsCounted : (-1))}");
					if ((Object)(object)_hitTarget == (Object)null && stickHealthScripts != null && stickHealthScripts.Count > 0)
					{
						_hitTarget = stickHealthScripts[0];
						_hitAt = unscaledTime + 20f;
					}
					if (stickHealthScripts != null && stickHealthScripts.Count > 0)
					{
						LogTargets(stickHealthScripts[0]);
					}
				}
			}
			if ((Object)(object)_hitTarget != (Object)null && !_hitDone && unscaledTime >= _hitAt)
			{
				HitTest(_hitTarget);
			}
			if ((Object)(object)_hitTarget != (Object)null && _hitDone && unscaledTime < _watchUntil)
			{
				Log($"  after: health={_hitTarget.StickCurrentHealth:F1} cp={_hitTarget.StickCurrentCP:F1} queue={_hitTarget.damageInfoList?.Count} dead={_hitTarget.isDead} enabled={((Behaviour)_hitTarget).enabled}");
			}
		}

		private static void HitTest(StickHealth stick)
		{
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			_hitDone = true;
			_watchUntil = Time.unscaledTime + 0.5f;
			Il2CppArrayBase<ColliderBridge> componentsInChildren = ((Component)stick).GetComponentsInChildren<ColliderBridge>(true);
			ColliderBridge val = ((IEnumerable<ColliderBridge>)componentsInChildren).FirstOrDefault((Func<ColliderBridge, bool>)((ColliderBridge b) => (b.damageRegion ?? "").Contains("Head"))) ?? ((IEnumerable<ColliderBridge>)componentsInChildren).FirstOrDefault();
			if ((Object)(object)val == (Object)null)
			{
				Log("hit test: no ColliderBridge");
				return;
			}
			Log($"hit test on '{Path(((Component)val).transform)}' region={val.damageRegion} mult={val.damageMultiplier}: before health={stick.StickCurrentHealth:F1} cp={stick.StickCurrentCP:F1} queue={stick.damageInfoList?.Count}");
			string text = ((val.bulletCalibers != null && val.bulletCalibers.Count > 0) ? val.bulletCalibers[0] : "9mm");
			Transform transform = ((Component)val).transform;
			val.HitByBullet(40f, text, transform.position, transform.forward, -transform.forward, 0.5f);
			Log($"  right after: health={stick.StickCurrentHealth:F1} queue={stick.damageInfoList?.Count}");
		}

		public static void OnInstantiated(EnemyWaveSpawner spawner, int enemNum, GameObject enemy)
		{
			if (DevOptions.Probe && !((Object)(object)enemy == (Object)null))
			{
				PrefabRef? prefabRef = EnemyCatalog.Identify(spawner, enemy);
				Log($"InstantiateEnemy({enemNum}) -> '{((Object)enemy).name}' prefab={prefabRef} active={enemy.activeSelf}");
				string item = prefabRef?.ToString() ?? ((Object)enemy).name;
				if (DumpedPrefabs.Add(item))
				{
					DumpHierarchy(enemy);
				}
			}
		}

		public static void OnSpawned(EnemyWaveSpawner spawner)
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			if (DevOptions.Probe)
			{
				List<StickHealth> stickHealthScripts = spawner.stickHealthScripts;
				if (stickHealthScripts != null && stickHealthScripts.Count != 0)
				{
					StickHealth val = stickHealthScripts[stickHealthScripts.Count - 1];
					DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(31, 3);
					defaultInterpolatedStringHandler.AppendLiteral("SpawnNewEnemy -> '");
					defaultInterpolatedStringHandler.AppendFormatted(((Object)((Component)val).transform.root).name);
					defaultInterpolatedStringHandler.AppendLiteral("' at ");
					Transform pelvisTransform = val.pelvisTransform;
					defaultInterpolatedStringHandler.AppendFormatted((pelvisTransform != null) ? new Vector3?(pelvisTransform.position) : ((Vector3?)null));
					defaultInterpolatedStringHandler.AppendLiteral(" health=");
					defaultInterpolatedStringHandler.AppendFormatted(val.StickCurrentHealth);
					Log(defaultInterpolatedStringHandler.ToStringAndClear());
					LogTargets(val);
				}
			}
		}

		private static void LogTargets(StickHealth stick)
		{
			//IL_007e: 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_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01da: Unknown result type (might be due to invalid IL or missing references)
			//IL_01df: Unknown result type (might be due to invalid IL or missing references)
			//IL_0341: Unknown result type (might be due to invalid IL or missing references)
			GameObject gameObject = ((Component)((Component)stick).transform.root).gameObject;
			Transform head = PlayerRig.Head;
			Transform val = (((Object)(object)stick.pelvisRb != (Object)null) ? ((Component)stick.pelvisRb).transform : null);
			if ((Object)(object)val != (Object)null && (Object)(object)head != (Object)null)
			{
				Log($"  enemy '{((Object)gameObject).name}' pelvis {val.position} player head {head.position} dist {Vector3.Distance(val.position, head.position):F1} health={stick.StickCurrentHealth:F0} dead={stick.isDead}");
			}
			MovementBasic componentInChildren = gameObject.GetComponentInChildren<MovementBasic>(true);
			if ((Object)(object)componentInChildren != (Object)null)
			{
				Log($"  MovementBasic target={Describe(componentInChildren.targetObject)} playerPelvis={Describe(componentInChildren.playerPelvisObject)} targetTransform={Describe(componentInChildren.targetTransformObject)}");
			}
			EnemyFOV componentInChildren2 = gameObject.GetComponentInChildren<EnemyFOV>(true);
			if ((Object)(object)componentInChildren2 != (Object)null)
			{
				DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(31, 3);
				defaultInterpolatedStringHandler.AppendLiteral("  EnemyFOV player=");
				defaultInterpolatedStringHandler.AppendFormatted(Describe(componentInChildren2.playerObject));
				defaultInterpolatedStringHandler.AppendLiteral(" mask=");
				LayerMask layerMask = componentInChildren2.layerMask;
				defaultInterpolatedStringHandler.AppendFormatted(((LayerMask)(ref layerMask)).value);
				defaultInterpolatedStringHandler.AppendLiteral(" start=");
				defaultInterpolatedStringHandler.AppendFormatted(Describe(componentInChildren2.viewConeStartPosition));
				Log(defaultInterpolatedStringHandler.ToStringAndClear());
			}
			StickGunManager componentInChildren3 = gameObject.GetComponentInChildren<StickGunManager>(true);
			if ((Object)(object)componentInChildren3 != (Object)null)
			{
				DefaultInterpolatedStringHandler defaultInterpolatedStringHandler2 = new DefaultInterpolatedStringHandler(56, 5);
				defaultInterpolatedStringHandler2.AppendLiteral("  StickGunManager gunTarget=");
				defaultInterpolatedStringHandler2.AppendFormatted(Describe(componentInChildren3.gunTarget));
				defaultInterpolatedStringHandler2.AppendLiteral(" head=");
				defaultInterpolatedStringHandler2.AppendFormatted(Describe(componentInChildren3.headTarget));
				defaultInterpolatedStringHandler2.AppendLiteral(" rHand=");
				defaultInterpolatedStringHandler2.AppendFormatted(Describe(componentInChildren3.rHandTarget));
				defaultInterpolatedStringHandler2.AppendLiteral(" lHand=");
				defaultInterpolatedStringHandler2.AppendFormatted(Describe(componentInChildren3.lHandTarget));
				defaultInterpolatedStringHandler2.AppendLiteral(" bullet=");
				GameObject bulletPrefab = componentInChildren3.bulletPrefab;
				defaultInterpolatedStringHandler2.AppendFormatted((bulletPrefab != null) ? ((Object)bulletPrefab).name : null);
				Log(defaultInterpolatedStringHandler2.ToStringAndClear());
			}
			EnemyMeleeManager componentInChildren4 = gameObject.GetComponentInChildren<EnemyMeleeManager>(true);
			if ((Object)(object)componentInChildren4 != (Object)null)
			{
				Log("  EnemyMeleeManager playerBodyPart=" + Describe(componentInChildren4.playerBodyPart));
			}
			EnemyAIManager componentInChildren5 = gameObject.GetComponentInChildren<EnemyAIManager>(true);
			if ((Object)(object)componentInChildren5 != (Object)null)
			{
				Log($"  EnemyAIManager type={componentInChildren5.enemyType} activated={componentInChildren5.isAIActivated}");
			}
		}

		private static void DumpHierarchy(GameObject root)
		{
			StringBuilder sb = new StringBuilder();
			StringBuilder stringBuilder = sb;
			StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(16, 1, stringBuilder);
			handler.AppendLiteral("hierarchy of '");
			handler.AppendFormatted(((Object)root).name);
			handler.AppendLiteral("':");
			stringBuilder.AppendLine(ref handler);
			Walk(root.transform, 1);
			Log(sb.ToString());
			void Walk(Transform t, int depth)
			{
				GameObject gameObject = ((Component)t).gameObject;
				IEnumerable<string> values = from c in (IEnumerable<Component>)gameObject.GetComponents<Component>()
					where (Object)(object)c != (Object)null
					select ((MemberInfo)((Object)c).GetIl2CppType()).Name into n
					where n != "Transform"
					select n;
				Rigidbody component = gameObject.GetComponent<Rigidbody>();
				Il2CppArrayBase<Collider> components = gameObject.GetComponents<Collider>();
				sb.Append(' ', depth * 2).Append(((Object)gameObject).name).Append(gameObject.activeSelf ? "" : " (inactive)")
					.Append(" [")
					.Append(LayerMask.LayerToName(gameObject.layer))
					.Append("] ")
					.Append(string.Join(",", values));
				if ((Object)(object)component != (Object)null)
				{
					StringBuilder stringBuilder2 = sb;
					StringBuilder stringBuilder3 = stringBuilder2;
					StringBuilder.AppendInterpolatedStringHandler handler2 = new StringBuilder.AppendInterpolatedStringHandler(12, 2, stringBuilder2);
					handler2.AppendLiteral(" rb(kin=");
					handler2.AppendFormatted(component.isKinematic);
					handler2.AppendLiteral(",m=");
					handler2.AppendFormatted(component.mass, "F1");
					handler2.AppendLiteral(")");
					stringBuilder3.Append(ref handler2);
				}
				if (components.Length > 0)
				{
					StringBuilder stringBuilder2 = sb;
					StringBuilder stringBuilder4 = stringBuilder2;
					StringBuilder.AppendInterpolatedStringHandler handler2 = new StringBuilder.AppendInterpolatedStringHandler(7, 1, stringBuilder2);
					handler2.AppendLiteral(" cols(");
					handler2.AppendFormatted(string.Join(",", ((IEnumerable<Collider>)components).Select((Collider c) => ((MemberInfo)((Object)c).GetIl2CppType()).Name + (c.isTrigger ? "*" : ""))));
					handler2.AppendLiteral(")");
					stringBuilder4.Append(ref handler2);
				}
				ColliderBridge component2 = gameObject.GetComponent<ColliderBridge>();
				if ((Object)(object)component2 != (Object)null)
				{
					StringBuilder stringBuilder2 = sb;
					StringBuilder stringBuilder5 = stringBuilder2;
					StringBuilder.AppendInterpolatedStringHandler handler2 = new StringBuilder.AppendInterpolatedStringHandler(8, 1, stringBuilder2);
					handler2.AppendLiteral(" region=");
					handler2.AppendFormatted(component2.damageRegion);
					stringBuilder5.Append(ref handler2);
				}
				sb.AppendLine();
				for (int num = 0; num < t.childCount; num++)
				{
					Walk(t.GetChild(num), depth + 1);
				}
			}
		}

		private static string Names(Il2CppReferenceArray<GameObject>? arr)
		{
			if (arr != null)
			{
				return "[" + string.Join(",", ((IEnumerable<GameObject>)arr).Select((GameObject g) => (!((Object)(object)g != (Object)null)) ? "null" : ((Object)g).name)) + "]";
			}
			return "-";
		}

		private static string Describe(GameObject? go)
		{
			if (!((Object)(object)go == (Object)null))
			{
				return $"'{Path(go.transform)}'[{LayerMask.LayerToName(go.layer)}]";
			}
			return "null";
		}

		private static string Describe(Transform? t)
		{
			if (!((Object)(object)t == (Object)null))
			{
				return Describe(((Component)t).gameObject);
			}
			return "null";
		}

		public static string Path(Transform t)
		{
			List<string> list = new List<string>();
			Transform val = t;
			while ((Object)(object)val != (Object)null)
			{
				list.Add(((Object)val).name);
				val = val.parent;
			}
			list.Reverse();
			return string.Join("/", list);
		}
	}
	[HarmonyPatch(typeof(EnemyWaveSpawner), "InstantiateEnemy")]
	internal static class ProbeInstantiatePatch
	{
		private static bool Prepare()
		{
			return DevOptions.Probe;
		}

		private static void Postfix(EnemyWaveSpawner __instance, int enemNum, GameObject __result)
		{
			try
			{
				DevProbe.OnInstantiated(__instance, enemNum, __result);
			}
			catch (Exception value)
			{
				MelonLogger.Error($"[probe] {value}");
			}
		}
	}
	[HarmonyPatch(typeof(EnemyWaveSpawner), "SpawnNewEnemy")]
	internal static class ProbeSpawnPatch
	{
		private static bool Prepare()
		{
			return DevOptions.Probe;
		}

		private static void Postfix(EnemyWaveSpawner __instance)
		{
			try
			{
				DevProbe.OnSpawned(__instance);
			}
			catch (Exception value)
			{
				MelonLogger.Error($"[probe] {value}");
			}
		}
	}
	internal enum PrefabKind : byte
	{
		Ranged,
		Melee,
		Zombie,
		Dummy
	}
	internal readonly record struct PrefabRef(PrefabKind Kind, int Index)
	{
		public override string ToString()
		{
			return $"{Kind}[{Index}]";
		}
	}
	internal static class EnemyCatalog
	{
		private static readonly Dictionary<int, string> PrefabSignatures = new Dictionary<int, string>();

		public static PrefabRef? Identify(EnemyWaveSpawner spawner, GameObject enemy)
		{
			string text = Signature(enemy);
			for (byte b = 0; b <= 3; b++)
			{
				IReadOnlyList<GameObject> readOnlyList = List(spawner, (PrefabKind)b);
				for (int i = 0; i < readOnlyList.Count; i++)
				{
					GameObject val = readOnlyList[i];
					if (!((Object)(object)val == (Object)null))
					{
						int instanceID = ((Object)val).GetInstanceID();
						if (!PrefabSignatures.TryGetValue(instanceID, out string value))
						{
							value = (PrefabSignatures[instanceID] = Signature(val));
						}
						if (value == text)
						{
							return new PrefabRef((PrefabKind)b, i);
						}
					}
				}
			}
			return null;
		}

		public static GameObject? Resolve(EnemyWaveSpawner spawner, PrefabRef r)
		{
			IReadOnlyList<GameObject> readOnlyList = List(spawner, r.Kind);
			if (r.Index < 0 || r.Index >= readOnlyList.Count)
			{
				return null;
			}
			return readOnlyList[r.Index];
		}

		private static IReadOnlyList<GameObject?> List(EnemyWaveSpawner spawner, PrefabKind kind)
		{
			return kind switch
			{
				PrefabKind.Ranged => Wrap(spawner.rangedPrefabs), 
				PrefabKind.Melee => Wrap(spawner.meleePrefabs), 
				PrefabKind.Zombie => Wrap(spawner.zombiePrefabs), 
				_ => (IReadOnlyList<GameObject?>)((!((Object)(object)spawner.dummyEnemyPrefab != (Object)null)) ? ((Array)Array.Empty<GameObject>()) : ((Array)new GameObject[1] { spawner.dummyEnemyPrefab })), 
			};
		}

		private static IReadOnlyList<GameObject?> Wrap(Il2CppReferenceArray<GameObject>? arr)
		{
			if (arr == null)
			{
				return Array.Empty<GameObject>();
			}
			GameObject[] array = (GameObject[])(object)new GameObject[((Il2CppArrayBase<GameObject>)(object)arr).Length];
			for (int i = 0; i < ((Il2CppArrayBase<GameObject>)(object)arr).Length; i++)
			{
				array[i] = ((Il2CppArrayBase<GameObject>)(object)arr)[i];
			}
			return array;
		}

		public static string Signature(GameObject root)
		{
			StringBuilder sb = new StringBuilder();
			Walk(root.transform);
			return sb.ToString();
			void Walk(Transform t)
			{
				for (int i = 0; i < t.childCount; i++)
				{
					Transform child = t.GetChild(i);
					if (!((Object)child).name.EndsWith("(Clone)"))
					{
						sb.Append(((Object)child).name).Append('|');
						MeshFilter component = ((Component)child).GetComponent<MeshFilter>();
						if ((Object)(object)component != (Object)null && (Object)(object)component.sharedMesh != (Object)null)
						{
							sb.Append(((Object)component.sharedMesh).name).Append('|');
						}
						Walk(child);
					}
				}
			}
		}

		public static int LookHash(GameObject root)
		{
			StringBuilder stringBuilder = new StringBuilder();
			for (int i = 0; i < root.transform.childCount; i++)
			{
				Transform child = root.transform.GetChild(i);
				if (((Component)child).gameObject.activeSelf)
				{
					stringBuilder.Append(((Object)child).name).Append('|');
				}
			}
			foreach (Transform componentsInChild in root.GetComponentsInChildren<Transform>(true))
			{
				if (((Object)componentsInChild).name.EndsWith("(Clone)"))
				{
					stringBuilder.Append(((Object)(object)componentsInChild.parent != (Object)null) ? ((Object)componentsInChild.parent).name : "").Append('>').Append(((Object)componentsInChild).name)
						.Append('|');
				}
			}
			return SceneHash.Of(stringBuilder.ToString());
		}

		public static string Extras(GameObject root)
		{
			StringBuilder stringBuilder = new StringBuilder();
			int num = 0;
			foreach (Transform componentsInChild in root.GetComponentsInChildren<Transform>(true))
			{
				num++;
				if (((Object)componentsInChild).name.Contains("(Clone)"))
				{
					stringBuilder.Append(((Object)(object)componentsInChild.parent != (Object)null) ? ((Object)componentsInChild.parent).name : "").Append('>').Append(((Object)componentsInChild).name)
						.Append(' ');
				}
			}
			return $"{num} parts; extras: {stringBuilder}";
		}
	}
	internal static class Guard
	{
		public static void Run(Action a, string where)
		{
			try
			{
				a();
			}
			catch (Exception value)
			{
				MelonLogger.Error($"{where}: {value}");
			}
		}

		public static bool Run(Func<bool> f, string where)
		{
			try
			{
				return f();
			}
			catch (Exception value)
			{
				MelonLogger.Error($"{where}: {value}");
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(EnemyWaveSpawner), "InstantiateEnemy")]
	internal static class InstantiateEnemyPatch
	{
		private static void Prefix()
		{
			Guard.Run((Action)EnemySync.BeforeInstantiate, "InstantiateEnemy");
		}

		private static void Postfix(int enemNum, GameObject __result)
		{
			Guard.Run(delegate
			{
				EnemySync.AfterInstantiate(enemNum, __result);
			}, "InstantiateEnemy");
		}
	}
	[HarmonyPatch(typeof(EnemyWaveSpawner), "SpawnNewEnemy")]
	internal static class SpawnNewEnemyPatch
	{
		private static bool Prefix()
		{
			return EnemySync.AllowLocalSpawning();
		}

		private static void Postfix(EnemyWaveSpawner __instance)
		{
			Guard.Run(delegate
			{
				EnemySync.AfterSpawn(__instance);
			}, "SpawnNewEnemy");
		}
	}
	[HarmonyPatch(typeof(EnemyWaveSpawner), "SpawnNewAIEnemy")]
	internal static class SpawnNewAIEnemyPatch
	{
		private static bool Prefix()
		{
			return EnemySync.AllowLocalSpawning();
		}

		private static void Postfix(EnemyWaveSpawner __instance)
		{
			Guard.Run(delegate
			{
				EnemySync.AfterSpawn(__instance);
			}, "SpawnNewAIEnemy");
		}
	}
	[HarmonyPatch(typeof(StickHatOnStart), "Start")]
	internal static class HatStartPatch
	{
		private static void Prefix(StickHatOnStart __instance)
		{
			Guard.Run(delegate
			{
				EnemySync.BeforeCosmeticStart((Component)(object)__instance, 1191);
			}, "StickHatOnStart");
		}
	}
	[HarmonyPatch(typeof(RandomClothesColorOnStart), "Start")]
	internal static class ClothesStartPatch
	{
		private static void Prefix(RandomClothesColorOnStart __instance)
		{
			Guard.Run(delegate
			{
				EnemySync.BeforeCosmeticStart((Component)(object)__instance, 451);
			}, "RandomClothesColorOnStart");
		}
	}
	[HarmonyPatch(typeof(BulletManager), "BulletRaycast")]
	internal static class BulletRaycastPatch
	{
		private static void Prefix(BulletManager __instance)
		{
			BulletForce.Current = __instance.bulletHitForce;
		}
	}
	[HarmonyPatch(typeof(BulletManager), "OnCollisionEnter")]
	internal static class BulletCollisionPatch
	{
		private static void Prefix(BulletManager __instance)
		{
			BulletForce.Current = __instance.bulletHitForce;
		}
	}
	[HarmonyPatch(typeof(ColliderBridge), "HitByBullet")]
	internal static class BridgeHitByBulletPatch
	{
		private static void Prefix(ColliderBridge __instance, float damage, string caliber, Vector3 hitPosition, Vector3 rayDir, Vector3 hitNormal, float exitWoundGoreValue, out int __state)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			int queued = -1;
			Guard.Run(delegate
			{
				//IL_0015: 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_0021: Unknown result type (might be due to invalid IL or missing references)
				queued = EnemySync.BeforePuppetBullet(__instance, BulletEvent.Hit, damage, caliber, hitPosition, rayDir, hitNormal, exitWoundGoreValue);
			}, "HitByBullet");
			__state = queued;
		}

		private static void Postfix(ColliderBridge __instance, float damage, string caliber, Vector3 hitPosition, Vector3 rayDir, Vector3 hitNormal, float exitWoundGoreValue, int __state)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: 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_0034: Unknown result type (might be due to invalid IL or missing references)
			Guard.Run(delegate
			{
				//IL_0025: Unknown result type (might be due to invalid IL or missing references)
				//IL_002b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0031: Unknown result type (might be due to invalid IL or missing references)
				EnemySync.AfterPuppetBullet(__instance, __state);
				EnemySync.AfterHostBullet(__instance, BulletEvent.Hit, damage, caliber, hitPosition, rayDir, hitNormal, exitWoundGoreValue);
			}, "HitByBullet");
		}
	}
	[HarmonyPatch(typeof(ColliderBridge), "ExitedBullet")]
	internal static class BridgeExitedBulletPatch
	{
		private static void Prefix(ColliderBridge __instance, Vector3 hitPosition, Vector3 hitNormal, string caliber, Vector3 exitDirection, float exitWoundGoreValue)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			Guard.Run(delegate
			{
				//IL_0013: Unknown result type (might be due to invalid IL or missing references)
				//IL_0019: Unknown result type (might be due to invalid IL or missing references)
				//IL_001f: Unknown result type (might be due to invalid IL or missing references)
				EnemySync.BeforePuppetBullet(__instance, BulletEvent.Exit, 0f, caliber, hitPosition, exitDirection, hitNormal, exitWoundGoreValue);
			}, "ExitedBullet");
		}

		private static void Postfix(ColliderBridge __instance, Vector3 hitPosition, Vector3 hitNormal, string caliber, Vector3 exitDirection, float exitWoundGoreValue)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			Guard.Run(delegate
			{
				//IL_0013: Unknown result type (might be due to invalid IL or missing references)
				//IL_0019: Unknown result type (might be due to invalid IL or missing references)
				//IL_001f: Unknown result type (might be due to invalid IL or missing references)
				EnemySync.AfterHostBullet(__instance, BulletEvent.Exit, 0f, caliber, hitPosition, exitDirection, hitNormal, exitWoundGoreValue);
			}, "ExitedBullet");
		}
	}
	[HarmonyPatch(typeof(ColliderBridge), "OnCollisionEnter")]
	internal static class BridgeCollisionPatch
	{
		private static void Postfix(ColliderBridge __instance, Collision collision)
		{
			Guard.Run(delegate
			{
				EnemySync.AfterPuppetCollision(__instance, collision);
			}, "ColliderBridge collision");
		}
	}
	[HarmonyPatch(typeof(StickHealth), "HitByBullet")]
	internal static class StickHitByBulletPatch
	{
		private static bool Prefix(StickHealth __instance)
		{
			return Guard.Run(() => EnemySync.AllowStickReaction(__instance), "StickHealth.HitByBullet");
		}
	}
	[HarmonyPatch(typeof(StickHealth), "StartStaggering")]
	internal static class StickStaggerPatch
	{
		private static bool Prefix(StickHealth __instance)
		{
			return Guard.Run(() => EnemySync.AllowStickReaction(__instance), "StickHealth.StartStaggering");
		}
	}
	[HarmonyPatch(typeof(StickHealth), "StartFlailingOrProtecting")]
	internal static class StickFlailPatch
	{
		private static bool Prefix(StickHealth __instance)
		{
			return Guard.Run(() => EnemySync.AllowStickReaction(__instance), "StickHealth.StartFlailingOrProtecting");
		}
	}
	[HarmonyPatch(typeof(StickHealth), "ForceUnbalance", new Type[] { })]
	internal static class StickUnbalancePatch
	{
		private static bool Prefix(StickHealth __instance)
		{
			return Guard.Run(() => EnemySync.AllowStickReaction(__instance), "StickHealth.ForceUnbalance");
		}
	}
	[HarmonyPatch(typeof(StickHealth), "ForceUnbalance", new Type[] { typeof(bool) })]
	internal static class StickUnbalance2Patch
	{
		private static bool Prefix(StickHealth __instance)
		{
			return Guard.Run(() => EnemySync.AllowStickReaction(__instance), "StickHealth.ForceUnbalance");
		}
	}
	internal static class PuppetAttack
	{
		public static bool Allow(Component c)
		{
			return !EnemySync.IsPuppet(((Component)c.transform.root).gameObject);
		}
	}
	[HarmonyPatch(typeof(EnemyMeleeManager), "OnCollisionEnter")]
	internal static class PuppetMeleePatch
	{
		private static bool Prefix(EnemyMeleeManager __instance)
		{
			return PuppetAttack.Allow((Component)(object)__instance);
		}
	}
	[HarmonyPatch(typeof(ZombieAttackTrigger), "OnTriggerEnter")]
	internal static class PuppetZombieTriggerPatch
	{
		private static bool Prefix(ZombieAttackTrigger __instance)
		{
			return PuppetAttack.Allow((Component)(object)__instance);
		}
	}
	[HarmonyPatch(typeof(ZombieAttackingManager), "PlayerEnteredTrigger")]
	internal static class PuppetZombieBitePatch
	{
		private static bool Prefix(ZombieAttackingManager __instance)
		{
			return PuppetAttack.Allow((Component)(object)__instance);
		}
	}
	[HarmonyPatch(typeof(ZombieSwipeAttackManager), "PlayerEnteredTrigger")]
	internal static class PuppetZombieSwipePatch
	{
		private static bool Prefix(ZombieSwipeAttackManager __instance)
		{
			return PuppetAttack.Allow((Component)(object)__instance);
		}
	}
	[HarmonyPatch(typeof(ZombieGrabScript), "OnTriggerEnter")]
	internal static class PuppetZombieGrabPatch
	{
		private static bool Prefix(ZombieGrabScript __instance)
		{
			return PuppetAttack.Allow((Component)(object)__instance);
		}
	}
	[HarmonyPatch(typeof(GrabScript), "OnTriggerEnter")]
	internal static class PuppetGrabPatch
	{
		private static bool Prefix(GrabScript __instance)
		{
			return PuppetAttack.Allow((Component)(object)__instance);
		}
	}
	[HarmonyPatch(typeof(SliceOnInputCustom), "InitiateSlicing", new Type[]
	{
		typeof(Plane),
		typeof(Vector3),
		typeof(Vector3),
		typeof(Rigidbody),
		typeof(Il2CppReferenceArray<Collider>),
		typeof(Rigidbody),
		typeof(float),
		typeof(Rigidbody)
	})]
	internal static class SlicePatch
	{
		private static void Prefix(SliceOnInputCustom __instance, Plane slicePlaneWorld, Vector3 hitPoint, Vector3 worldVelocity, Rigidbody directlyConnectedRb, float reEnableCollisionDistance)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			Guard.Run(delegate
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_000d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0013: Unknown result type (might be due to invalid IL or missing references)
				EnemySync.BeforeSlice(__instance, slicePlaneWorld, hitPoint, worldVelocity, directlyConnectedRb, reEnableCollisionDistance);
			}, "SliceOnInputCustom.InitiateSlicing");
		}
	}
	[HarmonyPatch(typeof(StickGunManager), "FireGun")]
	internal static class FireGunPatch
	{
		private static void Postfix(StickGunManager __instance)
		{
			Guard.Run(delegate
			{
				EnemySync.AfterHostShot(__instance);
			}, "FireGun");
		}
	}
	[HarmonyPatch(typeof(PlayerDamageBridge), "HitByBullet")]
	internal static class ProxyBulletPatch
	{
		private static bool Prefix(PlayerDamageBridge __instance)
		{
			return Guard.Run(() => PlayerProxies.OnBridgeBullet(__instance), "PlayerDamageBridge.HitByBullet");
		}
	}
	[HarmonyPatch(typeof(PlayerDamageBridge), "HitByZombie")]
	internal static class ProxyZombieHitPatch
	{
		private static bool Prefix(PlayerDamageBridge __instance, Vector3 hitPoint, float damage, Transform zombiePelvis, float hitForce)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			return Guard.Run(() => PlayerProxies.OnBridgeZombieHit(__instance, hitPoint, damage, zombiePelvis, hitForce), "PlayerDamageBridge.HitByZombie");
		}
	}
	[HarmonyPatch(typeof(PlayerDamageBridge), "BittenByZombie")]
	internal static class ProxyBitePatch
	{
		private static bool Prefix(PlayerDamageBridge __instance, float damage)
		{
			return Guard.Run(() => PlayerProxies.OnBridgeBite(__instance, damage), "PlayerDamageBridge.BittenByZombie");
		}
	}
	[HarmonyPatch(typeof(PlayerHealthManager), "HitDetected")]
	internal static class ProxyMeleePatch
	{
		private static bool Prefix(PlayerDamageBridge dmgBridge, Collision collision)
		{
			return Guard.Run(() => PlayerProxies.OnMeleeHit(dmgBridge, collision), "PlayerHealthManager.HitDetected");
		}
	}
	[HarmonyPatch(typeof(MissionComplete), "ContinueMission")]
	internal static class ContinueMissionPatch
	{
		private static bool Prefix()
		{
			return Guard.Run((Func<bool>)MissionSync.OnLocalContinue, "ContinueMission");
		}
	}
	[HarmonyPatch(typeof(MissionComplete), "ReturnToMainHub")]
	internal static class ReturnToHubPatch
	{
		private static void Prefix()
		{
			Guard.Run((Action)MissionSync.OnLocalReturnToHub, "ReturnToMainHub");
		}
	}
	internal static class EnemySync
	{
		private sealed class HostEnemy
		{
			public ushort NetId;

			public GameObject Root;

			public StickHealth Health;

			public EnemySpawnMsg? Spawn;

			public float RegisteredAt;

			public Transform[] Bones = Array.Empty<Transform>();

			public bool DeathSent;

			public bool CanReplicate;

			public int Seed;

			public int EnemNum;

			public string? Prefab;
		}

		private sealed class Puppet
		{
			public ushort NetId;

			public GameObject Root;

			public StickHealth? Health;

			public Transform?[] Bones = Array.Empty<Transform>();

			public readonly SnapshotBuffer<BonePose[]> Buffer = new SnapshotBuffer<BonePose[]>(BonePoses.Lerp)
			{
				InterpolationDelay = 0.12f
			};

			public Rigidbody[] Bodies = Array.Empty<Rigidbody>();

			public float NextFreeze;

			public int LookHash;

			public float LookCheckAt;

			public bool Dead;

			public float NextImpulse;

			public bool[] HiddenByUs = Array.Empty<bool>();
		}

		private sealed class LocalGrab
		{
			public ushort NetId;

			public int Root;
		}

		private sealed class RemoteGrab
		{
			public HostEnemy Enemy;

			public ConfigurableJoint? Joint;

			public GameObject Grip;

			public Rigidbody GripBody;

			public Vector3 Offset;

			public Vector3 TargetPos;

			public Quaternion TargetRot = Quaternion.Identity;

			public bool HasTarget;
		}

		private const float StateInterval = 0.05f;

		private const int MaxStatesBytes = 1100;

		private const float ManifestDelay = 0.25f;

		private static readonly Random Seeds = new Random();

		private static readonly Dictionary<int, (int Seed, int EnemNum)> Origins = new Dictionary<int, (int, int)>();

		private static int? _forcedSeed;

		private static int _seedInUse;

		private static readonly Dictionary<ushort, HostEnemy> HostEnemies = new Dictionary<ushort, HostEnemy>();

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

		private static ushort _nextNetId = 1;

		private static float _nextStates;

		private static int _statesTick;

		private static WaveStateMsg _lastWave;

		private static float _nextWaveCheck;

		private static readonly PacketWriter Scratch = new PacketWriter(1500);

		public const int SpawnSentToHost = -1;

		private static bool _replaying;

		private static readonly Dictionary<ushort, Puppet> Puppets = new Dictionary<ushort, Puppet>();

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

		private static bool _buildingPuppet;

		private static bool _applyingHostFx;

		private static readonly HashSet<string> PuppetDisables = new HashSet<string>
		{
			"StickHealth", "EnemyAIManager", "EnemyInfectionManager", "ZombieAttackingManager", "ZombieAttackTrigger", "ZombieSwipeAttackManager", "ZombieGrabScript", "GrabScript", "GrabRangeTrigger", "HandIKSmooth",
			"HeadIKSmooth", "StickBodyAnimator", "JointBreakerTreeManager", "EnemyVoiceManager", "MovementBasic", "EnemyFOV", "EnemyFOV2", "StickGunManager", "StickRifleManager", "EnemyMeleeManager",
			"ActRagJoint", "ActRagHipJoint", "ActRagJointWorld", "StickJointBreaker", "TimeStopManager", "FootStepManager", "EnemyDoorOpener", "EnemyListener", "EnemyPatrol", "IKLegMovement",
			"InverseKinematicsGecko", "DisableCollisionWithPlayer", "Animator", "NavMeshAgent", "HVRTriggerButtonEvents", "SpriteBillboardToMainCamera"
		};

		private static float _nextProbeLog;

		private static EnemyWaveSpawner? _spawner;

		private static bool _replayingSlice;

		private static Rigidbody? _sliceDummy;

		private static readonly LocalGrab?[] MyGrabs = new LocalGrab[2];

		private static float _nextHands;

		private static readonly Dictionary<(ulong Client, bool Left), RemoteGrab> Grabs = new Dictionary<(ulong, bool), RemoteGrab>();

		private static Session? Session => MultiplayerMod.Session;

		private static bool IsHost
		{
			get
			{
				Session? session = Session;
				if (session == null)
				{
					return false;
				}
				return session.Role == Role.Host;
			}
		}

		private static bool IsClient
		{
			get
			{
				Session? session = Session;
				if (session == null)
				{
					return false;
				}
				return session.Role == Role.Client;
			}
		}

		public static int SharedCount => HostEnemies.Count;

		public static int PuppetCount => Puppets.Count;

		public static bool BuildingPuppet => _buildingPuppet;

		public static bool SeenSlice { get; private set; }

		public static Rigidbody SliceDummy
		{
			get
			{
				//IL_0017: 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_0022: Expected O, but got Unknown
				//IL_0022: 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)
				if (PlayerRig.IsAlive((Object?)(object)_sliceDummy))
				{
					return _sliceDummy;
				}
				GameObject val = new GameObject("BMP slice stand-in");
				Object.DontDestroyOnLoad((Object)val);
				val.transform.position = new Vector3(0f, -10000f, 0f);
				_sliceDummy = val.AddComponent<Rigidbody>();
				_sliceDummy.isKinematic = true;
				_sliceDummy.useGravity = false;
				_sliceDummy.mass = 2f;
				return _sliceDummy;
			}
		}

		public static void BeforeInstantiate()
		{
			_seedInUse = _forcedSeed ?? Seeds.Next();
			Random.InitState(_seedInUse);
		}

		public static void AfterInstantiate(int enemNum, GameObject? enemy)
		{
			if ((Object)(object)enemy != (Object)null)
			{
				Origins[((Object)enemy).GetInstanceID()] = (_seedInUse, enemNum);
			}
		}

		public static void BeforeCosmeticStart(Component c, int salt)
		{
			if (Origins.TryGetValue(((Object)((Component)c.transform.root).gameObject).GetInstanceID(), out (int, int) value))
			{
				Random.InitState(value.Item1 ^ salt);
			}
		}

		public static void AfterSpawn(EnemyWaveSpawner spawner)
		{
			if (!IsHost)
			{
				return;
			}
			List<StickHealth> stickHealthScripts = spawner.stickHealthScripts;
			if (stickHealthScripts == null)
			{
				return;
			}
			for (int i = 0; i < stickHealthScripts.Count; i++)
			{
				StickHealth val = stickHealthScripts[i];
				if (!((Object)(object)val == (Object)null))
				{
					GameObject gameObject = ((Component)((Component)val).transform.root).gameObject;
					if (!HostByRoot.ContainsKey(((Object)gameObject).GetInstanceID()))
					{
						Register(gameObject, val);
					}
				}
			}
		}

		public static bool BeforeSpawnGun(SpawnGunManager gun, out int seed)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			seed = 0;
			SpawnableItemInfo currentSpawnableItem = gun.currentSpawnableItem;
			if ((Object)(object)currentSpawnableItem == (Object)null || (int)currentSpawnableItem.spawnableType != 0 || (Object)(object)currentSpawnableItem.itemPrefab == (Object)null)
			{
				return true;
			}
			if (IsClient)
			{
				PacketWriter w = Session.Begin(MsgType.EnemySpawnRequest);
				EnemySpawnRequestMsg enemySpawnRequestMsg = default(EnemySpawnRequestMsg);
				enemySpawnRequestMsg.Prefab = ((Object)currentSpawnableItem.itemPrefab).name;
				enemySpawnRequestMsg.Position = gun.spawnableTargetPos.ToN();
				enemySpawnRequestMsg.Yaw = gun.currentRotation;
				enemySpawnRequestMsg.Write(w);
				Session.SendToHost(reliable: true);
				MultiplayerMod.Toast("Spawning it in the host's game...");
				seed = -1;
				return false;
			}
			seed = Seeds.Next(1, int.MaxValue);
			Random.InitState(seed);
			return true;
		}

		public static void AfterSpawnGun(SpawnGunManager gun, int seed)
		{
			if (seed <= 0 || !IsHost)
			{
				return;
			}
			List<GameObject> spawnedItems = gun.spawnedItems;
			if (spawnedItems != null && spawnedItems.Count != 0)
			{
				GameObject val = spawnedItems[spawnedItems.Count - 1];
				SpawnableItemInfo currentSpawnableItem = gun.currentSpawnableItem;
				if ((Object)(object)val != (Object)null && (Object)(object)((currentSpawnableItem != null) ? currentSpawnableItem.itemPrefab : null) != (Object)null)
				{
					RegisterPrefabEnemy(val, ((Object)currentSpawnableItem.itemPrefab).name, seed);
				}
			}
		}

		private static void OnSpawnRequest(EnemySpawnRequestMsg m)
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = ItemCatalog.Find(m.Prefab);
			if ((Object)(object)val == (Object)null)
			{
				MelonLogger.Warning("A friend tried to spawn '" + m.Prefab + "', which this game doesn't have");
				return;
			}
			int num = Seeds.Next(1, int.MaxValue);
			Random.InitState(num);
			GameObject val2 = Object.Instantiate<GameObject>(val, m.Position.ToU(), Quaternion.Euler(0f, m.Yaw, 0f));
			if (!val2.activeSelf)
			{
				val2.SetActive(true);
			}
			RegisterPrefabEnemy(val2, m.Prefab, num);
		}

		private static void RegisterPrefabEnemy(GameObject go, string prefab, int seed)
		{
			StickHealth componentInChildren = go.GetComponentInChildren<StickHealth>(true);
			if (!((Object)(object)componentInChildren == (Object)null) && !HostByRoot.ContainsKey(((Object)go).GetInstanceID()))
			{
				Origins[((Object)go).GetInstanceID()] = (seed, -1);
				Register(go, componentInChildren);
				HostByRoot[((Object)go).GetInstanceID()].Prefab = prefab;
			}
		}

		private static void Register(GameObject root, StickHealth stick)
		{
			HostEnemy hostEnemy = new HostEnemy
			{
				NetId = _nextNetId++,
				Root = root,
				Health = stick,
				RegisteredAt = Time.unscaledTime
			};
			if (_nextNetId == 0)
			{
				_nextNetId = 1;
			}
			if (Origins.TryGetValue(((Object)root).GetInstanceID(), out (int, int) value))
			{
				hostEnemy.CanReplicate = true;
				(hostEnemy.Seed, hostEnemy.EnemNum) = value;
			}
			else
			{
				MelonLogger.Warning("Enemy '" + ((Object)root).name + "' wasn't made by the wave spawner; friends won't see it");
			}
			HostEnemies[hostEnemy.NetId] = hostEnemy;
			HostByRoot[((Object)root).GetInstanceID()] = hostEnemy;
		}

		private static void Announce(HostEnemy e)
		{
			List<Transform> list = new List<Transform>();
			List<string> list2 = new List<string>();
			foreach (Rigidbody componentsInChild in e.Root.GetComponentsInChildren<Rigidbody>(false))
			{
				if (!((Object)(object)componentsInChild == (Object)null) && !componentsInChild.isKinematic)
				{
					list.Add(((Component)componentsInChild).transform);
					list2.Add(Paths.Of(((Component)componentsInChild).transform, e.Root.transform));
					if (list.Count == 255)
					{
						break;
					}
				}
			}
			e.Bones = list.ToArray();
			PrefabRef? prefabRef = null;
			EnemyWaveSpawner val = FindSpawner();
			if ((Object)(object)val != (Object)null && e.Prefab == null)
			{
				prefabRef = EnemyCatalog.Identify(val, e.Root);
			}
			e.Spawn = new EnemySpawnMsg
			{
				NetId = e.NetId,
				Template = ((e.Prefab == null) ? EnemyTemplate.Spawner : EnemyTemplate.Prefab),
				ScenePath = (e.Prefab ?? ""),
				EnemNum = e.EnemNum,
				Seed = e.Seed,
				PrefabKind = (byte)(prefabRef?.Kind ?? ((PrefabKind)255)),
				PrefabIndex = (ushort)(prefabRef?.Index ?? 65535),
				LookHash = EnemyCatalog.LookHash(e.Root),
				Bones = list2.ToArray()
			};
			MelonLogger.Msg($"Sharing enemy {e.NetId} '{((Object)e.Root).name}' ({list.Count} bodies)");
			if (DevOptions.Probe)
			{
				MelonLogger.Msg($"[probe] host enemy {e.NetId} seed {e.Seed}: {EnemyCatalog.Extras(e.Root)}");
			}
			SendSpawn(e, 0uL);
		}

		private static void SendSpawn(HostEnemy e, ulong onlyTo)
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			if (e.Spawn != null)
			{
				PacketWriter w = Session.Begin(MsgType.FirstGameplay);
				e.Spawn.Write(w);
				Session.SendToClients(reliable: true, onlyTo);
				if (e.Health.isDead)
				{
					PacketWriter w2 = Session.Begin(MsgType.EnemyDied);
					EnemyDiedMsg enemyDiedMsg = default(EnemyDiedMsg);
					enemyDiedMsg.NetId = e.NetId;
					enemyDiedMsg.Cause = (byte)e.Health.causeOfDeath;
					enemyDiedMsg.Write(w2);
					Session.SendToClients(reliable: true, onlyTo);
				}
			}
		}

		public static void SendAllTo(ulong client)
		{
			if (!IsHost)
			{
				return;
			}
			foreach (HostEnemy value in HostEnemies.Values)
			{
				SendSpawn(value, client);
			}
			PacketWriter w = Session.Begin(MsgType.WaveState);
			_lastWave.Write(w);
			Session.SendToClients(reliable: true, client);
			ItemSync.SendAllTo(client);
		}

		private static void HostUpdate()
		{
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			float unscaledTime = Time.unscaledTime;
			UpdateGrabs();
			List<HostEnemy> list = new List<HostEnemy>();
			foreach (HostEnemy value in HostEnemies.Values)
			{
				if (!PlayerRig.IsAlive((Object?)(object)value.Root) || !value.Root.activeInHierarchy)
				{
					list.Add(value);
					continue;
				}
				if (value.Spawn == null && value.CanReplicate && unscaledTime - value.RegisteredAt >= 0.25f)
				{
					Announce(value);
				}
				if (value.Spawn != null && !value.DeathSent && value.Health.isDead)
				{
					value.DeathSent = true;
					PacketWriter w = Session.Begin(MsgType.EnemyDied);
					EnemyDiedMsg enemyDiedMsg = default(EnemyDiedMsg);
					enemyDiedMsg.NetId = value.NetId;
					enemyDiedMsg.Cause = (byte)value.Health.causeOfDeath;
					enemyDiedMsg.Write(w);
					Session.SendToClients(reliable: true, 0uL);
				}
			}
			foreach (HostEnemy item in list)
			{
				HostEnemies.Remove(item.NetId);
				HostByRoot.Remove(((Object)item.Root).GetInstanceID());
				if (item.Spawn != null)
				{
					PacketWriter w2 = Session.Begin(MsgType.EnemyDespawn);
					EnemyDespawnMsg enemyDespawnMsg = default(EnemyDespawnMsg);
					enemyDespawnMsg.NetId = item.NetId;
					enemyDespawnMsg.Write(w2);
					Session.SendToClients(reliable: true, 0uL);
				}
			}
			if (Session.Peers.Count == 0)
			{
				return;
			}
			if (unscaledTime >= _nextStates)
			{
				_nextStates = unscaledTime + 0.05f;
				_statesTick++;
				SendStates();
			}
			if (unscaledTime >= _nextWaveCheck)
			{
				_nextWaveCheck = unscaledTime + 0.5f;
				WaveStateMsg lastWave = ReadWave();
				if (!lastWave.Equals(_lastWave))
				{
					_lastWave = lastWave;
					PacketWriter w3 = Session.Begin(MsgType.WaveState);
					lastWave.Write(w3);
					Session.SendToClients(reliable: true, 0uL);
				}
			}
		}

		private static void SendStates()
		{
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			EnemyStatesMsg enemyStatesMsg = new EnemyStatesMsg
			{
				Time = MultiplayerMod.Now
			};
			int num = 5;
			foreach (HostEnemy value in HostEnemies.Values)
			{
				if (value.Spawn == null || (value.Health.isDead && _statesTick % 4 != 0))
				{
					continue;
				}
				EnemyState enemyState = new EnemyState
				{
					NetId = value.NetId,
					Flags = (value.Health.isDead ? EnemyFlags.Dead : EnemyFlags.None),
					Bones = new BonePose[value.Bones.Length]
				};
				for (int i = 0; i < value.Bones.Length; i++)
				{
					Transform val = value.Bones[i];
					if (PlayerRig.IsAlive((Object?)(object)val))
					{
						enemyState.Bones[i] = new BonePose
						{
							Position = val.position.ToN(),
							Rotation = val.rotation.ToN(),
							Active = ((Component)val).gameObject.activeInHierarchy
						};
					}
				}
				int num2 = EnemyStatesMsg.SizeOf(enemyState);
				if (num + num2 > 1100 && enemyStatesMsg.Enemies.Count > 0)
				{
					Flush(enemyStatesMsg);
					enemyStatesMsg = new EnemyStatesMsg
					{
						Time = enemyStatesMsg.Time
					};
					num = 5;
				}
				enemyStatesMsg.Enemies.Add(enemyState);
				num += num2;
			}
			if (enemyStatesMsg.Enemies.Count > 0)
			{
				Flush(enemyStatesMsg);
			}
			static void Flush(EnemyStatesMsg m)
			{
				PacketWriter w = Session.Begin(MsgType.EnemyStates);
				m.Write(w);
				Session.SendToClients(reliable: false, 0uL);
			}
		}

		private static WaveStateMsg ReadWave()
		{
			KillCounterScript instanceOfThis = KillCounterScript.instanceOfThis;
			EnemyWaveSpawner val = FindSpawner();
			if (!((Object)(object)instanceOfThis == (Object)null))
			{
				return new WaveStateMsg
				{
					Kills = instanceOfThis.killsCounted,
					BallisticKills = instanceOfThis.ballisticKillsCounted,
					HeadshotKills = instanceOfThis.headshotKillsCounted,
					MeleeKills = instanceOfThis.meleeKillsCounted,
					DecapitationKills = instanceOfThis.decapitationKillsCounted,
					DismemberKills = instanceOfThis.dismemberKillsCounted,
					AllBreachEnemiesKilled = ((Object)(object)val != (Object)null && val.allBreachEnemiesKilled)
				};
			}
			return default(WaveStateMsg);
		}

		private static void OnClientDamage(EnemyDamageMsg m)
		{
			if (!HostEnemies.TryGetValue(m.NetId, out HostEnemy value) || value.Health.isDead)
			{
				return;
			}
			if (DevOptions.Probe)
			{
				MelonLogger.Msg($"[probe] friend's damage on enemy {m.NetId}: {m.DamageType} {m.Amount} at {m.Region}");
			}
			Transform? obj = Paths.Resolve(value.Root.transform, m.BridgePath);
			ColliderBridge val = ((obj != null) ? ((Component)obj).GetComponent<ColliderBridge>() : null);
			value.Health.ReceivedGeneralDamage(m.Amount, (DamageType)m.DamageType, m.Region, val);
			if (m.ConsciousnessAmount != 0f || m.ThroatSlit)
			{
				List<DamageInfo> damageInfoList = value.Health.damageInfoList;
				if (damageInfoList != null && damageInfoList.Count > 0)
				{
					DamageInfo val2 = damageInfoList[damageInfoList.Count - 1];
					val2.consciousnessDamageAmount = m.ConsciousnessAmount;
					val2.isThroatSlit = m.ThroatSlit;
					damageInfoList[damageInfoList.Count - 1] = val2;
				}
			}
		}

		private static void OnClientBullet(ulong from, EnemyBulletMsg m)
		{
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_018b: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			if (!HostEnemies.TryGetValue(m.NetId, out HostEnemy value))
			{
				return;
			}
			if (DevOptions.Probe)
			{
				MelonLogger.Msg($"[probe] friend's bullet {m.Event} on enemy {m.NetId} ({m.Damage} dmg), health before {value.Health.StickCurrentHealth}");
			}
			Transform val = Paths.Resolve(value.Root.transform, m.BridgePath);
			ColliderBridge val2 = ((val != null) ? ((Component)val).GetComponent<ColliderBridge>() : null);
			if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null)
			{
				return;
			}
			Vector3 val3 = val.TransformPoint(m.LocalPosition.ToU());
			Vector3 val4 = val.TransformDirection(m.LocalDirection.ToU());
			Vector3 val5 = val.TransformDirection(m.LocalNormal.ToU());
			_replaying = true;
			try
			{
				if (m.Event == BulletEvent.Hit)
				{
					val2.HitByBullet(m.Damage, m.Caliber, val3, val4, val5, m.Gore);
					Rigidbody val6 = ((Component)val2).GetComponent<Rigidbody>() ?? ((Component)val2).GetComponentInParent<Rigidbody>();
					if ((Object)(object)val6 != (Object)null && m.Force > 0f)
					{
						val6.AddForceAtPosition((val4.ToN() * Math.Min(m.Force, 500f)).ToU(), val3, (ForceMode)1);
					}
				}
				else
				{
					val2.ExitedBullet(val3, val5, m.Caliber, val4, m.Gore);
				}
			}
			finally
			{
				_replaying = false;
			}
			PacketWriter w = Session.Begin(MsgType.EnemyBulletFx);
			m.Write(w);
			foreach (PeerState value2 in Session.Peers.Values)
			{
				if (value2.Direct && value2.Id != from)
				{
					Session.SendToClients(reliable: true, value2.Id);
				}
			}
		}

		private static void OnClientImpulse(EnemyImpulseMsg m)
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			if (!HostEnemies.TryGetValue(m.NetId, out HostEnemy value))
			{
				return;
			}
			Transform val = Paths.Resolve(value.Root.transform, m.BonePath);
			Rigidbody val2 = ((val != null) ? ((Component)val).GetComponent<Rigidbody>() : null);
			if (!((Object)(object)val == (Object)null) && !((Object)(object)val2 == (Object)null))
			{
				Vector3 vector = m.Impulse;
				if (vector.Length() > 200f)
				{
					vector = Vector3.Normalize(vector) * 200f;
				}
				val2.AddForceAtPosition(vector.ToU(), val.TransformPoint(m.LocalPoint.ToU()), (ForceMode)1);
			}
		}

		public static void AfterHostBullet(ColliderBridge bridge, BulletEvent ev, float damage, string caliber, Vector3 pos, Vector3 dir, Vector3 normal, float gore)
		{
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			if (IsHost && !_replaying && Session.Peers.Count != 0 && HostByRoot.TryGetValue(((Object)((Component)((Component)bridge).transform.root).gameObject).GetInstanceID(), out HostEnemy value) && value.Spawn != null)
			{
				Transform transform = ((Component)bridge).transform;
				PacketWriter w = Session.Begin(MsgType.EnemyBulletFx);
				EnemyBulletMsg enemyBulletMsg = default(EnemyBulletMsg);
				enemyBulletMsg.NetId = value.NetId;
				enemyBulletMsg.BridgePath = Paths.Of(transform, value.Root.transform);
				enemyBulletMsg.Event = ev;
				enemyBulletMsg.Damage = damage;
				enemyBulletMsg.Caliber = caliber ?? "";
				enemyBulletMsg.LocalPosition = transform.InverseTransformPoint(pos).ToN();
				enemyBulletMsg.LocalDirection = transform.InverseTransformDirection(dir).ToN();
				enemyBulletMsg.LocalNormal = transform.InverseTransformDirection(normal).ToN();
				enemyBulletMsg.Gore = gore;
				enemyBulletMsg.Write(w);
				Session.SendToClients(reliable: true, 0uL);
			}
		}

		public static void AfterHostShot(StickGunManager gun)
		{
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			if (IsHost && Session.Peers.Count != 0 && !((Object)(object)gun.bulletSpawnTransform == (Object)null) && HostByRoot.TryGetValue(((Object)((Component)((Component)gun).transform.root).gameObject).GetInstanceID(), out HostEnemy value) && value.Spawn != null)
			{
				Transform bulletSpawnTransform = gun.bulletSpawnTransform;
				PacketWriter w = Session.Begin(MsgType.EnemyShot);
				EnemyShotMsg enemyShotMsg = default(EnemyShotMsg);
				enemyShotMsg.NetId = value.NetId;
				enemyShotMsg.Position = bulletSpawnTransform.position.ToN();
				enemyShotMsg.Rotation = bulletSpawnTransform.rotation.ToN();
				enemyShotMsg.Velocity = bulletSpawnTransform.forward.ToN() * gun.bulletSpeed;
				enemyShotMsg.Write(w);
				Session.SendToClients(reliable: false, 0uL);
			}
		}

		public static bool IsHostEnemy(GameObject root)
		{
			return HostByRoot.ContainsKey(((Object)root).GetInstanceID());
		}

		public static IEnumerable<StickHealth> HostEnemyHealth()
		{
			foreach (HostEnemy value in HostEnemies.Values)
			{
				if (PlayerRig.IsAlive((Object?)(object)value.Health))
				{
					yield return value.Health;
				}
			}
		}

		public static bool AllowLocalSpawning()
		{
			return !IsClient;
		}

		public static bool IsPuppet(GameObject root)
		{
			return PuppetByRoot.ContainsKey(((Object)root).GetInstanceID());
		}

		private static void OnSpawn(EnemySpawnMsg m)
		{
			if (Puppets.ContainsKey(m.NetId))
			{
				return;
			}
			EnemyWaveSpawner val = FindSpawner();
			GameObject val2;
			if (m.Template == EnemyTemplate.Prefab)
			{
				if (MissionSync.HostScene != MultiplayerMod.CurrentScene)
				{
					return;
				}
				val2 = BuildFromPrefab(m);
				if ((Object)(object)val2 == (Object)null)
				{
					return;
				}
			}
			else
			{
				if ((Object)(object)val == (Object)null || m.Template != EnemyTemplate.Spawner)
				{
					return;
				}
				_forcedSeed = m.Seed;
				_buildingPuppet = true;
				try
				{
					val2 = val.InstantiateEnemy(m.EnemNum);
				}
				catch (Exception value)
				{
					MelonLogger.Error($"Couldn't build host enemy {m.NetId}: {value}");
					return;
				}
				finally
				{
					_forcedSeed = null;
					_buildingPuppet = false;
				}
				if ((Object)(object)val2 == (Object)null)
				{
					return;
				}
			}
			if (m.PrefabKind != byte.MaxValue && (Object)(object)val != (Object)null)
			{
				PrefabRef? prefabRef = EnemyCatalog.Identify(val, val2);
				if (!prefabRef.HasValue || (uint)prefabRef.Value.Kind != m.PrefabKind || prefabRef.Value.Index != m.PrefabIndex)
				{
					MelonLogger.Warning($"Host enemy {m.NetId} is {m.PrefabKind}[{m.PrefabIndex}] but ours came out {prefabRef?.ToString() ?? "unknown"}");
				}
			}
			Puppetize(val2);
			Puppet puppet = new Puppet
			{
				NetId = m.NetId,
				Root = val2,
				Health = val2.GetComponentInChildren<StickHealth>(true),
				Bodies = Il2CppArrayBase<Rigidbody>.op_Implicit(val2.GetComponentsInChildren<Rigidbody>(true)),
				LookHash = m.LookHash,
				LookCheckAt = Time.unscaledTime + 0.5f
			};
			puppet.Bones = (Transform?[])(object)new Transform[m.Bones.Length];
			int num = 0;
			for (int i = 0; i < m.Bones.Length; i++)
			{
				puppet.Bones[i] = Paths.Resolve(val2.transform, m.Bones[i]);
				if ((Object)(object)puppet.Bones[i] == (Object)null)
				{
					num++;
				}
			}
			puppet.HiddenByUs = new bool[puppet.Bones.Length];
			((Object)val2).name = $"BMP host enemy {m.NetId}";
			Puppets[m.NetId] = puppet;
			PuppetByRoot[((Object)val2).GetInstanceID()] = puppet;
			if (m.Template == EnemyTemplate.Prefab)
			{
				Transform parent = val2.transform.parent;
				Random.InitState(m.Seed);
				val2.transform.SetParent((Transform)null, true);
				if ((Object)(object)parent != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)parent).gameObject);
				}
			}
			val2.SetActive(true);
			MelonLogger.Msg($"Showing host enemy {m.NetId} ({m.Bones.Length} bodies{((num > 0) ? $", {num} not found on ours" : "")})");
		}

		private static GameObject? BuildFromPrefab(EnemySpawnMsg m)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected O, but got Unknown
			GameObject val = ItemCatalog.Find(m.ScenePath);
			if ((Object)(object)val == (Object)null)
			{
				MelonLogger.Warning("The host spawned '" + m.ScenePath + "', which this game doesn't have");
				return null;
			}
			GameObject val2 = new GameObject("BMP spawn holder");
			val2.SetActive(false);
			GameObject val3 = Object.Instantiate<GameObject>(val, val2.transform);
			if (!val3.activeSelf)
			{
				val3.SetActive(true);
			}
			Origins[((Object)val3).GetInstanceID()] = (m.Seed, -1);
			return val3;
		}

		private static void Puppetize(GameObject root)
		{
			foreach (Component componentsInChild in root.GetComponentsInChildren<Component>(true))
			{
				if (!((Object)(object)componentsInChild == (Object)null) && PuppetDisables.Contains(((MemberInfo)((Object)componentsInChild).GetIl2CppType()).Name))
				{
					Behaviour val = ((Il2CppObjectBase)componentsInChild).TryCast<Behaviour>();
					if ((Object)(object)val != (Object)null)
					{
						val.enabled = false;
					}
				}
			}
			foreach (Rigidbody componentsInChild2 in root.GetComponentsInChildren<Rigidbody>(true))
			{
				componentsInChild2.isKinematic = true;
				componentsInChild2.interpolation = (RigidbodyInterpolation)0;
			}
			foreach (Canvas componentsInChild3 in root.GetComponentsInChildren<Canvas>(true))
			{
				((Component)componentsInChild3).gameObject.SetActive(false);
			}
		}

		private static void OnStates(EnemyStatesMsg m)
		{
			foreach (EnemyState enemy in m.Enemies)
			{
				if (Puppets.TryGetValue(enemy.NetId, out Puppet value) && enemy.Bones.Length == value.Bones.Length)
				{
					value.Buffer.Add(m.Time, in enemy.Bones);
					if ((enemy.Flags & EnemyFlags.Dead) != EnemyFlags.None && !value.Dead)
					{
						MarkDead(value, 0);
					}
				}
			}
		}

		private static void OnDied(EnemyDiedMsg m)
		{
			if (Puppets.TryGetValue(m.NetId, out Puppet value) && !value.Dead)
			{
				MarkDead(value, m.Cause);
			}
		}

		private static void MarkDead(Puppet p, byte cause)
		{
			p.Dead = true;
			if ((Object)(object)p.Health != (Object)null)
			{
				p.Health.isDead = true;
			}
			EnemyWaveSpawner val = FindSpawner();
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			try
			{
				val.StickDeathRegen((DamageType)cause);
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("Kill regen failed: " + ex.Message);
			}
		}

		private static void OnDespawn(EnemyDespawnMsg m)
		{
			if (Puppets.Remove(m.NetId, out Puppet value))
			{
				PuppetByRoot.Remove(((Object)value.Root).GetInstanceID());
				if (PlayerRig.IsAlive((Object?)(object)value.Root))
				{
					Object.Destroy((Object)(object)value.Root);
				}
			}
		}

		private static void OnWaveState(WaveStateMsg m)
		{
			KillCounterScript instanceOfThis = KillCounterScript.instanceOfThis;
			if ((Object)(object)instanceOfThis != (Object)null)
			{
				instanceOfThis.killsCounted = m.Kills;
				instanceOfThis.ballisticKillsCounted = m.BallisticKills;
				instanceOfThis.headshotKillsCounted = m.HeadshotKills;
				instanceOfThis.meleeKillsCounted = m.MeleeKills;
				instanceOfThis.decapitationKillsCounted = m.DecapitationKills;
				instanceOfThis.dismemberKillsCounted = m.DismemberKills;
			}
			EnemyWaveSpawner val = FindSpawner();
			if ((Object)(object)val != (Object)null)
			{
				val.allBreachEnemiesKilled = m.AllBreachEnemiesKilled;
			}
		}

		private static void OnBulletFx(EnemyBulletMsg m)
		{
			//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_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			if (!Puppets.TryGetValue(m.NetId, out Puppet value))
			{
				return;
			}
			Transform val = Paths.Resolve(value.Root.transform, m.BridgePath);
			ColliderBridge val2 = ((val != null) ? ((Component)val).GetComponent<ColliderBridge>() : null);
			if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null)
			{
				return;
			}
			Vector3 val3 = val.TransformPoint(m.LocalPosition.ToU());
			Vector3 val4 = val.TransformDirection(m.LocalDirection.ToU());
			Vector3 val5 = val.TransformDirection(m.LocalNormal.ToU());
			_applyingHostFx = true;
			try
			{
				if (m.Event == BulletEvent.Hit)
				{
					val2.HitByBullet(m.Damage, m.Caliber, val3, val4, val5, m.Gore);
				}
				else
				{
					val2.ExitedBullet(val3, val5, m.Caliber, val4, m.Gore);
				}
			}
			finally
			{
				_applyingHostFx = false;
				StickHealth? health = value.Health;
				if (health != null)
				{
					health.damageInfoList?.Clear();
				}
			}
		}

		private static void OnShot(EnemyShotMsg m)
		{
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: 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)
			if (!Puppets.TryGetValue(m.NetId, out Puppet value))
			{
				return;
			}
			StickGunManager componentInChildren = value.Root.GetComponentInChildren<StickGunManager>(true);
			if ((Object)(object)componentInChildren == (Object)null || (Object)(object)componentInChildren.bulletPrefab == (Object)null)
			{
				return;
			}
			Vector3 val = m.Position.ToU();
			Quaternion val2 = m.Rotation.ToU();
			try
			{
				if ((Object)(object)componentInChildren.muzzleFlashParticles != (Object)null)
				{
					componentInChildren.muzzleFlashParticles.Play();
				}
				Il2CppReferenceArray<AudioClip> fireSounds = componentInChildren.fireSounds;
				if ((Object)(object)componentInChildren.fireSoundSource != (Object)null && fireSounds != null && ((Il2CppArrayBase<AudioClip>)(object)fireSounds).Length > 0)
				{
					componentInChildren.fireSoundSource.PlayOneShot(((Il2CppArrayBase<AudioClip>)(object)fireSounds)[Random.Range(0, ((Il2CppArrayBase<AudioClip>)(object)fireSounds).Length)]);
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("Muzzle effects failed: " + ex.Message);
			}
			GameObject val3 = Object.Instantiate<GameObject>(componentInChildren.bulletPrefab, val, val2);
			if (DevOptions.Probe)
			{
				MelonLogger.Msg($"[probe] host enemy {m.NetId} fired; our copy fired '{((Object)val3).name}' from {val}");
			}
		}

		private static void OnPlayerHit(PlayerHitMsg m)
		{
			PlayerDamage.ApplyFromHost(m);
		}

		private static void ClientUpdate(float dt)
		{
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0385: Unknown result type (might be due to invalid IL or missing references)
			//IL_039f: Unknown result type (might be due to invalid IL or missing references)
			float unscaledTime = Time.unscaledTime;
			if (DevOptions.Probe && unscaledTime >= _nextProbeLog && Puppets.Count > 0)
			{
				_nextProbeLog = unscaledTime + 3f;
				foreach (Puppet value in Puppets.Values)
				{
					Transform val = ((value.Bones.Length != 0) ? value.Bones[0] : null);
					MelonLogger.Msg($"[probe] puppet {value.NetId}: snapshots={value.Buffer.Count} last={value.Buffer.TimeSinceLastSnapshot:F2}s ago dead={value.Dead} body0={(((Object)(object)val != (Object)null) ? ((object)val.position/*cast due to .constrained prefix*/).ToString() : "-")}");
				}
			}
			foreach (Puppet value2 in Puppets.Values)
			{
				if (!PlayerRig.IsAlive((Object?)(object)value2.Root))
				{
					continue;
				}
				if (value2.LookCheckAt > 0f && unscaledTime >= value2.LookCheckAt)
				{
					value2.LookCheckAt = 0f;
					if (EnemyCatalog.LookHash(value2.Root) != value2.LookHash)
					{
						MelonLogger.Warning($"Host enemy {value2.NetId} came out looking different here (armor or hats won't match)");
					}
					if (DevOptions.Probe)
					{
						MelonLogger.Msg($"[probe] our copy of {value2.NetId}: {EnemyCatalog.Extras(value2.Root)}");
					}
					else if (DevOptions.Probe)
					{
						MelonLogger.Msg($"[probe] puppet {value2.NetId} matches the host's enemy part for part");
					}
				}
				if (unscaledTime >= value2.NextFreeze)
				{
					value2.NextFreeze = unscaledTime + 0.5f;
					Rigidbody[] bodies = value2.Bodies;
					foreach (Rigidbody val2 in bodies)
					{
						if ((Object)(object)val2 != (Object)null && !val2.isKinematic)
						{
							val2.isKinematic = true;
						}
					}
				}
				if (value2.Buffer.TrySample(dt, out BonePose[] state))
				{
					for (int j = 0; j < state.Length && j < value2.Bones.Length; j++)
					{
						Transform val3 = value2.Bones[j];
						if ((Object)(object)val3 == (Object)null)
						{
							continue;
						}
						GameObject gameObject = ((Component)val3).gameObject;
						if (!state[j].Active)
						{
							if (gameObject.activeSelf)
							{
								gameObject.SetActive(false);
								value2.HiddenByUs[j] = true;
							}
							continue;
						}
						if (!gameObject.activeSelf)
						{
							if (!value2.HiddenByUs[j])
							{
								continue;
							}
							gameObject.SetActive(true);
							value2.HiddenByUs[j] = false;
						}
						val3.position = state[j].Position.ToU();
						val3.rotation = state[j].Rotation.ToU();
					}
				}
				ForwardQueuedDamage(value2);
			}
			SendGrabHands(unscaledTime);
		}

		private static void ForwardQueuedDamage(Puppet p)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Invalid comparison between Unknown and I4
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Invalid comparison between Unknown and I4
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Invalid comparison between Unknown and I4
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Invalid comparison between Unknown and I4
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			StickHealth? health = p.Health;
			List<DamageInfo> val = ((health != null) ? health.damageInfoList : null);
			if (val == null || val.Count == 0)
			{
				return;
			}
			for (int num = 0; num < val.Count; num++)
			{
				DamageInfo val2 = val[num];
				DamageType damageType = val2.damageType;
				if ((int)dam