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 FreeUpgradeEveryRound v4.0.0
FreeUpgradeEveryRound.dll
Decompiled a week agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using MenuLib; using MenuLib.MonoBehaviors; using Microsoft.CodeAnalysis; using Photon.Pun; using Photon.Realtime; using REPOUpgradeAPI; using TMPro; using UnityEngine; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: IgnoresAccessChecksTo("")] [assembly: AssemblyCompany("Kai")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+871bd6358900aaf38ac3311f0b26e1369b55f7b2")] [assembly: AssemblyProduct("FreeUpgradeEveryRound")] [assembly: AssemblyTitle("FreeUpgradeEveryRound")] [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.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace FreeUpgradeEveryRound { internal static class DraftGenerator { internal static readonly string[] UpgradeKeys = new string[12] { "Stamina", "Health", "Wings", "Launch", "Climb", "Rest", "Speed", "Jump", "Strength", "Range", "Battery", "Map" }; internal static string[] GenerateDraft() { List<string> list = new List<string>(); string[] upgradeKeys = UpgradeKeys; foreach (string text in upgradeKeys) { int weight = UpgradeConfig.GetWeight(text); if (weight > 0) { for (int j = 0; j < weight; j++) { list.Add(text); } } } HashSet<string> hashSet = new HashSet<string>(); while (hashSet.Count < UpgradeConfig.DraftCount.Value && list.Count > 0) { int index = Random.Range(0, list.Count); string chosen = list[index]; hashSet.Add(chosen); list.RemoveAll((string x) => x == chosen); } return new List<string>(hashSet).ToArray(); } } internal static class DraftKeyMap { private static readonly Dictionary<string, string> map = new Dictionary<string, string> { { "Stamina", "energy" }, { "Health", "health" }, { "Wings", "wings" }, { "Launch", "launch" }, { "Climb", "climb" }, { "Rest", "crouch" }, { "Speed", "sprint" }, { "Jump", "jump" }, { "Strength", "grabstr" }, { "Range", "grabrange" }, { "Battery", "battery" }, { "Map", "mapcount" } }; public static string ToAPIKey(string draftKey) { if (draftKey == null) { return string.Empty; } if (map.TryGetValue(draftKey, out string value)) { return value; } FreeUpgradeEveryRound.Logger.LogWarning((object)("[FreeUpgrade] Unknown draft key: " + draftKey)); return draftKey.ToLowerInvariant(); } } [BepInPlugin("Kai.FreeUpgradeEveryRound", "FreeUpgradeEveryRound", "0.2.3")] public class FreeUpgradeEveryRound : BaseUnityPlugin { internal static Dictionary<string, int> FreeSlotsDict = new Dictionary<string, int>(); internal static Dictionary<string, int> TotalGrantedFreeSlotsDict = new Dictionary<string, int>(); internal static readonly Dictionary<string, string[]> RuntimeDrafts = new Dictionary<string, string[]>(); internal static FreeUpgradeEveryRound Instance { get; private set; } = null; internal static ManualLogSource Logger => Instance._logger; private ManualLogSource _logger => ((BaseUnityPlugin)this).Logger; internal Harmony? Harmony { get; set; } private void Awake() { Instance = this; ((Component)this).gameObject.transform.parent = null; ((Object)((Component)this).gameObject).hideFlags = (HideFlags)61; UpgradeConfig.Load(((BaseUnityPlugin)this).Config); UpgradeConfig.Init(((BaseUnityPlugin)this).Config); Patch(); Logger.LogInfo((object)$"{((BaseUnityPlugin)this).Info.Metadata.GUID} v{((BaseUnityPlugin)this).Info.Metadata.Version} has loaded!"); } internal void Patch() { //IL_0019: 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_0020: Expected O, but got Unknown //IL_0025: Expected O, but got Unknown if (Harmony == null) { Harmony val = new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID); Harmony val2 = val; Harmony = val; } Harmony.PatchAll(); } internal static int ReconcileFreeSlotsForPlayer(string steamID, int currentLevel) { if (!SemiFunc.IsMasterClientOrSingleplayer()) { return 0; } if (string.IsNullOrEmpty(steamID)) { Logger.LogWarning((object)"[FreeUpgrade] Reconcile skipped: steamID is empty."); return 0; } if (!FreeSlotsDict.ContainsKey(steamID)) { FreeSlotsDict[steamID] = 0; } int num = Mathf.Max(0, currentLevel); int num2 = Mathf.Max(0, UpgradeConfig.FreePerLevel.Value); if (!TotalGrantedFreeSlotsDict.TryGetValue(steamID, out var value)) { value = 0; TotalGrantedFreeSlotsDict[steamID] = 0; Logger.LogInfo((object)("[FreeUpgrade] Grant ledger created: steamID=" + steamID + ", totalGranted=0")); } int num3 = num * num2; int num4 = num3 - value; if (num4 <= 0) { Logger.LogInfo((object)($"[FreeUpgrade] No new grant: steamID={steamID}, level={num}, " + $"rate={num2}, entitlement={num3}, " + $"totalGranted={value}, remaining={FreeSlotsDict[steamID]}")); return 0; } FreeSlotsDict[steamID] += num4; TotalGrantedFreeSlotsDict[steamID] = value + num4; Logger.LogInfo((object)($"[FreeUpgrade] Grant reconciled: steamID={steamID}, +{num4}, " + $"level={num}, rate={num2}, " + $"entitlement={num3}, totalGranted={TotalGrantedFreeSlotsDict[steamID]}, " + $"remaining={FreeSlotsDict[steamID]}")); return num4; } internal static void SyncFromStatsManager() { if (!SemiFunc.IsMasterClientOrSingleplayer()) { return; } StatsManager instance = StatsManager.instance; if ((Object)(object)instance == (Object)null) { Logger.LogWarning((object)"[FreeUpgrade] StatsManager not found. Save sync skipped."); return; } SortedDictionary<string, Dictionary<string, int>> dictionaryOfDictionaries = instance.dictionaryOfDictionaries; if (dictionaryOfDictionaries.TryGetValue("FreeUpgrade_freeSlots", out var value)) { FreeSlotsDict = value; } if (dictionaryOfDictionaries.TryGetValue("FreeUpgrade_totalGranted", out var value2)) { TotalGrantedFreeSlotsDict = value2; } Logger.LogInfo((object)"[FreeUpgrade] StatsManager -> runtime state synchronized (FreeSlots / TotalGranted)."); } internal static void SyncToStatsManager() { if (SemiFunc.IsMasterClientOrSingleplayer()) { StatsManager instance = StatsManager.instance; if ((Object)(object)instance == (Object)null) { Logger.LogWarning((object)"[FreeUpgrade] StatsManager not found. Save writeback skipped."); return; } SortedDictionary<string, Dictionary<string, int>> dictionaryOfDictionaries = instance.dictionaryOfDictionaries; dictionaryOfDictionaries["FreeUpgrade_freeSlots"] = FreeSlotsDict; dictionaryOfDictionaries["FreeUpgrade_totalGranted"] = TotalGrantedFreeSlotsDict; Logger.LogInfo((object)"[FreeUpgrade] Runtime state -> StatsManager synchronized (FreeSlots / TotalGranted)."); } } } public class FreeUpgradeNet : MonoBehaviourPun { public static FreeUpgradeNet Instance { get; private set; } private void Awake() { Instance = this; } [PunRPC] public void FUE_ReceiveDraft(string steamID, string[] draft, PhotonMessageInfo info = default(PhotonMessageInfo)) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (!SemiFunc.MasterOnlyRPC(info)) { return; } FreeUpgradeEveryRound.RuntimeDrafts[steamID] = draft; if ((Object)(object)PlayerController.instance == (Object)null) { return; } string playerSteamID = PlayerController.instance.playerSteamID; if (playerSteamID != steamID) { return; } if (FreeUpgradeEveryRound.FreeSlotsDict.TryGetValue(steamID, out var value) && value <= 0) { CustomUI.Close(); return; } if ((Object)(object)MenuManager.instance != (Object)null) { MenuManager.instance.PageCloseAll(); } FreeUpgrade_InputPatch.OpenUI(steamID); } [PunRPC] public void FUE_UpdateFreeSlots(string steamID, int newFreeSlots, PhotonMessageInfo info = default(PhotonMessageInfo)) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (SemiFunc.MasterOnlyRPC(info)) { FreeUpgradeEveryRound.FreeSlotsDict[steamID] = newFreeSlots; FreeUpgradeEveryRound.Logger.LogInfo((object)$"[FreeUpgrade] Authoritative free slots for {steamID}: {newFreeSlots}"); if (!((Object)(object)PlayerController.instance == (Object)null) && !(PlayerController.instance.playerSteamID != steamID) && newFreeSlots <= 0) { CustomUI.Close(); } } } [PunRPC] public void FUE_SelectUpgrade(string steamID, string typeKey, PhotonMessageInfo info = default(PhotonMessageInfo)) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) if (SemiFunc.IsMasterClient()) { PlayerAvatar val = SemiFunc.PlayerAvatarGetFromSteamID(steamID); if ((Object)(object)val == (Object)null || (Object)(object)val.photonView == (Object)null || !SemiFunc.OwnerOnlyRPC(info, val.photonView)) { FreeUpgradeEveryRound.Logger.LogWarning((object)("[FreeUpgrade] Rejected selection: sender does not own steamID=" + steamID)); } else { ProcessAuthoritativeSelection(steamID, typeKey); } } } private static void ProcessAuthoritativeSelection(string steamID, string typeKey) { if (!SemiFunc.IsMasterClientOrSingleplayer()) { return; } if (!FreeUpgradeEveryRound.FreeSlotsDict.TryGetValue(steamID, out var value) || value <= 0) { FreeUpgradeEveryRound.Logger.LogWarning((object)("[FreeUpgrade] Player " + steamID + " has no authoritative free slots.")); return; } if (!FreeUpgradeEveryRound.RuntimeDrafts.TryGetValue(steamID, out string[] value2) || Array.IndexOf(value2, typeKey) < 0) { FreeUpgradeEveryRound.Logger.LogWarning((object)("[FreeUpgrade] Rejected selection outside authoritative draft: " + steamID + " -> " + typeKey)); return; } FreeUpgradeEveryRound.FreeSlotsDict[steamID] = value - 1; int num = FreeUpgradeEveryRound.FreeSlotsDict[steamID]; SendFreeSlotsToAll(steamID, num); string text = DraftKeyMap.ToAPIKey(typeKey); UpgradeAPI.Add(text, 1, steamID); FreeUpgradeEveryRound.Logger.LogInfo((object)("[FreeUpgrade] " + steamID + " selected " + typeKey + " -> " + text + ". " + $"Authoritative free slots now: {num}")); if (num > 0) { string[] array = DraftGenerator.GenerateDraft(); FreeUpgradeEveryRound.RuntimeDrafts[steamID] = array; SendDraftToAll(steamID, array); return; } FreeUpgradeEveryRound.RuntimeDrafts.Remove(steamID); if (!SemiFunc.IsMultiplayer()) { CustomUI.Close(); } } public static void SendFreeSlotsToAll(string steamID, int freeSlots) { if (SemiFunc.IsMasterClientOrSingleplayer()) { if (!SemiFunc.IsMultiplayer()) { FreeUpgradeEveryRound.FreeSlotsDict[steamID] = freeSlots; return; } if ((Object)(object)Instance == (Object)null || (Object)(object)((MonoBehaviourPun)Instance).photonView == (Object)null) { FreeUpgradeEveryRound.Logger.LogError((object)"[FreeUpgrade] Cannot sync free slots: FreeUpgradeNet is not ready."); return; } ((MonoBehaviourPun)Instance).photonView.RPC("FUE_UpdateFreeSlots", (RpcTarget)0, new object[2] { steamID, freeSlots }); } } public static void SendDraftToAll(string steamID, string[] draft) { if (SemiFunc.IsMasterClientOrSingleplayer()) { if (!SemiFunc.IsMultiplayer()) { FreeUpgradeEveryRound.RuntimeDrafts[steamID] = draft; FreeUpgrade_InputPatch.OpenUI(steamID); } else if ((Object)(object)Instance == (Object)null || (Object)(object)((MonoBehaviourPun)Instance).photonView == (Object)null) { FreeUpgradeEveryRound.Logger.LogError((object)"[FreeUpgrade] Cannot sync draft: FreeUpgradeNet is not ready."); } else { ((MonoBehaviourPun)Instance).photonView.RPC("FUE_ReceiveDraft", (RpcTarget)0, new object[2] { steamID, draft }); } } } public static void SendSelectToHost(string steamID, string typeKey) { if (!SemiFunc.IsMultiplayer()) { FreeUpgradeEveryRound.Logger.LogInfo((object)"[FreeUpgrade] Singleplayer: process selection locally as authority."); ProcessAuthoritativeSelection(steamID, typeKey); } else if ((Object)(object)Instance == (Object)null || (Object)(object)((MonoBehaviourPun)Instance).photonView == (Object)null) { FreeUpgradeEveryRound.Logger.LogError((object)"[FreeUpgrade] Cannot send selection: FreeUpgradeNet is not ready."); } else { ((MonoBehaviourPun)Instance).photonView.RPC("FUE_SelectUpgrade", (RpcTarget)2, new object[2] { steamID, typeKey }); } } } [HarmonyPatch(typeof(PunManager))] public static class FreeUpgradeNetPatcher { [HarmonyPostfix] [HarmonyPatch("Awake")] private static void AttachNet(PunManager __instance) { GameObject gameObject = ((Component)__instance).gameObject; if (!Object.op_Implicit((Object)(object)gameObject.GetComponent<FreeUpgradeNet>())) { gameObject.AddComponent<FreeUpgradeNet>(); } } } [HarmonyPatch(typeof(PlayerController))] internal static class FreeUpgrade_InputPatch { [HarmonyPostfix] [HarmonyPatch("Update")] private static void Postfix(PlayerController __instance) { //IL_0021: 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_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)PlayerController.instance != (Object)(object)__instance) && !((Object)(object)GameDirector.instance == (Object)null)) { KeyCode value = UpgradeConfig.OpenUIKey.Value; if ((int)value != 0 && Input.GetKeyDown(value)) { string playerSteamID = PlayerController.instance.playerSteamID; CustomUI.Open(playerSteamID); } if (Input.GetKeyDown((KeyCode)27) && (Object)(object)StatsUI.instance != (Object)null) { StatsUI.instance.showStatsTimer = 0f; } } } public static void OpenUI(string steamID) { CustomUI.Open(steamID); } } internal static class CustomUI { private static REPOPopupPage page; public static void Close() { if ((Object)(object)page != (Object)null) { page.ClosePage(false); page = null; } if ((Object)(object)StatsUI.instance != (Object)null) { StatsUI.instance.showStatsTimer = 0f; } } public static void Open(string steamID) { try { if (!FreeUpgradeEveryRound.RuntimeDrafts.TryGetValue(steamID, out string[] value)) { FreeUpgradeEveryRound.Logger.LogWarning((object)"[FreeUpgrade] No draft available."); return; } if (FreeUpgradeEveryRound.FreeSlotsDict.TryGetValue(steamID, out var value2) && value2 <= 0) { Close(); return; } if ((Object)(object)page != (Object)null) { page.ClosePage(false); Object.Destroy((Object)(object)((Component)page).gameObject); page = null; } if ((Object)(object)StatsUI.instance != (Object)null) { StatsUI.instance.Fetch(); StatsUI.instance.showStatsTimer = 999f; } page = MenuAPI.CreateREPOPopupPage("Free Upgrade UI", (PresetSide)1, false, 0f); UpgradeUI.CreateUpgradeSelectionUI(page, steamID, value); page.OpenPage(true); AdjustUIElementPositions((Transform)(object)page.menuScrollBox.scroller); FreeUpgradeEveryRound.Logger.LogInfo((object)"[FreeUpgrade] Custom UI opened."); } catch (Exception arg) { FreeUpgradeEveryRound.Logger.LogError((object)$"Custom UI failed: {arg}"); } } private static void AdjustUIElementPositions(Transform scroller) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_003b: 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) RectTransform component = ((Component)scroller).GetComponent<RectTransform>(); component.anchoredPosition = Vector2.zero; REPOButton[] componentsInChildren = ((Component)scroller).GetComponentsInChildren<REPOButton>(); float num = 40f; for (int i = 0; i < componentsInChildren.Length; i++) { RectTransform component2 = ((Component)componentsInChildren[i]).GetComponent<RectTransform>(); component2.anchoredPosition = new Vector2(0f, (float)i * num); } REPOLabel[] componentsInChildren2 = ((Component)scroller).GetComponentsInChildren<REPOLabel>(); REPOLabel[] array = componentsInChildren2; foreach (REPOLabel val in array) { RectTransform component3 = ((Component)val).GetComponent<RectTransform>(); component3.anchoredPosition = new Vector2(0f, (float)componentsInChildren.Length * num + 20f); } } } internal static class UpgradeUI { public static void CreateUpgradeSelectionUI(REPOPopupPage page, string steamID, string[] draft) { CreateFreeSlotLabel(page, steamID); float num = 60f; for (int i = 0; i < draft.Length; i++) { string upgradeKey = draft[i]; CreateUpgradeButton(steamID, upgradeKey, i, page, num * (float)i); } } private static void CreateUpgradeButton(string steamID, string upgradeKey, int index, REPOPopupPage page, float yOffset) { //IL_0033: 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_0050: Unknown result type (might be due to invalid IL or missing references) REPOButton val = MenuAPI.CreateREPOButton(upgradeKey, (Action)delegate { OnUpgradeSelected(steamID, upgradeKey); }, (Transform)(object)page.menuScrollBox.scroller, default(Vector2)); RectTransform component = ((Component)val).GetComponent<RectTransform>(); component.anchoredPosition = new Vector2(0f, 0f - yOffset); } private static void CreateFreeSlotLabel(REPOPopupPage page, string steamID) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_006d: Unknown result type (might be due to invalid IL or missing references) int remainingFreeSlots = GetRemainingFreeSlots(steamID); GameObject val = new GameObject("FreeSlotsLabel"); val.transform.SetParent((Transform)(object)page.menuScrollBox.scroller); TextMeshProUGUI val2 = val.AddComponent<TextMeshProUGUI>(); ((TMP_Text)val2).text = $"Free Slots: {remainingFreeSlots}"; ((TMP_Text)val2).fontSize = 24f; ((TMP_Text)val2).alignment = (TextAlignmentOptions)514; RectTransform component = ((Component)val2).GetComponent<RectTransform>(); component.anchoredPosition = new Vector2(0f, 70f); } private static void OnUpgradeSelected(string steamID, string upgradeKey) { FreeUpgradeNet.SendSelectToHost(steamID, upgradeKey); FreeUpgradeEveryRound.Logger.LogInfo((object)("[FreeUpgrade] Upgrade selected: " + upgradeKey + " by " + steamID)); } private static int GetRemainingFreeSlots(string steamID) { if (FreeUpgradeEveryRound.FreeSlotsDict.TryGetValue(steamID, out var value)) { return value; } return 0; } } public static class SceneUtil { private static bool isSceneLoaded; static SceneUtil() { SceneManager.sceneLoaded += OnSceneLoaded; } private static void OnSceneLoaded(Scene scene, LoadSceneMode mode) { isSceneLoaded = true; } public static string GetCurrentLevelName() { if (!isSceneLoaded) { FreeUpgradeEveryRound.Logger.LogWarning((object)"[FreeUpgrade] Waiting for scene to load, skipping level check."); return string.Empty; } RunManager instance = RunManager.instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.levelCurrent == (Object)null) { return string.Empty; } string text = ((Object)instance.levelCurrent).name.ToLowerInvariant(); FreeUpgradeEveryRound.Logger.LogInfo((object)("[FreeUpgrade] Current Level: " + text)); return text; } public static bool IsGameInProgress() { RunManager instance = RunManager.instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.levelCurrent == (Object)null) { return false; } bool flag = (SemiFunc.RunIsLevel() && !SemiFunc.RunIsRecording()) || SemiFunc.RunIsShop() || SemiFunc.RunIsLobby(); FreeUpgradeEveryRound.Logger.LogInfo((object)$"[FreeUpgrade] IsGameInProgress Check: Level={((Object)instance.levelCurrent).name}, IsInProgress={flag}"); return flag; } } internal static class UpgradeConfig { public static Dictionary<string, ConfigEntry<float>> Entries = new Dictionary<string, ConfigEntry<float>>(); public static Dictionary<string, float> Probabilities = new Dictionary<string, float>(); public static ConfigEntry<int> DraftCount; public static ConfigEntry<int> FreePerLevel; public static ConfigEntry<KeyCode> OpenUIKey; public static ConfigEntry<int> Stamina; public static ConfigEntry<int> Health; public static ConfigEntry<int> Wings; public static ConfigEntry<int> Launch; public static ConfigEntry<int> Climb; public static ConfigEntry<int> Rest; public static ConfigEntry<int> Speed; public static ConfigEntry<int> Jump; public static ConfigEntry<int> Strength; public static ConfigEntry<int> Range; public static ConfigEntry<int> Battery; public static ConfigEntry<int> MapCount; public static Dictionary<string, ConfigEntry<int>> WeightDict; public static void Init(ConfigFile config) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Expected O, but got Unknown //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Expected O, but got Unknown //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Expected O, but got Unknown //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Expected O, but got Unknown //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Expected O, but got Unknown //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Expected O, but got Unknown //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Expected O, but got Unknown //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Expected O, but got Unknown //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Expected O, but got Unknown //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Expected O, but got Unknown //IL_02c2: Unknown result type (might be due to invalid IL or missing references) //IL_02cc: Expected O, but got Unknown //IL_02ef: Unknown result type (might be due to invalid IL or missing references) //IL_02f9: Expected O, but got Unknown AcceptableValueRange<int> val = new AcceptableValueRange<int>(0, 100); Stamina = config.Bind<int>("UpgradeWeights", "Stamina", 100, new ConfigDescription("Weight (0–100)", (AcceptableValueBase)(object)val, Array.Empty<object>())); Health = config.Bind<int>("UpgradeWeights", "Health", 100, new ConfigDescription("Weight (0–100)", (AcceptableValueBase)(object)val, Array.Empty<object>())); Wings = config.Bind<int>("UpgradeWeights", "Wings", 100, new ConfigDescription("Weight (0–100)", (AcceptableValueBase)(object)val, Array.Empty<object>())); Launch = config.Bind<int>("UpgradeWeights", "Launch", 100, new ConfigDescription("Weight (0–100)", (AcceptableValueBase)(object)val, Array.Empty<object>())); Climb = config.Bind<int>("UpgradeWeights", "Climb", 100, new ConfigDescription("Weight (0–100)", (AcceptableValueBase)(object)val, Array.Empty<object>())); Rest = config.Bind<int>("UpgradeWeights", "Rest", 100, new ConfigDescription("Weight (0–100)", (AcceptableValueBase)(object)val, Array.Empty<object>())); Speed = config.Bind<int>("UpgradeWeights", "Speed", 100, new ConfigDescription("Weight (0–100)", (AcceptableValueBase)(object)val, Array.Empty<object>())); Jump = config.Bind<int>("UpgradeWeights", "Jump", 100, new ConfigDescription("Weight (0–100)", (AcceptableValueBase)(object)val, Array.Empty<object>())); Strength = config.Bind<int>("UpgradeWeights", "Strength", 100, new ConfigDescription("Weight (0–100)", (AcceptableValueBase)(object)val, Array.Empty<object>())); Range = config.Bind<int>("UpgradeWeights", "Range", 100, new ConfigDescription("Weight (0–100)", (AcceptableValueBase)(object)val, Array.Empty<object>())); Battery = config.Bind<int>("UpgradeWeights", "Battery", 0, new ConfigDescription("Weight (0–100)", (AcceptableValueBase)(object)val, Array.Empty<object>())); MapCount = config.Bind<int>("UpgradeWeights", "Map", 0, new ConfigDescription("Weight (0–100)", (AcceptableValueBase)(object)val, Array.Empty<object>())); WeightDict = new Dictionary<string, ConfigEntry<int>> { { "Stamina", Stamina }, { "Health", Health }, { "Wings", Wings }, { "Launch", Launch }, { "Climb", Climb }, { "Rest", Rest }, { "Speed", Speed }, { "Jump", Jump }, { "Strength", Strength }, { "Range", Range }, { "Battery", Battery }, { "Map", MapCount } }; DraftCount = config.Bind<int>("General", "DraftCount", 4, new ConfigDescription("How many upgrades appear (1-4)", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 4), Array.Empty<object>())); FreePerLevel = config.Bind<int>("General", "FreeSlotsPerLevel", 1, new ConfigDescription("Host setting: upgrades each player can select per completed round (0-10)", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 10), Array.Empty<object>())); } public static int GetWeight(string upgradeKey) { if (WeightDict.TryGetValue(upgradeKey, out ConfigEntry<int> value)) { return value.Value; } return 0; } internal static void Load(ConfigFile config) { OpenUIKey = config.Bind<KeyCode>("UI", "OpenUIKey", (KeyCode)112, "Key used to reopen the Free Upgrade UI. Set to None to disable."); Refresh(); } internal static void Refresh() { Probabilities.Clear(); foreach (KeyValuePair<string, ConfigEntry<float>> entry in Entries) { Probabilities[entry.Key] = entry.Value.Value; } } } } namespace FreeUpgradeEveryRound.Patchs { [HarmonyPatch(typeof(SemiFunc))] [HarmonyPatch("OnLevelGenDone")] public static class FreeUpgrade_LevelPatch { [HarmonyPostfix] private static void Postfix() { if (!SemiFunc.IsMasterClientOrSingleplayer()) { return; } if (!SceneUtil.IsGameInProgress()) { FreeUpgradeEveryRound.Logger.LogWarning((object)"[FreeUpgrade] Game is not in progress, skipping level-start reconciliation."); return; } if ((Object)(object)RunManager.instance == (Object)null || (Object)(object)GameDirector.instance == (Object)null) { FreeUpgradeEveryRound.Logger.LogWarning((object)"[FreeUpgrade] RunManager/GameDirector is not ready, skipping reconciliation."); return; } FreeUpgradeEveryRound.SyncFromStatsManager(); int levelsCompleted = RunManager.instance.levelsCompleted; FreeUpgradeEveryRound.Logger.LogInfo((object)($"[FreeUpgrade] Level-start reconciliation(authority): levelsCompleted={levelsCompleted}, " + $"rate={UpgradeConfig.FreePerLevel.Value}")); foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if ((Object)(object)player == (Object)null) { continue; } string text = ResolvePlayerId(player); if (!string.IsNullOrEmpty(text)) { FreeUpgradeEveryRound.ReconcileFreeSlotsForPlayer(text, levelsCompleted); FreeUpgradeEveryRound.FreeSlotsDict.TryGetValue(text, out var value); FreeUpgradeNet.SendFreeSlotsToAll(text, value); if (value <= 0) { FreeUpgradeEveryRound.RuntimeDrafts.Remove(text); continue; } string[] array = DraftGenerator.GenerateDraft(); FreeUpgradeEveryRound.RuntimeDrafts[text] = array; FreeUpgradeNet.SendDraftToAll(text, array); } } FreeUpgradeEveryRound.SyncToStatsManager(); } private static string ResolvePlayerId(PlayerAvatar avatar) { if (!string.IsNullOrEmpty(avatar.steamID)) { return avatar.steamID; } PhotonView photonView = avatar.photonView; Player val = (((Object)(object)photonView != (Object)null) ? photonView.Owner : null); string text; if (val != null && val.ActorNumber > 0) { text = $"LOCAL_{val.ActorNumber}"; } else { if (SemiFunc.IsMultiplayer()) { FreeUpgradeEveryRound.Logger.LogWarning((object)"[FreeUpgrade] Could not resolve stable player ID. Player skipped for this level."); return string.Empty; } text = "LOCAL_SINGLEPLAYER"; } avatar.steamID = text; FreeUpgradeEveryRound.Logger.LogWarning((object)("[FreeUpgrade] steamID was empty. Assigned fallback ID=" + text)); return text; } } [HarmonyPatch(typeof(StatsManager))] public static class FreeUpgrade_StatsPatch { [HarmonyPatch("Start")] [HarmonyPostfix] private static void InjectFreeUpgradeDictionaries(StatsManager __instance) { SortedDictionary<string, Dictionary<string, int>> dictionaryOfDictionaries = __instance.dictionaryOfDictionaries; if (!dictionaryOfDictionaries.ContainsKey("FreeUpgrade_freeSlots")) { dictionaryOfDictionaries.Add("FreeUpgrade_freeSlots", FreeUpgradeEveryRound.FreeSlotsDict); } if (!dictionaryOfDictionaries.ContainsKey("FreeUpgrade_totalGranted")) { dictionaryOfDictionaries.Add("FreeUpgrade_totalGranted", FreeUpgradeEveryRound.TotalGrantedFreeSlotsDict); } FreeUpgradeEveryRound.Logger.LogInfo((object)"[FreeUpgrade] Persistent dictionaries registered in StatsManager."); } } [HarmonyPatch(typeof(SemiFunc))] [HarmonyPatch("OnSceneSwitch")] public static class FreeUpgrade_SyncPatch { [HarmonyPostfix] private static void AfterSceneSwitch() { if (SemiFunc.IsMasterClientOrSingleplayer() && IsGameInProgress()) { FreeUpgradeEveryRound.SyncFromStatsManager(); } } private static bool IsGameInProgress() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Invalid comparison between Unknown and I4 if ((Object)(object)GameDirector.instance != (Object)null && (int)GameDirector.instance.currentState >= 2) { return SceneUtil.IsGameInProgress(); } return false; } } }