using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; 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 Microsoft.CodeAnalysis; using Pigeon; using Pigeon.Math; using Pigeon.Movement; using Sparroh.UI; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("Sparroh")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.1.0")] [assembly: AssemblyInformationalVersion("1.0.1")] [assembly: AssemblyProduct("UpgradeOptimizer")] [assembly: AssemblyTitle("UpgradeOptimizer")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.1.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; } } } public static class ConfigManager { private const float DebounceSeconds = 0.25f; private static ConfigFile _config; private static ManualLogSource _logger; private static FileSystemWatcher _configWatcher; private static volatile bool _reloadPending; private static float _reloadAt = -1f; public static ConfigEntry EnableBoost { get; private set; } public static ConfigEntry EnablePatternBrowser { get; private set; } public static ConfigEntry EnableMaxRolls { get; private set; } public static ConfigEntry BoostHotkey { get; private set; } public static ConfigEntry PatternBrowserHotkey { get; private set; } public static void Initialize(ConfigFile configFile, ManualLogSource log) { _config = configFile; _logger = log; EnableBoost = _config.Bind("Features", "Enable Boost", true, "Enable free boost/turbocharge toggle on hovered upgrades."); EnablePatternBrowser = _config.Bind("Features", "Enable Pattern Browser", true, "Enable the freeform pattern browser for override infusion."); EnableMaxRolls = _config.Bind("Features", "Enable Max Rolls", true, "Force turbocharged upgrades to always roll best property values."); BoostHotkey = _config.Bind("Hotkeys", "Boost Key", (Key)29, "Hotkey that toggles boost/turbocharge on the currently hovered upgrade."); PatternBrowserHotkey = _config.Bind("Hotkeys", "Pattern Browser Key", (Key)30, "Hotkey that opens/closes the pattern browser on the currently hovered upgrade."); _config.SettingChanged += OnSettingChanged; try { SetupFileWatcher(); } catch (Exception ex) { _logger.LogWarning((object)("Config file watcher unavailable: " + ex.Message)); } } public static void Tick() { if (_reloadPending) { _reloadPending = false; _reloadAt = Time.unscaledTime + 0.25f; } if (_reloadAt < 0f || Time.unscaledTime < _reloadAt) { return; } _reloadAt = -1f; try { _config.Reload(); _logger.LogInfo((object)"Config reloaded from disk."); } catch (Exception ex) { _logger.LogWarning((object)("Config reload failed: " + ex.Message)); } } public static void Dispose() { if (_config != null) { _config.SettingChanged -= OnSettingChanged; } if (_configWatcher != null) { try { _configWatcher.EnableRaisingEvents = false; _configWatcher.Changed -= OnConfigFileChanged; _configWatcher.Created -= OnConfigFileChanged; _configWatcher.Renamed -= OnConfigFileChanged; _configWatcher.Dispose(); } catch { } _configWatcher = null; } } private static void SetupFileWatcher() { string configFilePath = _config.ConfigFilePath; if (string.IsNullOrEmpty(configFilePath)) { _configWatcher = new FileSystemWatcher(Paths.ConfigPath, "sparroh.upgradeoptimizer.cfg"); } else { string directoryName = Path.GetDirectoryName(configFilePath); string fileName = Path.GetFileName(configFilePath); if (string.IsNullOrEmpty(directoryName) || string.IsNullOrEmpty(fileName)) { return; } if (!File.Exists(configFilePath)) { _config.Save(); } _configWatcher = new FileSystemWatcher(directoryName, fileName); } _configWatcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite; _configWatcher.Changed += OnConfigFileChanged; _configWatcher.Created += OnConfigFileChanged; _configWatcher.Renamed += OnConfigFileChanged; _configWatcher.EnableRaisingEvents = true; _logger.LogInfo((object)("Config hot-reload watching: " + _configWatcher.Path + _configWatcher.Filter)); } private static void OnConfigFileChanged(object sender, FileSystemEventArgs e) { _reloadPending = true; } private static void OnSettingChanged(object sender, SettingChangedEventArgs e) { if (e.ChangedSetting != null) { if ((object)e.ChangedSetting == EnablePatternBrowser && !EnablePatternBrowser.Value && PatternBrowser.IsOpen) { PatternBrowser.Close(); } _logger.LogInfo((object)$"Config updated: [{e.ChangedSetting.Definition.Section}] {e.ChangedSetting.Definition.Key} = {e.ChangedSetting.BoxedValue}"); } } } internal static class FreeBoostFeature { public static void TryHandleHotkey(Keyboard keyboard, Key hotkey) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) if (!PlayerInput.IsMenuEnabled || !UpgradeOptimizerPlugin.WasHotkeyPressed(keyboard, hotkey)) { return; } UpgradeInstance hoveredUpgrade = UpgradeHoverTracker.HoveredUpgrade; if (hoveredUpgrade != null && !((Object)(object)hoveredUpgrade.Upgrade == (Object)null)) { if (hoveredUpgrade.IsTurbocharged) { hoveredUpgrade.IsTurbocharged = false; RefreshUpgradeUI(hoveredUpgrade); UpgradeOptimizerPlugin.Log.LogInfo((object)("Removed boost from '" + hoveredUpgrade.Upgrade.Name + "'.")); } else if (!CanAutoBoost(hoveredUpgrade)) { UpgradeOptimizerPlugin.Log.LogInfo((object)("Upgrade '" + hoveredUpgrade.Upgrade.Name + "' cannot be boosted.")); } else { hoveredUpgrade.IsTurbocharged = true; RefreshUpgradeUI(hoveredUpgrade); PlayBoostFeedback(hoveredUpgrade); UpgradeOptimizerPlugin.Log.LogInfo((object)("Boosted '" + hoveredUpgrade.Upgrade.Name + "'.")); } } } private static bool CanAutoBoost(UpgradeInstance upgrade) { //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_0029: Invalid comparison between Unknown and I4 //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Invalid comparison between Unknown and I4 //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Invalid comparison between Unknown and I4 //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Invalid comparison between Unknown and I4 Upgrade upgrade2 = upgrade.Upgrade; if (upgrade2 == null) { return false; } if (!upgrade.IsUnlocked) { return false; } if (upgrade.RemoveAfterMission) { return false; } Rarity rarity = upgrade2.Rarity; if ((int)rarity > 3) { return false; } if ((int)rarity == 3) { return (upgrade2.Flags & 0x2000) > 0; } if (upgrade2.IsSkin()) { return false; } return (upgrade2.Flags & 0x40020) == 0; } private static void RefreshUpgradeUI(UpgradeInstance upgrade) { if ((Object)(object)HoverInfoDisplay.Instance != (Object)null) { HoverInfoDisplay.Instance.Refresh(); } GearUpgradeUI val = default(GearUpgradeUI); if (UIRaycaster.RaycastForComponent(ref val) && (Object)(object)val != (Object)null && ((HoverInfoUpgrade)val).Upgrade == upgrade) { val.SetUpgrade(upgrade, false); } GearDetailsWindow val2 = null; if ((Object)(object)Menu.Instance != (Object)null && Menu.Instance.IsOpen) { Window top = Menu.Instance.WindowSystem.GetTop(); val2 = (GearDetailsWindow)(object)((top is GearDetailsWindow) ? top : null); } if ((Object)(object)val2 != (Object)null && ((Component)val2).gameObject.activeSelf && upgrade.IsEquipped(val2.UpgradablePrefab)) { val2.RefreshUpgrade(upgrade); } } private static void PlayBoostFeedback(UpgradeInstance upgrade) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)PlayerLook.Instance == (Object)null)) { Global.GetRarity(upgrade.Upgrade.Rarity).raritySwitch.SetValue(((Component)PlayerLook.Instance).gameObject); Global.Instance.UpgradeUnlockSound.Post(((Component)PlayerLook.Instance).gameObject); Global.Instance.UpgradeDropSound.Post(((Component)PlayerLook.Instance).gameObject); } } } internal static class MaxRollsPatches { [HarmonyPatch] internal static class RangeFloatGetValuePatch { private static MethodBase TargetMethod() { return FindGetValue(typeof(Range)); } private static bool Prefix(ref Range __instance, ref Random rand, UpgradeInstance upgrade, BoostParams boostParams, ref float __result) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (!ShouldForceBest(upgrade, boostParams)) { return true; } __result = __instance.GetValue(ref rand, GetBestAddT(boostParams)); return false; } } [HarmonyPatch] internal static class RangeIntGetValuePatch { private static MethodBase TargetMethod() { return FindGetValue(typeof(Range)); } private static bool Prefix(ref Range __instance, ref Random rand, UpgradeInstance upgrade, BoostParams boostParams, ref int __result) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (!ShouldForceBest(upgrade, boostParams)) { return true; } __result = __instance.GetValue(ref rand, GetBestAddT(boostParams)); return false; } } [HarmonyPatch] internal static class RangeVector2GetValuePatch { private static MethodBase TargetMethod() { return FindGetValue(typeof(Range)); } private static bool Prefix(ref Range __instance, ref Random rand, UpgradeInstance upgrade, BoostParams boostParams, ref Vector2 __result) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) if (!ShouldForceBest(upgrade, boostParams)) { return true; } __result = __instance.GetValue(ref rand, GetBestAddT(boostParams)); return false; } } [HarmonyPatch] internal static class GetUniformRandomValuePatch { private static MethodBase TargetMethod() { return AccessTools.Method(typeof(Range), "GetUniformRandomValue", new Type[4] { typeof(Range), typeof(Random).MakeByRefType(), typeof(UpgradeInstance), typeof(BoostParams) }, (Type[])null); } private static bool Prefix(Range range, ref Random rand, UpgradeInstance upgrade, BoostParams boostParams, ref Vector2 __result) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: 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_0014: 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_002b: Unknown result type (might be due to invalid IL or missing references) if (!ShouldForceBest(upgrade, boostParams)) { return true; } __result = Vector2.Lerp(range.min, range.max, ((Random)(ref rand)).NextFloat() + GetBestAddT(boostParams)); return false; } } private static bool ShouldForceBest(UpgradeInstance upgrade, BoostParams boostParams) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Invalid comparison between Unknown and I4 if (!ConfigManager.EnableMaxRolls.Value) { return false; } if (upgrade == null || !upgrade.IsTurbocharged) { return false; } if (!boostParams.cannotBeBoosted) { return true; } Upgrade upgrade2 = upgrade.Upgrade; if (upgrade2 != null) { return (int)upgrade2.Rarity == 3; } return false; } private static float GetBestAddT(BoostParams boostParams) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (!boostParams.minIsBetter) { return 1f; } return -1f; } private static MethodBase FindGetValue(Type rangeType) { return AccessTools.Method(rangeType, "GetValue", new Type[3] { typeof(Random).MakeByRefType(), typeof(UpgradeInstance), typeof(BoostParams) }, (Type[])null); } } public static class PatternBrowser { private static UpgradeInstance _target; private static string _search = ""; private static bool _sameGearOnly; private static int _rarityFilterIndex; private static PatternCatalog.SortMode _sortMode = PatternCatalog.SortMode.Name; private static int _selectedIndex = -1; private static string _status = ""; private static int _targetCellCount; private static UIWindow _window; private static UIInputField _searchField; private static UIToggle _sameGearToggle; private static UIDragList _unused; private static RectTransform _listContent; private static UIScrollView _listScroll; private static UIText _headerText; private static UIText _currentPatternText; private static UIText _previewText; private static UIText _statusText; private static UIText _countText; private static UIButton _sortButton; private static UIButton _rarityButton; private static readonly List _listButtons = new List(); private static readonly string[] RarityFilterLabels = new string[7] { "All", "Standard", "Rare", "Epic", "Exotic", "Oddity", "Contraband" }; public static bool IsOpen { get; private set; } public static void Open(UpgradeInstance target) { if (target == null) { return; } _target = target; IsOpen = true; _selectedIndex = -1; _status = ""; _targetCellCount = PatternCatalog.SafeCellCount(target.Upgrade); try { HexMap pattern = target.GetPattern(false); if (pattern != null) { _targetCellCount = pattern.GetCellCount(); } } catch { } PatternCatalog.Rebuild(target); RefreshFilters(); EnsureWindow(); RefreshUi(); _window.Show(); ManualLogSource log = UpgradeOptimizerPlugin.Log; Upgrade upgrade = target.Upgrade; log.LogInfo((object)("Opened pattern browser for " + ((upgrade != null) ? upgrade.Name : null))); } public static void Close() { IsOpen = false; _target = null; _selectedIndex = -1; _status = ""; if (_window != null) { _window.Hide(true); } } public static void Draw() { } private static void EnsureWindow() { //IL_001c: 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) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Expected O, but got Unknown //IL_01d3: Unknown result type (might be due to invalid IL or missing references) //IL_0263: Unknown result type (might be due to invalid IL or missing references) //IL_02c6: Unknown result type (might be due to invalid IL or missing references) //IL_02d5: Expected O, but got Unknown //IL_032f: Unknown result type (might be due to invalid IL or missing references) //IL_03a3: Unknown result type (might be due to invalid IL or missing references) //IL_03b2: Expected O, but got Unknown //IL_040d: Unknown result type (might be due to invalid IL or missing references) //IL_0461: Unknown result type (might be due to invalid IL or missing references) //IL_04a4: Unknown result type (might be due to invalid IL or missing references) //IL_054b: Unknown result type (might be due to invalid IL or missing references) //IL_055a: Expected O, but got Unknown if (_window != null) { return; } UITheme.Initialize(); _window = UIWindow.Create("PatternBrowser", (Vector2?)new Vector2(720f, 520f), "UpgradeOptimizer — Pattern Browser", false, true, (int?)(UITheme.WindowSortingOrder + 10)); _window.OnClose((Action)Close); Transform content = _window.Content; UIFactory.AddVerticalLayout(((Component)content).gameObject, UITheme.S(6f), UITheme.ScaledPadding(10, 10, 8, 8), (TextAnchor)0, true, false, true, true); _headerText = UIText.Create(content, "Header", "", UITheme.ScaledFontBody, (Color?)UIColors.TextPrimary, (TextAlignmentOptions)513, false); GameObject gameObject = _headerText.GameObject; float? num = UITheme.S(22f); UIHelpers.EnsureLayoutElement(gameObject, (float?)null, num, (float?)null); _currentPatternText = UIText.Create(content, "Current", "", UITheme.ScaledFontSmall, (Color?)UIColors.TextSecondary, (TextAlignmentOptions)513, false); GameObject gameObject2 = _currentPatternText.GameObject; num = UITheme.S(20f); UIHelpers.EnsureLayoutElement(gameObject2, (float?)null, num, (float?)null); RectTransform val = UIFactory.CreateRect("SearchRow", content); GameObject gameObject3 = ((Component)val).gameObject; num = UITheme.ScaledInputHeight + UITheme.S(4f); UIHelpers.EnsureLayoutElement(gameObject3, (float?)null, num, (float?)null); UIFactory.AddHorizontalLayout(((Component)val).gameObject, UITheme.S(8f), new RectOffset(0, 0, 0, 0), (TextAnchor)3, false, false, true, true); UIText.Create((Transform)(object)val, "SearchLbl", "Search", UITheme.ScaledFontSmall, (Color?)UIColors.TextSecondary, (TextAlignmentOptions)513, false); _searchField = UIInputField.Create((Transform)(object)val, _search, "Filter patterns...", (Action)delegate(string s) { _search = s ?? ""; RefreshFilters(); RebuildList(); }, "InputField"); UIHelpers.EnsureLayoutElement(_searchField.GameObject, (float?)UITheme.S(220f), (float?)null, (float?)null); UIText.Create((Transform)(object)val, "LockedFilters", "Same gear · same rarity", UITheme.ScaledFontSmall, (Color?)UIColors.TextMuted, (TextAlignmentOptions)513, false); RectTransform val2 = UIFactory.CreateRect("FilterRow", content); GameObject gameObject4 = ((Component)val2).gameObject; num = UITheme.ScaledButtonHeight; UIHelpers.EnsureLayoutElement(gameObject4, (float?)null, num, (float?)null); UIFactory.AddHorizontalLayout(((Component)val2).gameObject, UITheme.S(8f), new RectOffset(0, 0, 0, 0), (TextAnchor)3, true, false, true, true); _sortButton = UIButton.Create((Transform)(object)val2, "Sort: Name", (Action)delegate { _sortMode = (PatternCatalog.SortMode)((int)(_sortMode + 1) % 4); RefreshFilters(); RebuildList(); RefreshFilterButtons(); }, (UIButtonStyle)0, (string)null, (float?)null).SetWidth(UITheme.S(170f)); _countText = UIText.Create((Transform)(object)val2, "Count", "0 patterns", UITheme.ScaledFontSmall, (Color?)UIColors.TextMuted, (TextAlignmentOptions)513, false); RectTransform obj = UIFactory.CreateRect("Mid", content); GameObject gameObject5 = ((Component)obj).gameObject; num = UITheme.S(280f); UIHelpers.EnsureLayoutElement(gameObject5, (float?)null, (float?)null, num).flexibleHeight = 1f; UIFactory.AddHorizontalLayout(((Component)obj).gameObject, UITheme.S(10f), new RectOffset(0, 0, 0, 0), (TextAnchor)0, true, true, true, true); _listScroll = UIScrollView.Create((Transform)(object)obj, "PatternList", true, false); UIHelpers.EnsureLayoutElement(_listScroll.GameObject, (float?)null, (float?)null, (float?)null).flexibleWidth = 1.2f; _listContent = _listScroll.Content; UIPanel obj2 = UIPanel.Create((Transform)(object)obj, "Preview", (Color?)UIColors.Surface, true); UIHelpers.EnsureLayoutElement(obj2.GameObject, (float?)null, (float?)null, (float?)null).flexibleWidth = 1f; _previewText = UIText.Create(obj2.Content, "PreviewText", "Select a pattern", UITheme.ScaledFontSmall, (Color?)UIColors.TextSecondary, (TextAlignmentOptions)257, true); UIHelpers.SetFillParent(_previewText.Rect, UITheme.S(8f)); _statusText = UIText.Create(content, "Status", "", UITheme.ScaledFontSmall, (Color?)UIColors.Sky, (TextAlignmentOptions)513, false); GameObject gameObject6 = _statusText.GameObject; num = UITheme.S(20f); UIHelpers.EnsureLayoutElement(gameObject6, (float?)null, num, (float?)null); RectTransform obj3 = UIFactory.CreateRect("Footer", content); GameObject gameObject7 = ((Component)obj3).gameObject; num = UITheme.ScaledButtonHeight + UITheme.S(4f); UIHelpers.EnsureLayoutElement(gameObject7, (float?)null, num, (float?)null); UIFactory.AddHorizontalLayout(((Component)obj3).gameObject, UITheme.S(8f), new RectOffset(0, 0, 0, 0), (TextAnchor)3, true, false, true, true); UIButton.Create((Transform)(object)obj3, "Clear Override", (Action)delegate { ApplyOverride(null); }, (UIButtonStyle)2, (string)null, (float?)null).SetWidth(UITheme.S(130f)); UIButton.Create((Transform)(object)obj3, "Apply", (Action)delegate { if (_selectedIndex >= 0 && _selectedIndex < PatternCatalog.CurrentFiltered.Count) { ApplyOverride(PatternCatalog.CurrentFiltered[_selectedIndex].Upgrade); } }, (UIButtonStyle)1, (string)null, (float?)null).SetWidth(UITheme.S(100f)); UIButton.Create((Transform)(object)obj3, "Close", (Action)Close, (UIButtonStyle)0, (string)null, (float?)null).SetWidth(UITheme.S(90f)); } private static void RefreshFilterButtons() { if (_sortButton != null) { _sortButton.SetText($"Sort: {_sortMode}"); } if (_rarityButton != null) { _rarityButton.SetText("Rarity: " + RarityFilterLabels[_rarityFilterIndex]); } if (_countText != null) { _countText.Text = $"{PatternCatalog.CurrentFiltered.Count} patterns"; } } private static void RefreshUi() { //IL_0061: 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_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) if (_headerText != null && _target != null) { string text = _target.Upgrade.GetInstanceName(_target) ?? _target.Upgrade.Name; _headerText.Text = "Target: " + text; } if (_currentPatternText != null && _target != null) { UpgradeID overriddenPattern = _target.OverriddenPattern; if (overriddenPattern.ID != 0) { Upgrade upgradeFromID = PlayerData.GetUpgradeFromID(overriddenPattern); _currentPatternText.Text = (((Object)(object)upgradeFromID != (Object)null) ? $"Current pattern: {upgradeFromID.Name} ({PatternCatalog.SafeCellCount(upgradeFromID)} cells)" : $"Current pattern: ID {overriddenPattern.ID}"); } else { _currentPatternText.Text = $"Current pattern: Original ({_targetCellCount} cells)"; } } if (_searchField != null) { _searchField.Text = _search ?? ""; } if (_sameGearToggle != null) { _sameGearToggle.IsOn = _sameGearOnly; } if (_statusText != null) { _statusText.Text = _status ?? ""; } RebuildList(); RefreshFilterButtons(); UpdatePreview(); } private static void RebuildList() { if ((Object)(object)_listContent == (Object)null) { return; } UIHelpers.DestroyChildren((Transform)(object)_listContent); _listButtons.Clear(); IReadOnlyList currentFiltered = PatternCatalog.CurrentFiltered; for (int i = 0; i < currentFiltered.Count; i++) { int index = i; PatternEntry patternEntry = currentFiltered[i]; int num = patternEntry.CellCount - _targetCellCount; string text = ((num > 0) ? $" (+{num})" : ((num < 0) ? $" ({num})" : " (=)")); string text2 = (_sameGearOnly ? patternEntry.UpgradeName : patternEntry.DisplayName); string text3 = ""; string text4 = $"{text2}{text3} {patternEntry.CellCount}c{text}"; UIButton item = UIButton.Create((Transform)(object)_listContent, text4, (Action)delegate { _selectedIndex = index; UpdatePreview(); }, (UIButtonStyle)((index == _selectedIndex) ? 3 : 0), (string)null, (float?)UITheme.S(26f)); _listButtons.Add(item); } if (_countText != null) { _countText.Text = $"{currentFiltered.Count} patterns"; } } private static void UpdatePreview() { //IL_0107: Unknown result type (might be due to invalid IL or missing references) if (_previewText == null) { return; } if (_selectedIndex < 0 || _selectedIndex >= PatternCatalog.CurrentFiltered.Count) { _previewText.Text = "Select a pattern to preview"; return; } PatternEntry patternEntry = PatternCatalog.CurrentFiltered[_selectedIndex]; HexMap val = PatternCatalog.SafePattern(patternEntry.Upgrade); int num = ((val != null) ? val.GetCellCount() : patternEntry.CellCount); int num2 = ((val != null) ? val.Width : 0); int num3 = ((val != null) ? val.Height : 0); string text = ""; if (val != null) { for (int i = 0; i < val.Height; i++) { for (int j = 0; j < val.Width; j++) { text += (val[j, i].enabled ? "■ " : "· "); } text += "\n"; } } _previewText.Text = $"{patternEntry.UpgradeName}\n{patternEntry.GearName} · {patternEntry.Rarity} · {num} cells ({num2}x{num3})\n\n{text}"; } private static void RefreshFilters() { //IL_0003: 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_0034: 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_0061: Unknown result type (might be due to invalid IL or missing references) bool filterByRarity = false; Rarity val = (Rarity)(-1); _sameGearOnly = true; UpgradeInstance target = _target; if ((Object)(object)((target != null) ? target.Upgrade : null) != (Object)null) { filterByRarity = true; val = _target.Upgrade.Rarity; _rarityFilterIndex = RarityToFilterIndex(val); } IUpgradable targetGear = ((_target != null) ? _target.Gear : null); PatternCatalog.ApplyFilters(_search, _sameGearOnly, targetGear, filterByRarity, val, _sortMode, _targetCellCount); if (_selectedIndex >= PatternCatalog.CurrentFiltered.Count) { _selectedIndex = PatternCatalog.CurrentFiltered.Count - 1; } } private unsafe static int RarityToFilterIndex(Rarity rarity) { string b = ((object)(*(Rarity*)(&rarity))/*cast due to .constrained prefix*/).ToString(); for (int i = 0; i < RarityFilterLabels.Length; i++) { if (string.Equals(RarityFilterLabels[i], b, StringComparison.OrdinalIgnoreCase)) { return i; } } return 0; } private static void ApplyOverride(Upgrade patternSource) { if (_target == null) { return; } _status = PatternOverride.Apply(_target, patternSource); if (_statusText != null) { _statusText.Text = _status; } if (_status != null && _status.StartsWith("Applied:", StringComparison.Ordinal)) { try { HexMap pattern = _target.GetPattern(false); _targetCellCount = ((pattern != null) ? pattern.GetCellCount() : PatternCatalog.SafeCellCount(_target.Upgrade)); } catch { _targetCellCount = PatternCatalog.SafeCellCount(_target.Upgrade); } RefreshFilters(); RefreshUi(); } } } public sealed class PatternEntry { public int CellCount; public string DisplayName; public IUpgradable Gear; public string GearName; public Rarity Rarity; public string SearchText; public Upgrade Upgrade; public string UpgradeName; } public static class PatternCatalog { public enum SortMode { Name, Rarity, CellCount, CellDelta } private static readonly List AllEntries = new List(256); private static readonly List Filtered = new List(256); public static IReadOnlyList CurrentFiltered => Filtered; public static void Rebuild(UpgradeInstance target) { //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) AllEntries.Clear(); if (target == null || PlayerData.Instance == null || PlayerData.Instance.collectedGear == null) { Filtered.Clear(); return; } Upgrade upgrade = target.Upgrade; foreach (GearData value in PlayerData.Instance.collectedGear.Values) { if (((value != null) ? value.Gear : null) == null || !value.IsUnlocked || (Object)(object)value.Gear.Info == (Object)null) { continue; } IUpgradable gear = value.Gear; string text = gear.Info.Name; if (string.IsNullOrEmpty(text)) { text = "Unknown Gear"; } List upgrades = gear.Info.Upgrades; if (upgrades == null) { continue; } foreach (Upgrade item in upgrades) { if (item != null && item != upgrade && !item.IsSkin()) { int cellCount = SafeCellCount(item); string text2 = item.Name ?? item.APIName ?? "Upgrade"; string text3 = text + " - " + text2; AllEntries.Add(new PatternEntry { Upgrade = item, Gear = gear, GearName = text, UpgradeName = text2, DisplayName = text3, Rarity = item.Rarity, CellCount = cellCount, SearchText = text3.ToLowerInvariant() }); } } } AllEntries.Sort((PatternEntry a, PatternEntry b) => string.Compare(a.DisplayName, b.DisplayName, StringComparison.OrdinalIgnoreCase)); } public static void ApplyFilters(string search, bool sameGearOnly, IUpgradable targetGear, bool filterByRarity, Rarity rarityFilter, SortMode sortMode, int targetCellCount) { //IL_005d: 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) Filtered.Clear(); string text = (string.IsNullOrWhiteSpace(search) ? null : search.Trim().ToLowerInvariant()); sameGearOnly = true; filterByRarity = true; foreach (PatternEntry allEntry in AllEntries) { if ((!sameGearOnly || targetGear == null || allEntry.Gear == targetGear) && (!filterByRarity || allEntry.Rarity == rarityFilter) && (text == null || allEntry.SearchText.IndexOf(text, StringComparison.Ordinal) >= 0)) { Filtered.Add(allEntry); } } switch (sortMode) { case SortMode.Rarity: Filtered.Sort(delegate(PatternEntry a, PatternEntry b) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) ref Rarity rarity = ref b.Rarity; object target = a.Rarity; int num = ((Enum)Unsafe.As(ref rarity)/*cast due to .constrained prefix*/).CompareTo(target); return (num == 0) ? string.Compare(a.DisplayName, b.DisplayName, StringComparison.OrdinalIgnoreCase) : num; }); break; case SortMode.CellCount: Filtered.Sort(delegate(PatternEntry a, PatternEntry b) { int num = b.CellCount.CompareTo(a.CellCount); return (num == 0) ? string.Compare(a.DisplayName, b.DisplayName, StringComparison.OrdinalIgnoreCase) : num; }); break; case SortMode.CellDelta: Filtered.Sort(delegate(PatternEntry a, PatternEntry b) { int num = Math.Abs(a.CellCount - targetCellCount); int value = Math.Abs(b.CellCount - targetCellCount); int num2 = num.CompareTo(value); if (num2 != 0) { return num2; } num2 = b.CellCount.CompareTo(a.CellCount); return (num2 == 0) ? string.Compare(a.DisplayName, b.DisplayName, StringComparison.OrdinalIgnoreCase) : num2; }); break; default: Filtered.Sort((PatternEntry a, PatternEntry b) => string.Compare(a.DisplayName, b.DisplayName, StringComparison.OrdinalIgnoreCase)); break; } } public static int SafeCellCount(Upgrade upgrade) { try { HexMap pattern = upgrade.GetPattern((UpgradeInstance)null, false); return (pattern != null) ? pattern.GetCellCount() : 0; } catch (Exception ex) { ManualLogSource log = UpgradeOptimizerPlugin.Log; if (log != null) { log.LogWarning((object)("Failed to read pattern for " + ((upgrade != null) ? upgrade.Name : null) + ": " + ex.Message)); } return 0; } } public static HexMap SafePattern(Upgrade upgrade) { try { return (upgrade != null) ? upgrade.GetPattern((UpgradeInstance)null, false) : null; } catch { return null; } } } public static class PatternOverride { public static string Apply(UpgradeInstance target, Upgrade patternSource) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: 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_0060: Unknown result type (might be due to invalid IL or missing references) if (target == null) { return ""; } if ((Object)(object)patternSource != (Object)null) { if (patternSource.IsSkin()) { UpgradeOptimizerPlugin.Log.LogWarning((object)("SparrohMode=false blocked skin pattern override: " + patternSource.Name)); return "Blocked: skin patterns disabled"; } Rarity val = (Rarity)((!((Object)(object)target.Upgrade != (Object)null)) ? (-1) : ((int)target.Upgrade.Rarity)); bool flag = patternSource.Rarity == val; bool flag2 = target.Gear == null || IsUpgradeOnGear(patternSource, target.Gear); if (!flag2 || !flag) { UpgradeOptimizerPlugin.Log.LogWarning((object)("SparrohMode=false blocked pattern override: " + patternSource.Name + " " + $"(rarity match={flag}, gear match={flag2})")); return "Blocked: same gear and rarity only"; } } try { List<(IUpgradable, sbyte, sbyte, byte)> list = CaptureEquippedPositions(target); if (list.Count > 0) { PlayerData.UnequipFromAll(target); } target.OverridePattern(patternSource); int num = 0; foreach (var item in list) { if (CanOccupy(item.Item1, target, item.Item2, item.Item3, item.Item4)) { target.Equip(item.Item1, item.Item2, item.Item3, item.Item4, true, false); num++; } } string text = (((Object)(object)patternSource != (Object)null) ? patternSource.Name : "Original"); string result = ((list.Count == 0) ? ("Applied: " + text) : ((num == list.Count) ? ("Applied: " + text + " (re-equipped)") : ((num != 0) ? $"Applied: {text} (re-equipped {num}/{list.Count})" : ("Applied: " + text + " (unequipped — new shape doesn't fit)")))); ManualLogSource log = UpgradeOptimizerPlugin.Log; Upgrade upgrade = target.Upgrade; log.LogInfo((object)("Pattern override on " + ((upgrade != null) ? upgrade.Name : null) + " -> " + text)); return result; } catch (Exception arg) { UpgradeOptimizerPlugin.Log.LogError((object)$"ApplyOverride failed: {arg}"); return "Failed to apply override"; } } public static List<(IUpgradable gear, sbyte x, sbyte y, byte rot)> CaptureEquippedPositions(UpgradeInstance instance) { List<(IUpgradable, sbyte, sbyte, byte)> list = new List<(IUpgradable, sbyte, sbyte, byte)>(); if (instance == null || (Object)(object)Global.Instance == (Object)null) { return list; } try { if (Global.Instance.Characters != null) { Character[] characters = Global.Instance.Characters; sbyte item = default(sbyte); sbyte item2 = default(sbyte); byte item3 = default(byte); foreach (Character val in characters) { if ((Object)(object)val != (Object)null && instance.IsEquipped((IUpgradable)(object)val) && instance.GetPosition((IUpgradable)(object)val, ref item, ref item2, ref item3)) { list.Add(((IUpgradable)(object)val, item, item2, item3)); } } } if (Global.Instance.AllGear != null) { IUpgradable[] allGear = Global.Instance.AllGear; sbyte item4 = default(sbyte); sbyte item5 = default(sbyte); byte item6 = default(byte); foreach (IUpgradable val2 in allGear) { if (val2 != null && instance.IsEquipped(val2) && instance.GetPosition(val2, ref item4, ref item5, ref item6)) { list.Add((val2, item4, item5, item6)); } } } } catch (Exception ex) { UpgradeOptimizerPlugin.Log.LogWarning((object)("CaptureEquippedPositions: " + ex.Message)); } return list; } public static bool CanOccupy(IUpgradable gear, UpgradeInstance upgrade, sbyte x, sbyte y, byte rotation) { try { HexMap pattern = upgrade.GetPattern(false); HexMap val = ((pattern != null) ? pattern.GetModifiedMap((int)rotation, (HexMap)null) : null); if (val == null) { return true; } int num = default(int); int num2 = default(int); gear.Info.GetUpgradeGridSize(ref num, ref num2, false); if (val.Width > num + 2 || val.Height > num2 + 2) { return false; } return true; } catch { return true; } } public static bool IsUpgradeOnGear(Upgrade upgrade, IUpgradable gear) { if (!((Object)(object)upgrade == (Object)null)) { object obj; if (gear == null) { obj = null; } else { GearInfo info = gear.Info; obj = ((info != null) ? info.Upgrades : null); } if (obj != null) { foreach (Upgrade upgrade2 in gear.Info.Upgrades) { if (upgrade2 == upgrade) { return true; } } return false; } } return false; } } [BepInPlugin("sparroh.upgradeoptimizer", "UpgradeOptimizer", "1.0.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] [MycoMod(/*Could not decode attribute arguments.*/)] public class UpgradeOptimizerPlugin : BaseUnityPlugin { public const string PluginGUID = "sparroh.upgradeoptimizer"; public const string PluginName = "UpgradeOptimizer"; public const string PluginVersion = "1.0.1"; internal const bool SparrohMode = false; private Harmony _harmony; internal static ManualLogSource Log { get; private set; } private void Awake() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown //IL_0059: 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) Log = ((BaseUnityPlugin)this).Logger; ConfigManager.Initialize(((BaseUnityPlugin)this).Config, Log); _harmony = new Harmony("sparroh.upgradeoptimizer"); _harmony.PatchAll(); Log.LogInfo((object)("UpgradeOptimizer v1.0.1 loaded. " + $"Boost={ConfigManager.EnableBoost.Value} ({ConfigManager.BoostHotkey.Value}), " + $"Patterns={ConfigManager.EnablePatternBrowser.Value} ({ConfigManager.PatternBrowserHotkey.Value}), " + $"MaxRolls={ConfigManager.EnableMaxRolls.Value}")); } private void Update() { //IL_0055: Unknown result type (might be due to invalid IL or missing references) try { ConfigManager.Tick(); Keyboard current = Keyboard.current; if (current == null) { return; } UpgradeHoverTracker.Tick(); if (IsTypingInSearchField()) { if (ConfigManager.EnablePatternBrowser.Value && PatternBrowser.IsOpen && WasHotkeyPressed(current, (Key)60)) { PatternBrowser.Close(); } return; } if (ConfigManager.EnableBoost.Value) { FreeBoostFeature.TryHandleHotkey(current, ConfigManager.BoostHotkey.Value); } if (ConfigManager.EnablePatternBrowser.Value) { HandlePatternBrowserHotkeys(current); } } catch (Exception ex) { Log.LogError((object)("Update error: " + ex)); } } private void OnDestroy() { ConfigManager.Dispose(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } private void OnGUI() { if (!ConfigManager.EnablePatternBrowser.Value) { return; } try { PatternBrowser.Draw(); } catch (Exception ex) { Log.LogError((object)("OnGUI error: " + ex)); } } private static void HandlePatternBrowserHotkeys(Keyboard keyboard) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) if (WasHotkeyPressed(keyboard, (Key)60) && PatternBrowser.IsOpen) { PatternBrowser.Close(); } else if (WasHotkeyPressed(keyboard, ConfigManager.PatternBrowserHotkey.Value)) { if (PatternBrowser.IsOpen) { PatternBrowser.Close(); } else if (UpgradeHoverTracker.HoveredUpgrade != null && UpgradeHoverTracker.HoveredUpgrade.IsUnlocked) { PatternBrowser.Open(UpgradeHoverTracker.HoveredUpgrade); } } } internal static bool WasHotkeyPressed(Keyboard keyboard, Key key) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) try { return ((ButtonControl)keyboard[key]).wasPressedThisFrame; } catch { return false; } } internal static bool IsTypingInSearchField() { EventSystem current = EventSystem.current; GameObject val = ((current != null) ? current.currentSelectedGameObject : null); if ((Object)(object)val != (Object)null) { return (Object)(object)val.GetComponent() != (Object)null; } return false; } } internal static class UpgradeHoverTracker { internal static UpgradeInstance HoveredUpgrade { get; private set; } public static void Tick() { if ((Object)(object)HoverInfoDisplay.Instance != (Object)null) { HoverInfo selectedInfo = HoverInfoDisplay.Instance.SelectedInfo; HoverInfoUpgrade val = (HoverInfoUpgrade)(object)((selectedInfo is HoverInfoUpgrade) ? selectedInfo : null); if (val != null && val.Upgrade != null) { HoveredUpgrade = val.Upgrade; return; } } GearUpgradeUI val2 = default(GearUpgradeUI); UpgradeIcon val3 = default(UpgradeIcon); if (UIRaycaster.RaycastForComponent(ref val2) && (Object)(object)val2 != (Object)null && ((HoverInfoUpgrade)val2).Upgrade != null) { HoveredUpgrade = ((HoverInfoUpgrade)val2).Upgrade; } else if (UIRaycaster.RaycastForComponent(ref val3) && (Object)(object)val3 != (Object)null && ((HoverInfoUpgrade)val3).Upgrade != null) { HoveredUpgrade = ((HoverInfoUpgrade)val3).Upgrade; } else { HoveredUpgrade = null; } } } namespace UpgradeOptimizer { public static class MyPluginInfo { public const string PLUGIN_GUID = "UpgradeOptimizer"; public const string PLUGIN_NAME = "UpgradeOptimizer"; public const string PLUGIN_VERSION = "1.0.1"; } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }