Decompiled source of LuckyUpgradesFork v1.1.0

LuckyUpgradesFork.dll

Decompiled 5 days 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 System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
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: AssemblyCompany("mihmi125")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyDescription("Fork of R.E.P.O LuckyUpgrades")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+a1c687166c1ced6afd24bf1ab4319eb62d93e513")]
[assembly: AssemblyProduct("LuckyUpgradesFork")]
[assembly: AssemblyTitle("LuckyUpgradesFork")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace LuckyUpgrades
{
	[BepInPlugin("LuckyUpgradesFork", "LuckyUpgradesFork", "1.0.0")]
	[BepInProcess("REPO.exe")]
	public class Plugin : BaseUnityPlugin
	{
		internal static ManualLogSource Logger;

		private static int _isApplyingSharedUpgrade = 0;

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

		private static readonly object _inFlightLock = new object();

		private static readonly object _randomLock = new object();

		private static readonly Random _random = new Random();

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

		private static readonly object _cooldownLock = new object();

		private static int _currentStreak = 0;

		private static readonly object _streakLock = new object();

		private static int _sessionUpgradesReceived = 0;

		private static int _sessionRollsTotal = 0;

		private static readonly object _sessionLock = new object();

		internal static readonly object SharedUpgradesLock = new object();

		internal static readonly object ModdedUpgradeRegistryLock = new object();

		internal static readonly object MySteamIDLock = new object();

		internal static string _mySteamID = null;

		internal static readonly Dictionary<string, int> _sharedUpgrades = new Dictionary<string, int>();

		internal static readonly Dictionary<string, (Action<string, int> apply, Func<int> getChance)> _moddedUpgradeRegistry = new Dictionary<string, (Action<string, int>, Func<int>)>();

		private Harmony _harmony;

		private static bool? _repoLibTypeSearched = null;

		private static Type _repoLibItemUpgradeType = null;

		private static FieldInfo _repoLibUpgradeIdField = null;

		private static Type _repoLibUpgradesModuleType = null;

		private static MethodInfo _repoLibGetUpgradeMethod = null;

		public static Plugin Instance { get; private set; }

		public static UpgradeConfig UpgradeConfiguration { get; private set; }

		private void Awake()
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Expected O, but got Unknown
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Expected O, but got Unknown
			//IL_0294: Unknown result type (might be due to invalid IL or missing references)
			//IL_029a: Expected O, but got Unknown
			//IL_02b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bd: Expected O, but got Unknown
			Instance = this;
			Logger = ((BaseUnityPlugin)this).Logger;
			Logger.LogInfo((object)"Plugin LuckyUpgradesFork is loaded!");
			UpgradeConfiguration = new UpgradeConfig(((BaseUnityPlugin)this).Config);
			_harmony = new Harmony("LuckyUpgradesFork");
			HarmonyMethod val = new HarmonyMethod(typeof(Plugin), "ItemUpgrade_PlayUpgrade_Postfix", (Type[])null);
			bool flag = false;
			HashSet<string> hashSet = new HashSet<string>
			{
				"Start", "Awake", "Update", "FixedUpdate", "LateUpdate", "OnEnable", "OnDisable", "OnDestroy", "Reset", "ButtonToggle",
				"ButtonToggleLogic", "ButtonToggleRPC"
			};
			string[] array = new string[8] { "PlayerUpgrade", "PlayUpgrade", "Use", "Upgrade", "Activate", "OnUse", "UseUpgrade", "Apply" };
			foreach (string text in array)
			{
				MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(ItemUpgrade), text, new Type[0], (Type[])null);
				if (methodInfo != null)
				{
					_harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					Logger.LogInfo((object)("[LuckyUpgrades] Patched ItemUpgrade." + text + " ✓"));
					flag = true;
					break;
				}
			}
			if (!flag)
			{
				MethodInfo[] methods = typeof(ItemUpgrade).GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (MethodInfo methodInfo2 in methods)
				{
					if (methodInfo2.ReturnType == typeof(void) && methodInfo2.GetParameters().Length == 0 && !methodInfo2.IsAbstract && !hashSet.Contains(methodInfo2.Name))
					{
						_harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						Logger.LogWarning((object)("[LuckyUpgrades] Fallback-patched ItemUpgrade." + methodInfo2.Name + " — verify this is the upgrade trigger!"));
						flag = true;
						break;
					}
				}
			}
			if (!flag)
			{
				Logger.LogError((object)"[LuckyUpgrades] Could not find any suitable method on ItemUpgrade to patch — sharing will not work! Check the method list above.");
			}
			GameObject val2 = new GameObject("LuckyUpgrades_UpdateRunner");
			val2.AddComponent<UpgradeReapplyRunner>();
			Object.DontDestroyOnLoad((Object)(object)val2);
			((Object)val2).hideFlags = (HideFlags)61;
			GameObject val3 = new GameObject("LuckyUpgrades_ToastUI");
			val3.AddComponent<UpgradeToastUI>();
			Object.DontDestroyOnLoad((Object)(object)val3);
			((Object)val3).hideFlags = (HideFlags)61;
			PreBindREPOLibUpgrades();
			Logger.LogInfo((object)"Harmony patches applied!");
		}

		public static void RegisterModdedUpgrade(string upgradeId, Action<string, int> applyAction, int shareChance = 25)
		{
			if (string.IsNullOrEmpty(upgradeId))
			{
				ManualLogSource logger = Logger;
				if (logger != null)
				{
					logger.LogError((object)"[LuckyUpgrades] RegisterModdedUpgrade: upgradeId cannot be null or empty.");
				}
				return;
			}
			if (applyAction == null)
			{
				ManualLogSource logger2 = Logger;
				if (logger2 != null)
				{
					logger2.LogError((object)("[LuckyUpgrades] RegisterModdedUpgrade: applyAction cannot be null (upgradeId: " + upgradeId + ")."));
				}
				return;
			}
			shareChance = Math.Max(0, Math.Min(100, shareChance));
			lock (ModdedUpgradeRegistryLock)
			{
				if (_moddedUpgradeRegistry.ContainsKey(upgradeId))
				{
					ManualLogSource logger3 = Logger;
					if (logger3 != null)
					{
						logger3.LogWarning((object)("[LuckyUpgrades] Upgrade '" + upgradeId + "' already registered — overwriting."));
					}
				}
				ConfigEntry<int> configEntry = UpgradeConfiguration?.BindModdedUpgrade(upgradeId, shareChance);
				Func<int> func = ((configEntry != null) ? ((Func<int>)(() => configEntry.Value)) : ((Func<int>)(() => shareChance)));
				_moddedUpgradeRegistry[upgradeId] = (applyAction, func);
				ManualLogSource logger4 = Logger;
				if (logger4 != null)
				{
					logger4.LogInfo((object)$"[LuckyUpgrades] ✓ REGISTERED modded upgrade: '{upgradeId}' ({func()}% share chance)");
				}
			}
		}

		public static void TriggerModdedUpgradeShare(string upgradeId, string sourceSteamID, int amount = 1)
		{
			Action<string, int> applyDelegate;
			int value2;
			lock (ModdedUpgradeRegistryLock)
			{
				if (!_moddedUpgradeRegistry.TryGetValue(upgradeId, out (Action<string, int>, Func<int>) value))
				{
					ManualLogSource logger = Logger;
					if (logger != null)
					{
						logger.LogError((object)("[LuckyUpgrades] ✗ TriggerModdedUpgradeShare: '" + upgradeId + "' NOT REGISTERED! Did you call RegisterModdedUpgrade()?"));
					}
					return;
				}
				applyDelegate = value.Item1;
				value2 = value.Item2();
			}
			ManualLogSource logger2 = Logger;
			if (logger2 != null)
			{
				logger2.LogInfo((object)("[LuckyUpgrades] → TriggerModdedUpgradeShare called: '" + upgradeId + "' from " + sourceSteamID));
			}
			string playerName = GetPlayerName(sourceSteamID);
			ApplySharedUpgradeToSelf(upgradeId, sourceSteamID, playerName, amount, delegate(int amt)
			{
				string mySteamID = GetMySteamID();
				if (!string.IsNullOrEmpty(mySteamID))
				{
					ManualLogSource logger3 = Logger;
					if (logger3 != null)
					{
						logger3.LogInfo((object)$"[LuckyUpgrades] → Applying modded upgrade '{upgradeId}' to player {mySteamID} (+{amt})");
					}
					applyDelegate(mySteamID, amt);
				}
				else
				{
					ManualLogSource logger4 = Logger;
					if (logger4 != null)
					{
						logger4.LogWarning((object)("[LuckyUpgrades] ✗ Cannot apply '" + upgradeId + "': SteamID is null/empty"));
					}
				}
			}, value2);
		}

		internal static string GetMySteamID()
		{
			lock (MySteamIDLock)
			{
				if (string.IsNullOrEmpty(_mySteamID))
				{
					PlayerAvatar val = SemiFunc.PlayerAvatarLocal();
					if ((Object)(object)val != (Object)null)
					{
						_mySteamID = SemiFunc.PlayerGetSteamID(val);
						if (!string.IsNullOrEmpty(_mySteamID))
						{
							ManualLogSource logger = Logger;
							if (logger != null)
							{
								logger.LogDebug((object)("[LuckyUpgrades] Player SteamID cached: " + _mySteamID));
							}
						}
						else
						{
							ManualLogSource logger2 = Logger;
							if (logger2 != null)
							{
								logger2.LogWarning((object)"[LuckyUpgrades] Failed to get player SteamID from local player");
							}
						}
					}
					else
					{
						ManualLogSource logger3 = Logger;
						if (logger3 != null)
						{
							logger3.LogDebug((object)"[LuckyUpgrades] Local player not found yet");
						}
					}
				}
				return _mySteamID;
			}
		}

		internal static void ReapplySharedUpgrades()
		{
			Dictionary<string, int> dictionary;
			lock (SharedUpgradesLock)
			{
				if (_sharedUpgrades.Count == 0)
				{
					return;
				}
				dictionary = new Dictionary<string, int>(_sharedUpgrades);
				_sharedUpgrades.Clear();
			}
			string mySteamID = GetMySteamID();
			if (string.IsNullOrEmpty(mySteamID))
			{
				return;
			}
			Logger.LogInfo((object)$"[LuckyUpgrades] Reapplying {dictionary.Count} upgrade type(s)...");
			try
			{
				Interlocked.Exchange(ref _isApplyingSharedUpgrade, 1);
				foreach (KeyValuePair<string, int> item in dictionary)
				{
					string key = item.Key;
					int value = item.Value;
					if (value <= 0)
					{
						continue;
					}
					try
					{
						if (ReapplySingleUpgrade(mySteamID, key, value))
						{
							Logger.LogInfo((object)$"[LuckyUpgrades] Reapplied: {key} +{value}");
						}
						else
						{
							Logger.LogWarning((object)("[LuckyUpgrades] Unknown upgrade type during reapply — skipped: '" + key + "'"));
						}
					}
					catch (Exception ex)
					{
						Logger.LogError((object)("[LuckyUpgrades] Error reapplying '" + key + "': " + ex.Message));
					}
				}
			}
			finally
			{
				Interlocked.Exchange(ref _isApplyingSharedUpgrade, 0);
			}
		}

		private static bool ReapplySingleUpgrade(string myID, string upgradeType, int amount)
		{
			switch (upgradeType)
			{
			case "Health":
			{
				for (int num6 = 0; num6 < amount; num6++)
				{
					PunManager.instance.UpgradePlayerHealth(myID, 1);
				}
				return true;
			}
			case "Energy":
			{
				for (int num = 0; num < amount; num++)
				{
					PunManager.instance.UpgradePlayerEnergy(myID, 1);
				}
				return true;
			}
			case "ExtraJump":
			{
				for (int num7 = 0; num7 < amount; num7++)
				{
					PunManager.instance.UpgradePlayerExtraJump(myID, 1);
				}
				return true;
			}
			case "GrabRange":
			{
				for (int l = 0; l < amount; l++)
				{
					PunManager.instance.UpgradePlayerGrabRange(myID, 1);
				}
				return true;
			}
			case "GrabStrength":
			{
				for (int num3 = 0; num3 < amount; num3++)
				{
					PunManager.instance.UpgradePlayerGrabStrength(myID, 1);
				}
				return true;
			}
			case "GrabThrow":
			{
				for (int k = 0; k < amount; k++)
				{
					PunManager.instance.UpgradePlayerThrowStrength(myID, 1);
				}
				return true;
			}
			case "SprintSpeed":
			{
				for (int num4 = 0; num4 < amount; num4++)
				{
					PunManager.instance.UpgradePlayerSprintSpeed(myID, 1);
				}
				return true;
			}
			case "TumbleLaunch":
			{
				for (int n = 0; n < amount; n++)
				{
					PunManager.instance.UpgradePlayerTumbleLaunch(myID, 1);
				}
				return true;
			}
			case "MapPlayerCount":
			{
				for (int i = 0; i < amount; i++)
				{
					PunManager.instance.UpgradeMapPlayerCount(myID, 1);
				}
				return true;
			}
			case "TumbleClimb":
			{
				for (int num5 = 0; num5 < amount; num5++)
				{
					PunManager.instance.UpgradePlayerTumbleClimb(myID, 1);
				}
				return true;
			}
			case "TumbleWings":
			{
				for (int num2 = 0; num2 < amount; num2++)
				{
					PunManager.instance.UpgradePlayerTumbleWings(myID, 1);
				}
				return true;
			}
			case "CrouchRest":
			{
				for (int m = 0; m < amount; m++)
				{
					PunManager.instance.UpgradePlayerCrouchRest(myID, 1);
				}
				return true;
			}
			case "DeathHeadBattery":
			{
				for (int j = 0; j < amount; j++)
				{
					PunManager.instance.UpgradeDeathHeadBattery(myID, 1);
				}
				return true;
			}
			default:
				lock (ModdedUpgradeRegistryLock)
				{
					if (_moddedUpgradeRegistry.TryGetValue(upgradeType, out (Action<string, int>, Func<int>) value))
					{
						value.Item1(myID, amount);
						return true;
					}
				}
				return false;
			}
		}

		private static void TrackSharedUpgrade(string upgradeType, int amount)
		{
			lock (SharedUpgradesLock)
			{
				if (!_sharedUpgrades.TryGetValue(upgradeType, out var value))
				{
					value = 0;
				}
				_sharedUpgrades[upgradeType] = value + amount;
				Logger.LogInfo((object)$"[LuckyUpgrades] Tracked: {upgradeType} (total: {_sharedUpgrades[upgradeType]})");
			}
		}

		public static void ItemUpgrade_PlayUpgrade_Postfix(ItemUpgrade __instance)
		{
			try
			{
				if (Interlocked.CompareExchange(ref _isApplyingSharedUpgrade, 0, 0) == 1)
				{
					return;
				}
				string mySteamID = GetMySteamID();
				string upgradeType = GetUpgradeType(__instance);
				if (string.IsNullOrEmpty(upgradeType))
				{
					return;
				}
				string steamIDFromItem = GetSteamIDFromItem(__instance);
				if (string.IsNullOrEmpty(steamIDFromItem) || string.IsNullOrEmpty(mySteamID))
				{
					return;
				}
				float num = UpgradeConfiguration?.UpgradeCooldownSeconds.Value ?? 1.5f;
				if (num > 0f)
				{
					int instanceID = ((Object)((Component)__instance).gameObject).GetInstanceID();
					float realtimeSinceStartup = Time.realtimeSinceStartup;
					lock (_cooldownLock)
					{
						if (_lastTriggerTime.TryGetValue(instanceID, out var value) && realtimeSinceStartup - value < num)
						{
							Logger.LogInfo((object)$"[LuckyUpgrades] Instance cooldown active for '{upgradeType}' (id:{instanceID}) — skipping ({realtimeSinceStartup - value:F2}s < {num}s)");
							return;
						}
						_lastTriggerTime[instanceID] = realtimeSinceStartup;
					}
				}
				if (mySteamID == steamIDFromItem)
				{
					UpgradeConfig upgradeConfiguration = UpgradeConfiguration;
					if (upgradeConfiguration == null || upgradeConfiguration.ShowPickerToast.Value)
					{
						UpgradeToastUI.ShowPickerToast(upgradeType);
					}
					return;
				}
				string playerName = GetPlayerName(steamIDFromItem);
				int? chanceOverride = null;
				lock (ModdedUpgradeRegistryLock)
				{
					if (_moddedUpgradeRegistry.TryGetValue(upgradeType, out (Action<string, int>, Func<int>) value2))
					{
						chanceOverride = value2.Item2();
					}
				}
				ApplySharedUpgradeToSelf(upgradeType, steamIDFromItem, playerName, 1, delegate
				{
					string mySteamID2 = GetMySteamID();
					if (!string.IsNullOrEmpty(mySteamID2))
					{
						ApplyUpgradeByType(upgradeType, mySteamID2);
					}
				}, chanceOverride);
			}
			catch (Exception ex)
			{
				Logger.LogError((object)("[LuckyUpgrades] Error in ItemUpgrade_PlayUpgrade_Postfix: " + ex.Message + "\n" + ex.StackTrace));
			}
		}

		private static string GetPlayerName(string steamID)
		{
			if (string.IsNullOrEmpty(steamID))
			{
				return "???";
			}
			try
			{
				PlayerAvatar[] array = Object.FindObjectsOfType<PlayerAvatar>();
				foreach (PlayerAvatar val in array)
				{
					try
					{
						string text = SemiFunc.PlayerGetSteamID(val);
						if (text == steamID)
						{
							string text2 = SemiFunc.PlayerGetName(val);
							if (!string.IsNullOrEmpty(text2))
							{
								return text2;
							}
						}
					}
					catch
					{
					}
				}
			}
			catch (Exception ex)
			{
				Logger.LogWarning((object)("[LuckyUpgrades] GetPlayerName failed: " + ex.Message));
			}
			return (steamID.Length > 6) ? ("..." + steamID.Substring(steamID.Length - 6)) : steamID;
		}

		private static string GetUpgradeType(ItemUpgrade item)
		{
			GameObject gameObject = ((Component)item).gameObject;
			if ((Object)(object)gameObject.GetComponent<ItemUpgradePlayerHealth>() != (Object)null)
			{
				return "Health";
			}
			if ((Object)(object)gameObject.GetComponent<ItemUpgradePlayerEnergy>() != (Object)null)
			{
				return "Energy";
			}
			if ((Object)(object)gameObject.GetComponent<ItemUpgradePlayerExtraJump>() != (Object)null)
			{
				return "ExtraJump";
			}
			if ((Object)(object)gameObject.GetComponent<ItemUpgradePlayerGrabRange>() != (Object)null)
			{
				return "GrabRange";
			}
			if ((Object)(object)gameObject.GetComponent<ItemUpgradePlayerGrabStrength>() != (Object)null)
			{
				return "GrabStrength";
			}
			if ((Object)(object)gameObject.GetComponent<ItemUpgradePlayerGrabThrow>() != (Object)null)
			{
				return "GrabThrow";
			}
			if ((Object)(object)gameObject.GetComponent<ItemUpgradePlayerSprintSpeed>() != (Object)null)
			{
				return "SprintSpeed";
			}
			if ((Object)(object)gameObject.GetComponent<ItemUpgradePlayerTumbleLaunch>() != (Object)null)
			{
				return "TumbleLaunch";
			}
			if ((Object)(object)gameObject.GetComponent<ItemUpgradePlayerTumbleClimb>() != (Object)null)
			{
				return "TumbleClimb";
			}
			if ((Object)(object)gameObject.GetComponent<ItemUpgradePlayerTumbleWings>() != (Object)null)
			{
				return "TumbleWings";
			}
			if ((Object)(object)gameObject.GetComponent<ItemUpgradePlayerCrouchRest>() != (Object)null)
			{
				return "CrouchRest";
			}
			if ((Object)(object)gameObject.GetComponent<ItemUpgradeDeathHeadBattery>() != (Object)null)
			{
				return "DeathHeadBattery";
			}
			if ((Object)(object)gameObject.GetComponent<ItemUpgradeMapPlayerCount>() != (Object)null)
			{
				return "MapPlayerCount";
			}
			return TryGetREPOLibUpgradeId(gameObject);
		}

		private static string TryGetREPOLibUpgradeId(GameObject go)
		{
			try
			{
				if (_repoLibTypeSearched != true)
				{
					_repoLibTypeSearched = false;
					Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
					foreach (Assembly assembly in assemblies)
					{
						try
						{
							Type[] types = assembly.GetTypes();
							foreach (Type type in types)
							{
								if (type.Name == "REPOLibItemUpgrade")
								{
									_repoLibItemUpgradeType = type;
									break;
								}
							}
						}
						catch
						{
						}
						if (_repoLibItemUpgradeType != null)
						{
							break;
						}
					}
					if (_repoLibItemUpgradeType != null)
					{
						Logger.LogInfo((object)"[LuckyUpgrades] Found REPOLibItemUpgrade — resolving upgradeId field...");
						_repoLibUpgradeIdField = _repoLibItemUpgradeType.GetField("upgradeId", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? _repoLibItemUpgradeType.GetField("_upgradeId", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? _repoLibItemUpgradeType.GetField("UpgradeId", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
						_repoLibTypeSearched = true;
					}
				}
				if (_repoLibItemUpgradeType == null)
				{
					return null;
				}
				Component component = go.GetComponent(_repoLibItemUpgradeType);
				if ((Object)(object)component == (Object)null)
				{
					return null;
				}
				if (_repoLibUpgradeIdField == null)
				{
					Logger.LogWarning((object)"[LuckyUpgrades] upgradeId field not resolved on REPOLibItemUpgrade — cannot share this upgrade type.");
					return null;
				}
				string text = _repoLibUpgradeIdField.GetValue(component) as string;
				if (string.IsNullOrEmpty(text))
				{
					return null;
				}
				lock (ModdedUpgradeRegistryLock)
				{
					if (!_moddedUpgradeRegistry.ContainsKey(text))
					{
						int initialChance = UpgradeConfiguration?.DefaultModdedUpgradeChance.Value ?? 25;
						ConfigEntry<int> configEntry = UpgradeConfiguration?.BindModdedUpgrade(text, initialChance);
						Func<int> func = ((configEntry != null) ? ((Func<int>)(() => configEntry.Value)) : ((Func<int>)(() => UpgradeConfiguration?.DefaultModdedUpgradeChance.Value ?? 25)));
						string capturedId = text;
						_moddedUpgradeRegistry[capturedId] = (delegate(string steamID, int amount)
						{
							ApplyREPOLibUpgrade(capturedId, steamID, amount);
						}, func);
						Logger.LogInfo((object)$"[LuckyUpgrades] Auto-registered REPOLib upgrade: '{capturedId}' ({func()}%)");
					}
				}
				return text;
			}
			catch (Exception ex)
			{
				Logger.LogWarning((object)("[LuckyUpgrades] TryGetREPOLibUpgradeId failed: " + ex.Message));
				return null;
			}
		}

		private static void PreBindREPOLibUpgrades()
		{
			try
			{
				Type type = null;
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				foreach (Assembly assembly in assemblies)
				{
					try
					{
						type = assembly.GetType("REPOLib.Modules.Upgrades");
					}
					catch
					{
					}
					if (type != null)
					{
						break;
					}
				}
				if (type == null)
				{
					Logger.LogInfo((object)"[LuckyUpgrades] REPOLib.Modules.Upgrades not found at startup — will bind on first encounter instead.");
					return;
				}
				List<object> list = new List<object>();
				string[] array = new string[3] { "GetAll", "GetUpgrades", "GetRegistered" };
				foreach (string name in array)
				{
					MethodInfo method = type.GetMethod(name, BindingFlags.Static | BindingFlags.Public);
					if (method == null)
					{
						continue;
					}
					object obj2 = method.Invoke(null, null);
					if (obj2 is IEnumerable enumerable)
					{
						foreach (object item in enumerable)
						{
							if (item != null)
							{
								list.Add(item);
							}
						}
					}
					if (list.Count > 0)
					{
						break;
					}
				}
				if (list.Count == 0)
				{
					FieldInfo[] fields = type.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					foreach (FieldInfo fieldInfo in fields)
					{
						object value = fieldInfo.GetValue(null);
						if (!(value is IDictionary dictionary))
						{
							continue;
						}
						foreach (object value2 in dictionary.Values)
						{
							if (value2 != null)
							{
								list.Add(value2);
							}
						}
						if (list.Count > 0)
						{
							break;
						}
					}
				}
				if (list.Count == 0)
				{
					Logger.LogInfo((object)"[LuckyUpgrades] Could not enumerate REPOLib upgrades at startup — will bind on first encounter.");
					return;
				}
				int initialChance = UpgradeConfiguration?.DefaultModdedUpgradeChance.Value ?? 25;
				int num = 0;
				foreach (object item2 in list)
				{
					Type type2 = item2.GetType();
					FieldInfo fieldInfo2 = type2.GetField("_upgradeId", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? type2.GetField("upgradeId", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? type2.GetField("UpgradeId", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					PropertyInfo propertyInfo = type2.GetProperty("UpgradeId", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? type2.GetProperty("upgradeId", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					string text = (fieldInfo2?.GetValue(item2) ?? propertyInfo?.GetValue(item2)) as string;
					Logger.LogInfo((object)("[LuckyUpgrades] Pre-bind: found REPOLib upgrade id='" + (text ?? "NULL") + "'"));
					if (string.IsNullOrEmpty(text))
					{
						continue;
					}
					lock (ModdedUpgradeRegistryLock)
					{
						if (!_moddedUpgradeRegistry.ContainsKey(text))
						{
							ConfigEntry<int> configEntry = UpgradeConfiguration?.BindModdedUpgrade(text, initialChance);
							Func<int> func = ((configEntry != null) ? ((Func<int>)(() => configEntry.Value)) : ((Func<int>)(() => UpgradeConfiguration?.DefaultModdedUpgradeChance.Value ?? 25)));
							string capturedId = text;
							_moddedUpgradeRegistry[capturedId] = (delegate(string steamID, int amount)
							{
								ApplyREPOLibUpgrade(capturedId, steamID, amount);
							}, func);
							num++;
							Logger.LogInfo((object)$"[LuckyUpgrades] Pre-bound '{capturedId}' ({func()}%) to config");
						}
					}
				}
				Logger.LogInfo((object)$"[LuckyUpgrades] Pre-bound {num} REPOLib upgrade(s) to config ✓");
			}
			catch (Exception ex)
			{
				Logger.LogWarning((object)("[LuckyUpgrades] PreBindREPOLibUpgrades failed: " + ex.Message));
			}
		}

		private static void ApplyREPOLibUpgrade(string upgradeId, string steamID, int amount)
		{
			try
			{
				PlayerAvatar val = null;
				foreach (PlayerAvatar item in SemiFunc.PlayerGetAll())
				{
					if (SemiFunc.PlayerGetSteamID(item) == steamID)
					{
						val = item;
						break;
					}
				}
				if ((Object)(object)val == (Object)null)
				{
					Logger.LogWarning((object)("[LuckyUpgrades] ApplyREPOLibUpgrade: no PlayerAvatar for steamID '" + steamID + "'"));
					return;
				}
				if (_repoLibUpgradesModuleType == null)
				{
					Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
					foreach (Assembly assembly in assemblies)
					{
						try
						{
							_repoLibUpgradesModuleType = assembly.GetType("REPOLib.Modules.Upgrades");
						}
						catch
						{
						}
						if (_repoLibUpgradesModuleType != null)
						{
							break;
						}
					}
				}
				if (_repoLibUpgradesModuleType == null)
				{
					Logger.LogError((object)"[LuckyUpgrades] Cannot find REPOLib.Modules.Upgrades");
					return;
				}
				if (_repoLibGetUpgradeMethod == null)
				{
					_repoLibGetUpgradeMethod = _repoLibUpgradesModuleType.GetMethod("GetUpgrade", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(string) }, null);
				}
				if (_repoLibGetUpgradeMethod == null)
				{
					Logger.LogError((object)"[LuckyUpgrades] REPOLib.Modules.Upgrades.GetUpgrade(string) not found");
					return;
				}
				MethodInfo repoLibGetUpgradeMethod = _repoLibGetUpgradeMethod;
				object obj2 = repoLibGetUpgradeMethod.Invoke(null, new object[1] { upgradeId });
				if (obj2 == null)
				{
					Logger.LogWarning((object)("[LuckyUpgrades] REPOLib upgrade '" + upgradeId + "' not in registry"));
					return;
				}
				Type type = obj2.GetType();
				MethodInfo method = type.GetMethod("AddLevel", BindingFlags.Instance | BindingFlags.Public, null, new Type[2]
				{
					typeof(PlayerAvatar),
					typeof(int)
				}, null);
				MethodInfo method2 = type.GetMethod("Upgrade", BindingFlags.Instance | BindingFlags.Public, null, new Type[1] { typeof(PlayerAvatar) }, null);
				MethodInfo method3 = type.GetMethod("AddLevel", BindingFlags.Instance | BindingFlags.Public, null, new Type[1] { typeof(PlayerAvatar) }, null);
				if (method != null)
				{
					method.Invoke(obj2, new object[2] { val, amount });
					Logger.LogInfo((object)$"[LuckyUpgrades] Applied REPOLib upgrade '{upgradeId}' to {steamID} x{amount} via AddLevel(PlayerAvatar, int) ✓");
					return;
				}
				if (method2 != null)
				{
					for (int j = 0; j < amount; j++)
					{
						method2.Invoke(obj2, new object[1] { val });
					}
					Logger.LogInfo((object)$"[LuckyUpgrades] Applied REPOLib upgrade '{upgradeId}' to {steamID} x{amount} via Upgrade(PlayerAvatar) ✓");
					return;
				}
				if (method3 != null)
				{
					for (int k = 0; k < amount; k++)
					{
						method3.Invoke(obj2, new object[1] { val });
					}
					Logger.LogInfo((object)$"[LuckyUpgrades] Applied REPOLib upgrade '{upgradeId}' to {steamID} x{amount} via AddLevel(PlayerAvatar) ✓");
					return;
				}
				Logger.LogWarning((object)("[LuckyUpgrades] No applicable upgrade method found on PlayerUpgrade for '" + upgradeId + "'. Methods available:"));
				MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public);
				foreach (MethodInfo methodInfo in methods)
				{
					Logger.LogWarning((object)("[LuckyUpgrades]   " + methodInfo.Name + "(" + string.Join(", ", Array.ConvertAll(methodInfo.GetParameters(), (ParameterInfo p) => p.ParameterType.Name)) + ")"));
				}
			}
			catch (Exception ex)
			{
				Logger.LogError((object)("[LuckyUpgrades] ApplyREPOLibUpgrade failed: " + ex.Message + "\n" + ex.StackTrace));
			}
		}

		private static void ApplyUpgradeByType(string upgradeType, string steamID, int amount = 1)
		{
			switch (upgradeType)
			{
			case "Health":
			{
				for (int num4 = 0; num4 < amount; num4++)
				{
					PunManager.instance.UpgradePlayerHealth(steamID, 1);
				}
				return;
			}
			case "Energy":
			{
				for (int j = 0; j < amount; j++)
				{
					PunManager.instance.UpgradePlayerEnergy(steamID, 1);
				}
				return;
			}
			case "ExtraJump":
			{
				for (int n = 0; n < amount; n++)
				{
					PunManager.instance.UpgradePlayerExtraJump(steamID, 1);
				}
				return;
			}
			case "GrabRange":
			{
				for (int num6 = 0; num6 < amount; num6++)
				{
					PunManager.instance.UpgradePlayerGrabRange(steamID, 1);
				}
				return;
			}
			case "GrabStrength":
			{
				for (int num2 = 0; num2 < amount; num2++)
				{
					PunManager.instance.UpgradePlayerGrabStrength(steamID, 1);
				}
				return;
			}
			case "GrabThrow":
			{
				for (int l = 0; l < amount; l++)
				{
					PunManager.instance.UpgradePlayerThrowStrength(steamID, 1);
				}
				return;
			}
			case "SprintSpeed":
			{
				for (int num7 = 0; num7 < amount; num7++)
				{
					PunManager.instance.UpgradePlayerSprintSpeed(steamID, 1);
				}
				return;
			}
			case "TumbleLaunch":
			{
				for (int num5 = 0; num5 < amount; num5++)
				{
					PunManager.instance.UpgradePlayerTumbleLaunch(steamID, 1);
				}
				return;
			}
			case "MapPlayerCount":
			{
				for (int num3 = 0; num3 < amount; num3++)
				{
					PunManager.instance.UpgradeMapPlayerCount(steamID, 1);
				}
				return;
			}
			case "TumbleClimb":
			{
				for (int num = 0; num < amount; num++)
				{
					PunManager.instance.UpgradePlayerTumbleClimb(steamID, 1);
				}
				return;
			}
			case "TumbleWings":
			{
				for (int m = 0; m < amount; m++)
				{
					PunManager.instance.UpgradePlayerTumbleWings(steamID, 1);
				}
				return;
			}
			case "CrouchRest":
			{
				for (int k = 0; k < amount; k++)
				{
					PunManager.instance.UpgradePlayerCrouchRest(steamID, 1);
				}
				return;
			}
			case "DeathHeadBattery":
			{
				for (int i = 0; i < amount; i++)
				{
					PunManager.instance.UpgradeDeathHeadBattery(steamID, 1);
				}
				return;
			}
			}
			lock (ModdedUpgradeRegistryLock)
			{
				if (_moddedUpgradeRegistry.TryGetValue(upgradeType, out (Action<string, int>, Func<int>) value))
				{
					value.Item1(steamID, amount);
				}
				else
				{
					Logger.LogWarning((object)("[LuckyUpgrades] ApplyUpgradeByType: no handler for '" + upgradeType + "'"));
				}
			}
		}

		private static string GetSteamIDFromItem(ItemUpgrade item)
		{
			if ((Object)(object)item == (Object)null)
			{
				return null;
			}
			try
			{
				FieldInfo field = ((object)item).GetType().GetField("playerAvatar", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					object? value = field.GetValue(item);
					PlayerAvatar val = (PlayerAvatar)((value is PlayerAvatar) ? value : null);
					if ((Object)(object)val != (Object)null)
					{
						return SemiFunc.PlayerGetSteamID(val);
					}
				}
				PhysGrabObject component = ((Component)item).GetComponent<PhysGrabObject>();
				if ((Object)(object)component != (Object)null)
				{
					List<PlayerAvatar> list = SemiFunc.PhysGrabObjectGetPlayerAvatarsGrabbing(component);
					if (list != null && list.Count > 0)
					{
						return SemiFunc.PlayerGetSteamID(list[0]);
					}
				}
			}
			catch (Exception ex)
			{
				Logger.LogWarning((object)("[LuckyUpgrades] GetSteamIDFromItem failed: " + ex.Message));
			}
			return null;
		}

		private static void ApplySharedUpgradeToSelf(string upgradeType, string sourceSteamID, string sourcePlayerName, int amount, Action<int> applyToSelf, int? chanceOverride = null)
		{
			try
			{
				int num = chanceOverride ?? UpgradeConfiguration.GetShareChance(upgradeType);
				if (num <= 0)
				{
					Logger.LogInfo((object)("[LuckyUpgrades] Shared upgrade skipped: " + upgradeType + " (0% chance) ✗"));
					return;
				}
				if (num >= 100)
				{
					bool flag = false;
					try
					{
						lock (_inFlightLock)
						{
							if (_inFlightUpgrades.Contains(upgradeType))
							{
								Logger.LogWarning((object)("[LuckyUpgrades] Duplicate apply blocked for '" + upgradeType + "' (in-flight guard) ✗"));
								return;
							}
							_inFlightUpgrades.Add(upgradeType);
						}
						Interlocked.Exchange(ref _isApplyingSharedUpgrade, 1);
						applyToSelf(amount);
						flag = true;
					}
					finally
					{
						Interlocked.Exchange(ref _isApplyingSharedUpgrade, 0);
						lock (_inFlightLock)
						{
							_inFlightUpgrades.Remove(upgradeType);
						}
					}
					if (flag)
					{
						TrackSharedUpgrade(upgradeType, amount);
						UpdateStreakAndSession(won: true);
						Logger.LogInfo((object)$"[LuckyUpgrades] Shared upgrade applied: {upgradeType} +{amount} (100% guaranteed) ✓");
						int streak = GetStreak();
						UpgradeToastUI.ShowToast(upgradeType, won: true, sourcePlayerName, streak);
					}
					return;
				}
				int num2;
				lock (_randomLock)
				{
					num2 = _random.Next(100);
				}
				if (num2 < num)
				{
					bool flag2 = false;
					try
					{
						lock (_inFlightLock)
						{
							if (_inFlightUpgrades.Contains(upgradeType))
							{
								Logger.LogWarning((object)("[LuckyUpgrades] Duplicate apply blocked for '" + upgradeType + "' (in-flight guard) ✗"));
								return;
							}
							_inFlightUpgrades.Add(upgradeType);
						}
						Interlocked.Exchange(ref _isApplyingSharedUpgrade, 1);
						applyToSelf(amount);
						flag2 = true;
					}
					finally
					{
						Interlocked.Exchange(ref _isApplyingSharedUpgrade, 0);
						lock (_inFlightLock)
						{
							_inFlightUpgrades.Remove(upgradeType);
						}
					}
					if (flag2)
					{
						TrackSharedUpgrade(upgradeType, amount);
						UpdateStreakAndSession(won: true);
						Logger.LogInfo((object)$"[LuckyUpgrades] Shared upgrade applied: {upgradeType} +{amount} (rolled: {num2} | chance: {num}%) ✓");
						int streak2 = GetStreak();
						UpgradeToastUI.ShowToast(upgradeType, won: true, sourcePlayerName, streak2);
					}
				}
				else
				{
					UpdateStreakAndSession(won: false);
					Logger.LogInfo((object)$"[LuckyUpgrades] Shared upgrade missed: {upgradeType} (rolled: {num2} | chance: {num}%) ✗");
					int streak3 = GetStreak();
					UpgradeToastUI.ShowToast(upgradeType, won: false, sourcePlayerName, streak3);
				}
			}
			catch (Exception ex)
			{
				Logger.LogError((object)("[LuckyUpgrades] Error in ApplySharedUpgradeToSelf: " + ex.Message + "\n" + ex.StackTrace));
			}
		}

		private static void UpdateStreakAndSession(bool won)
		{
			lock (_streakLock)
			{
				if (won)
				{
					_currentStreak = ((_currentStreak < 0) ? 1 : (_currentStreak + 1));
				}
				else
				{
					_currentStreak = ((_currentStreak <= 0) ? (_currentStreak - 1) : (-1));
				}
			}
			lock (_sessionLock)
			{
				_sessionRollsTotal++;
				if (won)
				{
					_sessionUpgradesReceived++;
				}
			}
		}

		private static int GetStreak()
		{
			lock (_streakLock)
			{
				return _currentStreak;
			}
		}

		internal static void ResetSessionStats()
		{
			lock (_streakLock)
			{
				_currentStreak = 0;
			}
			lock (_sessionLock)
			{
				_sessionUpgradesReceived = 0;
				_sessionRollsTotal = 0;
			}
			lock (_cooldownLock)
			{
				_lastTriggerTime.Clear();
			}
		}

		internal static (int received, int total) GetSessionStats()
		{
			lock (_sessionLock)
			{
				return (received: _sessionUpgradesReceived, total: _sessionRollsTotal);
			}
		}
	}
	public class UpgradeReapplyRunner : MonoBehaviour
	{
		private static readonly HashSet<string> SESSION_END_LEVELS = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "Level - Main Menu", "Level - Lobby Menu", "Level - MainMenu", "Level - LobbyMenu", "Main Menu", "Lobby", "Lobby Menu" };

		private string _lastLevelName = "";

		private float _reapplyDelay = 0f;

		private bool _pendingReapply = false;

		private const float REAPPLY_DELAY_SECONDS = 3f;

		private static bool IsSessionEndLevel(string levelName)
		{
			if (SESSION_END_LEVELS.Contains(levelName))
			{
				return true;
			}
			return levelName.IndexOf("menu", StringComparison.OrdinalIgnoreCase) >= 0 || levelName.IndexOf("lobby", StringComparison.OrdinalIgnoreCase) >= 0;
		}

		private void Update()
		{
			string text = "";
			try
			{
				if ((Object)(object)RunManager.instance != (Object)null && (Object)(object)RunManager.instance.levelCurrent != (Object)null)
				{
					text = ((Object)RunManager.instance.levelCurrent).name;
				}
			}
			catch
			{
			}
			if (!string.IsNullOrEmpty(text) && text != _lastLevelName)
			{
				Plugin.Logger.LogInfo((object)("[LuckyUpgrades] Level changed: " + _lastLevelName + " -> " + text));
				_lastLevelName = text;
				if (IsSessionEndLevel(text))
				{
					var (received, num) = Plugin.GetSessionStats();
					if (num > 0)
					{
						UpgradeConfig upgradeConfiguration = Plugin.UpgradeConfiguration;
						if (upgradeConfiguration == null || upgradeConfiguration.ShowSessionSummary.Value)
						{
							UpgradeToastUI.ShowSessionSummary(received, num);
						}
					}
					lock (Plugin.SharedUpgradesLock)
					{
						Plugin._sharedUpgrades.Clear();
					}
					lock (Plugin.MySteamIDLock)
					{
						Plugin._mySteamID = null;
					}
					Plugin.ResetSessionStats();
					Plugin.Logger.LogInfo((object)"[LuckyUpgrades] Session ended. All tracked data cleared.");
					return;
				}
				lock (Plugin.SharedUpgradesLock)
				{
					if (!PhotonNetwork.IsMasterClient && Plugin._sharedUpgrades.Count > 0)
					{
						_pendingReapply = true;
						_reapplyDelay = 3f;
						Plugin.Logger.LogInfo((object)$"[LuckyUpgrades] Scheduled reapply in {3f}s...");
					}
					else if (PhotonNetwork.IsMasterClient && Plugin._sharedUpgrades.Count > 0)
					{
						Plugin.Logger.LogInfo((object)"[LuckyUpgrades] Host detected — skipping reapply (upgrades persist natively).");
					}
				}
			}
			if (!_pendingReapply)
			{
				return;
			}
			_reapplyDelay -= Time.deltaTime;
			if (!(_reapplyDelay <= 0f))
			{
				return;
			}
			PlayerAvatar val = SemiFunc.PlayerAvatarLocal();
			if ((Object)(object)val == (Object)null)
			{
				_reapplyDelay = 0.5f;
				return;
			}
			string mySteamID = Plugin.GetMySteamID();
			if (string.IsNullOrEmpty(mySteamID))
			{
				_reapplyDelay = 0.5f;
				return;
			}
			_pendingReapply = false;
			Plugin.ReapplySharedUpgrades();
		}
	}
	public class UpgradeToastUI : MonoBehaviour
	{
		private enum ToastKind
		{
			ShareResult,
			Picker,
			SessionSummary
		}

		private struct ToastData
		{
			public string upgradeType;

			public bool won;

			public string sourcePlayerName;

			public int streak;

			public ToastKind toastKind;

			public int sessionReceived;

			public int sessionTotal;
		}

		private class ActiveToast
		{
			public string upgradeType;

			public bool won;

			public string sourcePlayerName;

			public int streak;

			public ToastKind toastKind;

			public int sessionReceived;

			public int sessionTotal;

			public float holdDuration;

			public float elapsed;
		}

		private const float W = 300f;

		private const float H = 115f;

		private const float HEADER_H = 22f;

		private const float ACCENT_W = 5f;

		private const float ICON_SIZE = 32f;

		private const float ICON_MARGIN = 10f;

		private const float BAR_H = 3f;

		private const float MARGIN_RIGHT = 20f;

		private const float MARGIN_BOTTOM = 80f;

		private const float STACK_GAP = 6f;

		private const float SLIDE_DUR = 0.2f;

		private const float FADE_DUR = 0.45f;

		private static readonly Color WIN_ACCENT = new Color(1f, 0.82f, 0f, 1f);

		private static readonly Color LOSE_ACCENT = new Color(0.9f, 0.18f, 0.18f, 1f);

		private static readonly Color BG_DARK = new Color(0.055f, 0.059f, 0.071f, 0.97f);

		private static readonly Color HEADER_BG = new Color(1f, 1f, 1f, 0.03f);

		private static readonly Color TEAL_HEADER = new Color(0.2f, 0.78f, 0.68f, 1f);

		private static readonly Color TEXT_MAIN = new Color(0.96f, 0.96f, 0.92f, 1f);

		private static readonly Color TEXT_DIM = new Color(1f, 1f, 1f, 0.3f);

		private static readonly Color BORDER_DIM = new Color(1f, 1f, 1f, 0.08f);

		private static readonly Color WIN_TEXT = new Color(1f, 0.9f, 0.2f, 1f);

		private static readonly Color LOSE_TEXT = new Color(1f, 0.38f, 0.38f, 1f);

		private static readonly Dictionary<string, string> Labels = new Dictionary<string, string>
		{
			{ "Health", "HEALTH" },
			{ "Energy", "ENERGY" },
			{ "SprintSpeed", "SPRINT SPEED" },
			{ "ExtraJump", "EXTRA JUMP" },
			{ "TumbleLaunch", "TUMBLE LAUNCH" },
			{ "TumbleClimb", "TUMBLE CLIMB" },
			{ "TumbleWings", "TUMBLE WINGS" },
			{ "CrouchRest", "CROUCH REST" },
			{ "GrabRange", "GRAB RANGE" },
			{ "GrabStrength", "GRAB STRENGTH" },
			{ "GrabThrow", "GRAB THROW" },
			{ "MapPlayerCount", "MAP PLAYER COUNT" },
			{ "DeathHeadBattery", "DEATH HEAD BATT." }
		};

		private static UpgradeToastUI _instance;

		private static readonly Queue<ToastData> _queue = new Queue<ToastData>();

		private readonly List<ActiveToast> _active = new List<ActiveToast>();

		private Texture2D _texWhite;

		private Texture2D _texScanline;

		private GUIStyle _stHeader;

		private GUIStyle _stBadge;

		private GUIStyle _stIcon;

		private GUIStyle _stName;

		private GUIStyle _stResult;

		private GUIStyle _stFoot;

		private bool _assetsReady;

		public static void ShowToast(string upgradeType, bool won, string sourcePlayerName = "", int streak = 0)
		{
			if ((Object)(object)_instance == (Object)null)
			{
				return;
			}
			UpgradeConfig upgradeConfiguration = Plugin.UpgradeConfiguration;
			if (upgradeConfiguration != null && !upgradeConfiguration.ShowUpgradeNotifications.Value)
			{
				return;
			}
			lock (_queue)
			{
				_queue.Enqueue(new ToastData
				{
					upgradeType = upgradeType,
					won = won,
					sourcePlayerName = sourcePlayerName,
					streak = streak,
					toastKind = ToastKind.ShareResult
				});
			}
		}

		public static void ShowPickerToast(string upgradeType)
		{
			if ((Object)(object)_instance == (Object)null)
			{
				return;
			}
			UpgradeConfig upgradeConfiguration = Plugin.UpgradeConfiguration;
			if (upgradeConfiguration != null && !upgradeConfiguration.ShowUpgradeNotifications.Value)
			{
				return;
			}
			lock (_queue)
			{
				_queue.Enqueue(new ToastData
				{
					upgradeType = upgradeType,
					won = true,
					toastKind = ToastKind.Picker
				});
			}
		}

		public static void ShowSessionSummary(int received, int total)
		{
			if ((Object)(object)_instance == (Object)null)
			{
				return;
			}
			UpgradeConfig upgradeConfiguration = Plugin.UpgradeConfiguration;
			if (upgradeConfiguration != null && !upgradeConfiguration.ShowUpgradeNotifications.Value)
			{
				return;
			}
			lock (_queue)
			{
				_queue.Enqueue(new ToastData
				{
					toastKind = ToastKind.SessionSummary,
					sessionReceived = received,
					sessionTotal = total,
					won = (received > 0)
				});
			}
		}

		private void Awake()
		{
			_instance = this;
		}

		private void OnDestroy()
		{
			if ((Object)(object)_instance == (Object)(object)this)
			{
				_instance = null;
			}
		}

		private void Update()
		{
			lock (_queue)
			{
				while (_queue.Count > 0)
				{
					ToastData toastData = _queue.Dequeue();
					float holdDuration = ((toastData.toastKind == ToastKind.SessionSummary) ? ((Plugin.UpgradeConfiguration?.NotificationDisplayTime.Value ?? 3f) + 1.5f) : (Plugin.UpgradeConfiguration?.NotificationDisplayTime.Value ?? 3f));
					_active.Add(new ActiveToast
					{
						upgradeType = toastData.upgradeType,
						won = toastData.won,
						sourcePlayerName = (toastData.sourcePlayerName ?? ""),
						streak = toastData.streak,
						toastKind = toastData.toastKind,
						sessionReceived = toastData.sessionReceived,
						sessionTotal = toastData.sessionTotal,
						holdDuration = holdDuration,
						elapsed = 0f
					});
				}
			}
			float unscaledDeltaTime = Time.unscaledDeltaTime;
			for (int num = _active.Count - 1; num >= 0; num--)
			{
				_active[num].elapsed += unscaledDeltaTime;
				if (_active[num].elapsed >= _active[num].holdDuration + 0.45f)
				{
					_active.RemoveAt(num);
				}
			}
		}

		private void OnGUI()
		{
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			if (_active.Count == 0)
			{
				return;
			}
			EnsureAssets();
			float num = Screen.width;
			float num2 = Screen.height;
			for (int i = 0; i < _active.Count; i++)
			{
				ActiveToast activeToast = _active[i];
				float num3 = num - 20f - 300f;
				float num4 = num2 - 80f - 115f - (float)i * 121f;
				float num5 = Mathf.Clamp01(activeToast.elapsed / 0.2f);
				float num6 = 1f - Mathf.Pow(1f - num5, 3f);
				float num7 = Mathf.Lerp(320f, 0f, num6);
				float x = num3 + num7;
				float y = num4;
				float a = 1f;
				if (activeToast.elapsed > activeToast.holdDuration)
				{
					a = 1f - Mathf.Clamp01((activeToast.elapsed - activeToast.holdDuration) / 0.45f);
				}
				DrawToast(x, y, activeToast, a);
			}
			GUI.color = Color.white;
		}

		private void DrawToast(float x, float y, ActiveToast t, float a)
		{
			switch (t.toastKind)
			{
			case ToastKind.Picker:
				DrawPickerToast(x, y, t, a);
				break;
			case ToastKind.SessionSummary:
				DrawSummaryToast(x, y, t, a);
				break;
			default:
				DrawShareResultToast(x, y, t, a);
				break;
			}
		}

		private void DrawShareResultToast(float x, float y, ActiveToast t, float a)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: 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_0051: 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_0070: 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_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_0196: Unknown result type (might be due to invalid IL or missing references)
			//IL_019d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_021c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0223: Unknown result type (might be due to invalid IL or missing references)
			//IL_023e: Unknown result type (might be due to invalid IL or missing references)
			//IL_037e: Unknown result type (might be due to invalid IL or missing references)
			//IL_038b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0313: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0318: Unknown result type (might be due to invalid IL or missing references)
			//IL_0325: Unknown result type (might be due to invalid IL or missing references)
			//IL_035b: Unknown result type (might be due to invalid IL or missing references)
			Color accent = (t.won ? WIN_ACCENT : LOSE_ACCENT);
			DrawPanel(x, y, 300f, 115f, accent, a);
			DrawBadge(x, y, t.won ? "WIN" : "MISS", accent, a);
			_stHeader.normal.textColor = Aa(TEAL_HEADER);
			GUI.Label(new Rect(x + 5f + 7f, y + 1f, 247f, 22f), "LUCKY UPGRADES", _stHeader);
			float num = x + 5f + 10f;
			float num2 = y + 22f + 8f;
			DrawIconBox(num, num2, t.won ? "+" : "x", accent, a);
			float num3 = num + 32f + 8f;
			float num4 = x + 300f - 8f - num3;
			string value;
			string text = (Labels.TryGetValue(t.upgradeType, out value) ? value : t.upgradeType.ToUpperInvariant());
			_stName.normal.textColor = Aa(TEXT_MAIN);
			GUI.Label(new Rect(num3, num2 - 1f, num4, 22f), text, _stName);
			_stResult.normal.textColor = (t.won ? Aa(WIN_TEXT) : Aa(LOSE_TEXT));
			GUI.Label(new Rect(num3, num2 + 18f, num4, 16f), t.won ? "+ YOU GOT IT!" : "- NOT THIS TIME", _stResult);
			bool flag = Plugin.UpgradeConfiguration?.ShowSourcePlayerName.Value ?? true;
			if (flag && !string.IsNullOrEmpty(t.sourcePlayerName))
			{
				_stFoot.normal.textColor = Aa(TEXT_DIM);
				GUI.Label(new Rect(num3, num2 + 36f, num4, 16f), "from " + t.sourcePlayerName, _stFoot);
			}
			bool flag2 = Plugin.UpgradeConfiguration?.ShowStreakCounter.Value ?? true;
			int num5 = Math.Abs(t.streak);
			if (flag2 && num5 >= 2)
			{
				string text2 = ((t.streak > 0) ? $"{num5} wins in a row!" : $"{num5} misses in a row");
				Color textColor = ((t.streak > 0) ? new Color(1f, 0.95f, 0.3f, 0.9f * a) : new Color(0.9f, 0.4f, 0.4f, 0.8f * a));
				_stFoot.normal.textColor = textColor;
				float num6 = ((flag && !string.IsNullOrEmpty(t.sourcePlayerName)) ? (num2 + 52f) : (num2 + 36f));
				GUI.Label(new Rect(num3, num6, num4, 16f), text2, _stFoot);
			}
			DrawProgressBar(x, y, t.elapsed, t.holdDuration, accent, a);
			GUI.color = Color.white;
			Color Aa(Color c)
			{
				//IL_0000: 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_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0012: 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)
				return new Color(c.r, c.g, c.b, c.a * a);
			}
		}

		private void DrawPickerToast(float x, float y, ActiveToast t, float a)
		{
			//IL_0033: 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_0060: 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_0090: 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_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01db: Unknown result type (might be due to invalid IL or missing references)
			//IL_0200: Unknown result type (might be due to invalid IL or missing references)
			//IL_020d: Unknown result type (might be due to invalid IL or missing references)
			Color accent = default(Color);
			((Color)(ref accent))..ctor(0.2f, 0.78f, 0.68f, 1f);
			DrawPanel(x, y, 300f, 115f, accent, a);
			DrawBadge(x, y, "YOU", accent, a);
			_stHeader.normal.textColor = Aa(TEAL_HEADER);
			GUI.Label(new Rect(x + 5f + 7f, y + 1f, 247f, 22f), "LUCKY UPGRADES", _stHeader);
			float num = x + 5f + 10f;
			float num2 = y + 22f + 8f;
			DrawIconBox(num, num2, ">", accent, a);
			float num3 = num + 32f + 8f;
			float num4 = x + 300f - 8f - num3;
			string value;
			string text = (Labels.TryGetValue(t.upgradeType, out value) ? value : t.upgradeType.ToUpperInvariant());
			_stName.normal.textColor = Aa(TEXT_MAIN);
			GUI.Label(new Rect(num3, num2 - 1f, num4, 22f), text, _stName);
			_stResult.normal.textColor = new Color(0.2f, 0.9f, 0.78f, a);
			GUI.Label(new Rect(num3, num2 + 18f, num4, 16f), "~ SHARING THIS UPGRADE", _stResult);
			_stFoot.normal.textColor = Aa(TEXT_DIM);
			GUI.Label(new Rect(num3, num2 + 36f, num4, 16f), "teammates are rolling now...", _stFoot);
			DrawProgressBar(x, y, t.elapsed, t.holdDuration, accent, a);
			GUI.color = Color.white;
			Color Aa(Color c)
			{
				//IL_0000: 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_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0012: 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)
				return new Color(c.r, c.g, c.b, c.a * a);
			}
		}

		private void DrawSummaryToast(float x, float y, ActiveToast t, float a)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: 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_005b: 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_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: 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_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01db: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0227: Unknown result type (might be due to invalid IL or missing references)
			//IL_0234: Unknown result type (might be due to invalid IL or missing references)
			Color accent = ((t.sessionReceived > 0) ? WIN_ACCENT : LOSE_ACCENT);
			DrawPanel(x, y, 300f, 115f, accent, a);
			DrawBadge(x, y, "RUN", accent, a);
			_stHeader.normal.textColor = Aa(TEAL_HEADER);
			GUI.Label(new Rect(x + 5f + 7f, y + 1f, 247f, 22f), "LUCKY UPGRADES", _stHeader);
			float num = x + 5f + 10f;
			float num2 = y + 22f + 8f;
			DrawIconBox(num, num2, "#", accent, a);
			float num3 = num + 32f + 8f;
			float num4 = x + 300f - 8f - num3;
			_stName.normal.textColor = Aa(TEXT_MAIN);
			GUI.Label(new Rect(num3, num2 - 1f, num4, 22f), "RUN COMPLETE", _stName);
			int num5 = ((t.sessionTotal > 0) ? (t.sessionReceived * 100 / t.sessionTotal) : 0);
			_stResult.normal.textColor = ((t.sessionReceived > 0) ? Aa(WIN_TEXT) : Aa(LOSE_TEXT));
			GUI.Label(new Rect(num3, num2 + 18f, num4, 16f), $"{t.sessionReceived} / {t.sessionTotal} upgrades", _stResult);
			_stFoot.normal.textColor = Aa(TEXT_DIM);
			GUI.Label(new Rect(num3, num2 + 36f, num4, 16f), $"{num5}% share rate this run", _stFoot);
			DrawProgressBar(x, y, t.elapsed, t.holdDuration, accent, a);
			GUI.color = Color.white;
			Color Aa(Color c)
			{
				//IL_0000: 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_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0012: 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)
				return new Color(c.r, c.g, c.b, c.a * a);
			}
		}

		private void DrawPanel(float x, float y, float w, float h, Color accent, float a)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: 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_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: 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_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: 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_00cf: 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_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			Tint(Aa(BG_DARK));
			Draw(x, y, w, h);
			Tint(Aa(accent));
			Draw(x, y, 5f, h);
			Tint(Aa(HEADER_BG));
			Draw(x + 5f, y, w - 5f, 22f);
			Tint(new Color(accent.r, accent.g, accent.b, 0.55f * a));
			Draw(x + 5f, y, w - 5f, 1f);
			Tint(new Color(accent.r, accent.g, accent.b, 0.22f * a));
			Draw(x + 5f, y + 22f, w - 5f, 1f);
			Tint(Aa(BORDER_DIM));
			Draw(x + 5f, y + h - 1f, w - 5f, 1f);
			Draw(x + w - 1f, y, 1f, h);
			Tint(Color.white);
			GUI.DrawTexture(new Rect(x, y, w, h), (Texture)(object)_texScanline, (ScaleMode)1);
			Color Aa(Color c)
			{
				//IL_0000: 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_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0012: 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)
				return new Color(c.r, c.g, c.b, c.a * a);
			}
		}

		private void DrawBadge(float x, float y, string text, Color accent, float a)
		{
			//IL_0018: 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_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: 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_0072: 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_0097: 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_00a5: 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_00c7: Unknown result type (might be due to invalid IL or missing references)
			float num = 36f;
			float num2 = x + 300f - num - 6f;
			Tint(new Color(accent.r, accent.g, accent.b, 0.22f * a));
			Draw(num2, y + 4f, num, 14f);
			DrawBorder(num2, y + 4f, num, 14f, new Color(accent.r, accent.g, accent.b, 0.55f * a));
			_stBadge.normal.textColor = new Color(accent.r, accent.g, accent.b, a);
			GUI.Label(new Rect(num2, y + 4f, num, 14f), text, _stBadge);
		}

		private void DrawIconBox(float iconX, float iconY, string glyph, Color accent, float a)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: 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_001f: 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_0051: 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_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: 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_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			Tint(new Color(accent.r, accent.g, accent.b, 0.15f * a));
			Draw(iconX, iconY, 32f, 32f);
			DrawBorder(iconX, iconY, 32f, 32f, new Color(accent.r, accent.g, accent.b, 0.55f * a));
			_stIcon.normal.textColor = new Color(accent.r, accent.g, accent.b, a);
			GUI.Label(new Rect(iconX, iconY, 32f, 32f), glyph, _stIcon);
		}

		private void DrawProgressBar(float x, float y, float elapsed, float hold, Color accent, float a)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: 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_0074: 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)
			float x2 = x + 5f + 10f;
			float y2 = y + 115f - 16f;
			float num = 277f;
			float num2 = Mathf.Clamp01(1f - elapsed / hold);
			Tint(new Color(1f, 1f, 1f, 0.07f * a));
			Draw(x2, y2, num, 3f);
			Tint(new Color(accent.r, accent.g, accent.b, 0.55f * a));
			Draw(x2, y2, num * num2, 3f);
		}

		private void Tint(Color c)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			GUI.color = c;
		}

		private void Draw(float x, float y, float w, float h)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			GUI.DrawTexture(new Rect(x, y, w, h), (Texture)(object)_texWhite);
		}

		private void DrawBorder(float x, float y, float w, float h, Color c)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			Tint(c);
			Draw(x, y, w, 1f);
			Draw(x, y + h - 1f, w, 1f);
			Draw(x, y, 1f, h);
			Draw(x + w - 1f, y, 1f, h);
		}

		private void EnsureAssets()
		{
			//IL_0018: 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_0031: 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_0046: Expected O, but got Unknown
			//IL_004e: 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_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: 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_00ea: Expected O, but got Unknown
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: 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_0117: Expected O, but got Unknown
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Expected O, but got Unknown
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Expected O, but got Unknown
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Expected O, but got Unknown
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01be: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Expected O, but got Unknown
			if (!_assetsReady)
			{
				_assetsReady = true;
				_texWhite = MakeSolid(Color.white);
				_texScanline = new Texture2D(1, 4, (TextureFormat)4, false)
				{
					filterMode = (FilterMode)0,
					wrapMode = (TextureWrapMode)0
				};
				_texScanline.SetPixel(0, 0, Color.clear);
				_texScanline.SetPixel(0, 1, Color.clear);
				_texScanline.SetPixel(0, 2, Color.clear);
				_texScanline.SetPixel(0, 3, new Color(0f, 0f, 0f, 0.08f));
				_texScanline.Apply();
				GUIStyle label = GUI.skin.label;
				_stHeader = new GUIStyle(label)
				{
					fontSize = 10,
					fontStyle = (FontStyle)1,
					alignment = (TextAnchor)3,
					wordWrap = false
				};
				_stBadge = new GUIStyle(label)
				{
					fontSize = 9,
					fontStyle = (FontStyle)1,
					alignment = (TextAnchor)4,
					wordWrap = false
				};
				_stIcon = new GUIStyle(label)
				{
					fontSize = 20,
					fontStyle = (FontStyle)1,
					alignment = (TextAnchor)4,
					wordWrap = false
				};
				_stName = new GUIStyle(label)
				{
					fontSize = 14,
					fontStyle = (FontStyle)1,
					alignment = (TextAnchor)0,
					wordWrap = false
				};
				_stResult = new GUIStyle(label)
				{
					fontSize = 11,
					fontStyle = (FontStyle)1,
					alignment = (TextAnchor)0,
					wordWrap = false
				};
				_stFoot = new GUIStyle(label)
				{
					fontSize = 10,
					fontStyle = (FontStyle)1,
					alignment = (TextAnchor)0,
					wordWrap = false
				};
			}
		}

		private static Texture2D MakeSolid(Color col)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = new Texture2D(1, 1, (TextureFormat)4, false)
			{
				filterMode = (FilterMode)0,
				wrapMode = (TextureWrapMode)1
			};
			val.SetPixel(0, 0, col);
			val.Apply();
			return val;
		}
	}
	public class UpgradeConfig
	{
		private readonly ConfigFile _config;

		public ConfigEntry<int> ChanceToActivatePlayerHealth { get; private set; }

		public ConfigEntry<int> ChanceToActivatePlayerEnergy { get; private set; }

		public ConfigEntry<int> ChanceToActivatePlayerSprintSpeed { get; private set; }

		public ConfigEntry<int> ChanceToActivatePlayerExtraJump { get; private set; }

		public ConfigEntry<int> ChanceToActivatePlayerTumbleLaunch { get; private set; }

		public ConfigEntry<int> ChanceToActivatePlayerGrabRange { get; private set; }

		public ConfigEntry<int> ChanceToActivatePlayerGrabStrength { get; private set; }

		public ConfigEntry<int> ChanceToActivatePlayerGrabThrow { get; private set; }

		public ConfigEntry<int> ChanceToActivatePlayerTumbleClimb { get; private set; }

		public ConfigEntry<int> ChanceToActivatePlayerTumbleWings { get; private set; }

		public ConfigEntry<int> ChanceToActivatePlayerCrouchRest { get; private set; }

		public ConfigEntry<int> ChanceToActivateDeathHeadBattery { get; private set; }

		public ConfigEntry<int> ChanceToActivateMapPlayerCount { get; private set; }

		public ConfigEntry<int> DefaultModdedUpgradeChance { get; private set; }

		public ConfigEntry<bool> ShowUpgradeNotifications { get; private set; }

		public ConfigEntry<float> NotificationDisplayTime { get; private set; }

		public ConfigEntry<bool> ShowSourcePlayerName { get; private set; }

		public ConfigEntry<bool> ShowPickerToast { get; private set; }

		public ConfigEntry<bool> ShowStreakCounter { get; private set; }

		public ConfigEntry<bool> ShowSessionSummary { get; private set; }

		public ConfigEntry<float> UpgradeCooldownSeconds { get; private set; }

		public ConfigEntry<bool> IndependentRolls { get; private set; }

		public UpgradeConfig(ConfigFile config)
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Expected O, but got Unknown
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Expected O, but got Unknown
			//IL_0296: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a0: Expected O, but got Unknown
			_config = config;
			ShowUpgradeNotifications = config.Bind<bool>("Notifications", "ShowUpgradeNotifications", true, "Show an in-game toast notification when an upgrade is shared or missed. Set false to disable.");
			NotificationDisplayTime = config.Bind<float>("Notifications", "NotificationDisplayTime", 3f, new ConfigDescription("How long (in seconds) each upgrade notification stays on screen before fading out. Does not include the 0.45s fade animation.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 15f), Array.Empty<object>()));
			ShowSourcePlayerName = config.Bind<bool>("Notifications", "ShowSourcePlayerName", true, "Show the name of the player whose pickup triggered the share roll on the toast notification.");
			ShowPickerToast = config.Bind<bool>("Notifications", "ShowPickerToast", true, "Show a toast to the player who picked up the upgrade, telling them the share roll is happening.");
			ShowStreakCounter = config.Bind<bool>("Notifications", "ShowStreakCounter", true, "Show a win/loss streak counter on the toast (e.g. '3 wins in a row!'). Resets on session end.");
			ShowSessionSummary = config.Bind<bool>("Notifications", "ShowSessionSummary", true, "Show a summary toast when returning to the lobby listing how many upgrades you received this run.");
			UpgradeCooldownSeconds = config.Bind<float>("Gameplay", "UpgradeCooldownSeconds", 1.5f, new ConfigDescription("Minimum seconds between share rolls for the same upgrade type. Prevents duplicate toasts caused by physics jitter firing the pickup twice. Set to 0 to disable.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
			IndependentRolls = config.Bind<bool>("Gameplay", "IndependentRolls", true, "When true (default), each player rolls their own dice independently — you may get the upgrade while a teammate doesn't. When false, everyone in the lobby shares the same outcome. All players must have the same setting for consistent behaviour.");
			ChanceToActivatePlayerHealth = Bind("ChanceToActivatePlayerHealth", "% Chance to share the Health upgrade");
			ChanceToActivatePlayerEnergy = Bind("ChanceToActivatePlayerEnergy", "% Chance to share the Energy (Stamina) upgrade");
			ChanceToActivatePlayerSprintSpeed = Bind("ChanceToActivatePlayerSprintSpeed", "% Chance to share the Sprint Speed upgrade");
			ChanceToActivatePlayerExtraJump = Bind("ChanceToActivatePlayerExtraJump", "% Chance to share the Extra Jump upgrade");
			ChanceToActivatePlayerTumbleLaunch = Bind("ChanceToActivatePlayerTumbleLaunch", "% Chance to share the Tumble Launch upgrade");
			ChanceToActivatePlayerGrabRange = Bind("ChanceToActivatePlayerGrabRange", "% Chance to share the Grab Range upgrade");
			ChanceToActivatePlayerGrabStrength = Bind("ChanceToActivatePlayerGrabStrength", "% Chance to share the Grab Strength upgrade");
			ChanceToActivatePlayerGrabThrow = Bind("ChanceToActivatePlayerGrabThrow", "% Chance to share the Grab Throw upgrade");
			ChanceToActivatePlayerTumbleClimb = Bind("ChanceToActivatePlayerTumbleClimb", "% Chance to share the Tumble Climb upgrade");
			ChanceToActivatePlayerTumbleWings = Bind("ChanceToActivatePlayerTumbleWings", "% Chance to share the Tumble Wings upgrade");
			ChanceToActivatePlayerCrouchRest = Bind("ChanceToActivatePlayerCrouchRest", "% Chance to share the Crouch Rest upgrade");
			ChanceToActivateDeathHeadBattery = Bind("ChanceToActivateDeathHeadBattery", "% Chance to share the Death Head Battery upgrade");
			ChanceToActivateMapPlayerCount = Bind("ChanceToActivateMapPlayerCount", "% Chance to share the Map Player Count upgrade");
			DefaultModdedUpgradeChance = config.Bind<int>("ModdedUpgrades", "DefaultModdedUpgradeChance", 25, new ConfigDescription("Default % chance used for modded upgrades that don't specify their own share chance. Also used as a fallback for any unrecognised upgrade type.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>()));
		}

		public int GetShareChance(string upgradeType)
		{
			switch (upgradeType)
			{
			case "Health":
				return ChanceToActivatePlayerHealth.Value;
			case "Energy":
				return ChanceToActivatePlayerEnergy.Value;
			case "SprintSpeed":
				return ChanceToActivatePlayerSprintSpeed.Value;
			case "ExtraJump":
				return ChanceToActivatePlayerExtraJump.Value;
			case "TumbleLaunch":
				return ChanceToActivatePlayerTumbleLaunch.Value;
			case "TumbleClimb":
				return ChanceToActivatePlayerTumbleClimb.Value;
			case "TumbleWings":
				return ChanceToActivatePlayerTumbleWings.Value;
			case "CrouchRest":
				return ChanceToActivatePlayerCrouchRest.Value;
			case "GrabRange":
				return ChanceToActivatePlayerGrabRange.Value;
			case "GrabStrength":
				return ChanceToActivatePlayerGrabStrength.Value;
			case "GrabThrow":
				return ChanceToActivatePlayerGrabThrow.Value;
			case "MapPlayerCount":
				return ChanceToActivateMapPlayerCount.Value;
			case "DeathHeadBattery":
				return ChanceToActivateDeathHeadBattery.Value;
			default:
			{
				ManualLogSource logger = Plugin.Logger;
				if (logger != null)
				{
					logger.LogWarning((object)("[LuckyUpgrades] GetShareChance: unknown upgrade type '" + upgradeType + "'. " + $"Using DefaultModdedUpgradeChance ({DefaultModdedUpgradeChance.Value}%)."));
				}
				return DefaultModdedUpgradeChance.Value;
			}
			}
		}

		public ConfigEntry<int> BindModdedUpgrade(string upgradeId, int initialChance = 25)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Expected O, but got Unknown
			initialChance = Math.Max(0, Math.Min(100, initialChance));
			ConfigEntry<int> val = _config.Bind<int>("ModdedUpgrades", upgradeId, 25, new ConfigDescription($"% Chance to share the '{upgradeId}' upgrade (added by another mod). Default: {25}%.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>()));
			if (val.Value == 25 && initialChance != 25)
			{
				val.Value = initialChance;
			}
			return val;
		}

		private ConfigEntry<int> Bind(string key, string description, int defaultValue = 25)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			return _config.Bind<int>("Upgrades", key, defaultValue, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>()));
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "LuckyUpgradesFork";

		public const string PLUGIN_NAME = "LuckyUpgradesFork";

		public const string PLUGIN_VERSION = "1.0.0";
	}
}