Decompiled source of ExtractionAutoRevive v1.3.1

ExtractionAutoRevive.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using UnityEngine;
using UnityEngine.SceneManagement;

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

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

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

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ExtractionAutoRevive
{
	[BepInPlugin("com.repojp.extractionautorevive", "ExtractionAutoRevive", "1.2.0")]
	public sealed class ExtractionAutoRevivePlugin : BaseUnityPlugin
	{
		private sealed class PendingReviveEntry
		{
			public PlayerDeathHead DeathHead;

			public PlayerAvatar PlayerAvatar;

			public int Key;

			public float QueuedAt;

			public float ElectrifiedEndedAt = -1f;

			public float VelocityStableStartedAt = -1f;

			public string PlayerLabel;
		}

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

			private object <>2__current;

			public ExtractionAutoRevivePlugin <>4__this;

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

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

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

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

			private bool MoveNext()
			{
				//IL_0106: Unknown result type (might be due to invalid IL or missing references)
				//IL_0110: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					break;
				case 1:
					<>1__state = -1;
					break;
				}
				while (<>4__this.pendingRevives.Count > 0)
				{
					if (!<>4__this.IsModEnabled())
					{
						<>4__this.ClearPendingRevives("mod disabled");
						break;
					}
					if (!<>4__this.CanRunHostLogic())
					{
						<>4__this.ClearPendingRevives("host lost");
						break;
					}
					if (<>4__this.ShouldClearPendingByRoundState())
					{
						<>4__this.ClearPendingRevives("round transition");
						break;
					}
					<>4__this.ProcessPendingRevives();
					if (<>4__this.pendingRevives.Count > 0)
					{
						<>2__current = (object)new WaitForSecondsRealtime(Mathf.Clamp(<>4__this.configPendingCheckIntervalSeconds.Value, 0.02f, 1f));
						<>1__state = 1;
						return true;
					}
				}
				<>4__this.pendingMonitorCoroutine = null;
				<>4__this.WriteVerbose("Pending monitor stopped");
				return false;
			}

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

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

		internal static ExtractionAutoRevivePlugin Instance;

		internal static ManualLogSource Log;

		private Harmony harmony;

		private static int customReviveContextDepth;

		private Coroutine pendingMonitorCoroutine;

		private readonly Dictionary<int, PendingReviveEntry> pendingRevives = new Dictionary<int, PendingReviveEntry>();

		private ConfigEntry<bool> configEnableMod;

		private ConfigEntry<bool> configEnableLog;

		private ConfigEntry<bool> configEnableVerboseLog;

		private ConfigEntry<float> configPitHeadExtraDelaySeconds;

		private ConfigEntry<float> configPitHeadMaxVelocityToRevive;

		private ConfigEntry<float> configPendingCheckIntervalSeconds;

		private static readonly FieldInfo FieldPlayerAvatarDeadSet = AccessTools.Field(typeof(PlayerAvatar), "deadSet");

		private static readonly FieldInfo FieldPlayerAvatarIsDisabled = AccessTools.Field(typeof(PlayerAvatar), "isDisabled");

		private static readonly FieldInfo FieldPlayerAvatarFinalHeal = AccessTools.Field(typeof(PlayerAvatar), "finalHeal");

		private static readonly FieldInfo FieldPlayerDeathHeadTriggered = AccessTools.Field(typeof(PlayerDeathHead), "triggered");

		private static readonly FieldInfo FieldPlayerDeathHeadInExtractionPoint = AccessTools.Field(typeof(PlayerDeathHead), "inExtractionPoint");

		private static readonly FieldInfo FieldPlayerDeathHeadInTruck = AccessTools.Field(typeof(PlayerDeathHead), "inTruck");

		private static readonly FieldInfo FieldPlayerDeathHeadPhysGrabObject = AccessTools.Field(typeof(PlayerDeathHead), "physGrabObject");

		private static readonly FieldInfo FieldPhysGrabObjectDeathPitEffect = AccessTools.Field(typeof(PhysGrabObject), "deathPitEffect");

		private static readonly FieldInfo FieldPhysGrabObjectDeathPitEffectDisableTimer = AccessTools.Field(typeof(PhysGrabObject), "deathPitEffectDisableTimer");

		private static readonly FieldInfo FieldDeathPitSaveEffectTimeCurrent = AccessTools.Field(typeof(DeathPitSaveEffect), "timeCurrent");

		private void Awake()
		{
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Expected O, but got Unknown
			try
			{
				((Component)this).transform.parent = null;
				Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)$"[ExtractionAutoRevive] Persist setup failed ex={arg}");
			}
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			SetupConfig();
			SceneManager.activeSceneChanged += OnActiveSceneChanged;
			harmony = new Harmony("com.repojp.extractionautorevive");
			harmony.PatchAll();
			WriteLog("Loaded");
		}

		private void OnDestroy()
		{
			ClearPendingRevives("plugin destroy");
			SceneManager.activeSceneChanged -= OnActiveSceneChanged;
			try
			{
				if (harmony != null)
				{
					harmony.UnpatchSelf();
				}
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)$"[ExtractionAutoRevive] Unpatch failed ex={arg}");
			}
			if (Instance == this)
			{
				Instance = null;
			}
		}

		private void SetupConfig()
		{
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Expected O, but got Unknown
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Expected O, but got Unknown
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Expected O, but got Unknown
			configEnableMod = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableMod", true, "MOD overall enable switch");
			configEnableLog = ((BaseUnityPlugin)this).Config.Bind<bool>("Log", "EnableLog", true, "Enable standard logs");
			configEnableVerboseLog = ((BaseUnityPlugin)this).Config.Bind<bool>("Log", "EnableVerboseLog", false, "Enable verbose logs");
			configPitHeadExtraDelaySeconds = ((BaseUnityPlugin)this).Config.Bind<float>("PitHead", "PitHeadExtraDelaySeconds", 0.35f, new ConfigDescription("Additional delay after death pit electricity ends", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>()));
			configPitHeadMaxVelocityToRevive = ((BaseUnityPlugin)this).Config.Bind<float>("PitHead", "PitHeadMaxVelocityToRevive", 1f, new ConfigDescription("Maximum rigidbody velocity magnitude allowed before revive", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 20f), Array.Empty<object>()));
			configPendingCheckIntervalSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("PitHead", "PendingCheckIntervalSeconds", 0.05f, new ConfigDescription("Pending pit head monitor interval", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.02f, 1f), Array.Empty<object>()));
		}

		private void OnActiveSceneChanged(Scene previousScene, Scene nextScene)
		{
			ClearPendingRevives("scene changed " + ((Scene)(ref previousScene)).name + " -> " + ((Scene)(ref nextScene)).name);
		}

		private void WriteLog(string message)
		{
			if (configEnableLog != null && configEnableLog.Value)
			{
				Log.LogInfo((object)("[ExtractionAutoRevive] " + message));
			}
		}

		private void WriteVerbose(string message)
		{
			if (configEnableVerboseLog != null && configEnableVerboseLog.Value)
			{
				Log.LogInfo((object)("[ExtractionAutoRevive] " + message));
			}
		}

		private void WriteWarning(string message)
		{
			Log.LogWarning((object)("[ExtractionAutoRevive] " + message));
		}

		internal static void EnterCustomReviveContext()
		{
			customReviveContextDepth++;
		}

		internal static void ExitCustomReviveContext()
		{
			if (customReviveContextDepth > 0)
			{
				customReviveContextDepth--;
			}
		}

		internal static bool IsCustomReviveContextActive()
		{
			return customReviveContextDepth > 0;
		}

		internal bool IsModEnabled()
		{
			return configEnableMod != null && configEnableMod.Value;
		}

		internal void HandleDeathHeadRevive(PlayerDeathHead deathHead)
		{
			if (IsModEnabled() && !((Object)(object)deathHead == (Object)null))
			{
				PlayerAvatar playerAvatar = deathHead.playerAvatar;
				DeathPitSaveEffect effect;
				if ((Object)(object)playerAvatar == (Object)null)
				{
					WriteWarning("Skip custom revive reason=playerAvatar null");
				}
				else if (!IsPlayerDead(playerAvatar, deathHead))
				{
					WriteVerbose("Skip custom revive player=" + GetPlayerLabel(playerAvatar) + " reason=alreadyAlive");
				}
				else if (!GetBoolField(FieldPlayerDeathHeadTriggered, deathHead, defaultValue: false))
				{
					WriteVerbose("Skip custom revive player=" + GetPlayerLabel(playerAvatar) + " reason=headNotTriggered");
				}
				else if (IsPitHeadElectrified(deathHead, out effect))
				{
					EnqueuePendingRevive(deathHead, playerAvatar);
				}
				else
				{
					ReviveNow(playerAvatar, "immediate");
				}
			}
		}

		private void EnqueuePendingRevive(PlayerDeathHead deathHead, PlayerAvatar playerAvatar)
		{
			if (!((Object)(object)deathHead == (Object)null) && !((Object)(object)playerAvatar == (Object)null))
			{
				int num = GetPendingKey(playerAvatar, deathHead);
				if (num == 0)
				{
					num = ((Object)playerAvatar).GetInstanceID();
				}
				if (pendingRevives.TryGetValue(num, out PendingReviveEntry value))
				{
					value.DeathHead = deathHead;
					value.PlayerAvatar = playerAvatar;
					value.PlayerLabel = GetPlayerLabel(playerAvatar);
					WriteVerbose($"Refresh pending revive player={value.PlayerLabel} key={num}");
					StartPendingMonitorIfNeeded();
				}
				else
				{
					PendingReviveEntry pendingReviveEntry = new PendingReviveEntry
					{
						DeathHead = deathHead,
						PlayerAvatar = playerAvatar,
						Key = num,
						QueuedAt = Time.unscaledTime,
						PlayerLabel = GetPlayerLabel(playerAvatar)
					};
					pendingRevives[num] = pendingReviveEntry;
					WriteLog($"Queue delayed revive player={pendingReviveEntry.PlayerLabel} key={num}");
					StartPendingMonitorIfNeeded();
				}
			}
		}

		private void StartPendingMonitorIfNeeded()
		{
			if (pendingRevives.Count > 0 && pendingMonitorCoroutine == null)
			{
				pendingMonitorCoroutine = ((MonoBehaviour)this).StartCoroutine(MonitorPendingRevives());
				WriteVerbose("Pending monitor started");
			}
		}

		[IteratorStateMachine(typeof(<MonitorPendingRevives>d__37))]
		private IEnumerator MonitorPendingRevives()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <MonitorPendingRevives>d__37(0)
			{
				<>4__this = this
			};
		}

		private void ProcessPendingRevives()
		{
			if (pendingRevives.Count <= 0)
			{
				return;
			}
			List<int> list = new List<int>(pendingRevives.Keys);
			foreach (int item in list)
			{
				if (!pendingRevives.TryGetValue(item, out PendingReviveEntry value))
				{
					continue;
				}
				if (value == null || (Object)(object)value.PlayerAvatar == (Object)null || (Object)(object)value.DeathHead == (Object)null)
				{
					RemovePendingRevive(item, "invalid reference");
					continue;
				}
				if (!IsPlayerDead(value.PlayerAvatar, value.DeathHead))
				{
					RemovePendingRevive(item, "already alive");
					continue;
				}
				if (!GetBoolField(FieldPlayerDeathHeadTriggered, value.DeathHead, defaultValue: false))
				{
					RemovePendingRevive(item, "head no longer triggered");
					continue;
				}
				if (ShouldLeaveToVanillaTruckRevive(value.DeathHead, value.PlayerAvatar))
				{
					RemovePendingRevive(item, "vanilla truck revive");
					continue;
				}
				if (IsPitHeadElectrified(value.DeathHead, out DeathPitSaveEffect _))
				{
					value.ElectrifiedEndedAt = -1f;
					value.VelocityStableStartedAt = -1f;
					WriteVerbose("Pending wait player=" + value.PlayerLabel + " reason=electrified");
					continue;
				}
				if (value.ElectrifiedEndedAt < 0f)
				{
					value.ElectrifiedEndedAt = Time.unscaledTime;
					value.VelocityStableStartedAt = -1f;
					WriteVerbose("Pending electricity ended player=" + value.PlayerLabel);
				}
				float num = Time.unscaledTime - value.ElectrifiedEndedAt;
				if (num < configPitHeadExtraDelaySeconds.Value)
				{
					WriteVerbose($"Pending extra delay player={value.PlayerLabel} elapsed={num:F2}");
					continue;
				}
				float headVelocityMagnitude = GetHeadVelocityMagnitude(value.DeathHead);
				if (headVelocityMagnitude > configPitHeadMaxVelocityToRevive.Value)
				{
					value.VelocityStableStartedAt = -1f;
					WriteVerbose($"Pending velocity wait player={value.PlayerLabel} velocity={headVelocityMagnitude:F2}");
					continue;
				}
				if (value.VelocityStableStartedAt < 0f)
				{
					value.VelocityStableStartedAt = Time.unscaledTime;
					WriteVerbose("Pending velocity stable start player=" + value.PlayerLabel);
				}
				ReviveNow(value.PlayerAvatar, "delayed");
				RemovePendingRevive(item, "revived");
			}
		}

		private void RemovePendingRevive(int key, string reason)
		{
			if (pendingRevives.TryGetValue(key, out PendingReviveEntry value))
			{
				WriteVerbose($"Pending remove player={value.PlayerLabel} key={key} reason={reason}");
				pendingRevives.Remove(key);
			}
		}

		internal void ClearPendingRevives(string reason)
		{
			if (pendingRevives.Count > 0)
			{
				WriteLog($"Clear pending revives count={pendingRevives.Count} reason={reason}");
				pendingRevives.Clear();
			}
		}

		private bool CanRunHostLogic()
		{
			try
			{
				return SemiFunc.IsMasterClientOrSingleplayer();
			}
			catch
			{
				return false;
			}
		}

		private bool ShouldClearPendingByRoundState()
		{
			try
			{
				if ((Object)(object)RunManager.instance == (Object)null)
				{
					return false;
				}
				if (SemiFunc.RunIsLobby() || SemiFunc.RunIsLobbyMenu() || (!SemiFunc.RunIsShop() && !SemiFunc.RunIsLevel()))
				{
					return true;
				}
				if ((Object)(object)RoundDirector.instance != (Object)null && RoundDirector.instance.allExtractionPointsCompleted)
				{
					return true;
				}
			}
			catch
			{
				return true;
			}
			return false;
		}

		private bool ShouldLeaveToVanillaTruckRevive(PlayerDeathHead deathHead, PlayerAvatar playerAvatar)
		{
			if ((Object)(object)deathHead == (Object)null || (Object)(object)playerAvatar == (Object)null)
			{
				return false;
			}
			if ((Object)(object)RoundDirector.instance == (Object)null)
			{
				return false;
			}
			if (!RoundDirector.instance.allExtractionPointsCompleted)
			{
				return false;
			}
			if (GetBoolField(FieldPlayerAvatarFinalHeal, playerAvatar, defaultValue: false))
			{
				return false;
			}
			if (!GetBoolField(FieldPlayerDeathHeadInTruck, deathHead, defaultValue: false))
			{
				return false;
			}
			return true;
		}

		private bool IsPlayerDead(PlayerAvatar playerAvatar, PlayerDeathHead deathHead)
		{
			if ((Object)(object)playerAvatar == (Object)null)
			{
				return false;
			}
			bool boolField = GetBoolField(FieldPlayerAvatarDeadSet, playerAvatar, defaultValue: false);
			bool boolField2 = GetBoolField(FieldPlayerAvatarIsDisabled, playerAvatar, defaultValue: false);
			bool activeSelf = ((Component)playerAvatar).gameObject.activeSelf;
			bool flag = (Object)(object)deathHead != (Object)null && GetBoolField(FieldPlayerDeathHeadTriggered, deathHead, defaultValue: false);
			if (boolField)
			{
				return true;
			}
			if (boolField2)
			{
				return true;
			}
			if (!activeSelf)
			{
				return true;
			}
			if (flag)
			{
				return true;
			}
			return false;
		}

		private void ReviveNow(PlayerAvatar playerAvatar, string reason)
		{
			if ((Object)(object)playerAvatar == (Object)null)
			{
				return;
			}
			if (!IsPlayerDead(playerAvatar, playerAvatar.playerDeathHead))
			{
				WriteVerbose("Skip revive player=" + GetPlayerLabel(playerAvatar) + " reason=alreadyAlive finalReason=" + reason);
				return;
			}
			try
			{
				playerAvatar.Revive(true);
				WriteLog("Revive(true) player=" + GetPlayerLabel(playerAvatar) + " reason=" + reason);
			}
			catch (Exception arg)
			{
				WriteWarning($"Revive(true) failed player={GetPlayerLabel(playerAvatar)} reason={reason} ex={arg}");
			}
		}

		private bool IsPitHeadElectrified(PlayerDeathHead deathHead, out DeathPitSaveEffect effect)
		{
			effect = null;
			if ((Object)(object)deathHead == (Object)null)
			{
				return false;
			}
			PhysGrabObject playerDeathHeadPhysGrabObject = GetPlayerDeathHeadPhysGrabObject(deathHead);
			if ((Object)(object)playerDeathHeadPhysGrabObject == (Object)null)
			{
				return false;
			}
			float floatField = GetFloatField(FieldPhysGrabObjectDeathPitEffectDisableTimer, playerDeathHeadPhysGrabObject, 0f);
			GameObject gameObjectField = GetGameObjectField(FieldPhysGrabObjectDeathPitEffect, playerDeathHeadPhysGrabObject);
			if ((Object)(object)gameObjectField == (Object)null)
			{
				if (floatField > 0f)
				{
					return false;
				}
				return false;
			}
			effect = gameObjectField.GetComponent<DeathPitSaveEffect>();
			if ((Object)(object)effect == (Object)null)
			{
				return false;
			}
			if ((Object)(object)effect.physGrabObject != (Object)null && (Object)(object)effect.physGrabObject != (Object)(object)playerDeathHeadPhysGrabObject)
			{
				return false;
			}
			float floatField2 = GetFloatField(FieldDeathPitSaveEffectTimeCurrent, effect, 0f);
			if (floatField2 > 0f)
			{
				return true;
			}
			return false;
		}

		private float GetHeadVelocityMagnitude(PlayerDeathHead deathHead)
		{
			//IL_0052: 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)
			if ((Object)(object)deathHead == (Object)null)
			{
				return 0f;
			}
			PhysGrabObject playerDeathHeadPhysGrabObject = GetPlayerDeathHeadPhysGrabObject(deathHead);
			if ((Object)(object)playerDeathHeadPhysGrabObject == (Object)null)
			{
				return 0f;
			}
			if ((Object)(object)playerDeathHeadPhysGrabObject.rb == (Object)null)
			{
				return 0f;
			}
			Vector3 velocity = playerDeathHeadPhysGrabObject.rb.velocity;
			return ((Vector3)(ref velocity)).magnitude;
		}

		private int GetPendingKey(PlayerAvatar playerAvatar, PlayerDeathHead deathHead)
		{
			if ((Object)(object)playerAvatar != (Object)null && (Object)(object)playerAvatar.photonView != (Object)null && playerAvatar.photonView.ViewID > 0)
			{
				return playerAvatar.photonView.ViewID;
			}
			PhotonView val = null;
			if ((Object)(object)deathHead != (Object)null)
			{
				val = ((Component)deathHead).GetComponent<PhotonView>();
			}
			if ((Object)(object)val != (Object)null && val.ViewID > 0)
			{
				return val.ViewID;
			}
			if ((Object)(object)playerAvatar != (Object)null)
			{
				return ((Object)playerAvatar).GetInstanceID();
			}
			return 0;
		}

		private string GetPlayerLabel(PlayerAvatar playerAvatar)
		{
			if ((Object)(object)playerAvatar == (Object)null)
			{
				return "null";
			}
			if (!string.IsNullOrEmpty(((Object)playerAvatar).name))
			{
				return ((Object)playerAvatar).name;
			}
			return "PlayerAvatar";
		}

		private PhysGrabObject GetPlayerDeathHeadPhysGrabObject(PlayerDeathHead deathHead)
		{
			if ((Object)(object)deathHead == (Object)null)
			{
				return null;
			}
			PhysGrabObject val = null;
			if (FieldPlayerDeathHeadPhysGrabObject != null)
			{
				try
				{
					object? value = FieldPlayerDeathHeadPhysGrabObject.GetValue(deathHead);
					val = (PhysGrabObject)((value is PhysGrabObject) ? value : null);
				}
				catch
				{
				}
			}
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			return ((Component)deathHead).GetComponent<PhysGrabObject>();
		}

		private static bool GetBoolField(FieldInfo fieldInfo, object instance, bool defaultValue)
		{
			if (fieldInfo == null || instance == null)
			{
				return defaultValue;
			}
			try
			{
				object value = fieldInfo.GetValue(instance);
				if (value is bool)
				{
					return (bool)value;
				}
			}
			catch
			{
			}
			return defaultValue;
		}

		private static float GetFloatField(FieldInfo fieldInfo, object instance, float defaultValue)
		{
			if (fieldInfo == null || instance == null)
			{
				return defaultValue;
			}
			try
			{
				object value = fieldInfo.GetValue(instance);
				if (value is float)
				{
					return (float)value;
				}
			}
			catch
			{
			}
			return defaultValue;
		}

		private static GameObject GetGameObjectField(FieldInfo fieldInfo, object instance)
		{
			if (fieldInfo == null || instance == null)
			{
				return null;
			}
			try
			{
				object? value = fieldInfo.GetValue(instance);
				return (GameObject)((value is GameObject) ? value : null);
			}
			catch
			{
				return null;
			}
		}
	}
	[HarmonyPatch(typeof(ExtractionPoint), "DestroyAllPhysObjectsInShoppingList")]
	internal static class ExtractionPoint_DestroyAllPhysObjectsInShoppingList_Patch
	{
		private static void Prefix()
		{
			if (SemiFunc.IsMasterClientOrSingleplayer())
			{
				ExtractionAutoRevivePlugin.EnterCustomReviveContext();
			}
		}

		private static Exception Finalizer(Exception __exception)
		{
			if (SemiFunc.IsMasterClientOrSingleplayer())
			{
				ExtractionAutoRevivePlugin.ExitCustomReviveContext();
			}
			return __exception;
		}
	}
	[HarmonyPatch(typeof(ExtractionPoint), "DestroyAllPhysObjectsInHaulList")]
	internal static class ExtractionPoint_DestroyAllPhysObjectsInHaulList_Patch
	{
		private static void Prefix()
		{
			if (SemiFunc.IsMasterClientOrSingleplayer())
			{
				ExtractionAutoRevivePlugin.EnterCustomReviveContext();
			}
		}

		private static Exception Finalizer(Exception __exception)
		{
			if (SemiFunc.IsMasterClientOrSingleplayer())
			{
				ExtractionAutoRevivePlugin.ExitCustomReviveContext();
			}
			return __exception;
		}
	}
	[HarmonyPatch(typeof(PlayerDeathHead), "Revive")]
	internal static class PlayerDeathHead_Revive_Patch
	{
		private static bool Prefix(PlayerDeathHead __instance)
		{
			if (!ExtractionAutoRevivePlugin.IsCustomReviveContextActive())
			{
				return true;
			}
			if ((Object)(object)ExtractionAutoRevivePlugin.Instance == (Object)null)
			{
				return true;
			}
			if (!ExtractionAutoRevivePlugin.Instance.IsModEnabled())
			{
				return true;
			}
			try
			{
				ExtractionAutoRevivePlugin.Instance.HandleDeathHeadRevive(__instance);
			}
			catch (Exception arg)
			{
				ManualLogSource log = ExtractionAutoRevivePlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)$"[ExtractionAutoRevive] PlayerDeathHead.Revive patch failed ex={arg}");
				}
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(RunManager), "ChangeLevel")]
	internal static class RunManager_ChangeLevel_Patch
	{
		private static void Prefix()
		{
			if ((Object)(object)ExtractionAutoRevivePlugin.Instance != (Object)null)
			{
				ExtractionAutoRevivePlugin.Instance.ClearPendingRevives("run manager change level");
			}
		}
	}
	[HarmonyPatch(typeof(RunManager), "RestartScene")]
	internal static class RunManager_RestartScene_Patch
	{
		private static void Prefix()
		{
			if ((Object)(object)ExtractionAutoRevivePlugin.Instance != (Object)null)
			{
				ExtractionAutoRevivePlugin.Instance.ClearPendingRevives("run manager restart scene");
			}
		}
	}
	[HarmonyPatch(typeof(RoundDirector), "ExtractionCompletedAllRPC")]
	internal static class RoundDirector_ExtractionCompletedAllRPC_Patch
	{
		private static void Postfix()
		{
			if ((Object)(object)ExtractionAutoRevivePlugin.Instance != (Object)null)
			{
				ExtractionAutoRevivePlugin.Instance.ClearPendingRevives("all extractions completed");
			}
		}
	}
	[HarmonyPatch(typeof(TruckHealer), "StateUpdate")]
	internal static class TruckHealer_StateUpdate_Patch
	{
		private static void Prefix(State _newState)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Invalid comparison between Unknown and I4
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			if (((int)_newState == 1 || (int)_newState == 2 || (int)_newState == 3) && (Object)(object)ExtractionAutoRevivePlugin.Instance != (Object)null)
			{
				ExtractionAutoRevivePlugin.Instance.ClearPendingRevives($"truck healer state={_newState}");
			}
		}
	}
}