Decompiled source of UltimateRevive v1.0.0

UltimateRevive.dll

Decompiled 21 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using LethalCompanyInputUtils.Api;
using Microsoft.CodeAnalysis;
using TMPro;
using UltimateRevive.Patches;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.InputSystem;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("UltimateRevive")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("UltimateRevive")]
[assembly: AssemblyTitle("UltimateRevive")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace UltimateRevive
{
	public static class DeathMarkerManager
	{
		[CompilerGenerated]
		private sealed class <BuildCandidateBundlePaths>d__32 : IEnumerable<(string Path, bool FromConfiguredPath)>, IEnumerable, IEnumerator<(string Path, bool FromConfiguredPath)>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private (string Path, bool FromConfiguredPath) <>2__current;

			private int <>l__initialThreadId;

			private HashSet<string> <unique>5__2;

			private (string RawPath, bool FromConfiguredPath)[] <>7__wrap2;

			private int <>7__wrap3;

			private (string RawPath, bool FromConfiguredPath) <source>5__5;

			private string <resolved>5__6;

			(string, bool) IEnumerator<(string, bool)>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

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

			[DebuggerHidden]
			public <BuildCandidateBundlePaths>d__32(int <>1__state)
			{
				this.<>1__state = <>1__state;
				<>l__initialThreadId = Environment.CurrentManagedThreadId;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<unique>5__2 = null;
				<>7__wrap2 = null;
				<source>5__5 = default((string, bool));
				<resolved>5__6 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
				{
					<>1__state = -1;
					<unique>5__2 = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
					(string, bool)[] array = new(string, bool)[2]
					{
						(ModConfig.DeathTombAssetBundlePath, true),
						("UltimateRevive/bundles/sm_tombstone_3_dark_rip.bundle", false)
					};
					<>7__wrap2 = array;
					<>7__wrap3 = 0;
					goto IL_016d;
				}
				case 1:
					<>1__state = -1;
					goto IL_00f9;
				case 2:
					{
						<>1__state = -1;
						goto IL_014c;
					}
					IL_016d:
					if (<>7__wrap3 < <>7__wrap2.Length)
					{
						<source>5__5 = <>7__wrap2[<>7__wrap3];
						string item = <source>5__5.RawPath;
						if (!string.IsNullOrWhiteSpace(item))
						{
							<resolved>5__6 = (Path.IsPathRooted(item) ? item : Path.Combine(Paths.PluginPath, item));
							if (<unique>5__2.Add(<resolved>5__6))
							{
								<>2__current = (<resolved>5__6, <source>5__5.FromConfiguredPath);
								<>1__state = 1;
								return true;
							}
							goto IL_00f9;
						}
						goto IL_015f;
					}
					<>7__wrap2 = null;
					return false;
					IL_00f9:
					if (!Path.HasExtension(<resolved>5__6))
					{
						string text = <resolved>5__6 + ".bundle";
						if (<unique>5__2.Add(text))
						{
							<>2__current = (text, <source>5__5.FromConfiguredPath);
							<>1__state = 2;
							return true;
						}
					}
					goto IL_014c;
					IL_014c:
					<resolved>5__6 = null;
					<source>5__5 = default((string, bool));
					goto IL_015f;
					IL_015f:
					<>7__wrap3++;
					goto IL_016d;
				}
			}

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

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

			[DebuggerHidden]
			IEnumerator<(string Path, bool FromConfiguredPath)> IEnumerable<(string, bool)>.GetEnumerator()
			{
				if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId)
				{
					<>1__state = 0;
					return this;
				}
				return new <BuildCandidateBundlePaths>d__32(0);
			}

			[DebuggerHidden]
			IEnumerator IEnumerable.GetEnumerator()
			{
				return ((IEnumerable<(string, bool)>)this).GetEnumerator();
			}
		}

		private const float TombWidth = 0.6f;

		private const float TombHeight = 1.35f;

		private const float TombDepth = 0.35f;

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

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

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

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

		private static ManualLogSource _log;

		private static int _lastTickFrame = -1;

		private static AssetBundle _assetBundle;

		private static bool _assetBundleLoadAttempted;

		private static GameObject _cachedPrefab;

		public static void SetLogSource(ManualLogSource logSource)
		{
			_log = logSource;
		}

		public static void InvalidateBundleCache()
		{
			_assetBundleLoadAttempted = false;
			_cachedPrefab = null;
			if ((Object)(object)_assetBundle != (Object)null)
			{
				_assetBundle.Unload(false);
				_assetBundle = null;
			}
		}

		public static void Tick()
		{
			if (ModState.Active && ModConfig.EnableDeathTombFallback && Time.frameCount != _lastTickFrame)
			{
				_lastTickFrame = Time.frameCount;
				StartOfRound instance = StartOfRound.Instance;
				if (instance?.allPlayerScripts != null)
				{
					_ = instance.mapScreen;
				}
			}
		}

		public static bool IsTombDeadBody(int playerClientId, DeadBodyInfo deadBody)
		{
			if ((Object)(object)deadBody != (Object)null && TombDeadBodies.TryGetValue(playerClientId, out var value))
			{
				return (Object)(object)value == (Object)(object)deadBody;
			}
			return false;
		}

		public static bool TryGetTombOwnerPlayerId(DeadBodyInfo deadBody, out int playerClientId)
		{
			playerClientId = -1;
			if ((Object)(object)deadBody == (Object)null)
			{
				return false;
			}
			foreach (KeyValuePair<int, DeadBodyInfo> tombDeadBody in TombDeadBodies)
			{
				if ((Object)(object)tombDeadBody.Value == (Object)(object)deadBody)
				{
					playerClientId = tombDeadBody.Key;
					return true;
				}
			}
			return false;
		}

		private static void DebugLog(string message)
		{
			if (ModConfig.EnableDebugLogs)
			{
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogInfo((object)message);
				}
			}
		}

		public static bool IsTombRagdollGrabbable(RagdollGrabbableObject body)
		{
			if ((Object)(object)body == (Object)null)
			{
				return false;
			}
			foreach (DeadBodyInfo value in TombDeadBodies.Values)
			{
				if ((Object)(object)value != (Object)null && (Object)(object)((Component)value).gameObject == (Object)(object)((Component)body).gameObject)
				{
					return true;
				}
			}
			return false;
		}

		public static bool TrySpawnTombForPlayer(PlayerControllerB player, Vector3 position, string reason = null)
		{
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: 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_01a2: Unknown result type (might be due to invalid IL or missing references)
			if (!ModState.Active || !ModConfig.EnableDeathTombFallback || (Object)(object)player == (Object)null)
			{
				return false;
			}
			int num = (int)player.playerClientId;
			if (ActiveMarkers.TryGetValue(num, out var value) && (Object)(object)value != (Object)null)
			{
				DeadBodyInfo component = value.GetComponent<DeadBodyInfo>();
				if ((Object)(object)component != (Object)null)
				{
					TombDeadBodies[num] = component;
				}
				GeneralUtil.SetPlayerDeathState(num, PlayerDeathType.Body, 0uL, value.transform.position, string.IsNullOrWhiteSpace(reason) ? "TombAlreadyActive" : (reason + ":alreadyActive"));
				return true;
			}
			ClearMarker(num);
			GameObject val = null;
			try
			{
				GameObject markerPrefab = GetMarkerPrefab();
				val = (((Object)(object)markerPrefab != (Object)null) ? Object.Instantiate<GameObject>(markerPrefab) : CreateProceduralTombMarker());
			}
			catch (Exception ex)
			{
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogWarning((object)("Tomb prefab instantiate failed, using procedural marker: " + ex.Message));
				}
				val = CreateProceduralTombMarker();
			}
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			((Object)val).name = $"UltimateReviveTomb_{num}";
			position = ProjectTombPositionToGround(position, num);
			val.transform.position = position;
			KeepMarkerUpright(val);
			DeadBodyInfo val2 = TombDeadBodySetup.SetupTombAsDeadBody(player, val, position);
			if ((Object)(object)val2 == (Object)null)
			{
				Object.Destroy((Object)(object)val);
				return false;
			}
			ActiveMarkers[num] = val;
			TombDeadBodies[num] = val2;
			GeneralUtil.SetPlayerDeathState(num, PlayerDeathType.Body, 0uL, position, string.IsNullOrWhiteSpace(reason) ? "TombSpawned" : reason);
			ManualLogSource log2 = _log;
			if (log2 != null)
			{
				log2.LogInfo((object)string.Format("[DeathMarker] tombSpawned player={0} pos=({1:0.00},{2:0.00},{3:0.00}) reason={4}", num, position.x, position.y, position.z, string.IsNullOrWhiteSpace(reason) ? "unspecified" : reason));
			}
			return true;
		}

		private static Vector3 ProjectTombPositionToGround(Vector3 deathPosition, int playerClientId)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: 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_003f: 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_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: 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_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: 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_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			float num = Mathf.Max(0f, ModConfig.DeathTombGroundOffset);
			float num2 = Mathf.Max(1f, ModConfig.DeathTombSpawnHeight);
			if (TryRaycastGroundBelow(deathPosition + Vector3.up * num2, out var point))
			{
				Vector3 val = default(Vector3);
				((Vector3)(ref val))..ctor(deathPosition.x, point.y + num, deathPosition.z);
				DebugLog($"[DeathMarker] tombGroundSnap player={playerClientId} " + $"from=({deathPosition.x:0.00},{deathPosition.y:0.00},{deathPosition.z:0.00}) " + $"to=({val.x:0.00},{val.y:0.00},{val.z:0.00})");
				return val;
			}
			ManualLogSource log = _log;
			if (log != null)
			{
				log.LogWarning((object)($"[DeathMarker] tombGroundSnapFailed player={playerClientId} " + $"pos=({deathPosition.x:0.00},{deathPosition.y:0.00},{deathPosition.z:0.00})"));
			}
			return deathPosition;
		}

		private static bool TryRaycastGroundBelow(Vector3 origin, out Vector3 point)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: 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_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			point = Vector3.zero;
			RaycastHit[] array = Physics.RaycastAll(origin, Vector3.down, 120f, -1, (QueryTriggerInteraction)1);
			if (array == null || array.Length == 0)
			{
				return false;
			}
			RaycastHit val = default(RaycastHit);
			float num = float.PositiveInfinity;
			bool flag = false;
			for (int i = 0; i < array.Length; i++)
			{
				RaycastHit val2 = array[i];
				Collider collider = ((RaycastHit)(ref val2)).collider;
				if (!((Object)(object)collider == (Object)null) && collider.enabled && !collider.isTrigger && !((Object)(object)((Component)collider).GetComponentInParent<PlayerControllerB>() != (Object)null) && !((Object)(object)((Component)collider).GetComponentInParent<DeadBodyInfo>() != (Object)null) && !((Object)(object)((Component)collider).GetComponentInParent<MaskedPlayerEnemy>() != (Object)null) && !((Object)(object)((Component)collider).GetComponentInParent<ForestGiantAI>() != (Object)null))
				{
					Transform root = ((Component)collider).transform.root;
					if ((!((Object)(object)root != (Object)null) || !((Object)root).name.StartsWith("UltimateReviveTomb_")) && ((RaycastHit)(ref val2)).distance < num)
					{
						num = ((RaycastHit)(ref val2)).distance;
						val = val2;
						flag = true;
					}
				}
			}
			if (!flag)
			{
				return false;
			}
			point = ((RaycastHit)(ref val)).point;
			return true;
		}

		public static void ClearMarker(int playerClientId)
		{
			if (!ActiveMarkers.TryGetValue(playerClientId, out var value))
			{
				return;
			}
			if (TombDeadBodies.TryGetValue(playerClientId, out var value2))
			{
				PlayerControllerB playerByClientId = GeneralUtil.GetPlayerByClientId(playerClientId);
				if ((Object)(object)playerByClientId != (Object)null && (Object)(object)playerByClientId.deadBody == (Object)(object)value2)
				{
					playerByClientId.deadBody = null;
				}
				TombDeadBodies.Remove(playerClientId);
			}
			UnregisterMarkerFromRadar(playerClientId);
			if ((Object)(object)value != (Object)null)
			{
				Object.Destroy((Object)(object)value);
			}
			ActiveMarkers.Remove(playerClientId);
		}

		public static bool HasActiveMarker(int playerClientId)
		{
			if (ActiveMarkers.TryGetValue(playerClientId, out var value))
			{
				return (Object)(object)value != (Object)null;
			}
			return false;
		}

		public static bool TryGetMarkerObject(int playerClientId, out GameObject markerObject)
		{
			if (ActiveMarkers.TryGetValue(playerClientId, out var value) && (Object)(object)value != (Object)null)
			{
				markerObject = value;
				return true;
			}
			markerObject = null;
			return false;
		}

		public static bool TryResolveTargetedMarkerPlayerId(ManualCameraRenderer mapScreen, out int playerClientId)
		{
			playerClientId = -1;
			PlayerControllerB val = mapScreen?.targetedPlayer;
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			DeadBodyInfo deadBody = val.deadBody;
			if ((Object)(object)deadBody == (Object)null)
			{
				return false;
			}
			int num = (int)val.playerClientId;
			if (IsTombDeadBody(num, deadBody))
			{
				playerClientId = num;
				return true;
			}
			foreach (KeyValuePair<int, DeadBodyInfo> tombDeadBody in TombDeadBodies)
			{
				if ((Object)(object)tombDeadBody.Value == (Object)(object)deadBody)
				{
					playerClientId = tombDeadBody.Key;
					return true;
				}
			}
			return false;
		}

		public static bool TryGetMarkerPosition(int playerClientId, out Vector3 position)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			if (ActiveMarkers.TryGetValue(playerClientId, out var value) && (Object)(object)value != (Object)null && (Object)(object)value.transform != (Object)null)
			{
				position = value.transform.position;
				return true;
			}
			position = default(Vector3);
			return false;
		}

		public static void Reset()
		{
			foreach (KeyValuePair<int, DeadBodyInfo> tombDeadBody in TombDeadBodies)
			{
				PlayerControllerB playerByClientId = GeneralUtil.GetPlayerByClientId(tombDeadBody.Key);
				if ((Object)(object)playerByClientId != (Object)null && (Object)(object)playerByClientId.deadBody == (Object)(object)tombDeadBody.Value)
				{
					playerByClientId.deadBody = null;
				}
			}
			TombDeadBodies.Clear();
			foreach (GameObject value in ActiveMarkers.Values)
			{
				if ((Object)(object)value != (Object)null)
				{
					Object.Destroy((Object)(object)value);
				}
			}
			ActiveMarkers.Clear();
			LastMarkerDecision.Clear();
			MarkerRadarIndexes.Clear();
			_lastTickFrame = -1;
			InvalidateBundleCache();
		}

		private static void UnregisterMarkerFromRadar(int playerClientId)
		{
			MarkerRadarIndexes.Remove(playerClientId);
		}

		private static void KeepMarkerUpright(GameObject marker)
		{
			//IL_001e: 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_0026: 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_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)marker == (Object)null) && !((Object)(object)marker.transform == (Object)null))
			{
				Quaternion rotation = marker.transform.rotation;
				Vector3 eulerAngles = ((Quaternion)(ref rotation)).eulerAngles;
				marker.transform.rotation = Quaternion.Euler(0f, eulerAngles.y, 0f);
			}
		}

		private static GameObject CreateProceduralTombMarker()
		{
			//IL_0026: 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_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Expected O, but got Unknown
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			GameObject obj = GameObject.CreatePrimitive((PrimitiveType)3);
			((Object)obj).name = "ReviveDeathMarker_ProceduralCube";
			obj.transform.localScale = new Vector3(0.6f, 1.35f, 0.35f);
			obj.transform.localPosition = new Vector3(0f, 0.675f, 0f);
			Renderer component = obj.GetComponent<Renderer>();
			if ((Object)(object)component != (Object)null)
			{
				Material val = new Material(Shader.Find("Standard"));
				val.color = new Color(0.42f, 0.42f, 0.42f, 1f);
				component.material = val;
			}
			return obj;
		}

		private static GameObject GetMarkerPrefab()
		{
			if ((Object)(object)_cachedPrefab != (Object)null)
			{
				return _cachedPrefab;
			}
			if (_assetBundleLoadAttempted)
			{
				return null;
			}
			_assetBundleLoadAttempted = true;
			List<(string, bool)> list = BuildCandidateBundlePaths().ToList();
			if (list.Count == 0)
			{
				return null;
			}
			for (int i = 0; i < list.Count; i++)
			{
				string item = list[i].Item1;
				bool flag = !list[i].Item2;
				if (!File.Exists(item))
				{
					continue;
				}
				try
				{
					_assetBundle = AssetBundle.LoadFromFile(item);
					if ((Object)(object)_assetBundle == (Object)null)
					{
						ManualLogSource log = _log;
						if (log != null)
						{
							log.LogWarning((object)("Failed to load death marker AssetBundle: " + item));
						}
						continue;
					}
					string deathTombPrefabName = ModConfig.DeathTombPrefabName;
					if (!string.IsNullOrWhiteSpace(deathTombPrefabName))
					{
						_cachedPrefab = _assetBundle.LoadAsset<GameObject>(deathTombPrefabName);
					}
					if ((Object)(object)_cachedPrefab == (Object)null)
					{
						_cachedPrefab = _assetBundle.LoadAsset<GameObject>("sm_tombstone_3_dark_rip");
					}
					if ((Object)(object)_cachedPrefab == (Object)null)
					{
						GameObject[] array = _assetBundle.LoadAllAssets<GameObject>();
						_cachedPrefab = ((array != null && array.Length != 0) ? array[0] : null);
					}
					if ((Object)(object)_cachedPrefab != (Object)null)
					{
						if (flag)
						{
							ManualLogSource log2 = _log;
							if (log2 != null)
							{
								log2.LogWarning((object)("Configured death marker bundle failed, fallback loaded: " + item));
							}
						}
						return _cachedPrefab;
					}
					ManualLogSource log3 = _log;
					if (log3 != null)
					{
						log3.LogWarning((object)("Death marker AssetBundle has no GameObject prefab: " + item));
					}
					_assetBundle.Unload(false);
					_assetBundle = null;
				}
				catch (Exception ex)
				{
					ManualLogSource log4 = _log;
					if (log4 != null)
					{
						log4.LogWarning((object)("Death marker loading failed for " + item + ": " + ex.Message));
					}
					if ((Object)(object)_assetBundle != (Object)null)
					{
						_assetBundle.Unload(false);
						_assetBundle = null;
					}
				}
			}
			ManualLogSource log5 = _log;
			if (log5 != null)
			{
				log5.LogWarning((object)"No valid death marker bundle found. Using primitive fallback marker.");
			}
			return null;
		}

		[IteratorStateMachine(typeof(<BuildCandidateBundlePaths>d__32))]
		private static IEnumerable<(string Path, bool FromConfiguredPath)> BuildCandidateBundlePaths()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <BuildCandidateBundlePaths>d__32(-2);
		}
	}
	public static class GeneralUtil
	{
		private static ManualLogSource _log;

		private static readonly BindingFlags InstanceFieldFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

		private static readonly FieldInfo BodyPlayerScriptField = typeof(RagdollGrabbableObject).GetField("playerScript", InstanceFieldFlags);

		private static readonly FieldInfo BodyPlayerObjectScriptField = typeof(RagdollGrabbableObject).GetField("playerObjectScript", InstanceFieldFlags);

		private static readonly FieldInfo BodySourcePlayerField = typeof(RagdollGrabbableObject).GetField("sourcePlayer", InstanceFieldFlags);

		public static void SetLogSource(ManualLogSource logSource)
		{
			_log = logSource;
		}

		public static PlayerControllerB GetPlayerByClientId(int playerClientId)
		{
			if ((Object)(object)StartOfRound.Instance == (Object)null || StartOfRound.Instance.allPlayerScripts == null)
			{
				return null;
			}
			PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts;
			foreach (PlayerControllerB val in allPlayerScripts)
			{
				if ((Object)(object)val != (Object)null && (int)val.playerClientId == playerClientId)
				{
					return val;
				}
			}
			return null;
		}

		public static PlayerControllerB GetClosestAlivePlayer(Vector3 position)
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)StartOfRound.Instance == (Object)null || StartOfRound.Instance.allPlayerScripts == null)
			{
				return null;
			}
			PlayerControllerB result = null;
			float num = float.MaxValue;
			PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts;
			foreach (PlayerControllerB val in allPlayerScripts)
			{
				if (!((Object)(object)val == (Object)null) && !val.isPlayerDead && val.isPlayerControlled)
				{
					float num2 = Vector3.Distance(((Component)val).transform.position, position);
					if (num2 < num)
					{
						num = num2;
						result = val;
					}
				}
			}
			return result;
		}

		public static RagdollGrabbableObject GetBodyForPlayer(int playerClientId)
		{
			RagdollGrabbableObject bodyFromDeadBodyReference = GetBodyFromDeadBodyReference(GetPlayerByClientId(playerClientId));
			if ((Object)(object)bodyFromDeadBodyReference != (Object)null)
			{
				return bodyFromDeadBodyReference;
			}
			RagdollGrabbableObject[] array = Object.FindObjectsOfType<RagdollGrabbableObject>();
			foreach (RagdollGrabbableObject val in array)
			{
				if (TryGetBodyOwner(val, out var targetPlayerClientId, out var _) && targetPlayerClientId == playerClientId)
				{
					return val;
				}
			}
			return null;
		}

		public static RagdollGrabbableObject GetBodyFromDeadBodyReference(PlayerControllerB player)
		{
			if ((Object)(object)player?.deadBody == (Object)null)
			{
				return null;
			}
			DeadBodyInfo deadBody = player.deadBody;
			RagdollGrabbableObject val = ((Component)deadBody).GetComponent<RagdollGrabbableObject>() ?? ((Component)deadBody).GetComponentInChildren<RagdollGrabbableObject>() ?? ((Component)deadBody).GetComponentInParent<RagdollGrabbableObject>();
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			if ((Object)(object)((Component)deadBody).transform == (Object)null)
			{
				return null;
			}
			RagdollGrabbableObject[] array = Object.FindObjectsOfType<RagdollGrabbableObject>();
			foreach (RagdollGrabbableObject val2 in array)
			{
				if (!((Object)(object)((val2 != null) ? ((Component)val2).transform : null) == (Object)null) && ((Object)(object)((Component)val2).transform == (Object)(object)((Component)deadBody).transform || ((Component)val2).transform.IsChildOf(((Component)deadBody).transform) || ((Component)deadBody).transform.IsChildOf(((Component)val2).transform)))
				{
					return val2;
				}
			}
			return null;
		}

		public static RagdollGrabbableObject GetBodyByNetworkObjectId(ulong networkObjectId)
		{
			if (networkObjectId == 0L)
			{
				return null;
			}
			if ((Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.SpawnManager != null && NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out var value) && (Object)(object)value != (Object)null)
			{
				RagdollGrabbableObject component = ((Component)value).GetComponent<RagdollGrabbableObject>();
				if ((Object)(object)component != (Object)null)
				{
					return component;
				}
				component = ((Component)value).GetComponentInChildren<RagdollGrabbableObject>();
				if ((Object)(object)component != (Object)null)
				{
					return component;
				}
			}
			RagdollGrabbableObject[] array = Object.FindObjectsOfType<RagdollGrabbableObject>();
			foreach (RagdollGrabbableObject val in array)
			{
				if ((Object)(object)((val != null) ? ((NetworkBehaviour)val).NetworkObject : null) != (Object)null && ((NetworkBehaviour)val).NetworkObject.NetworkObjectId == networkObjectId)
				{
					return val;
				}
			}
			return null;
		}

		public static RagdollGrabbableObject GetBodyNearPosition(Vector3 position, float maxDistance = 12f)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			RagdollGrabbableObject[] array = Object.FindObjectsOfType<RagdollGrabbableObject>();
			RagdollGrabbableObject result = null;
			float num = maxDistance * maxDistance;
			RagdollGrabbableObject[] array2 = array;
			foreach (RagdollGrabbableObject val in array2)
			{
				if (!((Object)(object)((val != null) ? ((Component)val).transform : null) == (Object)null))
				{
					Vector3 val2 = ((Component)val).transform.position - position;
					float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude;
					if (sqrMagnitude <= num)
					{
						num = sqrMagnitude;
						result = val;
					}
				}
			}
			return result;
		}

		public static DeadBodyInfo GetDeadBodyNearPosition(Vector3 position, float maxDistance = 12f)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			DeadBodyInfo[] array = Object.FindObjectsOfType<DeadBodyInfo>();
			DeadBodyInfo result = null;
			float num = maxDistance * maxDistance;
			DeadBodyInfo[] array2 = array;
			foreach (DeadBodyInfo val in array2)
			{
				if (!((Object)(object)((val != null) ? ((Component)val).transform : null) == (Object)null))
				{
					Vector3 val2 = ((Component)val).transform.position - position;
					float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude;
					if (sqrMagnitude <= num)
					{
						num = sqrMagnitude;
						result = val;
					}
				}
			}
			return result;
		}

		public static bool TryGetBodyOwner(RagdollGrabbableObject body, out int targetPlayerClientId, out PlayerControllerB targetPlayer)
		{
			targetPlayerClientId = -1;
			targetPlayer = null;
			if ((Object)(object)body == (Object)null)
			{
				return false;
			}
			targetPlayer = body.ragdoll?.playerScript;
			if ((Object)(object)targetPlayer != (Object)null)
			{
				targetPlayerClientId = (int)targetPlayer.playerClientId;
				return true;
			}
			object? obj = BodyPlayerScriptField?.GetValue(body);
			PlayerControllerB val = (PlayerControllerB)(((obj is PlayerControllerB) ? obj : null) ?? ((object)(/*isinst with value type is only supported in some contexts*/ ?? /*isinst with value type is only supported in some contexts*/)));
			if ((Object)(object)val != (Object)null)
			{
				targetPlayer = val;
				targetPlayerClientId = (int)val.playerClientId;
				return true;
			}
			if (StartOfRound.Instance?.allPlayerScripts != null)
			{
				PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts;
				foreach (PlayerControllerB val2 in allPlayerScripts)
				{
					if (!((Object)(object)val2 == (Object)null))
					{
						DeadBodyInfo deadBody = val2.deadBody;
						if (!((Object)(object)deadBody == (Object)null) && (deadBody == body || ((Object)(object)((Component)deadBody).transform != (Object)null && (Object)(object)((Component)body).transform != (Object)null && ((Object)(object)((Component)deadBody).transform == (Object)(object)((Component)body).transform || ((Component)deadBody).transform.IsChildOf(((Component)body).transform) || ((Component)body).transform.IsChildOf(((Component)deadBody).transform)))))
						{
							targetPlayer = val2;
							targetPlayerClientId = (int)val2.playerClientId;
							return true;
						}
					}
				}
			}
			return false;
		}

		public static PlayerReviveInfo GetOrCreateInfo(int playerClientId)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			PlayerReviveInfo playerReviveInfo = ModState.PlayerInfos.FirstOrDefault((PlayerReviveInfo x) => x.PlayerClientId == playerClientId);
			if (playerReviveInfo != null)
			{
				return playerReviveInfo;
			}
			playerReviveInfo = new PlayerReviveInfo
			{
				PlayerClientId = playerClientId,
				TimeDiedAt = Time.time,
				LastDeathType = PlayerDeathType.Unknown,
				LastDeathNetworkObjectId = 0uL,
				LastDeathPosition = Vector3.zero,
				HasLastDeathPosition = false
			};
			ModState.PlayerInfos.Add(playerReviveInfo);
			return playerReviveInfo;
		}

		public static void ResetAllPlayerInfos()
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			ModState.PlayerInfos.Clear();
			if (StartOfRound.Instance?.allPlayerScripts == null)
			{
				return;
			}
			PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts;
			foreach (PlayerControllerB val in allPlayerScripts)
			{
				if (!((Object)(object)val == (Object)null))
				{
					ModState.PlayerInfos.Add(new PlayerReviveInfo
					{
						PlayerClientId = (int)val.playerClientId,
						TimeDiedAt = Time.time,
						HasBeenTeleported = false,
						TimesRevivedThisLevel = 0,
						LastDeathType = PlayerDeathType.Unknown,
						LastDeathNetworkObjectId = 0uL,
						LastDeathPosition = Vector3.zero,
						HasLastDeathPosition = false
					});
				}
			}
		}

		public static void SetPlayerDiedAt(int playerClientId)
		{
			SetPlayerDeathState(playerClientId, PlayerDeathType.Body, 0uL, "SetPlayerDiedAt");
		}

		public static void SetPlayerDeathState(int playerClientId, PlayerDeathType deathType, ulong networkObjectId = 0uL, string reason = null)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			SetPlayerDeathState(playerClientId, deathType, networkObjectId, Vector3.zero, hasPosition: false, reason);
		}

		public static void SetPlayerDeathState(int playerClientId, PlayerDeathType deathType, ulong networkObjectId, Vector3 deathPosition, string reason = null)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			SetPlayerDeathState(playerClientId, deathType, networkObjectId, deathPosition, hasPosition: true, reason);
		}

		private static void SetPlayerDeathState(int playerClientId, PlayerDeathType deathType, ulong networkObjectId, Vector3 deathPosition, bool hasPosition, string reason)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: 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_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			PlayerReviveInfo orCreateInfo = GetOrCreateInfo(playerClientId);
			PlayerDeathType lastDeathType = orCreateInfo.LastDeathType;
			ulong lastDeathNetworkObjectId = orCreateInfo.LastDeathNetworkObjectId;
			bool hasLastDeathPosition = orCreateInfo.HasLastDeathPosition;
			Vector3 lastDeathPosition = orCreateInfo.LastDeathPosition;
			if (orCreateInfo.TimeDiedAt == 0f)
			{
				orCreateInfo.TimeDiedAt = Time.time;
			}
			orCreateInfo.LastDeathType = deathType;
			orCreateInfo.LastDeathNetworkObjectId = networkObjectId;
			orCreateInfo.HasLastDeathPosition = hasPosition;
			orCreateInfo.LastDeathPosition = (hasPosition ? deathPosition : Vector3.zero);
			if (lastDeathType == deathType && lastDeathNetworkObjectId == networkObjectId && hasLastDeathPosition == hasPosition)
			{
				if (!hasPosition)
				{
					return;
				}
				if (hasLastDeathPosition)
				{
					Vector3 val = lastDeathPosition - deathPosition;
					if (!(((Vector3)(ref val)).sqrMagnitude > 0.0025f))
					{
						return;
					}
				}
			}
			string text = (hasLastDeathPosition ? $"{lastDeathType}/obj:{lastDeathNetworkObjectId}/pos:{FormatVector(lastDeathPosition)}" : $"{lastDeathType}/obj:{lastDeathNetworkObjectId}/pos:none");
			string text2 = (hasPosition ? $"{deathType}/obj:{networkObjectId}/pos:{FormatVector(deathPosition)}" : $"{deathType}/obj:{networkObjectId}/pos:none");
			Plugin.DebugLog(string.Format("[DeathState] player={0} from={1} to={2} reason={3}", playerClientId, text, text2, string.IsNullOrWhiteSpace(reason) ? "unspecified" : reason));
		}

		private static string FormatVector(Vector3 value)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			return $"({value.x:0.00},{value.y:0.00},{value.z:0.00})";
		}

		public static PlayerDeathType GetPlayerLastDeathType(int playerClientId)
		{
			return GetOrCreateInfo(playerClientId).LastDeathType;
		}

		public static ulong GetPlayerLastDeathNetworkObjectId(int playerClientId)
		{
			return GetOrCreateInfo(playerClientId).LastDeathNetworkObjectId;
		}

		public static bool IsLatestDeathType(int playerClientId, PlayerDeathType expectedType)
		{
			return GetPlayerLastDeathType(playerClientId) == expectedType;
		}

		public static bool TryGetPlayerLastDeathPosition(int playerClientId, out Vector3 position)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			PlayerReviveInfo orCreateInfo = GetOrCreateInfo(playerClientId);
			position = orCreateInfo.LastDeathPosition;
			return orCreateInfo.HasLastDeathPosition;
		}

		public static void ClearPlayerDeathState(int playerClientId)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			PlayerReviveInfo orCreateInfo = GetOrCreateInfo(playerClientId);
			orCreateInfo.LastDeathType = PlayerDeathType.Unknown;
			orCreateInfo.LastDeathNetworkObjectId = 0uL;
			orCreateInfo.LastDeathPosition = Vector3.zero;
			orCreateInfo.HasLastDeathPosition = false;
			orCreateInfo.TimeDiedAt = 0f;
		}

		public static float GetPlayerDiedAtTime(int playerClientId)
		{
			return GetOrCreateInfo(playerClientId).TimeDiedAt;
		}

		public static void MarkTeleported(int playerClientId)
		{
			GetOrCreateInfo(playerClientId).HasBeenTeleported = true;
		}

		public static bool HasPlayerTeleported(int playerClientId)
		{
			return GetOrCreateInfo(playerClientId).HasBeenTeleported;
		}

		public static int IncrementReviveCountAndGetHealth(int playerClientId)
		{
			PlayerReviveInfo orCreateInfo = GetOrCreateInfo(playerClientId);
			orCreateInfo.TimesRevivedThisLevel++;
			return Mathf.Max(1, ModConfig.ReviveHealth - (orCreateInfo.TimesRevivedThisLevel - 1) * ModConfig.ExtraHealthLostPerRevive);
		}

		public static void SetRemainingRevivesFromPlayerCount()
		{
			int num = 0;
			if (StartOfRound.Instance?.allPlayerScripts != null)
			{
				num = StartOfRound.Instance.allPlayerScripts.Count((PlayerControllerB p) => (Object)(object)p != (Object)null && p.isPlayerControlled);
			}
			if (!ModConfig.LimitedRevives)
			{
				ModState.RemainingRevives = int.MaxValue;
			}
			else if (ModConfig.FixedRevivesPerLevel > 0)
			{
				ModState.RemainingRevives = ModConfig.FixedRevivesPerLevel;
			}
			else
			{
				ModState.RemainingRevives = Mathf.RoundToInt((float)num * ModConfig.RevivesPerLevelMultiplier);
			}
			ManualLogSource log = _log;
			if (log != null)
			{
				log.LogInfo((object)$"Remaining revives for this level: {ModState.RemainingRevives}");
			}
		}

		public static bool IsNetworkReady()
		{
			if ((Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.CustomMessagingManager != null)
			{
				return NetworkManager.Singleton.IsListening;
			}
			return false;
		}

		public static float GetReviveValidationDistance(PlayerControllerB reviver)
		{
			if ((Object)(object)reviver == (Object)null)
			{
				return 0f;
			}
			float num = Mathf.Max(0.1f, reviver.grabDistance);
			float num2 = Mathf.Max(0.1f, ModConfig.MaxReviveDistance);
			return Mathf.Max(num, num2);
		}

		public static float GetTombReviveValidationDistance(PlayerControllerB reviver)
		{
			if ((Object)(object)reviver == (Object)null)
			{
				return 0f;
			}
			float num = Mathf.Max(0.1f, reviver.grabDistance);
			float num2 = Mathf.Max(0.1f, ModConfig.MaxReviveDistanceTomb);
			return Mathf.Max(num, num2);
		}

		public static bool IsReviverCloseEnough(PlayerControllerB reviver, Vector3 targetPosition)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)reviver == (Object)null)
			{
				return false;
			}
			float reviveValidationDistance = GetReviveValidationDistance(reviver);
			return IsReviverCloseEnoughInternal(reviver, targetPosition, reviveValidationDistance);
		}

		public static bool IsReviverCloseEnoughToTomb(PlayerControllerB reviver, Vector3 targetPosition)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)reviver == (Object)null)
			{
				return false;
			}
			float tombReviveValidationDistance = GetTombReviveValidationDistance(reviver);
			return IsReviverCloseEnoughInternal(reviver, targetPosition, tombReviveValidationDistance);
		}

		private static bool IsReviverCloseEnoughInternal(PlayerControllerB reviver, Vector3 targetPosition, float maxDistance)
		{
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController;
			Vector3 val2;
			if ((Object)(object)val != (Object)null && (Object)(object)val == (Object)(object)reviver && (Object)(object)reviver.gameplayCamera != (Object)null)
			{
				val2 = ((Component)reviver.gameplayCamera).transform.position;
			}
			else
			{
				if (!((Object)(object)((Component)reviver).transform != (Object)null))
				{
					return false;
				}
				val2 = ((Component)reviver).transform.position + Vector3.up * 1.6f;
				maxDistance += 1.5f;
			}
			return Vector3.Distance(val2, targetPosition) <= maxDistance;
		}

		public static MaskedPlayerEnemy GetConvertedMaskedForPlayer(int playerClientId, bool requireDead = false)
		{
			if (!ModConfig.EnableMaskedPlayerRevive)
			{
				return null;
			}
			MaskedPlayerEnemy[] array = Object.FindObjectsOfType<MaskedPlayerEnemy>();
			foreach (MaskedPlayerEnemy val in array)
			{
				if (!((Object)(object)val == (Object)null) && (!requireDead || MaskedConversionTracker.IsMaskedDead(val)) && TryResolveConvertedMaskedPlayer(val, out var playerClientId2, out var _) && playerClientId2 == playerClientId)
				{
					return val;
				}
			}
			return null;
		}

		public static MaskedPlayerEnemy GetConvertedMaskedCorpseForPlayer(int playerClientId)
		{
			return GetConvertedMaskedForPlayer(playerClientId, requireDead: true);
		}

		public static bool TryResolveConvertedMaskedPlayer(MaskedPlayerEnemy masked, out int playerClientId, out string source)
		{
			playerClientId = -1;
			source = "none";
			if ((Object)(object)masked == (Object)null)
			{
				return false;
			}
			if (ModConfig.EnableMaskedPlayerRevive && MaskedConversionTracker.IsConvertedMasked(masked, out playerClientId))
			{
				source = "InternalTag";
				return true;
			}
			return false;
		}

		public static MaskedPlayerEnemy GetMaskedByNetworkObjectId(ulong networkObjectId)
		{
			if (networkObjectId == 0L)
			{
				return null;
			}
			if ((Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.SpawnManager != null && NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out var value) && (Object)(object)value != (Object)null)
			{
				MaskedPlayerEnemy component = ((Component)value).GetComponent<MaskedPlayerEnemy>();
				if ((Object)(object)component != (Object)null)
				{
					return component;
				}
				component = ((Component)value).GetComponentInChildren<MaskedPlayerEnemy>();
				if ((Object)(object)component != (Object)null)
				{
					return component;
				}
			}
			MaskedPlayerEnemy[] array = Object.FindObjectsOfType<MaskedPlayerEnemy>();
			foreach (MaskedPlayerEnemy val in array)
			{
				if (!((Object)(object)val == (Object)null))
				{
					NetworkObject component2 = ((Component)val).GetComponent<NetworkObject>();
					if ((Object)(object)component2 != (Object)null && component2.NetworkObjectId == networkObjectId)
					{
						return val;
					}
				}
			}
			return null;
		}

		private static bool IsReviveableConvertedMaskedCorpse(MaskedPlayerEnemy masked)
		{
			int playerClientId;
			string source;
			if ((Object)(object)masked != (Object)null && MaskedConversionTracker.IsMaskedDead(masked))
			{
				return TryResolveConvertedMaskedPlayer(masked, out playerClientId, out source);
			}
			return false;
		}

		internal static bool TryResolveConvertedMaskedPlayer(object clientMasked, out int maskedOwnerId, out string _)
		{
			throw new NotImplementedException();
		}
	}
	public static class MaskedConversionTracker
	{
		internal sealed class PendingConversion
		{
			public int PlayerClientId;

			public MaskedOrigin Origin;

			public Vector3 Position;

			public float TimeRegistered;
		}

		private const float PendingLifetimeSeconds = 20f;

		private const string MaskedTriggerHostName = "UltimateReviveMaskedTrigger";

		private static readonly List<PendingConversion> PendingConversions = new List<PendingConversion>();

		private static ManualLogSource _log;

		public static int? GetPlayerIdForMasked(MaskedPlayerEnemy masked)
		{
			if ((Object)(object)masked == (Object)null)
			{
				return null;
			}
			if ((Object)(object)masked.mimickingPlayer != (Object)null)
			{
				return (int)masked.mimickingPlayer.playerClientId;
			}
			return null;
		}

		public static void SetLogSource(ManualLogSource logSource)
		{
			_log = logSource;
		}

		public static void Reset()
		{
			PendingConversions.Clear();
		}

		public static bool HasPendingForPlayer(int playerClientId)
		{
			CleanupExpiredPending();
			return PendingConversions.Any((PendingConversion x) => x.PlayerClientId == playerClientId);
		}

		public static void TagMaskedAsConverted(MaskedPlayerEnemy masked, int playerClientId, MaskedOrigin origin, string reason = null)
		{
			//IL_008f: 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_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)masked == (Object)null) && ModConfig.EnableMaskedPlayerRevive && playerClientId >= 0)
			{
				if (origin != MaskedOrigin.ConvertedByMaskItem && origin != MaskedOrigin.ConvertedByMaskedEnemy)
				{
					origin = MaskedOrigin.ConvertedByMaskedEnemy;
				}
				MaskedOriginInfo orAddInfo = GetOrAddInfo(masked);
				orAddInfo.Origin = origin;
				orAddInfo.FormerPlayerClientId = playerClientId;
				orAddInfo.TaggedAt = Time.time;
				PendingConversions.RemoveAll((PendingConversion x) => x.PlayerClientId == playerClientId);
				ulong networkObjectId = GetNetworkObjectId(masked);
				Vector3 deathPosition = (((Object)(object)((Component)masked).transform != (Object)null) ? ((Component)masked).transform.position : Vector3.zero);
				GeneralUtil.SetPlayerDeathState(playerClientId, PlayerDeathType.Masked, networkObjectId, deathPosition, string.IsNullOrWhiteSpace(reason) ? "MaskedTag:direct" : reason);
				ReviveInteractTriggerManager.ForceRefreshPlayer(playerClientId, string.IsNullOrWhiteSpace(reason) ? "MaskedTag:direct" : reason);
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogInfo((object)string.Format("Tagged Masked {0} as {1} from player {2} ({3}).", networkObjectId, origin, playerClientId, string.IsNullOrWhiteSpace(reason) ? "direct" : reason));
				}
			}
		}

		public static bool TryTagMaskedFromPending(MaskedPlayerEnemy masked)
		{
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: 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 ((Object)(object)masked == (Object)null || !ModConfig.EnableMaskedPlayerRevive)
			{
				return false;
			}
			CleanupExpiredPending();
			if (PendingConversions.Count == 0)
			{
				return false;
			}
			PendingConversion pendingConversion = null;
			int mimicId = GetMimickingPlayerClientId(masked);
			if (mimicId >= 0)
			{
				pendingConversion = PendingConversions.FirstOrDefault((PendingConversion x) => x.PlayerClientId == mimicId);
			}
			if (pendingConversion == null && PendingConversions.Count == 1)
			{
				PendingConversion pendingConversion2 = PendingConversions[0];
				Vector3 val = (((Object)(object)((Component)masked).transform != (Object)null) ? ((Component)masked).transform.position : pendingConversion2.Position);
				Vector3 val2 = pendingConversion2.Position - val;
				if (((Vector3)(ref val2)).sqrMagnitude <= 900f)
				{
					pendingConversion = pendingConversion2;
				}
			}
			if (pendingConversion == null)
			{
				return false;
			}
			ApplyConvertedTag(masked, GetOrAddInfo(masked), pendingConversion);
			return true;
		}

		public static void RegisterPending(PlayerControllerB player, MaskedOrigin origin)
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: 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 (ModConfig.EnableMaskedPlayerRevive && !((Object)(object)player == (Object)null) && (origin == MaskedOrigin.ConvertedByMaskItem || origin == MaskedOrigin.ConvertedByMaskedEnemy))
			{
				int playerClientId = (int)player.playerClientId;
				Vector3 val = (((Object)(object)((Component)player).transform != (Object)null) ? ((Component)player).transform.position : Vector3.zero);
				PendingConversions.RemoveAll((PendingConversion x) => x.PlayerClientId == playerClientId);
				PendingConversions.Add(new PendingConversion
				{
					PlayerClientId = playerClientId,
					Origin = origin,
					Position = val,
					TimeRegistered = Time.time
				});
				GeneralUtil.SetPlayerDeathState(playerClientId, PlayerDeathType.Masked, 0uL, val, $"MaskedPending:{origin}");
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogInfo((object)$"Registered pending Masked conversion: player={playerClientId}, origin={origin}.");
				}
			}
		}

		public static MaskedPlayerEnemy GetMaskedForPlayer(int playerClientId)
		{
			MaskedPlayerEnemy[] array = Object.FindObjectsOfType<MaskedPlayerEnemy>();
			foreach (MaskedPlayerEnemy val in array)
			{
				MaskedOriginInfo component = ((Component)val).GetComponent<MaskedOriginInfo>();
				if ((Object)(object)component != (Object)null && component.FormerPlayerClientId == playerClientId && (component.Origin == MaskedOrigin.ConvertedByMaskItem || component.Origin == MaskedOrigin.ConvertedByMaskedEnemy))
				{
					return val;
				}
			}
			return null;
		}

		public static void TagMaskedOnSpawn(MaskedPlayerEnemy masked)
		{
			if ((Object)(object)masked == (Object)null || !ModConfig.EnableMaskedPlayerRevive)
			{
				return;
			}
			MaskedOriginInfo orAddInfo = GetOrAddInfo(masked);
			if (orAddInfo.Origin == MaskedOrigin.ConvertedByMaskItem || orAddInfo.Origin == MaskedOrigin.ConvertedByMaskedEnemy || TryTagMaskedFromPending(masked))
			{
				return;
			}
			if (orAddInfo.Origin == MaskedOrigin.Unknown)
			{
				if (!TryTagFromMimickingPlayer(masked, orAddInfo))
				{
					orAddInfo.Origin = MaskedOrigin.NaturalSpawn;
					orAddInfo.FormerPlayerClientId = -1;
					orAddInfo.TaggedAt = Time.time;
				}
			}
			else if (orAddInfo.Origin == MaskedOrigin.NaturalSpawn)
			{
				TryTagFromMimickingPlayer(masked, orAddInfo);
			}
		}

		private static bool TryTagFromMimickingPlayer(MaskedPlayerEnemy masked, MaskedOriginInfo info)
		{
			int mimickingPlayerClientId = GetMimickingPlayerClientId(masked);
			if (mimickingPlayerClientId < 0)
			{
				return false;
			}
			PlayerControllerB playerByClientId = GeneralUtil.GetPlayerByClientId(mimickingPlayerClientId);
			if ((Object)(object)playerByClientId == (Object)null)
			{
				return false;
			}
			EnemyAI redirectToEnemy = playerByClientId.redirectToEnemy;
			MaskedPlayerEnemy val = (MaskedPlayerEnemy)(object)((redirectToEnemy is MaskedPlayerEnemy) ? redirectToEnemy : null);
			if ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)masked)
			{
				return false;
			}
			if (!playerByClientId.isPlayerDead && (Object)(object)val != (Object)(object)masked)
			{
				return false;
			}
			TagMaskedAsConverted(masked, mimickingPlayerClientId, MaskedOrigin.ConvertedByMaskItem, "TagMasked:mimickingPlayerFallback");
			return true;
		}

		public static bool IsConvertedMasked(MaskedPlayerEnemy masked, out int playerClientId)
		{
			playerClientId = -1;
			if ((Object)(object)masked == (Object)null || !ModConfig.EnableMaskedPlayerRevive)
			{
				return false;
			}
			MaskedOriginInfo component = ((Component)masked).GetComponent<MaskedOriginInfo>();
			if ((Object)(object)component == (Object)null)
			{
				TagMaskedOnSpawn(masked);
				component = ((Component)masked).GetComponent<MaskedOriginInfo>();
			}
			if ((Object)(object)component == (Object)null)
			{
				return false;
			}
			if (component.Origin != MaskedOrigin.ConvertedByMaskItem && component.Origin != MaskedOrigin.ConvertedByMaskedEnemy)
			{
				return false;
			}
			playerClientId = component.FormerPlayerClientId;
			return playerClientId >= 0;
		}

		public static bool IsMaskedDead(MaskedPlayerEnemy masked)
		{
			if ((Object)(object)masked != (Object)null)
			{
				return ((EnemyAI)masked).isEnemyDead;
			}
			return false;
		}

		public static ulong GetNetworkObjectId(MaskedPlayerEnemy masked)
		{
			if ((Object)(object)masked == (Object)null)
			{
				return 0uL;
			}
			if ((Object)(object)((masked != null) ? ((NetworkBehaviour)masked).NetworkObject : null) == (Object)null)
			{
				return 0uL;
			}
			return ((NetworkBehaviour)masked).NetworkObject.NetworkObjectId;
		}

		public static void ForgetPlayer(int playerClientId)
		{
			PendingConversions.RemoveAll((PendingConversion x) => x.PlayerClientId == playerClientId);
		}

		private static void ApplyConvertedTag(MaskedPlayerEnemy masked, MaskedOriginInfo info, PendingConversion pending)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			info.Origin = pending.Origin;
			info.FormerPlayerClientId = pending.PlayerClientId;
			info.TaggedAt = Time.time;
			ulong networkObjectId = GetNetworkObjectId(masked);
			Vector3 deathPosition = (((Object)(object)((Component)masked).transform != (Object)null) ? ((Component)masked).transform.position : pending.Position);
			GeneralUtil.SetPlayerDeathState(pending.PlayerClientId, PlayerDeathType.Masked, networkObjectId, deathPosition, "MaskedTagAssigned");
			ReviveInteractTriggerManager.ForceRefreshPlayer(pending.PlayerClientId, "MaskedTagAssigned");
			ManualLogSource log = _log;
			if (log != null)
			{
				log.LogInfo((object)$"Tagged Masked {GetNetworkObjectId(masked)} as {pending.Origin} from player {pending.PlayerClientId}.");
			}
		}

		private static void RemoveMaskedTriggerChild(MaskedPlayerEnemy masked)
		{
			if (!((Object)(object)masked == (Object)null) && !((Object)(object)((Component)masked).transform == (Object)null))
			{
				Transform val = ((Component)masked).transform.Find("UltimateReviveMaskedTrigger");
				if ((Object)(object)val != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)val).gameObject);
				}
			}
		}

		private static MaskedOriginInfo GetOrAddInfo(MaskedPlayerEnemy masked)
		{
			MaskedOriginInfo maskedOriginInfo = ((Component)masked).GetComponent<MaskedOriginInfo>();
			if ((Object)(object)maskedOriginInfo == (Object)null)
			{
				maskedOriginInfo = ((Component)masked).gameObject.AddComponent<MaskedOriginInfo>();
			}
			return maskedOriginInfo;
		}

		private static int GetMimickingPlayerClientId(MaskedPlayerEnemy masked)
		{
			try
			{
				PlayerControllerB value = Traverse.Create((object)masked).Field("mimickingPlayer").GetValue<PlayerControllerB>();
				if ((Object)(object)value != (Object)null)
				{
					return (int)value.playerClientId;
				}
			}
			catch
			{
			}
			return -1;
		}

		private static void CleanupExpiredPending()
		{
			float now = Time.time;
			PendingConversions.RemoveAll((PendingConversion x) => now - x.TimeRegistered > 20f);
		}
	}
	public enum MaskedOrigin
	{
		Unknown,
		NaturalSpawn,
		ConvertedByMaskItem,
		ConvertedByMaskedEnemy
	}
	public sealed class MaskedOriginInfo : MonoBehaviour
	{
		public MaskedOrigin Origin;

		public int FormerPlayerClientId = -1;

		public float TaggedAt;
	}
	public static class ModConfig
	{
		public const string DefaultDeathTombBundlePath = "UltimateRevive/bundles/sm_tombstone_3_dark_rip.bundle";

		public const string DefaultDeathTombPrefabName = "sm_tombstone_3_dark_rip";

		public static float ReviveTime;

		public static bool CanPickUpBodies;

		public static float DeadBodyWeight;

		public static bool CanReviveTeleportedBodies;

		public static int ReviveHealth;

		public static int ExtraHealthLostPerRevive;

		public static bool LimitedRevives;

		public static float RevivesPerLevelMultiplier;

		public static int FixedRevivesPerLevel;

		public static bool InfiniteReviveTime;

		public static int TimeUntilCannotBeRevived;

		public static float MaxReviveDistance;

		public static float MaxReviveDistanceTomb;

		public static bool EnableMaskedPlayerRevive;

		public static bool EnableDeathTombFallback;

		public static bool EnableDeathTombCollider;

		public static string DeathTombAssetBundlePath;

		public static string DeathTombPrefabName;

		public static float DeathTombGroundOffset;

		public static float DeathTombSpawnHeight;

		public static bool EnableDebugLogs;

		public static bool DebugLogging { get; internal set; }

		public static void Bind(ConfigFile config)
		{
			ReviveTime = config.Bind<float>("Revive", "ReviveTime", 5f, "How long, in seconds, the revive button must be held.").Value;
			CanPickUpBodies = config.Bind<bool>("Bodies", "CanPickUpBodies", true, "If false, pressing interact on bodies is blocked so the revive prompt is easier to use.").Value;
			DeadBodyWeight = config.Bind<float>("Bodies", "DeadBodyWeight", 1.35f, "Weight multiplier applied to dead bodies.").Value;
			CanReviveTeleportedBodies = config.Bind<bool>("Rules", "CanReviveTeleportedBodies", true, "If false, bodies teleported back to the ship cannot be revived.").Value;
			ReviveHealth = config.Bind<int>("Rules", "HealthYouReviveWith", 35, "Base health after revive.").Value;
			ExtraHealthLostPerRevive = config.Bind<int>("Rules", "ExtraHealthLostPerRevive", 5, "Additional health penalty each time the same player is revived in the same round.").Value;
			LimitedRevives = config.Bind<bool>("Rules", "LimitAmountOfRevives", true, "If true, revives are limited per level.").Value;
			RevivesPerLevelMultiplier = config.Bind<float>("Rules", "RevivesPerLevelMultiplier", 1.25f, "Revives per level = controlled players * this value, unless FixedRevivesPerLevel is above 0.").Value;
			FixedRevivesPerLevel = config.Bind<int>("Rules", "FixedRevivesPerLevel", 0, "Overrides RevivesPerLevelMultiplier when above 0.").Value;
			InfiniteReviveTime = config.Bind<bool>("Rules", "InfiniteReviveTime", false, "If true, players can be revived no matter how long they have been dead.").Value;
			TimeUntilCannotBeRevived = config.Bind<int>("Rules", "TimeUntilCannotBeRevived", 120, "How long someone can be dead and still be revived, in seconds.").Value;
			MaxReviveDistance = config.Bind<float>("Revive", "MaxReviveDistance", 3f, "Maximum distance from reviver camera to body.").Value;
			MaxReviveDistanceTomb = config.Bind<float>("Revive", "MaxReviveDistanceTomb", 4.5f, "Maximum distance from reviver camera to tomb revive source.").Value;
			EnableMaskedPlayerRevive = config.Bind<bool>("Masked", "EnableMaskedPlayerRevive", true, "If true, a player converted into a Masked by a mask item or by a Masked enemy can be revived after the converted Masked is killed.").Value;
			EnableDeathTombFallback = config.Bind<bool>("DeathMarker", "EnableDeathTombFallback", true, "If true, spawn a 3D tomb marker at the last death position when the latest revive source is missing.").Value;
			EnableDeathTombCollider = config.Bind<bool>("DeathMarker", "EnableDeathTombCollider", true, "If false, the death tomb will have no collision, including colliders already present on bundle prefabs.").Value;
			DeathTombAssetBundlePath = config.Bind<string>("DeathMarker", "DeathTombAssetBundlePath", "UltimateRevive/bundles/sm_tombstone_3_dark_rip.bundle", "Optional absolute or relative path to an AssetBundle containing your tomb prefab.\n Valid options for this mod:\n -UltimateRevive/bundles/sm_tombstone_3_dark_rip.bundle.\n -UltimateRevive/bundles/sm_tombstone_3_dark_text.bundle.\n -UltimateRevive/bundles/sm_tombstone_3_light_rip.bundle.\n -UltimateRevive/bundles/sm_tombstone_3_light_text.bundle.").Value;
			DeathTombPrefabName = config.Bind<string>("DeathMarker", "DeathTombPrefabName", "sm_tombstone_3_dark_rip", "Prefab name inside the AssetBundle to instantiate as tomb marker.\n Valid options for this mod:\n -sm_tombstone_3_dark_rip.\n -sm_tombstone_3_dark_text.\n -sm_tombstone_3_light_rip.\n -sm_tombstone_3_light_text.").Value;
			DeathTombGroundOffset = config.Bind<float>("DeathMarker", "DeathTombGroundOffset", 0.02f, "Vertical offset applied above detected ground.").Value;
			DeathTombSpawnHeight = config.Bind<float>("DeathMarker", "DeathTombSpawnHeight", 4f, "Height above death position used for downward ground detection and gravity settle.").Value;
			EnableDebugLogs = config.Bind<bool>("Debug", "EnableDebugLogs", false, "If true, emit local-only debug logs (not synchronized from host).").Value;
		}

		public static void ApplySyncedConfig(float reviveTime, bool canPickUpBodies, float deadBodyWeight, bool canReviveTeleportedBodies, int reviveHealth, int extraHealthLostPerRevive, bool limitedRevives, float revivesPerLevelMultiplier, int fixedRevivesPerLevel, bool infiniteReviveTime, int timeUntilCannotBeRevived, float maxReviveDistance, float maxReviveDistanceTomb, bool enableMaskedPlayerRevive, bool enableDeathTombFallback, bool enableDeathTombCollider, string deathTombAssetBundlePath, string deathTombPrefabName, float deathTombGroundOffset, float deathTombSpawnHeight)
		{
			ReviveTime = reviveTime;
			CanPickUpBodies = canPickUpBodies;
			DeadBodyWeight = deadBodyWeight;
			CanReviveTeleportedBodies = canReviveTeleportedBodies;
			ReviveHealth = reviveHealth;
			ExtraHealthLostPerRevive = extraHealthLostPerRevive;
			LimitedRevives = limitedRevives;
			RevivesPerLevelMultiplier = revivesPerLevelMultiplier;
			FixedRevivesPerLevel = fixedRevivesPerLevel;
			InfiniteReviveTime = infiniteReviveTime;
			TimeUntilCannotBeRevived = timeUntilCannotBeRevived;
			MaxReviveDistance = maxReviveDistance;
			MaxReviveDistanceTomb = maxReviveDistanceTomb;
			EnableMaskedPlayerRevive = enableMaskedPlayerRevive;
			EnableDeathTombFallback = enableDeathTombFallback;
			EnableDeathTombCollider = enableDeathTombCollider;
			DeathTombAssetBundlePath = deathTombAssetBundlePath;
			DeathTombPrefabName = deathTombPrefabName;
			DeathTombGroundOffset = deathTombGroundOffset;
			DeathTombSpawnHeight = deathTombSpawnHeight;
			DeathMarkerManager.InvalidateBundleCache();
		}
	}
	public static class ModState
	{
		public static bool Active;

		public static int RemainingRevives = int.MaxValue;

		public static readonly List<PlayerReviveInfo> PlayerInfos = new List<PlayerReviveInfo>();

		public static void Reset()
		{
			Active = false;
			RemainingRevives = int.MaxValue;
			PlayerInfos.Clear();
			MaskedConversionTracker.Reset();
			DeathMarkerManager.Reset();
			ReviveInteractTriggerManager.Reset();
		}
	}
	public sealed class PlayerReviveInfo
	{
		public int PlayerClientId;

		public bool HasBeenTeleported;

		public float TimeDiedAt;

		public int TimesRevivedThisLevel;

		public PlayerDeathType LastDeathType;

		public ulong LastDeathNetworkObjectId;

		public Vector3 LastDeathPosition;

		public bool HasLastDeathPosition;
	}
	public enum PlayerDeathType
	{
		Unknown,
		Body,
		Masked
	}
	[BepInPlugin("chaps.UltimateRevive", "UltimateRevive", "1.1.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		private sealed class RuntimeDriver : MonoBehaviour
		{
		}

		public const string ModGuid = "chaps.UltimateRevive";

		public const string ModName = "UltimateRevive";

		public const string ModVersion = "1.1.0";

		internal static ManualLogSource Log;

		internal static ReviveInputActions InputActions;

		private Harmony _harmony;

		private bool _isQuitting;

		private static RuntimeDriver _runtimeDriver;

		private float _nextMaintenanceAt;

		private string _lastReviveBindingPath;

		public static Plugin Instance { get; private set; }

		internal static InputAction ReviveAction => InputActions?.Revive;

		private void Awake()
		{
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
			InputActions = new ReviveInputActions();
			ModConfig.Bind(((BaseUnityPlugin)this).Config);
			GeneralUtil.SetLogSource(Log);
			ReviveNetwork.SetLogSource(Log);
			ReviveLogic.SetLogSource(Log);
			MaskedConversionTracker.SetLogSource(Log);
			DeathMarkerManager.SetLogSource(Log);
			_harmony = new Harmony("chaps.UltimateRevive");
			try
			{
				_harmony.PatchAll();
				VerifyHarmonyHooks();
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"Harmony patching failed: {arg}");
			}
			InteractTriggerPatch.TryPatch(_harmony);
			Log.LogInfo((object)"UltimateRevive 1.1.0 loaded.");
		}

		private void Update()
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (Time.unscaledTime < _nextMaintenanceAt)
				{
					return;
				}
				_nextMaintenanceAt = Time.unscaledTime + 1f;
				ReviveNetwork.TryRegisterHandlers();
				ReviveNetwork.SyncConfigForNewClients();
				if (InputActions?.Revive == null)
				{
					return;
				}
				object obj;
				if (InputActions.Revive.bindings.Count <= 0)
				{
					obj = null;
				}
				else
				{
					InputBinding val = InputActions.Revive.bindings[0];
					obj = ((InputBinding)(ref val)).effectivePath;
				}
				string text = (string)obj;
				if (_lastReviveBindingPath != text)
				{
					InputActions.RefreshReviveAction();
					_lastReviveBindingPath = text;
					ManualLogSource log = Log;
					if (log != null)
					{
						log.LogInfo((object)("[UltimateRevive] Revive binding changed, action refreshed: " + text));
					}
				}
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"Runtime Update failed: {arg}");
			}
		}

		private void OnDestroy()
		{
			if (_isQuitting)
			{
				ReviveNetwork.UnregisterHandlers();
				Harmony harmony = _harmony;
				if (harmony != null)
				{
					harmony.UnpatchSelf();
				}
			}
		}

		private void OnApplicationQuit()
		{
			_isQuitting = true;
		}

		internal static void DebugLog(string message)
		{
			if (ModConfig.EnableDebugLogs)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogInfo((object)message);
				}
			}
		}

		internal static string GetReviveBindingLabel()
		{
			InputAction reviveAction = ReviveAction;
			if (reviveAction == null)
			{
				return "key";
			}
			string bindingDisplayString = InputActionRebindingExtensions.GetBindingDisplayString(reviveAction, (DisplayStringOptions)0, (string)null);
			if (!string.IsNullOrWhiteSpace(bindingDisplayString))
			{
				return bindingDisplayString;
			}
			return "key";
		}

		private void VerifyHarmonyHooks()
		{
			VerifyPatched(typeof(PlayerControllerB), "Update");
			VerifyPatched(typeof(PlayerControllerB), "SetHoverTipAndCurrentInteractTrigger");
			VerifyPatched(typeof(PlayerControllerB), "Interact_performed");
			VerifyPatched(typeof(PlayerControllerB), "KillPlayerClientRpc");
			VerifyPatched(typeof(StartOfRound), "Start");
			VerifyPatched(typeof(StartOfRound), "openingDoorsSequence");
		}

		private void VerifyPatched(Type type, string methodName)
		{
			MethodInfo methodInfo = AccessTools.Method(type, methodName, (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				Log.LogWarning((object)("Harmony target missing: " + type.Name + "." + methodName));
				return;
			}
			Patches patchInfo = Harmony.GetPatchInfo((MethodBase)methodInfo);
			if (patchInfo != null && ((patchInfo.Prefixes != null && patchInfo.Prefixes.Any((Patch p) => p.owner == "chaps.UltimateRevive")) || (patchInfo.Postfixes != null && patchInfo.Postfixes.Any((Patch p) => p.owner == "chaps.UltimateRevive")) || (patchInfo.Transpilers != null && patchInfo.Transpilers.Any((Patch p) => p.owner == "chaps.UltimateRevive")) || (patchInfo.Finalizers != null && patchInfo.Finalizers.Any((Patch p) => p.owner == "chaps.UltimateRevive"))))
			{
				Log.LogInfo((object)("Harmony hook active: " + type.Name + "." + methodName));
			}
			else
			{
				Log.LogWarning((object)("Harmony hook NOT active: " + type.Name + "." + methodName));
			}
		}

		private static void EnsureRuntimeDriver()
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected O, but got Unknown
			if (!((Object)(object)_runtimeDriver != (Object)null) || !((Object)(object)((Component)_runtimeDriver).gameObject != (Object)null))
			{
				GameObject val = new GameObject("UltimateRevive.RuntimeDriver");
				Object.DontDestroyOnLoad((Object)val);
				_runtimeDriver = val.AddComponent<RuntimeDriver>();
			}
		}

		internal static void RunCoroutineSafe(IEnumerator routine)
		{
			if (routine == null)
			{
				return;
			}
			try
			{
				if ((Object)(object)Instance != (Object)null && (Object)(object)((Component)Instance).gameObject != (Object)null && ((Behaviour)Instance).isActiveAndEnabled)
				{
					((MonoBehaviour)Instance).StartCoroutine(routine);
					return;
				}
				if ((Object)(object)StartOfRound.Instance != (Object)null && (Object)(object)((Component)StartOfRound.Instance).gameObject != (Object)null && ((Behaviour)StartOfRound.Instance).isActiveAndEnabled)
				{
					((MonoBehaviour)StartOfRound.Instance).StartCoroutine(routine);
					return;
				}
				EnsureRuntimeDriver();
				if ((Object)(object)_runtimeDriver != (Object)null && (Object)(object)((Component)_runtimeDriver).gameObject != (Object)null && ((Behaviour)_runtimeDriver).isActiveAndEnabled)
				{
					((MonoBehaviour)_runtimeDriver).StartCoroutine(routine);
					return;
				}
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogWarning((object)"[UltimateRevive] No active MonoBehaviour available for coroutine.");
				}
			}
			catch (Exception arg)
			{
				ManualLogSource log2 = Log;
				if (log2 != null)
				{
					log2.LogError((object)$"[UltimateRevive] Failed to start coroutine: {arg}");
				}
			}
		}
	}
	public static class ReviveAPI
	{
		public static bool CanRevive(RagdollGrabbableObject body)
		{
			if ((Object)(object)body == (Object)null)
			{
				return false;
			}
			if (!GeneralUtil.TryGetBodyOwner(body, out var targetPlayerClientId, out var _))
			{
				return false;
			}
			return CanRevivePlayer(targetPlayerClientId);
		}

		public static bool CanRevivePlayer(int targetPlayerClientId)
		{
			PlayerControllerB playerByClientId = GeneralUtil.GetPlayerByClientId(targetPlayerClientId);
			if (PassesGlobalRules(targetPlayerClientId, playerByClientId))
			{
				return HasRevivableSource(targetPlayerClientId);
			}
			return false;
		}

		public static bool CanReviveMasked(MaskedPlayerEnemy masked)
		{
			if ((Object)(object)masked == (Object)null || !ModConfig.EnableMaskedPlayerRevive)
			{
				return false;
			}
			if (!GeneralUtil.TryResolveConvertedMaskedPlayer(masked, out var playerClientId, out var _))
			{
				return false;
			}
			return CanRevivePlayer(playerClientId);
		}

		public static void RevivePlayer(int targetPlayerClientId)
		{
			ReviveNetwork.RequestRevive(targetPlayerClientId);
		}

		private static bool PassesGlobalRules(int targetPlayerClientId, PlayerControllerB targetPlayer)
		{
			if (!ModState.Active)
			{
				return false;
			}
			if ((Object)(object)targetPlayer == (Object)null || !targetPlayer.isPlayerDead)
			{
				return false;
			}
			if (ModConfig.LimitedRevives && ModState.RemainingRevives <= 0)
			{
				return false;
			}
			if (!ModConfig.CanReviveTeleportedBodies && GeneralUtil.HasPlayerTeleported(targetPlayerClientId))
			{
				return false;
			}
			if (!ModConfig.InfiniteReviveTime && Time.time - GeneralUtil.GetPlayerDiedAtTime(targetPlayerClientId) > (float)ModConfig.TimeUntilCannotBeRevived)
			{
				return false;
			}
			return true;
		}

		private static bool HasRevivableSource(int targetPlayerClientId)
		{
			PlayerDeathType playerLastDeathType = GeneralUtil.GetPlayerLastDeathType(targetPlayerClientId);
			ulong playerLastDeathNetworkObjectId = GeneralUtil.GetPlayerLastDeathNetworkObjectId(targetPlayerClientId);
			Vector3 position;
			if (playerLastDeathType == PlayerDeathType.Masked)
			{
				if (!ModConfig.EnableMaskedPlayerRevive)
				{
					return false;
				}
				MaskedPlayerEnemy val = ((playerLastDeathNetworkObjectId != 0L) ? GeneralUtil.GetMaskedByNetworkObjectId(playerLastDeathNetworkObjectId) : null);
				if ((Object)(object)val == (Object)null)
				{
					val = GeneralUtil.GetConvertedMaskedCorpseForPlayer(targetPlayerClientId);
				}
				if ((Object)(object)val != (Object)null)
				{
					return true;
				}
				if (ModConfig.EnableDeathTombFallback)
				{
					return GeneralUtil.TryGetPlayerLastDeathPosition(targetPlayerClientId, out position);
				}
				return false;
			}
			if ((Object)(object)((playerLastDeathNetworkObjectId != 0L) ? GeneralUtil.GetBodyByNetworkObjectId(playerLastDeathNetworkObjectId) : GeneralUtil.GetBodyForPlayer(targetPlayerClientId)) != (Object)null)
			{
				return true;
			}
			if (ModConfig.EnableDeathTombFallback)
			{
				return GeneralUtil.TryGetPlayerLastDeathPosition(targetPlayerClientId, out position);
			}
			return false;
		}
	}
	public sealed class ReviveInputActions : LcInputActions
	{
		[InputAction(/*Could not decode attribute arguments.*/)]
		public InputAction Revive { get; set; }

		public void RefreshReviveAction()
		{
			InputAction revive = Revive;
			if (revive != null)
			{
				revive.Disable();
			}
			InputAction revive2 = Revive;
			if (revive2 != null)
			{
				revive2.Enable();
			}
		}
	}
	public static class ReviveInteractTriggerManager
	{
		private enum ReviveSourceKind
		{
			None,
			Body,
			Masked,
			Tomb
		}

		private sealed class ReviveTriggerEntry
		{
			public int TargetPlayerId;

			public ReviveSourceKind SourceKind;

			public ulong SourceObjectId;

			public GameObject RootObject;

			public GameObject TriggerHostObject;

			public InteractTrigger Trigger;

			public Collider AddedCollider;

			public bool OwnsTrigger;

			public bool OwnsTriggerHostObject;

			public bool ChangedLayer;

			public int OriginalLayer;

			public GameObject LayerAdjustedObject;

			public Transform FollowTransform;

			public Vector3 StaticPosition;
		}

		private const string CustomBodyTriggerHostName = "UltimateReviveBodyTrigger";

		private const string CustomTombTriggerHostName = "UltimateReviveTombTrigger";

		private const float HoverGraceSeconds = 0.45f;

		private static readonly Vector3 MaskedTriggerAliveLocalOffset = new Vector3(0f, 0.9f, 0f);

		private static readonly Vector3 MaskedTriggerDeadLocalOffset = new Vector3(0f, 0.35f, 0f);

		private const float MaskedTriggerDeadForwardShift = 0.95f;

		private const float MaskedTriggerDeadVerticalLift = 0.35f;

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

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

		private static readonly string[] PreferredPlayerTriggerFieldNames = new string[4] { "hoveringOverTrigger", "currentInteractTrigger", "currentTriggerInRange", "hoveredOverTrigger" };

		private static readonly BindingFlags InstanceFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

		private static FieldInfo[] _cachedPlayerTriggerFields;

		private static PropertyInfo[] _cachedPlayerTriggerProperties;

		private static int _lastLoggedHoverTarget = -2;

		private static ReviveSourceKind _lastLoggedHoverKind = ReviveSourceKind.None;

		private static string _lastLoggedHoverPath = string.Empty;

		private static bool _lastLoggedHoverCanRevive;

		private static bool _hadHover;

		private static int _lastHoveredTargetId = -1;

		private static float _lastHoveredSeenAt = -999f;

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

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

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

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

		private static string _lastTickGateDiagnostic = string.Empty;

		public static void WarmUpCache()
		{
			CachePlayerTriggerMembers();
			FieldInfo[] cachedPlayerTriggerFields = _cachedPlayerTriggerFields;
			Plugin.DebugLog($"[ReviveTrigger] cache warmed: fields={((cachedPlayerTriggerFields != null) ? cachedPlayerTriggerFields.Length : 0)}");
		}

		public static void Tick()
		{
			//IL_0229: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f2: Unknown result type (might be due to invalid IL or missing references)
			StartOfRound instance = StartOfRound.Instance;
			_ = instance?.mapScreen;
			PlayerControllerB[] array = instance?.allPlayerScripts;
			PlayerControllerB[] array2;
			if (array != null)
			{
				array2 = array;
				foreach (PlayerControllerB val in array2)
				{
					if (!((Object)(object)val == (Object)null))
					{
						int playerClientId = (int)val.playerClientId;
						if (DeathMarkerManager.HasActiveMarker(playerClientId) && !val.isPlayerDead)
						{
							DeathMarkerManager.ClearMarker(playerClientId);
						}
					}
				}
			}
			if (!ModState.Active || StartOfRound.Instance?.allPlayerScripts == null)
			{
				LogTickGateDiagnostic($"gate=blocked active={ModState.Active} hasStartOfRound={(Object)(object)StartOfRound.Instance != (Object)null} hasPlayers={StartOfRound.Instance?.allPlayerScripts != null} trackedInfos={ModState.PlayerInfos.Count}");
				Reset();
				return;
			}
			LogTickGateDiagnostic($"gate=running players={StartOfRound.Instance.allPlayerScripts.Length} trackedInfos={ModState.PlayerInfos.Count}");
			PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts;
			HashSet<int> seenPlayerIds = new HashSet<int>();
			array2 = allPlayerScripts;
			foreach (PlayerControllerB val2 in array2)
			{
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				int num = (int)val2.playerClientId;
				PlayerDeathType playerLastDeathType = GeneralUtil.GetPlayerLastDeathType(num);
				bool flag = !val2.isPlayerDead && playerLastDeathType != 0 && !val2.isPlayerControlled;
				if (flag)
				{
					LogTickDiagnostic(num, $"overrideAliveSlot trackedDeath={playerLastDeathType} isControlled={val2.isPlayerControlled}");
				}
				if (!val2.isPlayerDead && !flag)
				{
					seenPlayerIds.Add(num);
					RemoveEntry(num);
					LogTickDiagnostic(num, $"skipAlive trackedDeath={playerLastDeathType} isControlled={val2.isPlayerControlled}");
					continue;
				}
				seenPlayerIds.Add(num);
				if (!TryResolveCurrentReviveSource(num, out var sourceKind, out var sourceObjectId, out var followTransform, out var staticPosition))
				{
					RemoveEntry(num);
					LogTickDiagnostic(num, "resolveFailed from=allPlayerScripts");
				}
				else
				{
					EnsureOrUpdateEntry(num, sourceKind, sourceObjectId, followTransform, staticPosition);
					LogTickDiagnostic(num, $"resolveSuccess from=allPlayerScripts source={sourceKind} obj={sourceObjectId}");
				}
			}
			foreach (PlayerReviveInfo playerInfo in ModState.PlayerInfos)
			{
				if (playerInfo == null)
				{
					continue;
				}
				int playerClientId2 = playerInfo.PlayerClientId;
				if (seenPlayerIds.Contains(playerClientId2))
				{
					continue;
				}
				if (playerInfo.LastDeathType == PlayerDeathType.Unknown)
				{
					RemoveEntry(playerClientId2);
					LogTickDiagnostic(playerClientId2, "skipTrackedUnknown");
					continue;
				}
				seenPlayerIds.Add(playerClientId2);
				if (!TryResolveCurrentReviveSource(playerClientId2, out var sourceKind2, out var sourceObjectId2, out var followTransform2, out var staticPosition2))
				{
					RemoveEntry(playerClientId2);
					LogTickDiagnostic(playerClientId2, "resolveFailed from=trackedInfos");
				}
				else
				{
					EnsureOrUpdateEntry(playerClientId2, sourceKind2, sourceObjectId2, followTransform2, staticPosition2);
					LogTickDiagnostic(playerClientId2, $"resolveSuccess from=trackedInfos source={sourceKind2} obj={sourceObjectId2}");
				}
			}
			foreach (int item in EntriesByPlayer.Keys.Where((int id) => !seenPlayerIds.Contains(id)).ToList())
			{
				RemoveEntry(item);
			}
		}

		public static void Reset()
		{
			foreach (int item in EntriesByPlayer.Keys.ToList())
			{
				RemoveEntry(item);
			}
			EntriesByPlayer.Clear();
			TriggerInstanceToPlayer.Clear();
			_lastResolveDiagnosticsByPlayer.Clear();
			_lastTickDiagnosticsByPlayer.Clear();
			_lastForceDiagnosticsByPlayer.Clear();
			_lastObservedTriggerDiagnosticsByPlayer.Clear();
			_lastTickGateDiagnostic = string.Empty;
			_lastHoveredTargetId = -1;
			_lastHoveredSeenAt = -999f;
		}

		public static void ForceRefreshPlayer(int playerId, string reason = null)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			if (ModState.Active)
			{
				if (!TryResolveCurrentReviveSource(playerId, out var sourceKind, out var sourceObjectId, out var followTransform, out var staticPosition))
				{
					RemoveEntry(playerId);
					LogForceDiagnostic(playerId, "result=none reason=" + (string.IsNullOrWhiteSpace(reason) ? "unspecified" : reason));
				}
				else
				{
					EnsureOrUpdateEntry(playerId, sourceKind, sourceObjectId, followTransform, staticPosition);
					LogForceDiagnostic(playerId, string.Format("result={0} obj={1} reason={2}", sourceKind, sourceObjectId, string.IsNullOrWhiteSpace(reason) ? "unspecified" : reason));
				}
			}
		}

		public static bool TryGetHoveredReviveTarget(PlayerControllerB reviver, out int targetPlayerId, out string reason, out bool canRevive)
		{
			targetPlayerId = -1;
			reason = "no revive trigger";
			canRevive = false;
			if (!TryGetHoveredEntry(reviver, out var entry))
			{
				if (_hadHover)
				{
					_hadHover = false;
					_lastLoggedHoverTarget = -2;
					_lastLoggedHoverKind = ReviveSourceKind.None;
					_lastLoggedHoverPath = string.Empty;
					_lastLoggedHoverCanRevive = false;
					Plugin.DebugLog("[ReviveHover] cleared");
				}
				return false;
			}
			targetPlayerId = entry.TargetPlayerId;
			canRevive = EvaluateCanRevive(entry, reviver, out var resolvedTargetId, out reason);
			if (resolvedTargetId >= 0)
			{
				targetPlayerId = resolvedTargetId;
			}
			string text = ((entry.SourceKind == ReviveSourceKind.Body) ? "vanilla-body" : "custom");
			if (entry.SourceKind != ReviveSourceKind.Body && ((Object)(object)entry.Trigger == (Object)null || !TriggerInstanceToPlayer.ContainsKey(((Object)entry.Trigger).GetInstanceID())))
			{
				text = "unmapped";
			}
			if (!_hadHover || _lastLoggedHoverTarget != targetPlayerId || _lastLoggedHoverKind != entry.SourceKind || _lastLoggedHoverPath != text || _lastLoggedHoverCanRevive != canRevive)
			{
				_hadHover = true;
				_lastLoggedHoverTarget = targetPlayerId;
				_lastLoggedHoverKind = entry.SourceKind;
				_lastLoggedHoverPath = text;
				_lastLoggedHoverCanRevive = canRevive;
				Plugin.DebugLog($"[ReviveHover] player={targetPlayerId} source={entry.SourceKind} path={text} canRevive={canRevive} reason={reason}");
			}
			return true;
		}

		public static bool IsHoveringTriggerForTarget(PlayerControllerB reviver, int targetPlayerId)
		{
			if (targetPlayerId < 0)
			{
				return false;
			}
			if (!TryGetHoveredEntry(reviver, out var entry))
			{
				return false;
			}
			if (entry != null)
			{
				return entry.TargetPlayerId == targetPlayerId;
			}
			return false;
		}

		public static bool IsHoveringReviveTrigger(PlayerControllerB reviver)
		{
			ReviveTriggerEntry entry;
			return TryGetHoveredEntry(reviver, out entry);
		}

		private static void EnsureOrUpdateEntry(int playerId, ReviveSourceKind sourceKind, ulong sourceObjectId, Transform followTransform, Vector3 staticPosition)
		{
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			if (!TryResolveSourceHostObject(playerId, sourceKind, sourceObjectId, followTransform, out var hostObject) || (Object)(object)hostObject == (Object)null)
			{
				RemoveEntry(playerId);
				return;
			}
			if (!EntriesByPlayer.TryGetValue(playerId, out var value) || value == null || (Object)(object)value.Trigger == (Object)null || (Object)(object)value.RootObject == (Object)null || (Object)(object)value.RootObject != (Object)(object)hostObject || value.SourceKind != sourceKind || value.SourceObjectId != sourceObjectId)
			{
				RemoveEntry(playerId);
				value = CreateEntry(playerId, sourceKind, sourceObjectId, hostObject);
				if (value == null)
				{
					return;
				}
				EntriesByPlayer[playerId] = value;
			}
			value.FollowTransform = followTransform;
			value.StaticPosition = staticPosition;
			if (sourceKind != ReviveSourceKind.Body)
			{
				ConfigureTrigger(value.Trigger, playerId, sourceKind);
			}
			if (sourceKind == ReviveSourceKind.Masked)
			{
				UpdateMaskedTriggerAnchor(value);
			}
		}

		private static ReviveTriggerEntry CreateEntry(int playerId, ReviveSourceKind sourceKind, ulong sourceObjectId, GameObject hostObject)
		{
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)hostObject == (Object)null)
			{
				return null;
			}
			switch (sourceKind)
			{
			case ReviveSourceKind.Body:
				return CreateBodyEntryUsingVanillaTrigger(playerId, sourceObjectId, hostObject);
			case ReviveSourceKind.Masked:
				return CreateMaskedEntryUsingChildTrigger(playerId, sourceObjectId, hostObject);
			case ReviveSourceKind.Tomb:
				return CreateTombEntryUsingChildTrigger(playerId, sourceObjectId, hostObject);
			default:
			{
				bool flag = false;
				InteractTrigger obj = hostObject.GetComponent<InteractTrigger>();
				bool flag2 = (Object)(object)obj == (Object)null;
				if (obj == null)
				{
					obj = hostObject.AddComponent<InteractTrigger>();
				}
				InteractTrigger val = obj;
				Collider val2 = null;
				if (!HasUsableInteractCollider(hostObject))
				{
					SphereCollider obj2 = hostObject.AddComponent<SphereCollider>();
					((Collider)obj2).isTrigger = true;
					obj2.radius = 0.8f;
					obj2.center = new Vector3(0f, 0.9f, 0f);
					val2 = (Collider)(object)obj2;
				}
				int num = LayerMask.NameToLayer("InteractableObject");
				bool changedLayer = false;
				int layer = hostObject.layer;
				if (num >= 0 && hostObject.layer != num)
				{
					hostObject.layer = num;
					changedLayer = true;
				}
				ConfigureTrigger(val, playerId, sourceKind);
				RegisterTriggerMapping(val, playerId, sourceKind, hostObject);
				Collider component = hostObject.GetComponent<Collider>();
				bool flag3 = HasUsableInteractCollider(hostObject);
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)$"[ReviveTrigger] attached source={sourceKind} player={playerId} host={((Object)hostObject).name} triggerHost={((Object)hostObject).name} ownsTrigger={flag2} ownsHost={flag} addedCollider={(Object)(object)val2 != (Object)null} hasCollider={(Object)(object)component != (Object)null} usableCollider={flag3} triggerId={((Object)val).GetInstanceID()} layer={hostObject.layer}");
				}
				return new ReviveTriggerEntry
				{
					TargetPlayerId = playerId,
					SourceKind = sourceKind,
					SourceObjectId = sourceObjectId,
					RootObject = hostObject,
					TriggerHostObject = hostObject,
					Trigger = val,
					AddedCollider = val2,
					OwnsTrigger = flag2,
					OwnsTriggerHostObject = flag,
					ChangedLayer = changedLayer,
					OriginalLayer = layer,
					LayerAdjustedObject = hostObject,
					FollowTransform = null,
					StaticPosition = Vector3.zero
				};
			}
			}
		}

		private static ReviveTriggerEntry CreateMaskedEntryUsingChildTrigger(int playerId, ulong sourceObjectId, GameObject hostObject)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)hostObject == (Object)null)
			{
				return null;
			}
			Transform val = hostObject.transform.Find("UltimateReviveMaskedTrigger");
			bool flag = (Object)(object)val == (Object)null;
			GameObject val2 = (GameObject)(((Object)(object)val != (Object)null) ? ((object)((Component)val).gameObject) : ((object)new GameObject("UltimateReviveMaskedTrigger")));
			if (flag)
			{
				val2.transform.SetParent(hostObject.transform, false);
				val2.transform.localPosition = MaskedTriggerAliveLocalOffset;
			}
			SphereCollider obj = val2.GetComponent<SphereCollider>() ?? val2.AddComponent<SphereCollider>();
			((Collider)obj).isTrigger = true;
			obj.radius = 0.6f;
			obj.center = Vector3.zero;
			InteractTrigger obj2 = val2.GetComponent<InteractTrigger>();
			bool ownsTrigger = (Object)(object)obj2 == (Object)null;
			if (obj2 == null)
			{
				obj2 = val2.AddComponent<InteractTrigger>();
			}
			InteractTrigger val3 = obj2;
			int num = LayerMask.NameToLayer("InteractableObject");
			bool changedLayer = false;
			int layer = val2.layer;
			if (num >= 0 && val2.layer != num)
			{
				val2.layer = num;
				changedLayer = true;
			}
			ConfigureTrigger(val3, playerId, ReviveSourceKind.Masked);
			RegisterTriggerMapping(val3, playerId, ReviveSourceKind.Masked, val2);
			bool flag2 = false;
			MaskedPlayerEnemy val4 = hostObject.GetComponent<MaskedPlayerEnemy>() ?? hostObject.GetComponentInChildren<MaskedPlayerEnemy>(true);
			if ((Object)(object)val4 != (Object)null)
			{
				flag2 = MaskedConversionTracker.IsMaskedDead(val4);
			}
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				log.LogInfo((object)$"[ReviveTrigger] maskedChildAttached player={playerId} host={((Object)hostObject).name} child={((Object)val2).name} ownsTriggerHost={flag} triggerId={((Object)val3).GetInstanceID()} layer={val2.layer} maskedDead={flag2}");
			}
			return new ReviveTriggerEntry
			{
				TargetPlayerId = playerId,
				SourceKind = ReviveSourceKind.Masked,
				SourceObjectId = sourceObjectId,
				RootObject = hostObject,
				TriggerHostObject = val2,
				Trigger = val3,
				AddedCollider = null,
				OwnsTrigger = ownsTrigger,
				OwnsTriggerHostObject = flag,
				ChangedLayer = changedLayer,
				OriginalLayer = layer,
				LayerAdjustedObject = val2,
				FollowTransform = null,
				StaticPosition = Vector3.zero
			};
		}

		private static ReviveTriggerEntry CreateTombEntryUsingChildTrigger(int playerId, ulong sourceObjectId, GameObject hostObject)
		{
			//IL_0032: 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_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)hostObject == (Object)null)
			{
				return null;
			}
			Transform val = hostObject.transform.Find("UltimateReviveTombTrigger");
			bool flag = (Object)(object)val == (Object)null;
			GameObject val2 = (GameObject)(((Object)(object)val != (Object)null) ? ((object)((Component)val).gameObject) : ((object)new GameObject("UltimateReviveTombTrigger")));
			if (flag)
			{
				val2.transform.SetParent(hostObject.transform, false);
				val2.transform.localPosition = new Vector3(0f, 0.8f, 0f);
			}
			SphereCollider obj = val2.GetComponent<SphereCollider>() ?? val2.AddComponent<SphereCollider>();
			((Collider)obj).isTrigger = true;
			obj.radius = 0.8f;
			obj.center = Vector3.zero;
			InteractTrigger obj2 = val2.GetComponent<InteractTrigger>();
			bool ownsTrigger = (Object)(object)obj2 == (Object)null;
			if (obj2 == null)
			{
				obj2 = val2.AddComponent<InteractTrigger>();
			}
			InteractTrigger val3 = obj2;
			int num = LayerMask.NameToLayer("InteractableObject");
			bool changedLayer = false;
			int layer = val2.layer;
			if (num >= 0 && val2.layer != num)
			{
				val2.layer = num;
				changedLayer = true;
			}
			ConfigureTrigger(val3, playerId, ReviveSourceKind.Tomb);
			RegisterTriggerMapping(val3, playerId, ReviveSourceKind.Tomb, val2);
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				log.LogInfo((object)$"[ReviveTrigger] tombChildAttached player={playerId} host={((Object)hostObject).name} child={((Object)val2).name} ownsTriggerHost={flag} triggerId={((Object)val3).GetInstanceID()} layer={val2.layer}");
			}
			return new ReviveTriggerEntry
			{
				TargetPlayerId = playerId,
				SourceKind = ReviveSourceKind.Tomb,
				SourceObjectId = sourceObjectId,
				RootObject = hostObject,
				TriggerHostObject = val2,
				Trigger = val3,
				AddedCollider = null,
				OwnsTrigger = ownsTrigger,
				OwnsTriggerHostObject = flag,
				ChangedLayer = changedLayer,
				OriginalLayer = layer,
				LayerAdjustedObject = val2,
				FollowTransform = null,
				StaticPosition = Vector3.zero
			};
		}

		private static void UpdateMaskedTriggerAnchor(ReviveTriggerEntry entry)
		{
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: 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_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			if (entry == null || entry.SourceKind != ReviveSourceKind.Masked || (Object)(object)entry.TriggerHostObject == (Object)null)
			{
				return;
			}
			MaskedPlayerEnemy val = (((Object)(object)entry.RootObject != (Object)null) ? entry.RootObject.GetComponent<MaskedPlayerEnemy>() : null);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			Transform transform = entry.TriggerHostObject.transform;
			if ((Object)(object)transform == (Object)null)
			{
				return;
			}
			bool flag = MaskedConversionTracker.IsMaskedDead(val);
			ConfigureMaskedTriggerText(entry, flag);
			if (!flag)
			{
				transform.localPosition = MaskedTriggerAliveLocalOffset;
				return;
			}
			Vector3 val2 = Vector3.ProjectOnPlane(((Component)val).transform.forward, Vector3.up);
			if (((Vector3)(ref val2)).sqrMagnitude > 0.0001f)
			{
				((Vector3)(ref val2)).Normalize();
			}
			else
			{
				val2 = ((Component)val).transform.forward;
			}
			if (TryGetMaskedDeadAnchorWorld(val, out var anchor))
			{
				transform.position = anchor;
			}
			else
			{
				transform.position = ((Component)val).transform.position + Vector3.up * MaskedTriggerDeadLocalOffset.y + val2 * 0.95f;
			}
		}

		public static void OnMaskedKilled(MaskedPlayerEnemy masked)
		{
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)masked == (Object)null))
			{
				if (!GeneralUtil.TryResolveConvertedMaskedPlayer(masked, out var playerClientId, out var source))
				{
					MaskedConversionTracker.TagMaskedOnSpawn(masked);
					GeneralUtil.TryResolveConvertedMaskedPlayer(masked, out playerCl