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 _inFlightUpgrades = new HashSet(); 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 _lastTriggerTime = new Dictionary(); 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 _sharedUpgrades = new Dictionary(); internal static readonly Dictionary apply, Func getChance)> _moddedUpgradeRegistry = new Dictionary, Func)>(); 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 hashSet = new HashSet { "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(); Object.DontDestroyOnLoad((Object)(object)val2); ((Object)val2).hideFlags = (HideFlags)61; GameObject val3 = new GameObject("LuckyUpgrades_ToastUI"); val3.AddComponent(); Object.DontDestroyOnLoad((Object)(object)val3); ((Object)val3).hideFlags = (HideFlags)61; PreBindREPOLibUpgrades(); Logger.LogInfo((object)"Harmony patches applied!"); } public static void RegisterModdedUpgrade(string upgradeId, Action 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 configEntry = UpgradeConfiguration?.BindModdedUpgrade(upgradeId, shareChance); Func func = ((configEntry != null) ? ((Func)(() => configEntry.Value)) : ((Func)(() => 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 applyDelegate; int value2; lock (ModdedUpgradeRegistryLock) { if (!_moddedUpgradeRegistry.TryGetValue(upgradeId, out (Action, Func) 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 dictionary; lock (SharedUpgradesLock) { if (_sharedUpgrades.Count == 0) { return; } dictionary = new Dictionary(_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 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, Func) 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, Func) 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(); 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() != (Object)null) { return "Health"; } if ((Object)(object)gameObject.GetComponent() != (Object)null) { return "Energy"; } if ((Object)(object)gameObject.GetComponent() != (Object)null) { return "ExtraJump"; } if ((Object)(object)gameObject.GetComponent() != (Object)null) { return "GrabRange"; } if ((Object)(object)gameObject.GetComponent() != (Object)null) { return "GrabStrength"; } if ((Object)(object)gameObject.GetComponent() != (Object)null) { return "GrabThrow"; } if ((Object)(object)gameObject.GetComponent() != (Object)null) { return "SprintSpeed"; } if ((Object)(object)gameObject.GetComponent() != (Object)null) { return "TumbleLaunch"; } if ((Object)(object)gameObject.GetComponent() != (Object)null) { return "TumbleClimb"; } if ((Object)(object)gameObject.GetComponent() != (Object)null) { return "TumbleWings"; } if ((Object)(object)gameObject.GetComponent() != (Object)null) { return "CrouchRest"; } if ((Object)(object)gameObject.GetComponent() != (Object)null) { return "DeathHeadBattery"; } if ((Object)(object)gameObject.GetComponent() != (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 configEntry = UpgradeConfiguration?.BindModdedUpgrade(text, initialChance); Func func = ((configEntry != null) ? ((Func)(() => configEntry.Value)) : ((Func)(() => 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 list = new List(); 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 configEntry = UpgradeConfiguration?.BindModdedUpgrade(text, initialChance); Func func = ((configEntry != null) ? ((Func)(() => configEntry.Value)) : ((Func)(() => 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, Func) 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(); if ((Object)(object)component != (Object)null) { List 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 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 SESSION_END_LEVELS = new HashSet(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 Labels = new Dictionary { { "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 _queue = new Queue(); private readonly List _active = new List(); 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 ChanceToActivatePlayerHealth { get; private set; } public ConfigEntry ChanceToActivatePlayerEnergy { get; private set; } public ConfigEntry ChanceToActivatePlayerSprintSpeed { get; private set; } public ConfigEntry ChanceToActivatePlayerExtraJump { get; private set; } public ConfigEntry ChanceToActivatePlayerTumbleLaunch { get; private set; } public ConfigEntry ChanceToActivatePlayerGrabRange { get; private set; } public ConfigEntry ChanceToActivatePlayerGrabStrength { get; private set; } public ConfigEntry ChanceToActivatePlayerGrabThrow { get; private set; } public ConfigEntry ChanceToActivatePlayerTumbleClimb { get; private set; } public ConfigEntry ChanceToActivatePlayerTumbleWings { get; private set; } public ConfigEntry ChanceToActivatePlayerCrouchRest { get; private set; } public ConfigEntry ChanceToActivateDeathHeadBattery { get; private set; } public ConfigEntry ChanceToActivateMapPlayerCount { get; private set; } public ConfigEntry DefaultModdedUpgradeChance { get; private set; } public ConfigEntry ShowUpgradeNotifications { get; private set; } public ConfigEntry NotificationDisplayTime { get; private set; } public ConfigEntry ShowSourcePlayerName { get; private set; } public ConfigEntry ShowPickerToast { get; private set; } public ConfigEntry ShowStreakCounter { get; private set; } public ConfigEntry ShowSessionSummary { get; private set; } public ConfigEntry UpgradeCooldownSeconds { get; private set; } public ConfigEntry 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("Notifications", "ShowUpgradeNotifications", true, "Show an in-game toast notification when an upgrade is shared or missed. Set false to disable."); NotificationDisplayTime = config.Bind("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(0.5f, 15f), Array.Empty())); ShowSourcePlayerName = config.Bind("Notifications", "ShowSourcePlayerName", true, "Show the name of the player whose pickup triggered the share roll on the toast notification."); ShowPickerToast = config.Bind("Notifications", "ShowPickerToast", true, "Show a toast to the player who picked up the upgrade, telling them the share roll is happening."); ShowStreakCounter = config.Bind("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("Notifications", "ShowSessionSummary", true, "Show a summary toast when returning to the lobby listing how many upgrades you received this run."); UpgradeCooldownSeconds = config.Bind("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(0f, 10f), Array.Empty())); IndependentRolls = config.Bind("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("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(0, 100), Array.Empty())); } 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 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 val = _config.Bind("ModdedUpgrades", upgradeId, 25, new ConfigDescription($"% Chance to share the '{upgradeId}' upgrade (added by another mod). Default: {25}%.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 100), Array.Empty())); if (val.Value == 25 && initialChance != 25) { val.Value = initialChance; } return val; } private ConfigEntry 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("Upgrades", key, defaultValue, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange(0, 100), Array.Empty())); } } public static class MyPluginInfo { public const string PLUGIN_GUID = "LuckyUpgradesFork"; public const string PLUGIN_NAME = "LuckyUpgradesFork"; public const string PLUGIN_VERSION = "1.0.0"; } }