Decompiled source of LuckyUpgradesFork v1.0.1

LuckyUpgradesFork.dll

Decompiled 18 hours 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+3220723b0df52f28ecf9f073b61fa83e0c9ba0f5")]
[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]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace 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 object _randomLock = new object();

		private static readonly Random _random = new Random();

		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
			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;
			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));
			}
			ApplySharedUpgradeToSelf(upgradeId, sourceSteamID, 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) || mySteamID == steamIDFromItem)
				{
					return;
				}
				int? chanceOverride = null;
				lock (ModdedUpgradeRegistryLock)
				{
					if (_moddedUpgradeRegistry.TryGetValue(upgradeType, out (Action<string, int>, Func<int>) value))
					{
						chanceOverride = value.Item2();
					}
				}
				ApplySharedUpgradeToSelf(upgradeType, steamIDFromItem, 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 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.GetValueOrDefault())
				{
					_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 defaultChance = UpgradeConfiguration?.DefaultModdedUpgradeChance.Value ?? 25;
						ConfigEntry<int> configEntry = UpgradeConfiguration?.BindModdedUpgrade(text, defaultChance);
						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 defaultChance = 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, defaultChance);
							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, 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
					{
						Interlocked.Exchange(ref _isApplyingSharedUpgrade, 1);
						applyToSelf(amount);
						flag = true;
					}
					finally
					{
						Interlocked.Exchange(ref _isApplyingSharedUpgrade, 0);
					}
					if (flag)
					{
						TrackSharedUpgrade(upgradeType, amount);
						Logger.LogInfo((object)$"[LuckyUpgrades] Shared upgrade applied: {upgradeType} +{amount} (100% guaranteed) ✓");
					}
					return;
				}
				int num2;
				lock (_randomLock)
				{
					num2 = _random.Next(100);
				}
				if (num2 < num)
				{
					bool flag2 = false;
					try
					{
						Interlocked.Exchange(ref _isApplyingSharedUpgrade, 1);
						applyToSelf(amount);
						flag2 = true;
					}
					finally
					{
						Interlocked.Exchange(ref _isApplyingSharedUpgrade, 0);
					}
					if (flag2)
					{
						TrackSharedUpgrade(upgradeType, amount);
						Logger.LogInfo((object)$"[LuckyUpgrades] Shared upgrade applied: {upgradeType} +{amount} (rolled: {num2} | chance: {num}%) ✓");
					}
				}
				else
				{
					Logger.LogInfo((object)$"[LuckyUpgrades] Shared upgrade missed: {upgradeType} (rolled: {num2} | chance: {num}%) ✗");
				}
			}
			catch (Exception ex)
			{
				Logger.LogError((object)("[LuckyUpgrades] Error in ApplySharedUpgradeToSelf: " + ex.Message + "\n" + ex.StackTrace));
			}
		}
	}
	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))
				{
					lock (Plugin.SharedUpgradesLock)
					{
						Plugin._sharedUpgrades.Clear();
					}
					lock (Plugin.MySteamIDLock)
					{
						Plugin._mySteamID = null;
					}
					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 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 UpgradeConfig(ConfigFile config)
		{
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Expected O, but got Unknown
			_config = config;
			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 defaultChance = 25)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Expected O, but got Unknown
			defaultChance = Math.Max(0, Math.Min(100, defaultChance));
			return _config.Bind<int>("ModdedUpgrades", upgradeId, defaultChance, new ConfigDescription("% Chance to share the '" + upgradeId + "' upgrade (added by another mod)", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>()));
		}

		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";
	}
}