Decompiled source of SelfReviveUpgrade v1.0.0

SelfReviveUpgrade.dll

Decompiled 2 weeks ago
using System;
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.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using REPOLib.Modules;
using REPOLib.Objects.Sdk;
using UnityEngine;

[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("McHorse")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+66113413f87d85f7a794401f134f1527319ecedb")]
[assembly: AssemblyProduct("SelfReviveUpgrade")]
[assembly: AssemblyTitle("SelfReviveUpgrade")]
[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 SelfReviveUpgrade
{
	internal static class SelfReviveRuntime
	{
		private struct CachedEquipEntry
		{
			internal int SpotIndex;

			internal int ItemPhotonViewId;

			internal CachedEquipEntry(int spotIndex, int itemPhotonViewId)
			{
				SpotIndex = spotIndex;
				ItemPhotonViewId = itemPhotonViewId;
			}
		}

		private static readonly Dictionary<string, float> PendingReviveBySteamId = new Dictionary<string, float>();

		private static readonly Dictionary<string, float> PostReviveTruckSnapUntilBySteamId = new Dictionary<string, float>();

		private static readonly HashSet<string> SuppressUnequipBySteamId = new HashSet<string>();

		private static readonly Dictionary<string, List<CachedEquipEntry>> CachedEquipsBySteamId = new Dictionary<string, List<CachedEquipEntry>>();

		private static readonly NetworkedEvent ReviveRequestEvent = new NetworkedEvent("SelfReviveUpgrade_ReviveRequest", (Action<EventData>)HandleReviveRequestEvent);

		internal static Item? RegisteredShopItem { get; set; }

		internal static bool ShouldSuppressUnequip(PlayerAvatar? avatar)
		{
			if ((Object)(object)avatar == (Object)null)
			{
				return false;
			}
			return SuppressUnequipBySteamId.Contains(avatar.steamID);
		}

		internal static void TryArmRevive(PlayerAvatar? avatar)
		{
			PlayerUpgrade val = default(PlayerUpgrade);
			if (!((Object)(object)avatar == (Object)null) && avatar.isLocal && avatar.deadSet && Upgrades.TryGetUpgrade("SelfRevive", ref val) && val.GetLevel(avatar) > 0)
			{
				string steamID = avatar.steamID;
				if (!PendingReviveBySteamId.ContainsKey(steamID))
				{
					SuppressUnequipBySteamId.Add(steamID);
					PendingReviveBySteamId[steamID] = Time.time + Mathf.Max(0.1f, SelfReviveUpgrade.ReviveDelaySeconds.Value);
					LogDebug("Armed self revive for " + steamID + ".");
				}
			}
		}

		internal static void ClearReviveState(string steamId)
		{
			PendingReviveBySteamId.Remove(steamId);
			SuppressUnequipBySteamId.Remove(steamId);
		}

		internal static bool WasPendingSelfRevive(string steamId)
		{
			return PendingReviveBySteamId.ContainsKey(steamId);
		}

		internal static void CaptureEquippedItems(PlayerAvatar avatar)
		{
			if (!avatar.isLocal || (Object)(object)Inventory.instance == (Object)null)
			{
				return;
			}
			List<InventorySpot> list = TraverseInventorySpots();
			if (list == null)
			{
				return;
			}
			List<CachedEquipEntry> list2 = new List<CachedEquipEntry>();
			foreach (InventorySpot item in list)
			{
				if (!((Object)(object)item == (Object)null) && item.IsOccupied() && !((Object)(object)item.CurrentItem == (Object)null))
				{
					PhotonView component = ((Component)item.CurrentItem).GetComponent<PhotonView>();
					if (!((Object)(object)component == (Object)null))
					{
						list2.Add(new CachedEquipEntry(item.inventorySpotIndex, component.ViewID));
					}
				}
			}
			if (list2.Count > 0)
			{
				CachedEquipsBySteamId[avatar.steamID] = list2;
			}
		}

		internal static void Update()
		{
			ProcessPostReviveTruckSnap();
			if (PendingReviveBySteamId.Count == 0)
			{
				return;
			}
			PlayerAvatar val = PlayerController.instance?.playerAvatarScript;
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			string steamID = val.steamID;
			if (PendingReviveBySteamId.TryGetValue(steamID, out var value) && !(Time.time < value))
			{
				if (GameManager.Multiplayer() && !PhotonNetwork.IsMasterClient)
				{
					RequestMasterRevive(val.steamID);
				}
				else
				{
					CommitRevive(val.steamID);
				}
			}
		}

		private static void RequestMasterRevive(string steamId)
		{
			//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_0012: Expected O, but got Unknown
			//IL_0014: Expected O, but got Unknown
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			Hashtable val = new Hashtable();
			((Dictionary<object, object>)val).Add((object)"SteamId", (object)steamId);
			Hashtable val2 = val;
			ReviveRequestEvent.RaiseEvent((object)val2, NetworkingEvents.RaiseMasterClient, SendOptions.SendReliable);
		}

		private static void HandleReviveRequestEvent(EventData eventData)
		{
			if (PhotonNetwork.IsMasterClient)
			{
				object customData = eventData.CustomData;
				Hashtable val = (Hashtable)((customData is Hashtable) ? customData : null);
				if (val != null && ((Dictionary<object, object>)(object)val).ContainsKey((object)"SteamId"))
				{
					string steamId = (string)val[(object)"SteamId"];
					CommitRevive(steamId);
				}
			}
		}

		private static void CommitRevive(string steamId)
		{
			PlayerAvatar val = SemiFunc.PlayerAvatarGetFromSteamID(steamId);
			if ((Object)(object)val == (Object)null)
			{
				ClearReviveState(steamId);
				return;
			}
			PlayerUpgrade val2 = default(PlayerUpgrade);
			if (!Upgrades.TryGetUpgrade("SelfRevive", ref val2))
			{
				ClearReviveState(steamId);
				return;
			}
			if (val2.GetLevel(steamId) <= 0)
			{
				ClearReviveState(steamId);
				return;
			}
			if (!val.deadSet)
			{
				ClearReviveState(steamId);
				return;
			}
			val2.RemoveLevel(steamId, 1);
			MoveDeathHeadToTruck(val);
			val.Revive(true);
			if (!val.isLocal)
			{
				ClearReviveState(steamId);
			}
			LogDebug("Committed self revive for " + steamId + ".");
		}

		private static void MoveDeathHeadToTruck(PlayerAvatar avatar)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: 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_0076: Unknown result type (might be due to invalid IL or missing references)
			PlayerDeathHead playerDeathHead = avatar.playerDeathHead;
			TruckSafetySpawnPoint instance = TruckSafetySpawnPoint.instance;
			Transform val = ((instance != null) ? ((Component)instance).transform : null);
			if (!((Object)(object)playerDeathHead == (Object)null) && !((Object)(object)val == (Object)null))
			{
				playerDeathHead.OverridePositionRotationReset();
				((Component)playerDeathHead).transform.SetPositionAndRotation(val.position, val.rotation);
				PhysGrabObject value = Traverse.Create((object)playerDeathHead).Field("physGrabObject").GetValue<PhysGrabObject>();
				if (value != null)
				{
					value.Teleport(val.position, val.rotation);
				}
			}
		}

		internal static void ApplyConfiguredHealth(PlayerAvatar avatar)
		{
			int maxHealth = avatar.playerHealth.maxHealth;
			int num = Mathf.Clamp(Mathf.RoundToInt((float)maxHealth * (SelfReviveUpgrade.ReviveHealthPercent.Value / 100f)), 1, maxHealth);
			int num2 = Mathf.Max(0, num - avatar.playerHealth.health);
			if (num2 > 0)
			{
				avatar.playerHealth.HealOther(num2, true);
			}
		}

		internal static void ApplyReviveImmunity(PlayerAvatar avatar)
		{
			float num = Mathf.Max(0f, SelfReviveUpgrade.ReviveImmunitySeconds.Value);
			if (!(num <= 0f))
			{
				avatar.playerHealth.InvincibleSet(num);
				avatar.FallDamageResetSet(num);
			}
		}

		internal static void SnapLocalAvatarToTruck(PlayerAvatar avatar)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			TruckSafetySpawnPoint instance = TruckSafetySpawnPoint.instance;
			Transform val = ((instance != null) ? ((Component)instance).transform : null);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			((Component)avatar).transform.SetPositionAndRotation(val.position, val.rotation);
			avatar.clientPositionCurrent = val.position;
			avatar.clientPosition = val.position;
			if ((Object)(object)avatar.playerTransform != (Object)null)
			{
				avatar.playerTransform.SetPositionAndRotation(val.position, val.rotation);
			}
			if (!SemiFunc.IsMasterClientOrSingleplayer())
			{
				return;
			}
			PlayerTumble tumble = avatar.tumble;
			if (tumble != null)
			{
				PhysGrabObject physGrabObject = tumble.physGrabObject;
				if (physGrabObject != null)
				{
					physGrabObject.Teleport(val.position, val.rotation);
				}
			}
		}

		internal static void ArmPostReviveTruckSnap(PlayerAvatar avatar)
		{
			if (avatar.isLocal)
			{
				PostReviveTruckSnapUntilBySteamId[avatar.steamID] = Time.time + 0.5f;
				SnapLocalAvatarToTruck(avatar);
			}
		}

		private static void ProcessPostReviveTruckSnap()
		{
			PlayerAvatar val = PlayerController.instance?.playerAvatarScript;
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			string steamID = val.steamID;
			if (PostReviveTruckSnapUntilBySteamId.TryGetValue(steamID, out var value))
			{
				if (Time.time > value || val.deadSet)
				{
					PostReviveTruckSnapUntilBySteamId.Remove(steamID);
				}
				else
				{
					SnapLocalAvatarToTruck(val);
				}
			}
		}

		internal static void RestoreEquippedItems(PlayerAvatar avatar)
		{
			if (!avatar.isLocal || !CachedEquipsBySteamId.TryGetValue(avatar.steamID, out List<CachedEquipEntry> value))
			{
				return;
			}
			Inventory instance = Inventory.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			int localPhysGrabberPhotonViewId = GetLocalPhysGrabberPhotonViewId(instance);
			foreach (CachedEquipEntry item in value)
			{
				InventorySpot spotByIndex = instance.GetSpotByIndex(item.SpotIndex);
				if (!((Object)(object)spotByIndex == (Object)null) && !spotByIndex.IsOccupied())
				{
					PhotonView val = PhotonView.Find(item.ItemPhotonViewId);
					ItemEquippable val2 = ((val != null) ? ((Component)val).GetComponent<ItemEquippable>() : null);
					if (!((Object)(object)val2 == (Object)null))
					{
						val2.RequestEquip(item.SpotIndex, localPhysGrabberPhotonViewId);
					}
				}
			}
			CachedEquipsBySteamId.Remove(avatar.steamID);
		}

		private static List<InventorySpot>? TraverseInventorySpots()
		{
			return Traverse.Create((object)Inventory.instance).Field("inventorySpots").GetValue<List<InventorySpot>>();
		}

		private static int GetLocalPhysGrabberPhotonViewId(Inventory inventory)
		{
			PhysGrabber value = Traverse.Create((object)inventory).Field("physGrabber").GetValue<PhysGrabber>();
			int? obj;
			if (value == null)
			{
				obj = null;
			}
			else
			{
				PhotonView photonView = value.photonView;
				obj = ((photonView != null) ? new int?(photonView.ViewID) : null);
			}
			int? num = obj;
			return num.GetValueOrDefault(-1);
		}

		private static void LogDebug(string message)
		{
			if (SelfReviveUpgrade.DebugLogging.Value)
			{
				SelfReviveUpgrade.Logger.LogInfo((object)message);
			}
		}
	}
	[BepInPlugin("McHorse.SelfReviveUpgrade", "SelfReviveUpgrade", "1.0")]
	public class SelfReviveUpgrade : BaseUnityPlugin
	{
		private static AssetBundle? _embeddedBundle;

		internal const string ReviveUpgradeId = "SelfRevive";

		internal static SelfReviveUpgrade Instance { get; private set; }

		internal static ManualLogSource Logger => Instance._logger;

		private ManualLogSource _logger => ((BaseUnityPlugin)this).Logger;

		internal Harmony? Harmony { get; set; }

		internal static ConfigEntry<float> ReviveHealthPercent { get; private set; }

		internal static ConfigEntry<float> ReviveDelaySeconds { get; private set; }

		internal static ConfigEntry<float> ReviveImmunitySeconds { get; private set; }

		internal static ConfigEntry<bool> DebugLogging { get; private set; }

		private void Awake()
		{
			Instance = this;
			((Component)this).gameObject.transform.parent = null;
			((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
			BindConfig();
			RegisterBundledItemAndUpgrade();
			Patch();
			Logger.LogInfo((object)$"{((BaseUnityPlugin)this).Info.Metadata.GUID} v{((BaseUnityPlugin)this).Info.Metadata.Version} has loaded!");
		}

		internal void Patch()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			//IL_0026: Expected O, but got Unknown
			if (Harmony == null)
			{
				Harmony val = new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID);
				Harmony val2 = val;
				Harmony = val;
			}
			Harmony.PatchAll();
		}

		internal void Unpatch()
		{
			Harmony? harmony = Harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		private void Update()
		{
			SelfReviveRuntime.Update();
		}

		private void BindConfig()
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Expected O, but got Unknown
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Expected O, but got Unknown
			ReviveHealthPercent = ((BaseUnityPlugin)this).Config.Bind<float>("Revive", "HealthPercent", 35f, new ConfigDescription("Health percent restored after self revive.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f), Array.Empty<object>()));
			ReviveDelaySeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Revive", "DelaySeconds", 1f, new ConfigDescription("Delay after death before self revive triggers.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 10f), Array.Empty<object>()));
			ReviveImmunitySeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Revive", "ImmunitySeconds", 3f, new ConfigDescription("Damage and fall immunity duration right after self revive.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
			DebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "EnableVerboseLogs", false, "Enable extra debug logs.");
		}

		private void RegisterBundledItemAndUpgrade()
		{
			byte[] array = ReadEmbeddedBundleBytes();
			if (array == null || array.Length == 0)
			{
				Logger.LogError((object)"Embedded bundle 'selfreviveupgrade' not found or empty.");
				return;
			}
			_embeddedBundle = AssetBundle.LoadFromMemory(array);
			if ((Object)(object)_embeddedBundle == (Object)null)
			{
				Logger.LogError((object)"AssetBundle.LoadFromMemory failed.");
				return;
			}
			ItemContent val = _embeddedBundle.LoadAsset<ItemContent>("Item Content");
			if ((Object)(object)val == (Object)null)
			{
				Logger.LogError((object)"Could not load ItemContent from bundle at path: Item Content");
				return;
			}
			Items.RegisterItem(val);
			Item val2 = val.Prefab?.item;
			if ((Object)(object)val2 == (Object)null)
			{
				Logger.LogError((object)"ItemContent has no Item on prefab.");
				return;
			}
			val2.maxPurchase = false;
			val2.value.valueMin = 12500f;
			val2.value.valueMax = 17500f;
			SelfReviveRuntime.RegisteredShopItem = val2;
			if (Upgrades.RegisterUpgrade("SelfRevive", val2, (Action<PlayerAvatar, int>)null, (Action<PlayerAvatar, int>)null) == null)
			{
				Logger.LogError((object)"Failed to register upgrade (ID may already be registered).");
			}
			else
			{
				Logger.LogInfo((object)("Registered item '" + val2.itemName + "' and upgrade 'SelfRevive'."));
			}
		}

		private static byte[]? ReadEmbeddedBundleBytes()
		{
			Assembly assembly = typeof(SelfReviveUpgrade).Assembly;
			string text = assembly.GetManifestResourceNames().FirstOrDefault((string n) => n.EndsWith("selfreviveupgrade", StringComparison.OrdinalIgnoreCase));
			if (text == null)
			{
				return null;
			}
			using Stream stream = assembly.GetManifestResourceStream(text);
			if (stream == null)
			{
				return null;
			}
			using MemoryStream memoryStream = new MemoryStream();
			stream.CopyTo(memoryStream);
			return memoryStream.ToArray();
		}
	}
}
namespace SelfReviveUpgrade.Patches
{
	[HarmonyPatch]
	internal static class DeathPatches
	{
		[HarmonyPatch(typeof(PlayerAvatar), "PlayerDeathDone")]
		[HarmonyPrefix]
		private static void PlayerDeathDonePrefix(PlayerAvatar __instance)
		{
			SelfReviveRuntime.CaptureEquippedItems(__instance);
		}

		[HarmonyPatch(typeof(PlayerAvatar), "PlayerDeathDone")]
		[HarmonyPostfix]
		private static void PlayerDeathDonePostfix(PlayerAvatar __instance)
		{
			SelfReviveRuntime.TryArmRevive(__instance);
		}

		[HarmonyPatch(typeof(Inventory), "ForceUnequip")]
		[HarmonyPrefix]
		private static bool ForceUnequipPrefix(Inventory __instance)
		{
			PlayerAvatar value = Traverse.Create((object)__instance).Field("playerAvatar").GetValue<PlayerAvatar>();
			return !SelfReviveRuntime.ShouldSuppressUnequip(value);
		}

		[HarmonyPatch(typeof(PlayerAvatar), "ReviveRPC")]
		[HarmonyPostfix]
		private static void ReviveRpcPostfix(PlayerAvatar __instance)
		{
			if (__instance.isLocal && SelfReviveRuntime.WasPendingSelfRevive(__instance.steamID))
			{
				SelfReviveRuntime.ArmPostReviveTruckSnap(__instance);
				SelfReviveRuntime.ApplyReviveImmunity(__instance);
				SelfReviveRuntime.ApplyConfiguredHealth(__instance);
				SelfReviveRuntime.RestoreEquippedItems(__instance);
				SelfReviveRuntime.ClearReviveState(__instance.steamID);
			}
		}
	}
}