Decompiled source of RaiseToUpgrade v0.1.0

BepInEx\plugins\RaiseToUpgrade\RaiseToUpgrade.dll

Decompiled a day ago
using System;
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.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using UnityEngine;

[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("artrointel")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Hold up a shop upgrade and look skyward for two seconds to claim a free personal +1 each shop.")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+4d5a3c5223e0f5bf2d8471f0d16f72805a00cdb8")]
[assembly: AssemblyProduct("RaiseToUpgrade")]
[assembly: AssemblyTitle("RaiseToUpgrade")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.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]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace RaiseToUpgrade
{
	[HarmonyPatch(typeof(ItemUpgrade), "Start")]
	internal static class ItemUpgradePatch
	{
		private static readonly IReadOnlyDictionary<string, string> UpgradeNames = new Dictionary<string, string>
		{
			["ItemUpgradeDeathHeadBattery"] = "DeathHeadBattery",
			["ItemUpgradeMapPlayerCount"] = "MapPlayerCount",
			["ItemUpgradePlayerCrouchRest"] = "CrouchRest",
			["ItemUpgradePlayerEnergy"] = "Stamina",
			["ItemUpgradePlayerExtraJump"] = "ExtraJump",
			["ItemUpgradePlayerGrabRange"] = "Range",
			["ItemUpgradePlayerGrabStrength"] = "Strength",
			["ItemUpgradePlayerGrabThrow"] = "Throw",
			["ItemUpgradePlayerHealth"] = "Health",
			["ItemUpgradePlayerSprintSpeed"] = "Speed",
			["ItemUpgradePlayerTumbleClimb"] = "TumbleClimb",
			["ItemUpgradePlayerTumbleLaunch"] = "Launch",
			["ItemUpgradePlayerTumbleWings"] = "TumbleWings"
		};

		[HarmonyPostfix]
		private static void Postfix(ItemUpgrade __instance)
		{
			if ((Object)(object)((Component)__instance).GetComponent<UpgradeEffectCarrier>() != (Object)null || (Object)(object)((Component)__instance).GetComponent<UpgradeHoldTracker>() != (Object)null)
			{
				return;
			}
			PhysGrabObject component = ((Component)__instance).GetComponent<PhysGrabObject>();
			if ((Object)(object)component == (Object)null)
			{
				return;
			}
			MonoBehaviour[] components = ((Component)__instance).GetComponents<MonoBehaviour>();
			foreach (MonoBehaviour val in components)
			{
				if ((Object)(object)val != (Object)null && UpgradeNames.TryGetValue(((object)val).GetType().Name, out string value))
				{
					UpgradeHoldTracker upgradeHoldTracker = ((Component)__instance).gameObject.AddComponent<UpgradeHoldTracker>();
					upgradeHoldTracker.Initialize(__instance, component, value);
					break;
				}
			}
		}
	}
	[BepInPlugin("artrointel.RaiseToUpgrade", "RaiseToUpgrade", "0.1.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "artrointel.RaiseToUpgrade";

		public const string PluginName = "RaiseToUpgrade";

		public const string PluginVersion = "0.1.0";

		private Harmony? _harmony;

		internal static ManualLogSource ModLogger { get; private set; }

		private void Awake()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			ModLogger = ((BaseUnityPlugin)this).Logger;
			_harmony = new Harmony("artrointel.RaiseToUpgrade");
			_harmony.PatchAll();
			ModLogger.LogInfo((object)"RaiseToUpgrade v0.1.0 loaded.");
		}

		private void Update()
		{
			ShopUpgradeService.TickShopState();
		}

		private void OnDestroy()
		{
			Harmony? harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	internal static class ShopUpgradeService
	{
		private sealed class PreparedUpgrade
		{
			internal int SourceInstanceId { get; }

			internal string UpgradeName { get; }

			internal GameObject Carrier { get; }

			internal PreparedUpgrade(int sourceInstanceId, string upgradeName, GameObject carrier)
			{
				SourceInstanceId = sourceInstanceId;
				UpgradeName = upgradeName;
				Carrier = carrier;
			}
		}

		private const string SetPositionRpcName = "SetPositionRPC";

		private const string ToggleItemRpcName = "ToggleItemRPC";

		private static readonly Vector3 CarrierStagingOffset = new Vector3(4096f, 4096f, 4096f);

		private static readonly HashSet<string> ClaimedPlayers = new HashSet<string>(StringComparer.Ordinal);

		private static readonly Dictionary<string, PreparedUpgrade> PreparedByPlayer = new Dictionary<string, PreparedUpgrade>(StringComparer.Ordinal);

		private static bool _wasInShop;

		internal static bool IsHostInShop
		{
			get
			{
				if ((Object)(object)RunManager.instance != (Object)null && (Object)(object)GameManager.instance != (Object)null && SemiFunc.RunIsShop())
				{
					return SemiFunc.IsMasterClientOrSingleplayer();
				}
				return false;
			}
		}

		internal static void TickShopState()
		{
			if ((Object)(object)RunManager.instance == (Object)null)
			{
				ClearPreparedUpgrades();
				_wasInShop = false;
				return;
			}
			bool flag = SemiFunc.RunIsShop();
			if (flag && !_wasInShop)
			{
				ClearPreparedUpgrades();
				ClaimedPlayers.Clear();
				Plugin.ModLogger.LogInfo((object)"Entered the shop. Every player may claim one held upgrade.");
			}
			else if (!flag && _wasInShop)
			{
				ClearPreparedUpgrades();
			}
			_wasInShop = flag;
		}

		internal static bool HasClaimed(string steamId)
		{
			return ClaimedPlayers.Contains(steamId);
		}

		internal static bool EnsurePrepared(ItemUpgrade sourceUpgrade, PlayerAvatar playerAvatar, string upgradeName)
		{
			if (!IsHostInShop)
			{
				return false;
			}
			string text = SemiFunc.PlayerGetSteamID(playerAvatar);
			if (string.IsNullOrWhiteSpace(text) || HasClaimed(text))
			{
				return false;
			}
			int instanceID = ((Object)sourceUpgrade).GetInstanceID();
			if (PreparedByPlayer.TryGetValue(text, out PreparedUpgrade value))
			{
				if (value.SourceInstanceId == instanceID && value.UpgradeName == upgradeName && (Object)(object)value.Carrier != (Object)null)
				{
					return true;
				}
				PreparedByPlayer.Remove(text);
				DestroyCarrier(value.Carrier);
			}
			GameObject carrier;
			try
			{
				carrier = CreateCarrier(sourceUpgrade, upgradeName);
			}
			catch (Exception arg)
			{
				Plugin.ModLogger.LogError((object)$"Could not prepare {upgradeName} feedback for {text}: {arg}");
				return false;
			}
			PreparedByPlayer[text] = new PreparedUpgrade(instanceID, upgradeName, carrier);
			return false;
		}

		internal static void CancelPreparation(string steamId, ItemUpgrade sourceUpgrade)
		{
			if (!string.IsNullOrWhiteSpace(steamId) && PreparedByPlayer.TryGetValue(steamId, out PreparedUpgrade value) && value.SourceInstanceId == ((Object)sourceUpgrade).GetInstanceID())
			{
				PreparedByPlayer.Remove(steamId);
				DestroyCarrier(value.Carrier);
			}
		}

		internal static void CancelPreparationsForSource(ItemUpgrade sourceUpgrade)
		{
			int instanceID = ((Object)sourceUpgrade).GetInstanceID();
			List<string> list = new List<string>();
			foreach (KeyValuePair<string, PreparedUpgrade> item in PreparedByPlayer)
			{
				if (item.Value.SourceInstanceId == instanceID)
				{
					list.Add(item.Key);
				}
			}
			foreach (string item2 in list)
			{
				PreparedUpgrade preparedUpgrade = PreparedByPlayer[item2];
				PreparedByPlayer.Remove(item2);
				DestroyCarrier(preparedUpgrade.Carrier);
			}
		}

		internal static bool TryClaim(ItemUpgrade sourceUpgrade, PlayerAvatar playerAvatar, string upgradeName)
		{
			if (!IsHostInShop)
			{
				return false;
			}
			string text = SemiFunc.PlayerGetSteamID(playerAvatar);
			if (string.IsNullOrWhiteSpace(text) || HasClaimed(text))
			{
				return false;
			}
			if (!PreparedByPlayer.TryGetValue(text, out PreparedUpgrade value) || value.SourceInstanceId != ((Object)sourceUpgrade).GetInstanceID() || value.UpgradeName != upgradeName || (Object)(object)value.Carrier == (Object)null)
			{
				return false;
			}
			ClaimedPlayers.Add(text);
			PreparedByPlayer.Remove(text);
			try
			{
				TriggerVanillaUpgrade(value.Carrier, sourceUpgrade, playerAvatar);
				Plugin.ModLogger.LogInfo((object)("Played vanilla " + upgradeName + " +1 use for " + SemiFunc.PlayerGetName(playerAvatar) + " (" + text + ")."));
				return true;
			}
			catch (Exception arg)
			{
				ClaimedPlayers.Remove(text);
				DestroyCarrier(value.Carrier);
				Plugin.ModLogger.LogError((object)$"Failed to grant {upgradeName} +1 to player {text}: {arg}");
				return false;
			}
		}

		private static GameObject CreateCarrier(ItemUpgrade sourceUpgrade, string upgradeName)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: 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_0058: 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_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			Item item = sourceUpgrade.itemAttributes.item;
			Vector3 val = ((Component)sourceUpgrade).transform.position + CarrierStagingOffset;
			Quaternion rotation = ((Component)sourceUpgrade).transform.rotation;
			GameObject val2 = ((!SemiFunc.IsMultiplayer()) ? Object.Instantiate<GameObject>(((PrefabRef<GameObject>)(object)item.prefab).Prefab, val, rotation) : PhotonNetwork.InstantiateRoomObject(((PrefabRef<GameObject>)(object)item.prefab).ResourcePath, val, rotation, (byte)0, (object[])null));
			if ((Object)(object)val2 == (Object)null)
			{
				throw new InvalidOperationException("Could not prepare the vanilla effect carrier for " + upgradeName + ".");
			}
			val2.AddComponent<UpgradeEffectCarrier>();
			Rigidbody component = val2.GetComponent<Rigidbody>();
			if ((Object)(object)component != (Object)null)
			{
				component.velocity = Vector3.zero;
				component.angularVelocity = Vector3.zero;
				component.detectCollisions = false;
				component.isKinematic = true;
			}
			Collider[] componentsInChildren = val2.GetComponentsInChildren<Collider>();
			foreach (Collider val3 in componentsInChildren)
			{
				val3.enabled = false;
			}
			return val2;
		}

		private static void TriggerVanillaUpgrade(GameObject carrier, ItemUpgrade sourceUpgrade, PlayerAvatar playerAvatar)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//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)
			//IL_0062: 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_00f3: 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_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			ItemToggle component = carrier.GetComponent<ItemToggle>();
			PhysGrabObject component2 = carrier.GetComponent<PhysGrabObject>();
			if ((Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null)
			{
				throw new MissingComponentException("The prepared vanilla effect carrier is missing required components.");
			}
			string name = ((Object)sourceUpgrade.itemAttributes.item).name;
			int value;
			bool hadPurchasedEntry = StatsManager.instance.itemsPurchased.TryGetValue(name, out value);
			try
			{
				Vector3 position = ((Component)sourceUpgrade).transform.position;
				Quaternion rotation = ((Component)sourceUpgrade).transform.rotation;
				int viewID = playerAvatar.photonView.ViewID;
				if (SemiFunc.IsMultiplayer())
				{
					PhotonView component3 = carrier.GetComponent<PhotonView>();
					if ((Object)(object)component3 == (Object)null || component3.ViewID == 0)
					{
						throw new MissingComponentException("The prepared effect carrier has no networked PhotonView.");
					}
					component3.RPC("SetPositionRPC", (RpcTarget)0, new object[2] { position, rotation });
					component3.RPC("ToggleItemRPC", (RpcTarget)0, new object[2] { true, viewID });
				}
				else
				{
					component2.SetPositionLogic(position, rotation);
					component.ToggleItemLogic(true, viewID);
				}
			}
			finally
			{
				RestorePurchasedItemCount(name, hadPurchasedEntry, value);
			}
		}

		private static void RestorePurchasedItemCount(string itemName, bool hadPurchasedEntry, int originalCount)
		{
			if (hadPurchasedEntry)
			{
				StatsManager.instance.itemsPurchased[itemName] = originalCount;
			}
			else
			{
				StatsManager.instance.itemsPurchased.Remove(itemName);
			}
		}

		private static void ClearPreparedUpgrades()
		{
			if (PreparedByPlayer.Count == 0)
			{
				return;
			}
			foreach (PreparedUpgrade value in PreparedByPlayer.Values)
			{
				DestroyCarrier(value.Carrier);
			}
			PreparedByPlayer.Clear();
		}

		private static void DestroyCarrier(GameObject? carrier)
		{
			if ((Object)(object)carrier == (Object)null)
			{
				return;
			}
			try
			{
				if (SemiFunc.IsMultiplayer())
				{
					PhotonNetwork.Destroy(carrier);
				}
				else
				{
					Object.Destroy((Object)(object)carrier);
				}
			}
			catch (Exception arg)
			{
				Plugin.ModLogger.LogWarning((object)$"Could not clean up a prepared upgrade effect carrier: {arg}");
			}
		}
	}
	internal sealed class UpgradeEffectCarrier : MonoBehaviour
	{
	}
	[HarmonyPatch(typeof(ShopManager), "ShoppingListItemAdd")]
	internal static class UpgradeEffectCarrierShopPatch
	{
		[HarmonyPrefix]
		private static bool Prefix(ItemAttributes item)
		{
			if (!((Object)(object)item == (Object)null))
			{
				return (Object)(object)((Component)item).GetComponent<UpgradeEffectCarrier>() == (Object)null;
			}
			return true;
		}
	}
	internal sealed class UpgradeHoldTracker : MonoBehaviour
	{
		internal const float RequiredHoldSeconds = 2f;

		internal const float MinimumLookUpDot = 0.8660254f;

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

		private readonly HashSet<string> _currentlyGrabbing = new HashSet<string>();

		private readonly HashSet<string> _previouslyGrabbing = new HashSet<string>();

		private readonly List<string> _stalePlayers = new List<string>();

		private readonly List<PhysGrabber> _pendingReleases = new List<PhysGrabber>();

		private PhysGrabObject _physGrabObject;

		private ItemUpgrade _itemUpgrade;

		private string _upgradeName = string.Empty;

		internal void Initialize(ItemUpgrade itemUpgrade, PhysGrabObject physGrabObject, string upgradeName)
		{
			_itemUpgrade = itemUpgrade;
			_physGrabObject = physGrabObject;
			_upgradeName = upgradeName;
		}

		private void Update()
		{
			if ((Object)(object)_physGrabObject == (Object)null || !ShopUpgradeService.IsHostInShop)
			{
				CancelTrackedPreparations();
				_holdSeconds.Clear();
				return;
			}
			_currentlyGrabbing.Clear();
			_pendingReleases.Clear();
			foreach (PhysGrabber item in _physGrabObject.playerGrabbing)
			{
				if ((Object)(object)item == (Object)null || (Object)(object)item.playerAvatar == (Object)null)
				{
					continue;
				}
				PlayerAvatar playerAvatar = item.playerAvatar;
				string text = SemiFunc.PlayerGetSteamID(playerAvatar);
				if (string.IsNullOrWhiteSpace(text) || ShopUpgradeService.HasClaimed(text) || !_currentlyGrabbing.Add(text))
				{
					continue;
				}
				if (!ShopUpgradeService.EnsurePrepared(_itemUpgrade, playerAvatar, _upgradeName))
				{
					_holdSeconds.Remove(text);
					continue;
				}
				if (!IsLookingUp(playerAvatar))
				{
					_holdSeconds.Remove(text);
					continue;
				}
				_holdSeconds.TryGetValue(text, out var value);
				value += Time.unscaledDeltaTime;
				_holdSeconds[text] = value;
				if (value >= 2f && ShopUpgradeService.TryClaim(_itemUpgrade, playerAvatar, _upgradeName))
				{
					_holdSeconds.Remove(text);
					_pendingReleases.Add(item);
				}
			}
			RemoveInterruptedHolds();
			ReleaseSuccessfulPlayers();
		}

		private static bool IsLookingUp(PlayerAvatar playerAvatar)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			PlayerLocalCamera localCamera = playerAvatar.localCamera;
			if ((Object)(object)localCamera == (Object)null)
			{
				return false;
			}
			return Vector3.Dot(((Component)localCamera).transform.forward, Vector3.up) >= 0.8660254f;
		}

		private void RemoveInterruptedHolds()
		{
			_stalePlayers.Clear();
			foreach (string key in _holdSeconds.Keys)
			{
				if (!_currentlyGrabbing.Contains(key))
				{
					_stalePlayers.Add(key);
				}
			}
			foreach (string stalePlayer in _stalePlayers)
			{
				_holdSeconds.Remove(stalePlayer);
			}
			_stalePlayers.Clear();
			foreach (string item in _previouslyGrabbing)
			{
				if (!_currentlyGrabbing.Contains(item))
				{
					_stalePlayers.Add(item);
				}
			}
			foreach (string stalePlayer2 in _stalePlayers)
			{
				ShopUpgradeService.CancelPreparation(stalePlayer2, _itemUpgrade);
			}
			_previouslyGrabbing.Clear();
			_previouslyGrabbing.UnionWith(_currentlyGrabbing);
		}

		private void ReleaseSuccessfulPlayers()
		{
			int num = (((Object)(object)_physGrabObject.photonView != (Object)null) ? _physGrabObject.photonView.ViewID : (-1));
			foreach (PhysGrabber pendingRelease in _pendingReleases)
			{
				if (!((Object)(object)pendingRelease == (Object)null))
				{
					try
					{
						pendingRelease.OverrideGrabRelease(num, 0.25f);
					}
					catch (Exception arg)
					{
						Plugin.ModLogger.LogWarning((object)$"Upgrade was granted, but the held item could not be released: {arg}");
					}
				}
			}
			_pendingReleases.Clear();
		}

		private void OnDestroy()
		{
			CancelTrackedPreparations();
			if ((Object)(object)_itemUpgrade != (Object)null)
			{
				ShopUpgradeService.CancelPreparationsForSource(_itemUpgrade);
			}
		}

		private void CancelTrackedPreparations()
		{
			if ((Object)(object)_itemUpgrade == (Object)null)
			{
				return;
			}
			foreach (string item in _previouslyGrabbing)
			{
				ShopUpgradeService.CancelPreparation(item, _itemUpgrade);
			}
			foreach (string item2 in _currentlyGrabbing)
			{
				ShopUpgradeService.CancelPreparation(item2, _itemUpgrade);
			}
			_previouslyGrabbing.Clear();
			_currentlyGrabbing.Clear();
		}
	}
}