Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of LuckyUpgradesFork v1.1.0
LuckyUpgradesFork.dll
Decompiled 5 days agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Threading; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("mihmi125")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyDescription("Fork of R.E.P.O LuckyUpgrades")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+a1c687166c1ced6afd24bf1ab4319eb62d93e513")] [assembly: AssemblyProduct("LuckyUpgradesFork")] [assembly: AssemblyTitle("LuckyUpgradesFork")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace LuckyUpgrades { [BepInPlugin("LuckyUpgradesFork", "LuckyUpgradesFork", "1.0.0")] [BepInProcess("REPO.exe")] public class Plugin : BaseUnityPlugin { internal static ManualLogSource Logger; private static int _isApplyingSharedUpgrade = 0; private static readonly HashSet<string> _inFlightUpgrades = new HashSet<string>(); private static readonly object _inFlightLock = new object(); private static readonly object _randomLock = new object(); private static readonly Random _random = new Random(); private static readonly Dictionary<int, float> _lastTriggerTime = new Dictionary<int, float>(); private static readonly object _cooldownLock = new object(); private static int _currentStreak = 0; private static readonly object _streakLock = new object(); private static int _sessionUpgradesReceived = 0; private static int _sessionRollsTotal = 0; private static readonly object _sessionLock = new object(); internal static readonly object SharedUpgradesLock = new object(); internal static readonly object ModdedUpgradeRegistryLock = new object(); internal static readonly object MySteamIDLock = new object(); internal static string _mySteamID = null; internal static readonly Dictionary<string, int> _sharedUpgrades = new Dictionary<string, int>(); internal static readonly Dictionary<string, (Action<string, int> apply, Func<int> getChance)> _moddedUpgradeRegistry = new Dictionary<string, (Action<string, int>, Func<int>)>(); private Harmony _harmony; private static bool? _repoLibTypeSearched = null; private static Type _repoLibItemUpgradeType = null; private static FieldInfo _repoLibUpgradeIdField = null; private static Type _repoLibUpgradesModuleType = null; private static MethodInfo _repoLibGetUpgradeMethod = null; public static Plugin Instance { get; private set; } public static UpgradeConfig UpgradeConfiguration { get; private set; } private void Awake() { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Expected O, but got Unknown //IL_0294: Unknown result type (might be due to invalid IL or missing references) //IL_029a: Expected O, but got Unknown //IL_02b6: Unknown result type (might be due to invalid IL or missing references) //IL_02bd: Expected O, but got Unknown Instance = this; Logger = ((BaseUnityPlugin)this).Logger; Logger.LogInfo((object)"Plugin LuckyUpgradesFork is loaded!"); UpgradeConfiguration = new UpgradeConfig(((BaseUnityPlugin)this).Config); _harmony = new Harmony("LuckyUpgradesFork"); HarmonyMethod val = new HarmonyMethod(typeof(Plugin), "ItemUpgrade_PlayUpgrade_Postfix", (Type[])null); bool flag = false; HashSet<string> hashSet = new HashSet<string> { "Start", "Awake", "Update", "FixedUpdate", "LateUpdate", "OnEnable", "OnDisable", "OnDestroy", "Reset", "ButtonToggle", "ButtonToggleLogic", "ButtonToggleRPC" }; string[] array = new string[8] { "PlayerUpgrade", "PlayUpgrade", "Use", "Upgrade", "Activate", "OnUse", "UseUpgrade", "Apply" }; foreach (string text in array) { MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(ItemUpgrade), text, new Type[0], (Type[])null); if (methodInfo != null) { _harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Logger.LogInfo((object)("[LuckyUpgrades] Patched ItemUpgrade." + text + " ✓")); flag = true; break; } } if (!flag) { MethodInfo[] methods = typeof(ItemUpgrade).GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo2 in methods) { if (methodInfo2.ReturnType == typeof(void) && methodInfo2.GetParameters().Length == 0 && !methodInfo2.IsAbstract && !hashSet.Contains(methodInfo2.Name)) { _harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Logger.LogWarning((object)("[LuckyUpgrades] Fallback-patched ItemUpgrade." + methodInfo2.Name + " — verify this is the upgrade trigger!")); flag = true; break; } } } if (!flag) { Logger.LogError((object)"[LuckyUpgrades] Could not find any suitable method on ItemUpgrade to patch — sharing will not work! Check the method list above."); } GameObject val2 = new GameObject("LuckyUpgrades_UpdateRunner"); val2.AddComponent<UpgradeReapplyRunner>(); Object.DontDestroyOnLoad((Object)(object)val2); ((Object)val2).hideFlags = (HideFlags)61; GameObject val3 = new GameObject("LuckyUpgrades_ToastUI"); val3.AddComponent<UpgradeToastUI>(); Object.DontDestroyOnLoad((Object)(object)val3); ((Object)val3).hideFlags = (HideFlags)61; PreBindREPOLibUpgrades(); Logger.LogInfo((object)"Harmony patches applied!"); } public static void RegisterModdedUpgrade(string upgradeId, Action<string, int> applyAction, int shareChance = 25) { if (string.IsNullOrEmpty(upgradeId)) { ManualLogSource logger = Logger; if (logger != null) { logger.LogError((object)"[LuckyUpgrades] RegisterModdedUpgrade: upgradeId cannot be null or empty."); } return; } if (applyAction == null) { ManualLogSource logger2 = Logger; if (logger2 != null) { logger2.LogError((object)("[LuckyUpgrades] RegisterModdedUpgrade: applyAction cannot be null (upgradeId: " + upgradeId + ").")); } return; } shareChance = Math.Max(0, Math.Min(100, shareChance)); lock (ModdedUpgradeRegistryLock) { if (_moddedUpgradeRegistry.ContainsKey(upgradeId)) { ManualLogSource logger3 = Logger; if (logger3 != null) { logger3.LogWarning((object)("[LuckyUpgrades] Upgrade '" + upgradeId + "' already registered — overwriting.")); } } ConfigEntry<int> configEntry = UpgradeConfiguration?.BindModdedUpgrade(upgradeId, shareChance); Func<int> func = ((configEntry != null) ? ((Func<int>)(() => configEntry.Value)) : ((Func<int>)(() => shareChance))); _moddedUpgradeRegistry[upgradeId] = (applyAction, func); ManualLogSource logger4 = Logger; if (logger4 != null) { logger4.LogInfo((object)$"[LuckyUpgrades] ✓ REGISTERED modded upgrade: '{upgradeId}' ({func()}% share chance)"); } } } public static void TriggerModdedUpgradeShare(string upgradeId, string sourceSteamID, int amount = 1) { Action<string, int> applyDelegate; int value2; lock (ModdedUpgradeRegistryLock) { if (!_moddedUpgradeRegistry.TryGetValue(upgradeId, out (Action<string, int>, Func<int>) value)) { ManualLogSource logger = Logger; if (logger != null) { logger.LogError((object)("[LuckyUpgrades] ✗ TriggerModdedUpgradeShare: '" + upgradeId + "' NOT REGISTERED! Did you call RegisterModdedUpgrade()?")); } return; } applyDelegate = value.Item1; value2 = value.Item2(); } ManualLogSource logger2 = Logger; if (logger2 != null) { logger2.LogInfo((object)("[LuckyUpgrades] → TriggerModdedUpgradeShare called: '" + upgradeId + "' from " + sourceSteamID)); } string playerName = GetPlayerName(sourceSteamID); ApplySharedUpgradeToSelf(upgradeId, sourceSteamID, playerName, amount, delegate(int amt) { string mySteamID = GetMySteamID(); if (!string.IsNullOrEmpty(mySteamID)) { ManualLogSource logger3 = Logger; if (logger3 != null) { logger3.LogInfo((object)$"[LuckyUpgrades] → Applying modded upgrade '{upgradeId}' to player {mySteamID} (+{amt})"); } applyDelegate(mySteamID, amt); } else { ManualLogSource logger4 = Logger; if (logger4 != null) { logger4.LogWarning((object)("[LuckyUpgrades] ✗ Cannot apply '" + upgradeId + "': SteamID is null/empty")); } } }, value2); } internal static string GetMySteamID() { lock (MySteamIDLock) { if (string.IsNullOrEmpty(_mySteamID)) { PlayerAvatar val = SemiFunc.PlayerAvatarLocal(); if ((Object)(object)val != (Object)null) { _mySteamID = SemiFunc.PlayerGetSteamID(val); if (!string.IsNullOrEmpty(_mySteamID)) { ManualLogSource logger = Logger; if (logger != null) { logger.LogDebug((object)("[LuckyUpgrades] Player SteamID cached: " + _mySteamID)); } } else { ManualLogSource logger2 = Logger; if (logger2 != null) { logger2.LogWarning((object)"[LuckyUpgrades] Failed to get player SteamID from local player"); } } } else { ManualLogSource logger3 = Logger; if (logger3 != null) { logger3.LogDebug((object)"[LuckyUpgrades] Local player not found yet"); } } } return _mySteamID; } } internal static void ReapplySharedUpgrades() { Dictionary<string, int> dictionary; lock (SharedUpgradesLock) { if (_sharedUpgrades.Count == 0) { return; } dictionary = new Dictionary<string, int>(_sharedUpgrades); _sharedUpgrades.Clear(); } string mySteamID = GetMySteamID(); if (string.IsNullOrEmpty(mySteamID)) { return; } Logger.LogInfo((object)$"[LuckyUpgrades] Reapplying {dictionary.Count} upgrade type(s)..."); try { Interlocked.Exchange(ref _isApplyingSharedUpgrade, 1); foreach (KeyValuePair<string, int> item in dictionary) { string key = item.Key; int value = item.Value; if (value <= 0) { continue; } try { if (ReapplySingleUpgrade(mySteamID, key, value)) { Logger.LogInfo((object)$"[LuckyUpgrades] Reapplied: {key} +{value}"); } else { Logger.LogWarning((object)("[LuckyUpgrades] Unknown upgrade type during reapply — skipped: '" + key + "'")); } } catch (Exception ex) { Logger.LogError((object)("[LuckyUpgrades] Error reapplying '" + key + "': " + ex.Message)); } } } finally { Interlocked.Exchange(ref _isApplyingSharedUpgrade, 0); } } private static bool ReapplySingleUpgrade(string myID, string upgradeType, int amount) { switch (upgradeType) { case "Health": { for (int num6 = 0; num6 < amount; num6++) { PunManager.instance.UpgradePlayerHealth(myID, 1); } return true; } case "Energy": { for (int num = 0; num < amount; num++) { PunManager.instance.UpgradePlayerEnergy(myID, 1); } return true; } case "ExtraJump": { for (int num7 = 0; num7 < amount; num7++) { PunManager.instance.UpgradePlayerExtraJump(myID, 1); } return true; } case "GrabRange": { for (int l = 0; l < amount; l++) { PunManager.instance.UpgradePlayerGrabRange(myID, 1); } return true; } case "GrabStrength": { for (int num3 = 0; num3 < amount; num3++) { PunManager.instance.UpgradePlayerGrabStrength(myID, 1); } return true; } case "GrabThrow": { for (int k = 0; k < amount; k++) { PunManager.instance.UpgradePlayerThrowStrength(myID, 1); } return true; } case "SprintSpeed": { for (int num4 = 0; num4 < amount; num4++) { PunManager.instance.UpgradePlayerSprintSpeed(myID, 1); } return true; } case "TumbleLaunch": { for (int n = 0; n < amount; n++) { PunManager.instance.UpgradePlayerTumbleLaunch(myID, 1); } return true; } case "MapPlayerCount": { for (int i = 0; i < amount; i++) { PunManager.instance.UpgradeMapPlayerCount(myID, 1); } return true; } case "TumbleClimb": { for (int num5 = 0; num5 < amount; num5++) { PunManager.instance.UpgradePlayerTumbleClimb(myID, 1); } return true; } case "TumbleWings": { for (int num2 = 0; num2 < amount; num2++) { PunManager.instance.UpgradePlayerTumbleWings(myID, 1); } return true; } case "CrouchRest": { for (int m = 0; m < amount; m++) { PunManager.instance.UpgradePlayerCrouchRest(myID, 1); } return true; } case "DeathHeadBattery": { for (int j = 0; j < amount; j++) { PunManager.instance.UpgradeDeathHeadBattery(myID, 1); } return true; } default: lock (ModdedUpgradeRegistryLock) { if (_moddedUpgradeRegistry.TryGetValue(upgradeType, out (Action<string, int>, Func<int>) value)) { value.Item1(myID, amount); return true; } } return false; } } private static void TrackSharedUpgrade(string upgradeType, int amount) { lock (SharedUpgradesLock) { if (!_sharedUpgrades.TryGetValue(upgradeType, out var value)) { value = 0; } _sharedUpgrades[upgradeType] = value + amount; Logger.LogInfo((object)$"[LuckyUpgrades] Tracked: {upgradeType} (total: {_sharedUpgrades[upgradeType]})"); } } public static void ItemUpgrade_PlayUpgrade_Postfix(ItemUpgrade __instance) { try { if (Interlocked.CompareExchange(ref _isApplyingSharedUpgrade, 0, 0) == 1) { return; } string mySteamID = GetMySteamID(); string upgradeType = GetUpgradeType(__instance); if (string.IsNullOrEmpty(upgradeType)) { return; } string steamIDFromItem = GetSteamIDFromItem(__instance); if (string.IsNullOrEmpty(steamIDFromItem) || string.IsNullOrEmpty(mySteamID)) { return; } float num = UpgradeConfiguration?.UpgradeCooldownSeconds.Value ?? 1.5f; if (num > 0f) { int instanceID = ((Object)((Component)__instance).gameObject).GetInstanceID(); float realtimeSinceStartup = Time.realtimeSinceStartup; lock (_cooldownLock) { if (_lastTriggerTime.TryGetValue(instanceID, out var value) && realtimeSinceStartup - value < num) { Logger.LogInfo((object)$"[LuckyUpgrades] Instance cooldown active for '{upgradeType}' (id:{instanceID}) — skipping ({realtimeSinceStartup - value:F2}s < {num}s)"); return; } _lastTriggerTime[instanceID] = realtimeSinceStartup; } } if (mySteamID == steamIDFromItem) { UpgradeConfig upgradeConfiguration = UpgradeConfiguration; if (upgradeConfiguration == null || upgradeConfiguration.ShowPickerToast.Value) { UpgradeToastUI.ShowPickerToast(upgradeType); } return; } string playerName = GetPlayerName(steamIDFromItem); int? chanceOverride = null; lock (ModdedUpgradeRegistryLock) { if (_moddedUpgradeRegistry.TryGetValue(upgradeType, out (Action<string, int>, Func<int>) value2)) { chanceOverride = value2.Item2(); } } ApplySharedUpgradeToSelf(upgradeType, steamIDFromItem, playerName, 1, delegate { string mySteamID2 = GetMySteamID(); if (!string.IsNullOrEmpty(mySteamID2)) { ApplyUpgradeByType(upgradeType, mySteamID2); } }, chanceOverride); } catch (Exception ex) { Logger.LogError((object)("[LuckyUpgrades] Error in ItemUpgrade_PlayUpgrade_Postfix: " + ex.Message + "\n" + ex.StackTrace)); } } private static string GetPlayerName(string steamID) { if (string.IsNullOrEmpty(steamID)) { return "???"; } try { PlayerAvatar[] array = Object.FindObjectsOfType<PlayerAvatar>(); foreach (PlayerAvatar val in array) { try { string text = SemiFunc.PlayerGetSteamID(val); if (text == steamID) { string text2 = SemiFunc.PlayerGetName(val); if (!string.IsNullOrEmpty(text2)) { return text2; } } } catch { } } } catch (Exception ex) { Logger.LogWarning((object)("[LuckyUpgrades] GetPlayerName failed: " + ex.Message)); } return (steamID.Length > 6) ? ("..." + steamID.Substring(steamID.Length - 6)) : steamID; } private static string GetUpgradeType(ItemUpgrade item) { GameObject gameObject = ((Component)item).gameObject; if ((Object)(object)gameObject.GetComponent<ItemUpgradePlayerHealth>() != (Object)null) { return "Health"; } if ((Object)(object)gameObject.GetComponent<ItemUpgradePlayerEnergy>() != (Object)null) { return "Energy"; } if ((Object)(object)gameObject.GetComponent<ItemUpgradePlayerExtraJump>() != (Object)null) { return "ExtraJump"; } if ((Object)(object)gameObject.GetComponent<ItemUpgradePlayerGrabRange>() != (Object)null) { return "GrabRange"; } if ((Object)(object)gameObject.GetComponent<ItemUpgradePlayerGrabStrength>() != (Object)null) { return "GrabStrength"; } if ((Object)(object)gameObject.GetComponent<ItemUpgradePlayerGrabThrow>() != (Object)null) { return "GrabThrow"; } if ((Object)(object)gameObject.GetComponent<ItemUpgradePlayerSprintSpeed>() != (Object)null) { return "SprintSpeed"; } if ((Object)(object)gameObject.GetComponent<ItemUpgradePlayerTumbleLaunch>() != (Object)null) { return "TumbleLaunch"; } if ((Object)(object)gameObject.GetComponent<ItemUpgradePlayerTumbleClimb>() != (Object)null) { return "TumbleClimb"; } if ((Object)(object)gameObject.GetComponent<ItemUpgradePlayerTumbleWings>() != (Object)null) { return "TumbleWings"; } if ((Object)(object)gameObject.GetComponent<ItemUpgradePlayerCrouchRest>() != (Object)null) { return "CrouchRest"; } if ((Object)(object)gameObject.GetComponent<ItemUpgradeDeathHeadBattery>() != (Object)null) { return "DeathHeadBattery"; } if ((Object)(object)gameObject.GetComponent<ItemUpgradeMapPlayerCount>() != (Object)null) { return "MapPlayerCount"; } return TryGetREPOLibUpgradeId(gameObject); } private static string TryGetREPOLibUpgradeId(GameObject go) { try { if (_repoLibTypeSearched != true) { _repoLibTypeSearched = false; Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { try { Type[] types = assembly.GetTypes(); foreach (Type type in types) { if (type.Name == "REPOLibItemUpgrade") { _repoLibItemUpgradeType = type; break; } } } catch { } if (_repoLibItemUpgradeType != null) { break; } } if (_repoLibItemUpgradeType != null) { Logger.LogInfo((object)"[LuckyUpgrades] Found REPOLibItemUpgrade — resolving upgradeId field..."); _repoLibUpgradeIdField = _repoLibItemUpgradeType.GetField("upgradeId", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? _repoLibItemUpgradeType.GetField("_upgradeId", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? _repoLibItemUpgradeType.GetField("UpgradeId", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _repoLibTypeSearched = true; } } if (_repoLibItemUpgradeType == null) { return null; } Component component = go.GetComponent(_repoLibItemUpgradeType); if ((Object)(object)component == (Object)null) { return null; } if (_repoLibUpgradeIdField == null) { Logger.LogWarning((object)"[LuckyUpgrades] upgradeId field not resolved on REPOLibItemUpgrade — cannot share this upgrade type."); return null; } string text = _repoLibUpgradeIdField.GetValue(component) as string; if (string.IsNullOrEmpty(text)) { return null; } lock (ModdedUpgradeRegistryLock) { if (!_moddedUpgradeRegistry.ContainsKey(text)) { int initialChance = UpgradeConfiguration?.DefaultModdedUpgradeChance.Value ?? 25; ConfigEntry<int> configEntry = UpgradeConfiguration?.BindModdedUpgrade(text, initialChance); Func<int> func = ((configEntry != null) ? ((Func<int>)(() => configEntry.Value)) : ((Func<int>)(() => UpgradeConfiguration?.DefaultModdedUpgradeChance.Value ?? 25))); string capturedId = text; _moddedUpgradeRegistry[capturedId] = (delegate(string steamID, int amount) { ApplyREPOLibUpgrade(capturedId, steamID, amount); }, func); Logger.LogInfo((object)$"[LuckyUpgrades] Auto-registered REPOLib upgrade: '{capturedId}' ({func()}%)"); } } return text; } catch (Exception ex) { Logger.LogWarning((object)("[LuckyUpgrades] TryGetREPOLibUpgradeId failed: " + ex.Message)); return null; } } private static void PreBindREPOLibUpgrades() { try { Type type = null; Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { try { type = assembly.GetType("REPOLib.Modules.Upgrades"); } catch { } if (type != null) { break; } } if (type == null) { Logger.LogInfo((object)"[LuckyUpgrades] REPOLib.Modules.Upgrades not found at startup — will bind on first encounter instead."); return; } List<object> list = new List<object>(); string[] array = new string[3] { "GetAll", "GetUpgrades", "GetRegistered" }; foreach (string name in array) { MethodInfo method = type.GetMethod(name, BindingFlags.Static | BindingFlags.Public); if (method == null) { continue; } object obj2 = method.Invoke(null, null); if (obj2 is IEnumerable enumerable) { foreach (object item in enumerable) { if (item != null) { list.Add(item); } } } if (list.Count > 0) { break; } } if (list.Count == 0) { FieldInfo[] fields = type.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { object value = fieldInfo.GetValue(null); if (!(value is IDictionary dictionary)) { continue; } foreach (object value2 in dictionary.Values) { if (value2 != null) { list.Add(value2); } } if (list.Count > 0) { break; } } } if (list.Count == 0) { Logger.LogInfo((object)"[LuckyUpgrades] Could not enumerate REPOLib upgrades at startup — will bind on first encounter."); return; } int initialChance = UpgradeConfiguration?.DefaultModdedUpgradeChance.Value ?? 25; int num = 0; foreach (object item2 in list) { Type type2 = item2.GetType(); FieldInfo fieldInfo2 = type2.GetField("_upgradeId", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? type2.GetField("upgradeId", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? type2.GetField("UpgradeId", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); PropertyInfo propertyInfo = type2.GetProperty("UpgradeId", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? type2.GetProperty("upgradeId", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); string text = (fieldInfo2?.GetValue(item2) ?? propertyInfo?.GetValue(item2)) as string; Logger.LogInfo((object)("[LuckyUpgrades] Pre-bind: found REPOLib upgrade id='" + (text ?? "NULL") + "'")); if (string.IsNullOrEmpty(text)) { continue; } lock (ModdedUpgradeRegistryLock) { if (!_moddedUpgradeRegistry.ContainsKey(text)) { ConfigEntry<int> configEntry = UpgradeConfiguration?.BindModdedUpgrade(text, initialChance); Func<int> func = ((configEntry != null) ? ((Func<int>)(() => configEntry.Value)) : ((Func<int>)(() => UpgradeConfiguration?.DefaultModdedUpgradeChance.Value ?? 25))); string capturedId = text; _moddedUpgradeRegistry[capturedId] = (delegate(string steamID, int amount) { ApplyREPOLibUpgrade(capturedId, steamID, amount); }, func); num++; Logger.LogInfo((object)$"[LuckyUpgrades] Pre-bound '{capturedId}' ({func()}%) to config"); } } } Logger.LogInfo((object)$"[LuckyUpgrades] Pre-bound {num} REPOLib upgrade(s) to config ✓"); } catch (Exception ex) { Logger.LogWarning((object)("[LuckyUpgrades] PreBindREPOLibUpgrades failed: " + ex.Message)); } } private static void ApplyREPOLibUpgrade(string upgradeId, string steamID, int amount) { try { PlayerAvatar val = null; foreach (PlayerAvatar item in SemiFunc.PlayerGetAll()) { if (SemiFunc.PlayerGetSteamID(item) == steamID) { val = item; break; } } if ((Object)(object)val == (Object)null) { Logger.LogWarning((object)("[LuckyUpgrades] ApplyREPOLibUpgrade: no PlayerAvatar for steamID '" + steamID + "'")); return; } if (_repoLibUpgradesModuleType == null) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { try { _repoLibUpgradesModuleType = assembly.GetType("REPOLib.Modules.Upgrades"); } catch { } if (_repoLibUpgradesModuleType != null) { break; } } } if (_repoLibUpgradesModuleType == null) { Logger.LogError((object)"[LuckyUpgrades] Cannot find REPOLib.Modules.Upgrades"); return; } if (_repoLibGetUpgradeMethod == null) { _repoLibGetUpgradeMethod = _repoLibUpgradesModuleType.GetMethod("GetUpgrade", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(string) }, null); } if (_repoLibGetUpgradeMethod == null) { Logger.LogError((object)"[LuckyUpgrades] REPOLib.Modules.Upgrades.GetUpgrade(string) not found"); return; } MethodInfo repoLibGetUpgradeMethod = _repoLibGetUpgradeMethod; object obj2 = repoLibGetUpgradeMethod.Invoke(null, new object[1] { upgradeId }); if (obj2 == null) { Logger.LogWarning((object)("[LuckyUpgrades] REPOLib upgrade '" + upgradeId + "' not in registry")); return; } Type type = obj2.GetType(); MethodInfo method = type.GetMethod("AddLevel", BindingFlags.Instance | BindingFlags.Public, null, new Type[2] { typeof(PlayerAvatar), typeof(int) }, null); MethodInfo method2 = type.GetMethod("Upgrade", BindingFlags.Instance | BindingFlags.Public, null, new Type[1] { typeof(PlayerAvatar) }, null); MethodInfo method3 = type.GetMethod("AddLevel", BindingFlags.Instance | BindingFlags.Public, null, new Type[1] { typeof(PlayerAvatar) }, null); if (method != null) { method.Invoke(obj2, new object[2] { val, amount }); Logger.LogInfo((object)$"[LuckyUpgrades] Applied REPOLib upgrade '{upgradeId}' to {steamID} x{amount} via AddLevel(PlayerAvatar, int) ✓"); return; } if (method2 != null) { for (int j = 0; j < amount; j++) { method2.Invoke(obj2, new object[1] { val }); } Logger.LogInfo((object)$"[LuckyUpgrades] Applied REPOLib upgrade '{upgradeId}' to {steamID} x{amount} via Upgrade(PlayerAvatar) ✓"); return; } if (method3 != null) { for (int k = 0; k < amount; k++) { method3.Invoke(obj2, new object[1] { val }); } Logger.LogInfo((object)$"[LuckyUpgrades] Applied REPOLib upgrade '{upgradeId}' to {steamID} x{amount} via AddLevel(PlayerAvatar) ✓"); return; } Logger.LogWarning((object)("[LuckyUpgrades] No applicable upgrade method found on PlayerUpgrade for '" + upgradeId + "'. Methods available:")); MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public); foreach (MethodInfo methodInfo in methods) { Logger.LogWarning((object)("[LuckyUpgrades] " + methodInfo.Name + "(" + string.Join(", ", Array.ConvertAll(methodInfo.GetParameters(), (ParameterInfo p) => p.ParameterType.Name)) + ")")); } } catch (Exception ex) { Logger.LogError((object)("[LuckyUpgrades] ApplyREPOLibUpgrade failed: " + ex.Message + "\n" + ex.StackTrace)); } } private static void ApplyUpgradeByType(string upgradeType, string steamID, int amount = 1) { switch (upgradeType) { case "Health": { for (int num4 = 0; num4 < amount; num4++) { PunManager.instance.UpgradePlayerHealth(steamID, 1); } return; } case "Energy": { for (int j = 0; j < amount; j++) { PunManager.instance.UpgradePlayerEnergy(steamID, 1); } return; } case "ExtraJump": { for (int n = 0; n < amount; n++) { PunManager.instance.UpgradePlayerExtraJump(steamID, 1); } return; } case "GrabRange": { for (int num6 = 0; num6 < amount; num6++) { PunManager.instance.UpgradePlayerGrabRange(steamID, 1); } return; } case "GrabStrength": { for (int num2 = 0; num2 < amount; num2++) { PunManager.instance.UpgradePlayerGrabStrength(steamID, 1); } return; } case "GrabThrow": { for (int l = 0; l < amount; l++) { PunManager.instance.UpgradePlayerThrowStrength(steamID, 1); } return; } case "SprintSpeed": { for (int num7 = 0; num7 < amount; num7++) { PunManager.instance.UpgradePlayerSprintSpeed(steamID, 1); } return; } case "TumbleLaunch": { for (int num5 = 0; num5 < amount; num5++) { PunManager.instance.UpgradePlayerTumbleLaunch(steamID, 1); } return; } case "MapPlayerCount": { for (int num3 = 0; num3 < amount; num3++) { PunManager.instance.UpgradeMapPlayerCount(steamID, 1); } return; } case "TumbleClimb": { for (int num = 0; num < amount; num++) { PunManager.instance.UpgradePlayerTumbleClimb(steamID, 1); } return; } case "TumbleWings": { for (int m = 0; m < amount; m++) { PunManager.instance.UpgradePlayerTumbleWings(steamID, 1); } return; } case "CrouchRest": { for (int k = 0; k < amount; k++) { PunManager.instance.UpgradePlayerCrouchRest(steamID, 1); } return; } case "DeathHeadBattery": { for (int i = 0; i < amount; i++) { PunManager.instance.UpgradeDeathHeadBattery(steamID, 1); } return; } } lock (ModdedUpgradeRegistryLock) { if (_moddedUpgradeRegistry.TryGetValue(upgradeType, out (Action<string, int>, Func<int>) value)) { value.Item1(steamID, amount); } else { Logger.LogWarning((object)("[LuckyUpgrades] ApplyUpgradeByType: no handler for '" + upgradeType + "'")); } } } private static string GetSteamIDFromItem(ItemUpgrade item) { if ((Object)(object)item == (Object)null) { return null; } try { FieldInfo field = ((object)item).GetType().GetField("playerAvatar", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { object? value = field.GetValue(item); PlayerAvatar val = (PlayerAvatar)((value is PlayerAvatar) ? value : null); if ((Object)(object)val != (Object)null) { return SemiFunc.PlayerGetSteamID(val); } } PhysGrabObject component = ((Component)item).GetComponent<PhysGrabObject>(); if ((Object)(object)component != (Object)null) { List<PlayerAvatar> list = SemiFunc.PhysGrabObjectGetPlayerAvatarsGrabbing(component); if (list != null && list.Count > 0) { return SemiFunc.PlayerGetSteamID(list[0]); } } } catch (Exception ex) { Logger.LogWarning((object)("[LuckyUpgrades] GetSteamIDFromItem failed: " + ex.Message)); } return null; } private static void ApplySharedUpgradeToSelf(string upgradeType, string sourceSteamID, string sourcePlayerName, int amount, Action<int> applyToSelf, int? chanceOverride = null) { try { int num = chanceOverride ?? UpgradeConfiguration.GetShareChance(upgradeType); if (num <= 0) { Logger.LogInfo((object)("[LuckyUpgrades] Shared upgrade skipped: " + upgradeType + " (0% chance) ✗")); return; } if (num >= 100) { bool flag = false; try { lock (_inFlightLock) { if (_inFlightUpgrades.Contains(upgradeType)) { Logger.LogWarning((object)("[LuckyUpgrades] Duplicate apply blocked for '" + upgradeType + "' (in-flight guard) ✗")); return; } _inFlightUpgrades.Add(upgradeType); } Interlocked.Exchange(ref _isApplyingSharedUpgrade, 1); applyToSelf(amount); flag = true; } finally { Interlocked.Exchange(ref _isApplyingSharedUpgrade, 0); lock (_inFlightLock) { _inFlightUpgrades.Remove(upgradeType); } } if (flag) { TrackSharedUpgrade(upgradeType, amount); UpdateStreakAndSession(won: true); Logger.LogInfo((object)$"[LuckyUpgrades] Shared upgrade applied: {upgradeType} +{amount} (100% guaranteed) ✓"); int streak = GetStreak(); UpgradeToastUI.ShowToast(upgradeType, won: true, sourcePlayerName, streak); } return; } int num2; lock (_randomLock) { num2 = _random.Next(100); } if (num2 < num) { bool flag2 = false; try { lock (_inFlightLock) { if (_inFlightUpgrades.Contains(upgradeType)) { Logger.LogWarning((object)("[LuckyUpgrades] Duplicate apply blocked for '" + upgradeType + "' (in-flight guard) ✗")); return; } _inFlightUpgrades.Add(upgradeType); } Interlocked.Exchange(ref _isApplyingSharedUpgrade, 1); applyToSelf(amount); flag2 = true; } finally { Interlocked.Exchange(ref _isApplyingSharedUpgrade, 0); lock (_inFlightLock) { _inFlightUpgrades.Remove(upgradeType); } } if (flag2) { TrackSharedUpgrade(upgradeType, amount); UpdateStreakAndSession(won: true); Logger.LogInfo((object)$"[LuckyUpgrades] Shared upgrade applied: {upgradeType} +{amount} (rolled: {num2} | chance: {num}%) ✓"); int streak2 = GetStreak(); UpgradeToastUI.ShowToast(upgradeType, won: true, sourcePlayerName, streak2); } } else { UpdateStreakAndSession(won: false); Logger.LogInfo((object)$"[LuckyUpgrades] Shared upgrade missed: {upgradeType} (rolled: {num2} | chance: {num}%) ✗"); int streak3 = GetStreak(); UpgradeToastUI.ShowToast(upgradeType, won: false, sourcePlayerName, streak3); } } catch (Exception ex) { Logger.LogError((object)("[LuckyUpgrades] Error in ApplySharedUpgradeToSelf: " + ex.Message + "\n" + ex.StackTrace)); } } private static void UpdateStreakAndSession(bool won) { lock (_streakLock) { if (won) { _currentStreak = ((_currentStreak < 0) ? 1 : (_currentStreak + 1)); } else { _currentStreak = ((_currentStreak <= 0) ? (_currentStreak - 1) : (-1)); } } lock (_sessionLock) { _sessionRollsTotal++; if (won) { _sessionUpgradesReceived++; } } } private static int GetStreak() { lock (_streakLock) { return _currentStreak; } } internal static void ResetSessionStats() { lock (_streakLock) { _currentStreak = 0; } lock (_sessionLock) { _sessionUpgradesReceived = 0; _sessionRollsTotal = 0; } lock (_cooldownLock) { _lastTriggerTime.Clear(); } } internal static (int received, int total) GetSessionStats() { lock (_sessionLock) { return (received: _sessionUpgradesReceived, total: _sessionRollsTotal); } } } public class UpgradeReapplyRunner : MonoBehaviour { private static readonly HashSet<string> SESSION_END_LEVELS = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "Level - Main Menu", "Level - Lobby Menu", "Level - MainMenu", "Level - LobbyMenu", "Main Menu", "Lobby", "Lobby Menu" }; private string _lastLevelName = ""; private float _reapplyDelay = 0f; private bool _pendingReapply = false; private const float REAPPLY_DELAY_SECONDS = 3f; private static bool IsSessionEndLevel(string levelName) { if (SESSION_END_LEVELS.Contains(levelName)) { return true; } return levelName.IndexOf("menu", StringComparison.OrdinalIgnoreCase) >= 0 || levelName.IndexOf("lobby", StringComparison.OrdinalIgnoreCase) >= 0; } private void Update() { string text = ""; try { if ((Object)(object)RunManager.instance != (Object)null && (Object)(object)RunManager.instance.levelCurrent != (Object)null) { text = ((Object)RunManager.instance.levelCurrent).name; } } catch { } if (!string.IsNullOrEmpty(text) && text != _lastLevelName) { Plugin.Logger.LogInfo((object)("[LuckyUpgrades] Level changed: " + _lastLevelName + " -> " + text)); _lastLevelName = text; if (IsSessionEndLevel(text)) { var (received, num) = Plugin.GetSessionStats(); if (num > 0) { UpgradeConfig upgradeConfiguration = Plugin.UpgradeConfiguration; if (upgradeConfiguration == null || upgradeConfiguration.ShowSessionSummary.Value) { UpgradeToastUI.ShowSessionSummary(received, num); } } lock (Plugin.SharedUpgradesLock) { Plugin._sharedUpgrades.Clear(); } lock (Plugin.MySteamIDLock) { Plugin._mySteamID = null; } Plugin.ResetSessionStats(); Plugin.Logger.LogInfo((object)"[LuckyUpgrades] Session ended. All tracked data cleared."); return; } lock (Plugin.SharedUpgradesLock) { if (!PhotonNetwork.IsMasterClient && Plugin._sharedUpgrades.Count > 0) { _pendingReapply = true; _reapplyDelay = 3f; Plugin.Logger.LogInfo((object)$"[LuckyUpgrades] Scheduled reapply in {3f}s..."); } else if (PhotonNetwork.IsMasterClient && Plugin._sharedUpgrades.Count > 0) { Plugin.Logger.LogInfo((object)"[LuckyUpgrades] Host detected — skipping reapply (upgrades persist natively)."); } } } if (!_pendingReapply) { return; } _reapplyDelay -= Time.deltaTime; if (!(_reapplyDelay <= 0f)) { return; } PlayerAvatar val = SemiFunc.PlayerAvatarLocal(); if ((Object)(object)val == (Object)null) { _reapplyDelay = 0.5f; return; } string mySteamID = Plugin.GetMySteamID(); if (string.IsNullOrEmpty(mySteamID)) { _reapplyDelay = 0.5f; return; } _pendingReapply = false; Plugin.ReapplySharedUpgrades(); } } public class UpgradeToastUI : MonoBehaviour { private enum ToastKind { ShareResult, Picker, SessionSummary } private struct ToastData { public string upgradeType; public bool won; public string sourcePlayerName; public int streak; public ToastKind toastKind; public int sessionReceived; public int sessionTotal; } private class ActiveToast { public string upgradeType; public bool won; public string sourcePlayerName; public int streak; public ToastKind toastKind; public int sessionReceived; public int sessionTotal; public float holdDuration; public float elapsed; } private const float W = 300f; private const float H = 115f; private const float HEADER_H = 22f; private const float ACCENT_W = 5f; private const float ICON_SIZE = 32f; private const float ICON_MARGIN = 10f; private const float BAR_H = 3f; private const float MARGIN_RIGHT = 20f; private const float MARGIN_BOTTOM = 80f; private const float STACK_GAP = 6f; private const float SLIDE_DUR = 0.2f; private const float FADE_DUR = 0.45f; private static readonly Color WIN_ACCENT = new Color(1f, 0.82f, 0f, 1f); private static readonly Color LOSE_ACCENT = new Color(0.9f, 0.18f, 0.18f, 1f); private static readonly Color BG_DARK = new Color(0.055f, 0.059f, 0.071f, 0.97f); private static readonly Color HEADER_BG = new Color(1f, 1f, 1f, 0.03f); private static readonly Color TEAL_HEADER = new Color(0.2f, 0.78f, 0.68f, 1f); private static readonly Color TEXT_MAIN = new Color(0.96f, 0.96f, 0.92f, 1f); private static readonly Color TEXT_DIM = new Color(1f, 1f, 1f, 0.3f); private static readonly Color BORDER_DIM = new Color(1f, 1f, 1f, 0.08f); private static readonly Color WIN_TEXT = new Color(1f, 0.9f, 0.2f, 1f); private static readonly Color LOSE_TEXT = new Color(1f, 0.38f, 0.38f, 1f); private static readonly Dictionary<string, string> Labels = new Dictionary<string, string> { { "Health", "HEALTH" }, { "Energy", "ENERGY" }, { "SprintSpeed", "SPRINT SPEED" }, { "ExtraJump", "EXTRA JUMP" }, { "TumbleLaunch", "TUMBLE LAUNCH" }, { "TumbleClimb", "TUMBLE CLIMB" }, { "TumbleWings", "TUMBLE WINGS" }, { "CrouchRest", "CROUCH REST" }, { "GrabRange", "GRAB RANGE" }, { "GrabStrength", "GRAB STRENGTH" }, { "GrabThrow", "GRAB THROW" }, { "MapPlayerCount", "MAP PLAYER COUNT" }, { "DeathHeadBattery", "DEATH HEAD BATT." } }; private static UpgradeToastUI _instance; private static readonly Queue<ToastData> _queue = new Queue<ToastData>(); private readonly List<ActiveToast> _active = new List<ActiveToast>(); private Texture2D _texWhite; private Texture2D _texScanline; private GUIStyle _stHeader; private GUIStyle _stBadge; private GUIStyle _stIcon; private GUIStyle _stName; private GUIStyle _stResult; private GUIStyle _stFoot; private bool _assetsReady; public static void ShowToast(string upgradeType, bool won, string sourcePlayerName = "", int streak = 0) { if ((Object)(object)_instance == (Object)null) { return; } UpgradeConfig upgradeConfiguration = Plugin.UpgradeConfiguration; if (upgradeConfiguration != null && !upgradeConfiguration.ShowUpgradeNotifications.Value) { return; } lock (_queue) { _queue.Enqueue(new ToastData { upgradeType = upgradeType, won = won, sourcePlayerName = sourcePlayerName, streak = streak, toastKind = ToastKind.ShareResult }); } } public static void ShowPickerToast(string upgradeType) { if ((Object)(object)_instance == (Object)null) { return; } UpgradeConfig upgradeConfiguration = Plugin.UpgradeConfiguration; if (upgradeConfiguration != null && !upgradeConfiguration.ShowUpgradeNotifications.Value) { return; } lock (_queue) { _queue.Enqueue(new ToastData { upgradeType = upgradeType, won = true, toastKind = ToastKind.Picker }); } } public static void ShowSessionSummary(int received, int total) { if ((Object)(object)_instance == (Object)null) { return; } UpgradeConfig upgradeConfiguration = Plugin.UpgradeConfiguration; if (upgradeConfiguration != null && !upgradeConfiguration.ShowUpgradeNotifications.Value) { return; } lock (_queue) { _queue.Enqueue(new ToastData { toastKind = ToastKind.SessionSummary, sessionReceived = received, sessionTotal = total, won = (received > 0) }); } } private void Awake() { _instance = this; } private void OnDestroy() { if ((Object)(object)_instance == (Object)(object)this) { _instance = null; } } private void Update() { lock (_queue) { while (_queue.Count > 0) { ToastData toastData = _queue.Dequeue(); float holdDuration = ((toastData.toastKind == ToastKind.SessionSummary) ? ((Plugin.UpgradeConfiguration?.NotificationDisplayTime.Value ?? 3f) + 1.5f) : (Plugin.UpgradeConfiguration?.NotificationDisplayTime.Value ?? 3f)); _active.Add(new ActiveToast { upgradeType = toastData.upgradeType, won = toastData.won, sourcePlayerName = (toastData.sourcePlayerName ?? ""), streak = toastData.streak, toastKind = toastData.toastKind, sessionReceived = toastData.sessionReceived, sessionTotal = toastData.sessionTotal, holdDuration = holdDuration, elapsed = 0f }); } } float unscaledDeltaTime = Time.unscaledDeltaTime; for (int num = _active.Count - 1; num >= 0; num--) { _active[num].elapsed += unscaledDeltaTime; if (_active[num].elapsed >= _active[num].holdDuration + 0.45f) { _active.RemoveAt(num); } } } private void OnGUI() { //IL_0120: Unknown result type (might be due to invalid IL or missing references) if (_active.Count == 0) { return; } EnsureAssets(); float num = Screen.width; float num2 = Screen.height; for (int i = 0; i < _active.Count; i++) { ActiveToast activeToast = _active[i]; float num3 = num - 20f - 300f; float num4 = num2 - 80f - 115f - (float)i * 121f; float num5 = Mathf.Clamp01(activeToast.elapsed / 0.2f); float num6 = 1f - Mathf.Pow(1f - num5, 3f); float num7 = Mathf.Lerp(320f, 0f, num6); float x = num3 + num7; float y = num4; float a = 1f; if (activeToast.elapsed > activeToast.holdDuration) { a = 1f - Mathf.Clamp01((activeToast.elapsed - activeToast.holdDuration) / 0.45f); } DrawToast(x, y, activeToast, a); } GUI.color = Color.white; } private void DrawToast(float x, float y, ActiveToast t, float a) { switch (t.toastKind) { case ToastKind.Picker: DrawPickerToast(x, y, t, a); break; case ToastKind.SessionSummary: DrawSummaryToast(x, y, t, a); break; default: DrawShareResultToast(x, y, t, a); break; } } private void DrawShareResultToast(float x, float y, ActiveToast t, float a) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_021c: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_023e: Unknown result type (might be due to invalid IL or missing references) //IL_037e: Unknown result type (might be due to invalid IL or missing references) //IL_038b: Unknown result type (might be due to invalid IL or missing references) //IL_0313: Unknown result type (might be due to invalid IL or missing references) //IL_02f1: Unknown result type (might be due to invalid IL or missing references) //IL_0318: Unknown result type (might be due to invalid IL or missing references) //IL_0325: Unknown result type (might be due to invalid IL or missing references) //IL_035b: Unknown result type (might be due to invalid IL or missing references) Color accent = (t.won ? WIN_ACCENT : LOSE_ACCENT); DrawPanel(x, y, 300f, 115f, accent, a); DrawBadge(x, y, t.won ? "WIN" : "MISS", accent, a); _stHeader.normal.textColor = Aa(TEAL_HEADER); GUI.Label(new Rect(x + 5f + 7f, y + 1f, 247f, 22f), "LUCKY UPGRADES", _stHeader); float num = x + 5f + 10f; float num2 = y + 22f + 8f; DrawIconBox(num, num2, t.won ? "+" : "x", accent, a); float num3 = num + 32f + 8f; float num4 = x + 300f - 8f - num3; string value; string text = (Labels.TryGetValue(t.upgradeType, out value) ? value : t.upgradeType.ToUpperInvariant()); _stName.normal.textColor = Aa(TEXT_MAIN); GUI.Label(new Rect(num3, num2 - 1f, num4, 22f), text, _stName); _stResult.normal.textColor = (t.won ? Aa(WIN_TEXT) : Aa(LOSE_TEXT)); GUI.Label(new Rect(num3, num2 + 18f, num4, 16f), t.won ? "+ YOU GOT IT!" : "- NOT THIS TIME", _stResult); bool flag = Plugin.UpgradeConfiguration?.ShowSourcePlayerName.Value ?? true; if (flag && !string.IsNullOrEmpty(t.sourcePlayerName)) { _stFoot.normal.textColor = Aa(TEXT_DIM); GUI.Label(new Rect(num3, num2 + 36f, num4, 16f), "from " + t.sourcePlayerName, _stFoot); } bool flag2 = Plugin.UpgradeConfiguration?.ShowStreakCounter.Value ?? true; int num5 = Math.Abs(t.streak); if (flag2 && num5 >= 2) { string text2 = ((t.streak > 0) ? $"{num5} wins in a row!" : $"{num5} misses in a row"); Color textColor = ((t.streak > 0) ? new Color(1f, 0.95f, 0.3f, 0.9f * a) : new Color(0.9f, 0.4f, 0.4f, 0.8f * a)); _stFoot.normal.textColor = textColor; float num6 = ((flag && !string.IsNullOrEmpty(t.sourcePlayerName)) ? (num2 + 52f) : (num2 + 36f)); GUI.Label(new Rect(num3, num6, num4, 16f), text2, _stFoot); } DrawProgressBar(x, y, t.elapsed, t.holdDuration, accent, a); GUI.color = Color.white; Color Aa(Color c) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) return new Color(c.r, c.g, c.b, c.a * a); } } private void DrawPickerToast(float x, float y, ActiveToast t, float a) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_0200: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Unknown result type (might be due to invalid IL or missing references) Color accent = default(Color); ((Color)(ref accent))..ctor(0.2f, 0.78f, 0.68f, 1f); DrawPanel(x, y, 300f, 115f, accent, a); DrawBadge(x, y, "YOU", accent, a); _stHeader.normal.textColor = Aa(TEAL_HEADER); GUI.Label(new Rect(x + 5f + 7f, y + 1f, 247f, 22f), "LUCKY UPGRADES", _stHeader); float num = x + 5f + 10f; float num2 = y + 22f + 8f; DrawIconBox(num, num2, ">", accent, a); float num3 = num + 32f + 8f; float num4 = x + 300f - 8f - num3; string value; string text = (Labels.TryGetValue(t.upgradeType, out value) ? value : t.upgradeType.ToUpperInvariant()); _stName.normal.textColor = Aa(TEXT_MAIN); GUI.Label(new Rect(num3, num2 - 1f, num4, 22f), text, _stName); _stResult.normal.textColor = new Color(0.2f, 0.9f, 0.78f, a); GUI.Label(new Rect(num3, num2 + 18f, num4, 16f), "~ SHARING THIS UPGRADE", _stResult); _stFoot.normal.textColor = Aa(TEXT_DIM); GUI.Label(new Rect(num3, num2 + 36f, num4, 16f), "teammates are rolling now...", _stFoot); DrawProgressBar(x, y, t.elapsed, t.holdDuration, accent, a); GUI.color = Color.white; Color Aa(Color c) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) return new Color(c.r, c.g, c.b, c.a * a); } } private void DrawSummaryToast(float x, float y, ActiveToast t, float a) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_0227: Unknown result type (might be due to invalid IL or missing references) //IL_0234: Unknown result type (might be due to invalid IL or missing references) Color accent = ((t.sessionReceived > 0) ? WIN_ACCENT : LOSE_ACCENT); DrawPanel(x, y, 300f, 115f, accent, a); DrawBadge(x, y, "RUN", accent, a); _stHeader.normal.textColor = Aa(TEAL_HEADER); GUI.Label(new Rect(x + 5f + 7f, y + 1f, 247f, 22f), "LUCKY UPGRADES", _stHeader); float num = x + 5f + 10f; float num2 = y + 22f + 8f; DrawIconBox(num, num2, "#", accent, a); float num3 = num + 32f + 8f; float num4 = x + 300f - 8f - num3; _stName.normal.textColor = Aa(TEXT_MAIN); GUI.Label(new Rect(num3, num2 - 1f, num4, 22f), "RUN COMPLETE", _stName); int num5 = ((t.sessionTotal > 0) ? (t.sessionReceived * 100 / t.sessionTotal) : 0); _stResult.normal.textColor = ((t.sessionReceived > 0) ? Aa(WIN_TEXT) : Aa(LOSE_TEXT)); GUI.Label(new Rect(num3, num2 + 18f, num4, 16f), $"{t.sessionReceived} / {t.sessionTotal} upgrades", _stResult); _stFoot.normal.textColor = Aa(TEXT_DIM); GUI.Label(new Rect(num3, num2 + 36f, num4, 16f), $"{num5}% share rate this run", _stFoot); DrawProgressBar(x, y, t.elapsed, t.holdDuration, accent, a); GUI.color = Color.white; Color Aa(Color c) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) return new Color(c.r, c.g, c.b, c.a * a); } } private void DrawPanel(float x, float y, float w, float h, Color accent, float a) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) Tint(Aa(BG_DARK)); Draw(x, y, w, h); Tint(Aa(accent)); Draw(x, y, 5f, h); Tint(Aa(HEADER_BG)); Draw(x + 5f, y, w - 5f, 22f); Tint(new Color(accent.r, accent.g, accent.b, 0.55f * a)); Draw(x + 5f, y, w - 5f, 1f); Tint(new Color(accent.r, accent.g, accent.b, 0.22f * a)); Draw(x + 5f, y + 22f, w - 5f, 1f); Tint(Aa(BORDER_DIM)); Draw(x + 5f, y + h - 1f, w - 5f, 1f); Draw(x + w - 1f, y, 1f, h); Tint(Color.white); GUI.DrawTexture(new Rect(x, y, w, h), (Texture)(object)_texScanline, (ScaleMode)1); Color Aa(Color c) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) return new Color(c.r, c.g, c.b, c.a * a); } } private void DrawBadge(float x, float y, string text, Color accent, float a) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) float num = 36f; float num2 = x + 300f - num - 6f; Tint(new Color(accent.r, accent.g, accent.b, 0.22f * a)); Draw(num2, y + 4f, num, 14f); DrawBorder(num2, y + 4f, num, 14f, new Color(accent.r, accent.g, accent.b, 0.55f * a)); _stBadge.normal.textColor = new Color(accent.r, accent.g, accent.b, a); GUI.Label(new Rect(num2, y + 4f, num, 14f), text, _stBadge); } private void DrawIconBox(float iconX, float iconY, string glyph, Color accent, float a) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) Tint(new Color(accent.r, accent.g, accent.b, 0.15f * a)); Draw(iconX, iconY, 32f, 32f); DrawBorder(iconX, iconY, 32f, 32f, new Color(accent.r, accent.g, accent.b, 0.55f * a)); _stIcon.normal.textColor = new Color(accent.r, accent.g, accent.b, a); GUI.Label(new Rect(iconX, iconY, 32f, 32f), glyph, _stIcon); } private void DrawProgressBar(float x, float y, float elapsed, float hold, Color accent, float a) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) float x2 = x + 5f + 10f; float y2 = y + 115f - 16f; float num = 277f; float num2 = Mathf.Clamp01(1f - elapsed / hold); Tint(new Color(1f, 1f, 1f, 0.07f * a)); Draw(x2, y2, num, 3f); Tint(new Color(accent.r, accent.g, accent.b, 0.55f * a)); Draw(x2, y2, num * num2, 3f); } private void Tint(Color c) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) GUI.color = c; } private void Draw(float x, float y, float w, float h) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) GUI.DrawTexture(new Rect(x, y, w, h), (Texture)(object)_texWhite); } private void DrawBorder(float x, float y, float w, float h, Color c) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) Tint(c); Draw(x, y, w, 1f); Draw(x, y + h - 1f, w, 1f); Draw(x, y, 1f, h); Draw(x + w - 1f, y, 1f, h); } private void EnsureAssets() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Expected O, but got Unknown //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Expected O, but got Unknown //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Expected O, but got Unknown //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Expected O, but got Unknown //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Expected O, but got Unknown //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Expected O, but got Unknown if (!_assetsReady) { _assetsReady = true; _texWhite = MakeSolid(Color.white); _texScanline = new Texture2D(1, 4, (TextureFormat)4, false) { filterMode = (FilterMode)0, wrapMode = (TextureWrapMode)0 }; _texScanline.SetPixel(0, 0, Color.clear); _texScanline.SetPixel(0, 1, Color.clear); _texScanline.SetPixel(0, 2, Color.clear); _texScanline.SetPixel(0, 3, new Color(0f, 0f, 0f, 0.08f)); _texScanline.Apply(); GUIStyle label = GUI.skin.label; _stHeader = new GUIStyle(label) { fontSize = 10, fontStyle = (FontStyle)1, alignment = (TextAnchor)3, wordWrap = false }; _stBadge = new GUIStyle(label) { fontSize = 9, fontStyle = (FontStyle)1, alignment = (TextAnchor)4, wordWrap = false }; _stIcon = new GUIStyle(label) { fontSize = 20, fontStyle = (FontStyle)1, alignment = (TextAnchor)4, wordWrap = false }; _stName = new GUIStyle(label) { fontSize = 14, fontStyle = (FontStyle)1, alignment = (TextAnchor)0, wordWrap = false }; _stResult = new GUIStyle(label) { fontSize = 11, fontStyle = (FontStyle)1, alignment = (TextAnchor)0, wordWrap = false }; _stFoot = new GUIStyle(label) { fontSize = 10, fontStyle = (FontStyle)1, alignment = (TextAnchor)0, wordWrap = false }; } } private static Texture2D MakeSolid(Color col) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown //IL_001e: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(1, 1, (TextureFormat)4, false) { filterMode = (FilterMode)0, wrapMode = (TextureWrapMode)1 }; val.SetPixel(0, 0, col); val.Apply(); return val; } } public class UpgradeConfig { private readonly ConfigFile _config; public ConfigEntry<int> ChanceToActivatePlayerHealth { get; private set; } public ConfigEntry<int> ChanceToActivatePlayerEnergy { get; private set; } public ConfigEntry<int> ChanceToActivatePlayerSprintSpeed { get; private set; } public ConfigEntry<int> ChanceToActivatePlayerExtraJump { get; private set; } public ConfigEntry<int> ChanceToActivatePlayerTumbleLaunch { get; private set; } public ConfigEntry<int> ChanceToActivatePlayerGrabRange { get; private set; } public ConfigEntry<int> ChanceToActivatePlayerGrabStrength { get; private set; } public ConfigEntry<int> ChanceToActivatePlayerGrabThrow { get; private set; } public ConfigEntry<int> ChanceToActivatePlayerTumbleClimb { get; private set; } public ConfigEntry<int> ChanceToActivatePlayerTumbleWings { get; private set; } public ConfigEntry<int> ChanceToActivatePlayerCrouchRest { get; private set; } public ConfigEntry<int> ChanceToActivateDeathHeadBattery { get; private set; } public ConfigEntry<int> ChanceToActivateMapPlayerCount { get; private set; } public ConfigEntry<int> DefaultModdedUpgradeChance { get; private set; } public ConfigEntry<bool> ShowUpgradeNotifications { get; private set; } public ConfigEntry<float> NotificationDisplayTime { get; private set; } public ConfigEntry<bool> ShowSourcePlayerName { get; private set; } public ConfigEntry<bool> ShowPickerToast { get; private set; } public ConfigEntry<bool> ShowStreakCounter { get; private set; } public ConfigEntry<bool> ShowSessionSummary { get; private set; } public ConfigEntry<float> UpgradeCooldownSeconds { get; private set; } public ConfigEntry<bool> IndependentRolls { get; private set; } public UpgradeConfig(ConfigFile config) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Expected O, but got Unknown //IL_0296: Unknown result type (might be due to invalid IL or missing references) //IL_02a0: Expected O, but got Unknown _config = config; ShowUpgradeNotifications = config.Bind<bool>("Notifications", "ShowUpgradeNotifications", true, "Show an in-game toast notification when an upgrade is shared or missed. Set false to disable."); NotificationDisplayTime = config.Bind<float>("Notifications", "NotificationDisplayTime", 3f, new ConfigDescription("How long (in seconds) each upgrade notification stays on screen before fading out. Does not include the 0.45s fade animation.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 15f), Array.Empty<object>())); ShowSourcePlayerName = config.Bind<bool>("Notifications", "ShowSourcePlayerName", true, "Show the name of the player whose pickup triggered the share roll on the toast notification."); ShowPickerToast = config.Bind<bool>("Notifications", "ShowPickerToast", true, "Show a toast to the player who picked up the upgrade, telling them the share roll is happening."); ShowStreakCounter = config.Bind<bool>("Notifications", "ShowStreakCounter", true, "Show a win/loss streak counter on the toast (e.g. '3 wins in a row!'). Resets on session end."); ShowSessionSummary = config.Bind<bool>("Notifications", "ShowSessionSummary", true, "Show a summary toast when returning to the lobby listing how many upgrades you received this run."); UpgradeCooldownSeconds = config.Bind<float>("Gameplay", "UpgradeCooldownSeconds", 1.5f, new ConfigDescription("Minimum seconds between share rolls for the same upgrade type. Prevents duplicate toasts caused by physics jitter firing the pickup twice. Set to 0 to disable.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>())); IndependentRolls = config.Bind<bool>("Gameplay", "IndependentRolls", true, "When true (default), each player rolls their own dice independently — you may get the upgrade while a teammate doesn't. When false, everyone in the lobby shares the same outcome. All players must have the same setting for consistent behaviour."); ChanceToActivatePlayerHealth = Bind("ChanceToActivatePlayerHealth", "% Chance to share the Health upgrade"); ChanceToActivatePlayerEnergy = Bind("ChanceToActivatePlayerEnergy", "% Chance to share the Energy (Stamina) upgrade"); ChanceToActivatePlayerSprintSpeed = Bind("ChanceToActivatePlayerSprintSpeed", "% Chance to share the Sprint Speed upgrade"); ChanceToActivatePlayerExtraJump = Bind("ChanceToActivatePlayerExtraJump", "% Chance to share the Extra Jump upgrade"); ChanceToActivatePlayerTumbleLaunch = Bind("ChanceToActivatePlayerTumbleLaunch", "% Chance to share the Tumble Launch upgrade"); ChanceToActivatePlayerGrabRange = Bind("ChanceToActivatePlayerGrabRange", "% Chance to share the Grab Range upgrade"); ChanceToActivatePlayerGrabStrength = Bind("ChanceToActivatePlayerGrabStrength", "% Chance to share the Grab Strength upgrade"); ChanceToActivatePlayerGrabThrow = Bind("ChanceToActivatePlayerGrabThrow", "% Chance to share the Grab Throw upgrade"); ChanceToActivatePlayerTumbleClimb = Bind("ChanceToActivatePlayerTumbleClimb", "% Chance to share the Tumble Climb upgrade"); ChanceToActivatePlayerTumbleWings = Bind("ChanceToActivatePlayerTumbleWings", "% Chance to share the Tumble Wings upgrade"); ChanceToActivatePlayerCrouchRest = Bind("ChanceToActivatePlayerCrouchRest", "% Chance to share the Crouch Rest upgrade"); ChanceToActivateDeathHeadBattery = Bind("ChanceToActivateDeathHeadBattery", "% Chance to share the Death Head Battery upgrade"); ChanceToActivateMapPlayerCount = Bind("ChanceToActivateMapPlayerCount", "% Chance to share the Map Player Count upgrade"); DefaultModdedUpgradeChance = config.Bind<int>("ModdedUpgrades", "DefaultModdedUpgradeChance", 25, new ConfigDescription("Default % chance used for modded upgrades that don't specify their own share chance. Also used as a fallback for any unrecognised upgrade type.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())); } public int GetShareChance(string upgradeType) { switch (upgradeType) { case "Health": return ChanceToActivatePlayerHealth.Value; case "Energy": return ChanceToActivatePlayerEnergy.Value; case "SprintSpeed": return ChanceToActivatePlayerSprintSpeed.Value; case "ExtraJump": return ChanceToActivatePlayerExtraJump.Value; case "TumbleLaunch": return ChanceToActivatePlayerTumbleLaunch.Value; case "TumbleClimb": return ChanceToActivatePlayerTumbleClimb.Value; case "TumbleWings": return ChanceToActivatePlayerTumbleWings.Value; case "CrouchRest": return ChanceToActivatePlayerCrouchRest.Value; case "GrabRange": return ChanceToActivatePlayerGrabRange.Value; case "GrabStrength": return ChanceToActivatePlayerGrabStrength.Value; case "GrabThrow": return ChanceToActivatePlayerGrabThrow.Value; case "MapPlayerCount": return ChanceToActivateMapPlayerCount.Value; case "DeathHeadBattery": return ChanceToActivateDeathHeadBattery.Value; default: { ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogWarning((object)("[LuckyUpgrades] GetShareChance: unknown upgrade type '" + upgradeType + "'. " + $"Using DefaultModdedUpgradeChance ({DefaultModdedUpgradeChance.Value}%).")); } return DefaultModdedUpgradeChance.Value; } } } public ConfigEntry<int> BindModdedUpgrade(string upgradeId, int initialChance = 25) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected O, but got Unknown initialChance = Math.Max(0, Math.Min(100, initialChance)); ConfigEntry<int> val = _config.Bind<int>("ModdedUpgrades", upgradeId, 25, new ConfigDescription($"% Chance to share the '{upgradeId}' upgrade (added by another mod). Default: {25}%.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())); if (val.Value == 25 && initialChance != 25) { val.Value = initialChance; } return val; } private ConfigEntry<int> Bind(string key, string description, int defaultValue = 25) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown return _config.Bind<int>("Upgrades", key, defaultValue, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())); } } public static class MyPluginInfo { public const string PLUGIN_GUID = "LuckyUpgradesFork"; public const string PLUGIN_NAME = "LuckyUpgradesFork"; public const string PLUGIN_VERSION = "1.0.0"; } }