using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
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 Sparroh.UI;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.UI;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Sparroh")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("2.1.3.0")]
[assembly: AssemblyInformationalVersion("2.1.3")]
[assembly: AssemblyProduct("BatchScrapping")]
[assembly: AssemblyTitle("BatchScrapping")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("2.1.3.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 lastReloadTime;
public static ConfigEntry<Key> TrashMarkKey { get; private set; }
public static ConfigEntry<bool> EnableInstantScrapping { get; private set; }
public static ConfigEntry<bool> EnableFixedTimer { get; private set; }
public static ConfigEntry<float> FixedTimerDuration { get; private set; }
public static void Initialize(ConfigFile configFile, ManualLogSource log)
{
//IL_0092: 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)
config = configFile;
logger = log;
TrashMarkKey = config.Bind<Key>("Keybinds", "Trash Mark Keybind", (Key)34, "Key to toggle trash mark on upgrades");
EnableInstantScrapping = config.Bind<bool>("General", "Instant Scrap", false, "Enable instant scrapping without hold timer");
EnableFixedTimer = config.Bind<bool>("General", "Fixed Scrap Time", false, "Use fixed scrap timer instead of default (ignored when Instant Scrap is on)");
FixedTimerDuration = config.Bind<float>("General", "Scrap Duration", 1f, "Duration in seconds for fixed scrap timer");
ScrapHandlingMod.currentTrashKey = TrashMarkKey.Value;
TrashMarkKey.SettingChanged += OnTrashMarkKeyChanged;
try
{
SetupFileWatcher();
}
catch (Exception ex)
{
logger.LogError((object)("Error setting up config file watcher: " + ex.Message));
}
}
public static void Tick()
{
//IL_003e: 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)
if (!reloadPending || Time.unscaledTime - lastReloadTime < 0.25f)
{
return;
}
reloadPending = false;
lastReloadTime = Time.unscaledTime;
try
{
config.Reload();
ScrapHandlingMod.currentTrashKey = TrashMarkKey.Value;
logger.LogInfo((object)"Config reloaded from disk.");
}
catch (Exception ex)
{
logger.LogError((object)("Error reloading config: " + ex.Message));
}
}
public static void Dispose()
{
if (TrashMarkKey != null)
{
TrashMarkKey.SettingChanged -= OnTrashMarkKeyChanged;
}
if (configWatcher != null)
{
configWatcher.EnableRaisingEvents = false;
configWatcher.Changed -= OnConfigFileChanged;
configWatcher.Created -= OnConfigFileChanged;
configWatcher.Renamed -= OnConfigFileChanged;
configWatcher.Dispose();
configWatcher = null;
}
}
private static void SetupFileWatcher()
{
configWatcher = new FileSystemWatcher(Paths.ConfigPath, "sparroh.batchscrapping.cfg");
configWatcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite;
configWatcher.Changed += OnConfigFileChanged;
configWatcher.Created += OnConfigFileChanged;
configWatcher.Renamed += OnConfigFileChanged;
configWatcher.EnableRaisingEvents = true;
}
private static void OnConfigFileChanged(object sender, FileSystemEventArgs e)
{
reloadPending = true;
}
private static void OnTrashMarkKeyChanged(object sender, EventArgs e)
{
//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)
ScrapHandlingMod.currentTrashKey = TrashMarkKey.Value;
}
}
public static class InstantPatches
{
private const float InstantDuration = 0.05f;
private static Harmony _harmony;
public static bool ShouldModifyDuration
{
get
{
if (ConfigManager.EnableInstantScrapping == null || !ConfigManager.EnableInstantScrapping.Value)
{
if (ConfigManager.EnableFixedTimer != null)
{
return ConfigManager.EnableFixedTimer.Value;
}
return false;
}
return true;
}
}
public static void Initialize()
{
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
//IL_000f: Expected O, but got Unknown
//IL_005d: Unknown result type (might be due to invalid IL or missing references)
//IL_006a: Expected O, but got Unknown
try
{
_harmony = new Harmony("sparroh.batchscrapping.instant");
MethodInfo methodInfo = AccessTools.Method(typeof(GearUpgradeUI), "HasUnlockAction", new Type[1] { typeof(UnlockActionParams).MakeByRefType() }, (Type[])null);
if (methodInfo != null)
{
try
{
_harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(InstantScrapPatches), "HasUnlockActionPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
SparrohPlugin.Logger.LogInfo((object)"Instant Scrap: patched GearUpgradeUI.HasUnlockAction");
return;
}
catch (Exception ex)
{
SparrohPlugin.Logger.LogError((object)("Failed to patch HasUnlockAction: " + ex.Message));
return;
}
}
SparrohPlugin.Logger.LogError((object)"Instant Scrap: could not find GearUpgradeUI.HasUnlockAction(out UnlockActionParams). Instant scrap disabled.");
}
catch (Exception ex2)
{
SparrohPlugin.Logger.LogError((object)("Critical error during Instant Scrap initialization: " + ex2.Message));
}
}
public static void Destroy()
{
Harmony harmony = _harmony;
if (harmony != null)
{
harmony.UnpatchSelf();
}
_harmony = null;
}
public static float GetModifiedDuration(float original)
{
if (ConfigManager.EnableInstantScrapping != null && ConfigManager.EnableInstantScrapping.Value)
{
return 0.05f;
}
if (ConfigManager.EnableFixedTimer != null && ConfigManager.EnableFixedTimer.Value)
{
return Mathf.Max(0.05f, ConfigManager.FixedTimerDuration.Value);
}
return original;
}
}
public static class InstantScrapPatches
{
public static void HasUnlockActionPostfix(ref UnlockActionParams data)
{
try
{
if (data.OnSecondaryComplete != null && InstantPatches.ShouldModifyDuration)
{
data.SecondaryDuration = InstantPatches.GetModifiedDuration(data.SecondaryDuration);
}
}
catch (Exception ex)
{
SparrohPlugin.Logger.LogError((object)("Error in HasUnlockActionPostfix: " + ex.Message));
}
}
}
[BepInPlugin("sparroh.batchscrapping", "BatchScrapping", "2.1.3")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[MycoMod(/*Could not decode attribute arguments.*/)]
public class SparrohPlugin : BaseUnityPlugin
{
public const string PluginGUID = "sparroh.batchscrapping";
public const string PluginName = "BatchScrapping";
public const string PluginVersion = "2.1.3";
internal static ManualLogSource Logger;
public static SparrohPlugin Instance;
private bool _barRegistered;
private bool _lastUndoCan;
private int _lastUndoCount = -1;
private void Awake()
{
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
//IL_001c: Expected O, but got Unknown
//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
//IL_0101: Expected O, but got Unknown
//IL_0138: Unknown result type (might be due to invalid IL or missing references)
//IL_0145: Expected O, but got Unknown
//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
//IL_01ad: Expected O, but got Unknown
//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
//IL_0203: Expected O, but got Unknown
//IL_038c: Unknown result type (might be due to invalid IL or missing references)
//IL_039a: Expected O, but got Unknown
//IL_0239: Unknown result type (might be due to invalid IL or missing references)
//IL_024e: Unknown result type (might be due to invalid IL or missing references)
//IL_025b: Expected O, but got Unknown
//IL_025b: Expected O, but got Unknown
//IL_028c: Unknown result type (might be due to invalid IL or missing references)
//IL_0293: Expected O, but got Unknown
try
{
Logger = ((BaseUnityPlugin)this).Logger;
Instance = this;
Harmony val = new Harmony("sparroh.batchscrapping");
try
{
ConfigManager.Initialize(((BaseUnityPlugin)this).Config, Logger);
ScrapHandlingMod.ScrapMarkedAction = delegate
{
ScrapHandlingMod.TryScrapMarkedUpgrades((MonoBehaviour)(object)this);
};
ScrapHandlingMod.ScrapNonFavoriteAction = delegate
{
ScrapHandlingMod.TryScrapNonFavoriteUpgrades((MonoBehaviour)(object)this);
};
ScrapHandlingMod.LoadTrashSprite();
}
catch (Exception ex)
{
Logger.LogError((object)("Failed to setup configuration bindings: " + ex.Message));
}
try
{
InstantPatches.Initialize();
}
catch (Exception ex2)
{
Logger.LogError((object)("Failed to initialize Instant Scrap: " + ex2.Message));
}
try
{
val.PatchAll();
}
catch (Exception ex3)
{
Logger.LogError((object)("Failed to apply Harmony patches: " + ex3.Message));
}
try
{
MethodInfo methodInfo = AccessTools.Method(typeof(GearDetailsWindow), "Update", (Type[])null, (Type[])null);
if (methodInfo != null)
{
val.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(ScrapUIPatches), "UpdatePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
}
MethodInfo methodInfo2 = AccessTools.Method(typeof(GearUpgradeUI), "UpdateFavoriteIcon", (Type[])null, (Type[])null);
if (methodInfo2 != null)
{
val.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(ScrapUIPatches), "UpdateFavoriteIconPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
}
MethodInfo methodInfo3 = AccessTools.Method(typeof(GearUpgradeUI), "OnAdditionalAction", new Type[2]
{
typeof(int),
typeof(bool).MakeByRefType()
}, (Type[])null);
if (methodInfo3 != null)
{
val.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, new HarmonyMethod(typeof(ScrapUIPatches), "OnAdditionalActionPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
}
MethodInfo methodInfo4 = AccessTools.Method(typeof(GearUpgradeUI), "EnableGridView", new Type[1] { typeof(bool) }, (Type[])null);
if (methodInfo4 != null)
{
val.Patch((MethodBase)methodInfo4, (HarmonyMethod)null, new HarmonyMethod(typeof(ScrapUIPatches), "EnableGridViewPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
}
MethodInfo methodInfo5 = AccessTools.Method(typeof(GearUpgradeUI), "Dismantle", (Type[])null, (Type[])null);
if (methodInfo5 != null)
{
val.Patch((MethodBase)methodInfo5, new HarmonyMethod(typeof(ScrapUndoPatches), "DismantlePrefix", (Type[])null), new HarmonyMethod(typeof(ScrapUndoPatches), "DismantlePostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
Logger.LogInfo((object)"Patched GearUpgradeUI.Dismantle for scrap undo.");
}
else
{
Logger.LogError((object)"Could not find GearUpgradeUI.Dismantle — single-scrap undo unavailable.");
}
HarmonyMethod val2 = new HarmonyMethod(typeof(BatchPickupFeedbackPatches), "ShowPickupInfoPrefix", (Type[])null);
int num = 0;
foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(typeof(GameManager)))
{
if (!(declaredMethod == null) && !(declaredMethod.Name != "ShowPickupInfo"))
{
try
{
val.Patch((MethodBase)declaredMethod, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
num++;
}
catch (Exception ex4)
{
Logger.LogWarning((object)$"Failed to patch ShowPickupInfo ({declaredMethod}): {ex4.Message}");
}
}
}
if (num > 0)
{
Logger.LogInfo((object)$"Patched {num} GameManager.ShowPickupInfo overload(s) for batch toast consolidation.");
}
else
{
Logger.LogWarning((object)"Could not find GameManager.ShowPickupInfo — batch pickup toasts may spam.");
}
MethodInfo methodInfo6 = AccessTools.Method(typeof(PlayerResource), "PlayPickupSound", (Type[])null, (Type[])null);
if (methodInfo6 != null)
{
val.Patch((MethodBase)methodInfo6, new HarmonyMethod(typeof(BatchPickupFeedbackPatches), "PlayPickupSoundPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
Logger.LogInfo((object)"Patched PlayerResource.PlayPickupSound for batch scrap.");
}
}
catch (Exception ex5)
{
Logger.LogError((object)("Failed to apply scrap patches: " + ex5.Message));
}
}
catch (Exception ex6)
{
Logger.LogError((object)("Critical error during mod initialization: " + ex6.Message + "\n" + ex6.StackTrace));
}
Logger.LogInfo((object)"BatchScrapping v2.1.3 loaded successfully.");
}
private void Update()
{
ConfigManager.Tick();
GearActionBar.Tick();
if (!GearActionBar.IsGearMenuOpen())
{
return;
}
if (!_barRegistered)
{
GearActionBar.Register("scrap_marked", "Scrap Marked", 120, (Action)delegate
{
UIDialog.Confirm("Scrap Marked", "Scrap all trash-marked upgrades? This can be undone.", (Action)delegate
{
ScrapHandlingMod.ScrapMarkedAction?.Invoke();
}, (Action)null, "Confirm", "Cancel");
}, (UIButtonStyle)0);
GearActionBar.Register("scrap_nonfav", "Scrap No-Fav", 130, (Action)delegate
{
UIDialog.Confirm("Scrap Non-Favorite", "Scrap ALL non-favorite upgrades? This can be undone.", (Action)delegate
{
ScrapHandlingMod.ScrapNonFavoriteAction?.Invoke();
}, (Action)null, "Confirm", "Cancel");
}, (UIButtonStyle)2);
GearActionBar.Register("undo_scrap", "Undo Scrap", 140, (Action)delegate
{
UndoPatches.TryUndo();
}, (UIButtonStyle)1);
_barRegistered = true;
_lastUndoCan = !UndoPatches.CanUndo;
_lastUndoCount = -1;
}
bool canUndo = UndoPatches.CanUndo;
int undoCount = UndoPatches.UndoCount;
if (canUndo != _lastUndoCan || undoCount != _lastUndoCount)
{
_lastUndoCan = canUndo;
_lastUndoCount = undoCount;
GearActionBar.SetInteractable("undo_scrap", canUndo);
GearActionBar.SetText("undo_scrap", canUndo ? $"Undo ({undoCount})" : "Undo");
}
}
private void OnDestroy()
{
try
{
ConfigManager.Dispose();
InstantPatches.Destroy();
UndoPatches.Clear();
GearActionBar.Unregister("scrap_marked");
GearActionBar.Unregister("scrap_nonfav");
GearActionBar.Unregister("undo_scrap");
_barRegistered = false;
}
catch (Exception ex)
{
Logger.LogError((object)("Failed to destroy BatchScrapping UI: " + ex.Message));
}
}
}
public static class ScrapHandlingMod
{
private const float HOLD_DURATION = 1f;
private const int BATCH_SIZE = 1000000;
private const float BATCH_INTERVAL = 0f;
private const byte FavoriteFlag = 1;
private const byte TrashMarkFlag = 32;
private static bool wasScrappingSkins;
public static Key currentTrashKey;
public static Action ScrapMarkedAction;
public static Action ScrapNonFavoriteAction;
public static Sprite starSprite;
public static Sprite trashSprite;
public static bool IsScrapping { get; private set; }
public static bool SuppressPickupFeedback { get; private set; }
public static void LoadTrashSprite()
{
//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
//IL_00c3: Expected O, but got Unknown
//IL_0122: Unknown result type (might be due to invalid IL or missing references)
//IL_0131: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)trashSprite != (Object)null)
{
return;
}
try
{
Assembly executingAssembly = Assembly.GetExecutingAssembly();
string text = null;
string[] manifestResourceNames = executingAssembly.GetManifestResourceNames();
foreach (string text2 in manifestResourceNames)
{
if (text2.EndsWith("trashcan.png", StringComparison.OrdinalIgnoreCase))
{
text = text2;
break;
}
}
if (text == null)
{
SparrohPlugin.Logger.LogWarning((object)"trashcan.png embedded resource not found; trash marks will use red star.");
return;
}
using Stream stream = executingAssembly.GetManifestResourceStream(text);
if (stream == null)
{
SparrohPlugin.Logger.LogWarning((object)"Failed to open trashcan.png resource stream.");
return;
}
byte[] array = new byte[stream.Length];
int num;
for (int j = 0; j < array.Length; j += num)
{
if ((num = stream.Read(array, j, array.Length - j)) <= 0)
{
break;
}
}
Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
if (!ImageConversion.LoadImage(val, array, false))
{
SparrohPlugin.Logger.LogWarning((object)"Failed to decode trashcan.png.");
Object.Destroy((Object)(object)val);
return;
}
((Texture)val).filterMode = (FilterMode)1;
((Texture)val).wrapMode = (TextureWrapMode)1;
((Object)val).name = "BatchScrapping_Trashcan";
trashSprite = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f);
((Object)trashSprite).name = "BatchScrapping_Trashcan";
SparrohPlugin.Logger.LogInfo((object)$"Loaded trashcan sprite ({((Texture)val).width}x{((Texture)val).height}).");
}
catch (Exception ex)
{
SparrohPlugin.Logger.LogError((object)("Failed to load trashcan sprite: " + ex.Message));
}
}
public static Sprite GetTrashIconSprite()
{
Sprite obj;
if (!((Object)(object)trashSprite != (Object)null))
{
obj = starSprite;
if (obj == null)
{
return Resources.Load<Sprite>("favorite star");
}
}
else
{
obj = trashSprite;
}
return obj;
}
public static void ApplyMarkIcon(Image favoriteIcon, UpgradeInstance upgrade)
{
//IL_00d2: 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)
if ((Object)(object)favoriteIcon == (Object)null || upgrade == null)
{
return;
}
if (IsFavorite(upgrade))
{
if ((Object)(object)starSprite == (Object)null && (Object)(object)favoriteIcon.sprite != (Object)null && (Object)(object)favoriteIcon.sprite != (Object)(object)trashSprite)
{
starSprite = favoriteIcon.sprite;
}
favoriteIcon.sprite = starSprite ?? favoriteIcon.sprite;
((Component)favoriteIcon).gameObject.SetActive(true);
((Graphic)favoriteIcon).color = Color.white;
}
else if (IsTrashMarked(upgrade))
{
if ((Object)(object)starSprite == (Object)null && (Object)(object)favoriteIcon.sprite != (Object)null && (Object)(object)favoriteIcon.sprite != (Object)(object)trashSprite)
{
starSprite = favoriteIcon.sprite;
}
favoriteIcon.sprite = GetTrashIconSprite();
((Component)favoriteIcon).gameObject.SetActive(true);
((Graphic)favoriteIcon).color = Color.red;
}
else
{
((Component)favoriteIcon).gameObject.SetActive(false);
}
}
private static bool TryScrapInstance(UpgradeInstance inst)
{
if (inst == null)
{
return false;
}
Upgrade upgrade;
try
{
upgrade = inst.Upgrade;
}
catch
{
return false;
}
if ((Object)(object)upgrade == (Object)null)
{
return false;
}
try
{
PlayerData.UnequipFromAll(inst);
}
catch
{
}
try
{
if (!inst.Destroy())
{
return false;
}
upgrade.GiveDismantleResources(inst);
return true;
}
catch (Exception ex)
{
SparrohPlugin.Logger.LogError((object)("TryScrapInstance failed: " + ex.Message));
return false;
}
}
private static IEnumerator FinishBatchScrapCoroutine(bool success, bool isSkinsMode)
{
try
{
if (success)
{
UndoPatches.EndBatch();
}
else
{
UndoPatches.CancelBatch();
}
}
catch (Exception ex)
{
SparrohPlugin.Logger.LogError((object)("FinishBatchScrap EndBatch failed: " + ex.Message));
UndoPatches.CancelBatch();
success = false;
}
SuppressPickupFeedback = false;
if (success)
{
try
{
wasScrappingSkins = isSkinsMode;
RefreshOpenWindows();
}
catch (Exception ex2)
{
SparrohPlugin.Logger.LogError((object)("FinishBatchScrap refresh failed: " + ex2.Message));
}
yield return null;
try
{
BatchPickupFeedbackPatches.FlushAccumulatedPickups();
}
catch (Exception ex3)
{
SparrohPlugin.Logger.LogWarning((object)("FinishBatchScrap popup flush failed: " + ex3.Message));
}
}
else
{
BatchPickupFeedbackPatches.ClearAccumulated();
}
IsScrapping = false;
}
public static IEnumerator ScrapMarkedUpgrades()
{
int num = 0;
bool flag;
List<UpgradeInstance> list;
bool flag2;
try
{
if (IsScrapping)
{
SparrohPlugin.Logger.LogWarning((object)"ScrapMarkedUpgrades: Already scrapping, aborting.");
yield break;
}
IsScrapping = true;
SuppressPickupFeedback = true;
BatchPickupFeedbackPatches.ClearAccumulated();
SparrohPlugin.Logger.LogInfo((object)"Starting ScrapMarkedUpgrades operation.");
GearDetailsWindow val = Object.FindObjectOfType<GearDetailsWindow>();
if ((Object)(object)val == (Object)null)
{
SparrohPlugin.Logger.LogError((object)"ScrapMarkedUpgrades: GearDetailsWindow not found.");
SuppressPickupFeedback = false;
IsScrapping = false;
yield break;
}
IUpgradable upgradablePrefab = val.UpgradablePrefab;
if (upgradablePrefab == null)
{
SparrohPlugin.Logger.LogError((object)"ScrapMarkedUpgrades: UpgradablePrefab is null.");
SuppressPickupFeedback = false;
IsScrapping = false;
yield break;
}
flag = (bool)AccessTools.Field(typeof(GearDetailsWindow), "inSkinMode").GetValue(val);
IEnumerable<UpgradeInfo> enumerable = (flag ? PlayerData.GetAllSkins(upgradablePrefab, true) : PlayerData.GetAllUpgrades(upgradablePrefab, true));
int num2 = 0;
foreach (UpgradeInfo item in enumerable)
{
if (item?.Instances != null)
{
num2 += item.Instances.Count;
}
}
list = new List<UpgradeInstance>(num2);
foreach (UpgradeInfo item2 in enumerable)
{
if (item2?.Instances == null)
{
continue;
}
foreach (UpgradeInstance instance in item2.Instances)
{
if (instance != null && IsTrashMarked(instance))
{
list.Add(instance);
}
}
}
if (list.Count == 0)
{
SparrohPlugin.Logger.LogInfo((object)"ScrapMarkedUpgrades: No marked upgrades found.");
SuppressPickupFeedback = false;
IsScrapping = false;
yield break;
}
UndoPatches.BeginBatch($"Scrap Marked ({list.Count})");
foreach (UpgradeInstance item3 in list)
{
UndoPatches.AddToBatch(item3);
}
SparrohPlugin.Logger.LogInfo((object)$"ScrapMarkedUpgrades: Processing {list.Count} upgrades.");
flag2 = true;
}
catch (Exception ex)
{
SparrohPlugin.Logger.LogError((object)("ScrapMarkedUpgrades: Setup failed: " + ex.Message));
UndoPatches.CancelBatch();
SuppressPickupFeedback = false;
IsScrapping = false;
yield break;
}
if (flag2 && list != null)
{
for (int i = 0; i < list.Count; i += 1000000)
{
int num3 = Mathf.Min(i + 1000000, list.Count);
for (int j = i; j < num3; j++)
{
if (TryScrapInstance(list[j]))
{
num++;
}
}
}
}
yield return FinishBatchScrapCoroutine(num > 0, flag);
}
public static bool IsFavorite(UpgradeInstance instance)
{
if (instance == null)
{
return false;
}
return ((byte)AccessTools.Field(typeof(UpgradeInstance), "flags").GetValue(instance) & 1) != 0;
}
public static bool IsTrashMarked(UpgradeInstance instance)
{
if (instance == null)
{
return false;
}
return ((byte)AccessTools.Field(typeof(UpgradeInstance), "flags").GetValue(instance) & 0x20) != 0;
}
public static void SetTrashMark(UpgradeInstance instance, bool marked)
{
if (instance != null)
{
FieldInfo fieldInfo = AccessTools.Field(typeof(UpgradeInstance), "flags");
byte b = (byte)fieldInfo.GetValue(instance);
if (marked)
{
b |= 0x20;
b &= 0xFE;
}
else
{
b &= 0xDF;
}
fieldInfo.SetValue(instance, b);
}
}
public static void SetFavorite(UpgradeInstance instance, bool favorite)
{
if (instance != null)
{
FieldInfo fieldInfo = AccessTools.Field(typeof(UpgradeInstance), "flags");
byte b = (byte)fieldInfo.GetValue(instance);
if (favorite)
{
b |= 1;
b &= 0xDF;
}
else
{
b &= 0xFE;
}
fieldInfo.SetValue(instance, b);
}
}
public static void TryScrapMarkedUpgrades(MonoBehaviour owner)
{
GearDetailsWindow val = Object.FindObjectOfType<GearDetailsWindow>();
if ((Object)(object)val == (Object)null)
{
return;
}
IUpgradable upgradablePrefab = val.UpgradablePrefab;
if (upgradablePrefab == null)
{
return;
}
List<UpgradeInfo> obj = (((bool)AccessTools.Field(typeof(GearDetailsWindow), "inSkinMode").GetValue(val)) ? PlayerData.GetAllSkins(upgradablePrefab, true) : PlayerData.GetAllUpgrades(upgradablePrefab, true));
bool flag = false;
foreach (UpgradeInfo item in obj)
{
if (item?.Instances != null && item.Instances.Any((UpgradeInstance inst) => inst != null && IsTrashMarked(inst)))
{
flag = true;
break;
}
}
if (flag)
{
owner.StartCoroutine(ScrapMarkedUpgrades());
}
}
public static IEnumerator ScrapNonFavoriteUpgrades()
{
int num = 0;
bool flag;
List<UpgradeInstance> list;
bool flag2;
try
{
if (IsScrapping)
{
SparrohPlugin.Logger.LogWarning((object)"ScrapNonFavoriteUpgrades: Already scrapping, aborting.");
yield break;
}
IsScrapping = true;
SuppressPickupFeedback = true;
BatchPickupFeedbackPatches.ClearAccumulated();
SparrohPlugin.Logger.LogInfo((object)"Starting ScrapNonFavoriteUpgrades operation.");
GearDetailsWindow val = Object.FindObjectOfType<GearDetailsWindow>();
if ((Object)(object)val == (Object)null)
{
SparrohPlugin.Logger.LogError((object)"ScrapNonFavoriteUpgrades: GearDetailsWindow not found.");
SuppressPickupFeedback = false;
IsScrapping = false;
yield break;
}
IUpgradable upgradablePrefab = val.UpgradablePrefab;
if (upgradablePrefab == null)
{
SparrohPlugin.Logger.LogError((object)"ScrapNonFavoriteUpgrades: UpgradablePrefab is null.");
SuppressPickupFeedback = false;
IsScrapping = false;
yield break;
}
flag = (bool)AccessTools.Field(typeof(GearDetailsWindow), "inSkinMode").GetValue(val);
IEnumerable<UpgradeInfo> enumerable = (flag ? PlayerData.GetAllSkins(upgradablePrefab, true) : PlayerData.GetAllUpgrades(upgradablePrefab, true));
int num2 = 0;
foreach (UpgradeInfo item in enumerable)
{
if (item?.Instances != null)
{
num2 += item.Instances.Count;
}
}
list = new List<UpgradeInstance>(num2);
foreach (UpgradeInfo item2 in enumerable)
{
if (item2?.Instances == null)
{
continue;
}
foreach (UpgradeInstance instance in item2.Instances)
{
if (instance != null && !IsFavorite(instance))
{
list.Add(instance);
}
}
}
if (list.Count == 0)
{
SparrohPlugin.Logger.LogInfo((object)"ScrapNonFavoriteUpgrades: No non-favorite upgrades found.");
SuppressPickupFeedback = false;
IsScrapping = false;
yield break;
}
UndoPatches.BeginBatch($"Scrap Non-Favorite ({list.Count})");
foreach (UpgradeInstance item3 in list)
{
UndoPatches.AddToBatch(item3);
}
SparrohPlugin.Logger.LogInfo((object)$"ScrapNonFavoriteUpgrades: Processing {list.Count} upgrades.");
flag2 = true;
}
catch (Exception ex)
{
SparrohPlugin.Logger.LogError((object)("ScrapNonFavoriteUpgrades: Setup failed: " + ex.Message));
UndoPatches.CancelBatch();
SuppressPickupFeedback = false;
IsScrapping = false;
yield break;
}
if (flag2 && list != null)
{
for (int i = 0; i < list.Count; i += 1000000)
{
int num3 = Mathf.Min(i + 1000000, list.Count);
for (int j = i; j < num3; j++)
{
if (TryScrapInstance(list[j]))
{
num++;
}
}
}
}
yield return FinishBatchScrapCoroutine(num > 0, flag);
}
public static void TryScrapNonFavoriteUpgrades(MonoBehaviour owner)
{
GearDetailsWindow val = Object.FindObjectOfType<GearDetailsWindow>();
if ((Object)(object)val == (Object)null)
{
return;
}
IUpgradable upgradablePrefab = val.UpgradablePrefab;
if (upgradablePrefab == null)
{
return;
}
List<UpgradeInfo> obj = (((bool)AccessTools.Field(typeof(GearDetailsWindow), "inSkinMode").GetValue(val)) ? PlayerData.GetAllSkins(upgradablePrefab, true) : PlayerData.GetAllUpgrades(upgradablePrefab, true));
bool flag = false;
foreach (UpgradeInfo item in obj)
{
if (item?.Instances != null && item.Instances.Any((UpgradeInstance inst) => inst != null && !IsFavorite(inst)))
{
flag = true;
break;
}
}
if (flag)
{
owner.StartCoroutine(ScrapNonFavoriteUpgrades());
}
}
private static void RefreshOpenWindows()
{
if (!((Object)(object)Menu.Instance != (Object)null) || !Menu.Instance.IsOpen)
{
return;
}
Window top = Menu.Instance.WindowSystem.GetTop();
if (!((Object)(object)top != (Object)null))
{
return;
}
top.OnOpen(Menu.Instance.WindowSystem);
if (wasScrappingSkins)
{
GearDetailsWindow val = (GearDetailsWindow)(object)((top is GearDetailsWindow) ? top : null);
if ((Object)(object)val != (Object)null)
{
AccessTools.Field(typeof(GearDetailsWindow), "inSkinMode").SetValue(val, true);
}
wasScrappingSkins = false;
}
}
}
public class ScrapUIPatches
{
private static readonly HashSet<UpgradeInstance> toggledThisSession = new HashSet<UpgradeInstance>();
private static void AddScrapButton(GearDetailsWindow window)
{
//IL_005b: Unknown result type (might be due to invalid IL or missing references)
//IL_0061: Expected O, but got Unknown
//IL_0087: Unknown result type (might be due to invalid IL or missing references)
//IL_009d: Unknown result type (might be due to invalid IL or missing references)
//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
//IL_00db: Unknown result type (might be due to invalid IL or missing references)
//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
//IL_0101: Unknown result type (might be due to invalid IL or missing references)
//IL_011f: Unknown result type (might be due to invalid IL or missing references)
//IL_0124: 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_0134: 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_014a: Unknown result type (might be due to invalid IL or missing references)
//IL_0156: Unknown result type (might be due to invalid IL or missing references)
//IL_015b: Unknown result type (might be due to invalid IL or missing references)
//IL_016d: Unknown result type (might be due to invalid IL or missing references)
//IL_0175: Unknown result type (might be due to invalid IL or missing references)
//IL_019b: Unknown result type (might be due to invalid IL or missing references)
//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
//IL_01cb: Expected O, but got Unknown
//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
//IL_0209: Unknown result type (might be due to invalid IL or missing references)
//IL_021f: Unknown result type (might be due to invalid IL or missing references)
//IL_0235: Unknown result type (might be due to invalid IL or missing references)
//IL_0243: Unknown result type (might be due to invalid IL or missing references)
//IL_0258: Unknown result type (might be due to invalid IL or missing references)
//IL_026a: Unknown result type (might be due to invalid IL or missing references)
//IL_0289: Unknown result type (might be due to invalid IL or missing references)
//IL_028e: Unknown result type (might be due to invalid IL or missing references)
//IL_0292: Unknown result type (might be due to invalid IL or missing references)
//IL_029e: Unknown result type (might be due to invalid IL or missing references)
//IL_02aa: Unknown result type (might be due to invalid IL or missing references)
//IL_02b4: Unknown result type (might be due to invalid IL or missing references)
//IL_02c0: Unknown result type (might be due to invalid IL or missing references)
//IL_02c5: Unknown result type (might be due to invalid IL or missing references)
//IL_02d8: Unknown result type (might be due to invalid IL or missing references)
//IL_02e0: Unknown result type (might be due to invalid IL or missing references)
//IL_0306: Unknown result type (might be due to invalid IL or missing references)
Transform val = ((Component)window).transform.Find("ModScrapButtonMarked");
if ((Object)(object)val != (Object)null)
{
Object.DestroyImmediate((Object)(object)((Component)val).gameObject);
}
Transform val2 = ((Component)window).transform.Find("ModScrapButtonNonFavorite");
if ((Object)(object)val2 != (Object)null)
{
Object.DestroyImmediate((Object)(object)((Component)val2).gameObject);
}
RectTransform component = ((Component)((Component)window).transform).GetComponent<RectTransform>();
GameObject val3 = new GameObject("ModScrapButtonMarked");
val3.transform.SetParent(((Component)window).transform, false);
RectTransform val4 = val3.AddComponent<RectTransform>();
val4.sizeDelta = new Vector2(200f, 50f);
val4.anchorMin = new Vector2(1f, 0f);
val4.anchorMax = new Vector2(1f, 0f);
val4.pivot = new Vector2(1f, 0f);
Rect rect = component.rect;
val4.anchoredPosition = new Vector2((0f - ((Rect)(ref rect)).width) * 0.25f, 10f);
Image obj = val3.AddComponent<Image>();
((Graphic)obj).color = Color.gray;
((Graphic)obj).raycastTarget = true;
Button obj2 = val3.AddComponent<Button>();
((Selectable)obj2).transition = (Transition)1;
ColorBlock colors = ((Selectable)obj2).colors;
((ColorBlock)(ref colors)).normalColor = Color.gray;
((ColorBlock)(ref colors)).highlightedColor = Color.white;
((ColorBlock)(ref colors)).pressedColor = Color.blue;
((Selectable)obj2).colors = colors;
GameObject val5 = new GameObject("Text");
val5.transform.SetParent(val3.transform, false);
val5.AddComponent<RectTransform>().sizeDelta = val4.sizeDelta;
TextMeshProUGUI obj3 = val5.AddComponent<TextMeshProUGUI>();
((TMP_Text)obj3).text = "Scrap Marked";
((TMP_Text)obj3).alignment = (TextAlignmentOptions)514;
((Graphic)obj3).color = Color.white;
((TMP_Text)obj3).fontSize = 24f;
val3.AddComponent<HoldButtonHandler>().onHoldComplete = ScrapHandlingMod.ScrapMarkedAction;
GameObject val6 = new GameObject("ModScrapButtonNonFavorite");
val6.transform.SetParent(((Component)window).transform, false);
RectTransform val7 = val6.AddComponent<RectTransform>();
val7.sizeDelta = new Vector2(250f, 50f);
val7.anchorMin = new Vector2(1f, 0f);
val7.anchorMax = new Vector2(1f, 0f);
val7.pivot = new Vector2(1f, 0f);
val7.anchoredPosition = new Vector2(val4.anchoredPosition.x + 210f, 50f);
Image obj4 = val6.AddComponent<Image>();
((Graphic)obj4).color = Color.red;
((Graphic)obj4).raycastTarget = true;
Button obj5 = val6.AddComponent<Button>();
((Selectable)obj5).transition = (Transition)1;
ColorBlock colors2 = ((Selectable)obj5).colors;
((ColorBlock)(ref colors2)).normalColor = Color.red;
((ColorBlock)(ref colors2)).highlightedColor = Color.white;
((ColorBlock)(ref colors2)).pressedColor = Color.blue;
((Selectable)obj5).colors = colors2;
GameObject val8 = new GameObject("Text");
val8.transform.SetParent(val6.transform, false);
val8.AddComponent<RectTransform>().sizeDelta = val7.sizeDelta;
TextMeshProUGUI obj6 = val8.AddComponent<TextMeshProUGUI>();
((TMP_Text)obj6).text = "Scrap All Non-Favorite";
((TMP_Text)obj6).alignment = (TextAlignmentOptions)514;
((Graphic)obj6).color = Color.white;
((TMP_Text)obj6).fontSize = 20f;
val6.AddComponent<HoldButtonHandler>().onHoldComplete = ScrapHandlingMod.ScrapNonFavoriteAction;
}
public static void UpdateFavoriteIconPostfix(GearUpgradeUI __instance)
{
//IL_0022: Unknown result type (might be due to invalid IL or missing references)
//IL_0028: Expected O, but got Unknown
if (ScrapHandlingMod.IsScrapping)
{
return;
}
Image val = (Image)AccessTools.Field(typeof(GearUpgradeUI), "favoriteIcon").GetValue(__instance);
if ((Object)(object)val != (Object)null && ((HoverInfoUpgrade)__instance).Upgrade != null)
{
ScrapHandlingMod.ApplyMarkIcon(val, ((HoverInfoUpgrade)__instance).Upgrade);
if (ScrapHandlingMod.IsTrashMarked(((HoverInfoUpgrade)__instance).Upgrade))
{
Canvas.ForceUpdateCanvases();
}
}
}
public static void OnAdditionalActionPostfix(GearUpgradeUI __instance, int index, ref bool refreshUI)
{
if (index == 0 && ((HoverInfoUpgrade)__instance).Upgrade != null)
{
ScrapHandlingMod.SetFavorite(((HoverInfoUpgrade)__instance).Upgrade, ((HoverInfoUpgrade)__instance).Upgrade.Favorite);
}
}
public static void EnableGridViewPostfix(GearUpgradeUI __instance, bool grid)
{
//IL_002b: Unknown result type (might be due to invalid IL or missing references)
//IL_0031: Expected O, but got Unknown
if (!ScrapHandlingMod.IsScrapping && ((HoverInfoUpgrade)__instance).Upgrade != null)
{
Image val = (Image)AccessTools.Field(typeof(GearUpgradeUI), "favoriteIcon").GetValue(__instance);
if ((Object)(object)val != (Object)null)
{
ScrapHandlingMod.ApplyMarkIcon(val, ((HoverInfoUpgrade)__instance).Upgrade);
}
}
}
public static void UpdatePrefix(GearDetailsWindow __instance)
{
//IL_0017: Unknown result type (might be due to invalid IL or missing references)
//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
//IL_00ae: Expected O, but got Unknown
if (ScrapHandlingMod.IsScrapping || Keyboard.current == null)
{
return;
}
if (!((ButtonControl)Keyboard.current[ScrapHandlingMod.currentTrashKey]).isPressed)
{
if (toggledThisSession.Count > 0)
{
toggledThisSession.Clear();
}
return;
}
GearUpgradeUI val = null;
if (!UIRaycaster.RaycastForComponent<GearUpgradeUI>(ref val))
{
return;
}
UpgradeInstance upgrade = ((HoverInfoUpgrade)val).Upgrade;
if (upgrade != null && !ScrapHandlingMod.IsFavorite(upgrade) && !toggledThisSession.Contains(upgrade))
{
bool flag = ScrapHandlingMod.IsTrashMarked(upgrade);
ScrapHandlingMod.SetTrashMark(upgrade, !flag);
toggledThisSession.Add(upgrade);
Image val2 = (Image)(AccessTools.Field(typeof(GearUpgradeUI), "favoriteIcon")?.GetValue(val));
if ((Object)(object)val2 != (Object)null)
{
ScrapHandlingMod.ApplyMarkIcon(val2, upgrade);
Canvas.ForceUpdateCanvases();
}
}
}
}
public class HoldButtonHandler : MonoBehaviour, IPointerDownHandler, IEventSystemHandler, IPointerUpHandler
{
private const float HOLD_DURATION = 1f;
private float holdTimer;
private bool isHolding;
public Action onHoldComplete;
private void Update()
{
if (isHolding)
{
holdTimer += Time.deltaTime;
if (holdTimer >= 1f)
{
onHoldComplete?.Invoke();
isHolding = false;
}
}
}
public void OnPointerDown(PointerEventData eventData)
{
isHolding = true;
holdTimer = 0f;
}
public void OnPointerUp(PointerEventData eventData)
{
isHolding = false;
}
}
public static class BatchPickupFeedbackPatches
{
private struct AccumEntry
{
public object Item;
public int Amount;
public MethodBase Method;
public object Instance;
public object[] TemplateArgs;
public int AmountArgIndex;
}
private static readonly Dictionary<object, AccumEntry> accumulated = new Dictionary<object, AccumEntry>();
private static int interceptedCount;
public static void ClearAccumulated()
{
accumulated.Clear();
interceptedCount = 0;
}
public static bool ShowPickupInfoPrefix(object __instance, object[] __args, MethodBase __originalMethod)
{
if (!ScrapHandlingMod.SuppressPickupFeedback)
{
return true;
}
try
{
if (__args == null || __args.Length < 2 || __originalMethod == null)
{
return false;
}
object obj = null;
int num = 0;
int num2 = -1;
for (int i = 0; i < __args.Length; i++)
{
object obj2 = __args[i];
if (obj2 != null)
{
if (obj == null && (obj2 is PlayerResource || obj2 is IInventoryItem))
{
obj = obj2;
}
else if (obj2 is int num3 && num2 < 0)
{
num = num3;
num2 = i;
}
}
}
if (obj == null || num2 < 0 || num <= 0)
{
return false;
}
interceptedCount++;
object key = obj;
PlayerResource val = (PlayerResource)((obj is PlayerResource) ? obj : null);
if (val != null && !string.IsNullOrEmpty(val.ID))
{
key = val.ID;
}
else
{
IInventoryItem val2 = (IInventoryItem)((obj is IInventoryItem) ? obj : null);
if (val2 != null && !string.IsNullOrEmpty(val2.ID))
{
key = val2.ID;
}
}
if (accumulated.TryGetValue(key, out var value))
{
value.Amount += num;
accumulated[key] = value;
}
else
{
object[] array = new object[__args.Length];
Array.Copy(__args, array, __args.Length);
accumulated[key] = new AccumEntry
{
Item = obj,
Amount = num,
Method = __originalMethod,
Instance = __instance,
TemplateArgs = array,
AmountArgIndex = num2
};
}
}
catch (Exception ex)
{
SparrohPlugin.Logger.LogWarning((object)("ShowPickupInfoPrefix accumulate failed: " + ex.Message));
}
return false;
}
public static bool PlayPickupSoundPrefix()
{
return !ScrapHandlingMod.SuppressPickupFeedback;
}
public static void FlushAccumulatedPickups()
{
try
{
SparrohPlugin.Logger.LogInfo((object)$"FlushAccumulatedPickups: intercepted={interceptedCount}, unique={accumulated.Count}");
if (accumulated.Count == 0)
{
SparrohPlugin.Logger.LogWarning((object)"FlushAccumulatedPickups: nothing accumulated — no resource toasts to show (grants may still have applied).");
return;
}
int num = 0;
foreach (KeyValuePair<object, AccumEntry> item in accumulated)
{
AccumEntry value = item.Value;
if (value.Method == null || value.TemplateArgs == null || value.Amount <= 0)
{
continue;
}
try
{
object[] array = new object[value.TemplateArgs.Length];
Array.Copy(value.TemplateArgs, array, value.TemplateArgs.Length);
if (value.AmountArgIndex >= 0 && value.AmountArgIndex < array.Length)
{
array[value.AmountArgIndex] = value.Amount;
}
object obj = value.Instance ?? GameManager.Instance;
if (obj != null)
{
goto IL_0125;
}
MethodBase method = value.Method;
if (!(method is MethodInfo) || method.IsStatic)
{
goto IL_0125;
}
SparrohPlugin.Logger.LogWarning((object)"FlushAccumulatedPickups: no GameManager instance.");
goto end_IL_0098;
IL_0125:
value.Method.Invoke(obj, array);
num++;
end_IL_0098:;
}
catch (Exception ex)
{
Exception ex2 = ((ex is TargetInvocationException { InnerException: not null } ex3) ? ex3.InnerException : ex);
SparrohPlugin.Logger.LogWarning((object)$"FlushAccumulatedPickups: failed for key={item.Key} x{value.Amount}: {ex2.Message}");
}
}
SparrohPlugin.Logger.LogInfo((object)$"FlushAccumulatedPickups: showed {num} consolidated toast(s).");
}
finally
{
ClearAccumulated();
}
}
}
public static class UndoPatches
{
public sealed class UpgradeSnapshot
{
public IUpgradable EquippedOnGear;
public byte EquipRotation;
public sbyte EquipX;
public sbyte EquipY;
public byte Flags;
public IUpgradable Gear;
public bool HasBeenSeen;
public bool IsUnlocked;
public UpgradeID OverriddenPattern;
public bool RemoveOnQuit;
public int Seed;
public Upgrade Upgrade;
public bool WasEquipped;
}
public sealed class ScrapAction
{
public string Description;
public Dictionary<PlayerResource, int> ResourcesGranted = new Dictionary<PlayerResource, int>();
public List<UpgradeSnapshot> Upgrades = new List<UpgradeSnapshot>();
}
public const int MaxDepth = 5;
private static readonly List<ScrapAction> undoList = new List<ScrapAction>(5);
private static readonly FieldInfo flagsField = AccessTools.Field(typeof(UpgradeInstance), "flags");
private static readonly PropertyInfo hasBeenSeenProp = AccessTools.Property(typeof(UpgradeInstance), "HasBeenSeen");
private static readonly PropertyInfo seedProp = AccessTools.Property(typeof(UpgradeInstance), "Seed");
private static readonly PropertyInfo removeOnQuitProp = AccessTools.Property(typeof(UpgradeInstance), "RemoveOnQuit");
private static ScrapAction pendingBatch;
private static Dictionary<PlayerResource, int> resourceBaseline;
private static readonly Queue<ScrapAction> deferredActions = new Queue<ScrapAction>();
private static bool flushScheduled;
public static int UndoCount => undoList.Count;
public static bool CanUndo => undoList.Count > 0;
public static void Clear()
{
undoList.Clear();
deferredActions.Clear();
flushScheduled = false;
pendingBatch = null;
resourceBaseline = null;
}
public static UpgradeSnapshot CaptureUpgrade(UpgradeInstance instance)
{
//IL_0092: 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)
if (instance == null)
{
return null;
}
Upgrade upgrade;
try
{
upgrade = instance.Upgrade;
}
catch
{
return null;
}
if ((Object)(object)upgrade == (Object)null)
{
return null;
}
UpgradeSnapshot upgradeSnapshot = new UpgradeSnapshot
{
Gear = instance.Gear,
Upgrade = upgrade,
Seed = instance.Seed,
Flags = (byte)((flagsField != null) ? ((byte)flagsField.GetValue(instance)) : 0),
IsUnlocked = instance.IsUnlocked,
HasBeenSeen = instance.HasBeenSeen,
RemoveOnQuit = instance.RemoveOnQuit,
OverriddenPattern = instance.OverriddenPattern,
WasEquipped = false
};
try
{
IUpgradable gear = instance.Gear;
sbyte equipX = default(sbyte);
sbyte equipY = default(sbyte);
byte equipRotation = default(byte);
if (gear != null && instance.IsEquipped(gear) && instance.GetPosition(gear, ref equipX, ref equipY, ref equipRotation))
{
upgradeSnapshot.WasEquipped = true;
upgradeSnapshot.EquippedOnGear = gear;
upgradeSnapshot.EquipX = equipX;
upgradeSnapshot.EquipY = equipY;
upgradeSnapshot.EquipRotation = equipRotation;
}
}
catch (Exception ex)
{
SparrohPlugin.Logger.LogWarning((object)("ScrapUndo: failed to capture equip state: " + ex.Message));
}
return upgradeSnapshot;
}
public static Dictionary<PlayerResource, int> SnapshotResources()
{
Dictionary<PlayerResource, int> dictionary = new Dictionary<PlayerResource, int>();
try
{
if (PlayerData.Instance == null)
{
return dictionary;
}
if (Global.Instance?.PlayerResources != null)
{
PlayerResource[] playerResources = Global.Instance.PlayerResources;
foreach (PlayerResource val in playerResources)
{
if (!((Object)(object)val == (Object)null))
{
dictionary[val] = PlayerData.Instance.GetResource(val);
}
}
}
TryTrack(dictionary, "strange_comp");
TryTrack(dictionary, "oyster");
TryTrack(dictionary, "ouroscrap");
TryTrack(dictionary, "ourosample");
Global instance = Global.Instance;
if ((Object)(object)((instance != null) ? instance.ScripResource : null) != (Object)null)
{
dictionary[Global.Instance.ScripResource] = PlayerData.Instance.GetResource(Global.Instance.ScripResource);
}
}
catch (Exception ex)
{
SparrohPlugin.Logger.LogWarning((object)("ScrapUndo: resource snapshot failed: " + ex.Message));
}
return dictionary;
}
private static void TryTrack(Dictionary<PlayerResource, int> dict, string name)
{
PlayerResource val = default(PlayerResource);
if (PlayerResource.TryGetResource(name, ref val) && (Object)(object)val != (Object)null)
{
dict[val] = PlayerData.Instance.GetResource(val);
}
}
public static Dictionary<PlayerResource, int> DiffResources(Dictionary<PlayerResource, int> before, Dictionary<PlayerResource, int> after)
{
Dictionary<PlayerResource, int> dictionary = new Dictionary<PlayerResource, int>();
if (before == null || after == null)
{
return dictionary;
}
foreach (KeyValuePair<PlayerResource, int> item in after)
{
before.TryGetValue(item.Key, out var value);
int num = item.Value - value;
if (num > 0)
{
dictionary[item.Key] = num;
}
}
return dictionary;
}
public static void PushAction(ScrapAction action)
{
if (action == null || action.Upgrades == null || action.Upgrades.Count == 0)
{
return;
}
try
{
while (undoList.Count >= 5)
{
undoList.RemoveAt(0);
}
undoList.Add(action);
SparrohPlugin.Logger.LogInfo((object)$"ScrapUndo: recorded action ({action.Upgrades.Count} upgrade(s)). Stack depth: {undoList.Count}/{5}");
}
catch (Exception ex)
{
SparrohPlugin.Logger.LogError((object)("ScrapUndo PushAction failed: " + ex.Message));
}
}
public static void PushSingle(UpgradeSnapshot snap, Dictionary<PlayerResource, int> resourcesGranted)
{
if (snap != null)
{
PushAction(new ScrapAction
{
Description = (((Object)(object)snap.Upgrade != (Object)null) ? snap.Upgrade.Name : "upgrade"),
ResourcesGranted = (resourcesGranted ?? new Dictionary<PlayerResource, int>()),
Upgrades = { snap }
});
}
}
public static void QueueSingleDeferred(UpgradeSnapshot snap, Dictionary<PlayerResource, int> resourcesBefore)
{
if (snap == null)
{
return;
}
try
{
Dictionary<PlayerResource, int> after = SnapshotResources();
Dictionary<PlayerResource, int> resourcesGranted = DiffResources(resourcesBefore, after);
ScrapAction scrapAction = new ScrapAction
{
Description = (((Object)(object)snap.Upgrade != (Object)null) ? snap.Upgrade.Name : "upgrade"),
ResourcesGranted = resourcesGranted
};
scrapAction.Upgrades.Add(snap);
deferredActions.Enqueue(scrapAction);
ScheduleFlush();
}
catch (Exception ex)
{
SparrohPlugin.Logger.LogError((object)("ScrapUndo QueueSingleDeferred failed: " + ex.Message));
}
}
private static void ScheduleFlush()
{
if (!flushScheduled && !((Object)(object)SparrohPlugin.Instance == (Object)null))
{
flushScheduled = true;
((MonoBehaviour)SparrohPlugin.Instance).StartCoroutine(FlushDeferredNextFrame());
}
}
private static IEnumerator FlushDeferredNextFrame()
{
yield return null;
flushScheduled = false;
try
{
while (deferredActions.Count > 0)
{
PushAction(deferredActions.Dequeue());
}
}
catch (Exception ex)
{
SparrohPlugin.Logger.LogError((object)("ScrapUndo flush failed: " + ex.Message));
deferredActions.Clear();
}
}
public static void BeginBatch(string description)
{
pendingBatch = new ScrapAction
{
Description = description
};
resourceBaseline = SnapshotResources();
}
public static void AddToBatch(UpgradeInstance instance)
{
if (pendingBatch != null)
{
UpgradeSnapshot upgradeSnapshot = CaptureUpgrade(instance);
if (upgradeSnapshot != null)
{
pendingBatch.Upgrades.Add(upgradeSnapshot);
}
}
}
public static Dictionary<PlayerResource, int> EndBatch()
{
Dictionary<PlayerResource, int> dictionary = null;
if (pendingBatch == null)
{
return new Dictionary<PlayerResource, int>();
}
try
{
Dictionary<PlayerResource, int> after = SnapshotResources();
pendingBatch.ResourcesGranted = DiffResources(resourceBaseline, after);
dictionary = pendingBatch.ResourcesGranted;
if (pendingBatch.Upgrades.Count > 0)
{
PushAction(pendingBatch);
}
}
catch (Exception ex)
{
SparrohPlugin.Logger.LogError((object)("ScrapUndo EndBatch failed: " + ex.Message));
}
pendingBatch = null;
resourceBaseline = null;
return dictionary ?? new Dictionary<PlayerResource, int>();
}
public static void CancelBatch()
{
pendingBatch = null;
resourceBaseline = null;
}
public static bool TryUndo()
{
if (undoList.Count == 0)
{
SparrohPlugin.Logger.LogInfo((object)"ScrapUndo: nothing to undo.");
return false;
}
int index = undoList.Count - 1;
ScrapAction scrapAction = undoList[index];
undoList.RemoveAt(index);
int num = 0;
try
{
if (scrapAction?.Upgrades != null)
{
foreach (UpgradeSnapshot upgrade in scrapAction.Upgrades)
{
if (RestoreUpgrade(upgrade))
{
num++;
}
}
}
if (scrapAction?.ResourcesGranted != null)
{
foreach (KeyValuePair<PlayerResource, int> item in scrapAction.ResourcesGranted)
{
if (!((Object)(object)item.Key == (Object)null) && item.Value > 0)
{
int num2 = Mathf.Min(PlayerData.Instance.GetResource(item.Key), item.Value);
if (num2 > 0)
{
PlayerData.Instance.TryRemoveResource(item.Key, num2);
}
}
}
}
RefreshOpenWindows();
SparrohPlugin.Logger.LogInfo((object)$"ScrapUndo: restored {num}/{(scrapAction?.Upgrades?.Count).GetValueOrDefault()} upgrade(s). Remaining stack: {undoList.Count}");
return num > 0;
}
catch (Exception ex)
{
SparrohPlugin.Logger.LogError((object)("ScrapUndo: undo failed: " + ex.Message + "\n" + ex.StackTrace));
return false;
}
}
private static bool RestoreUpgrade(UpgradeSnapshot snap)
{
//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
if (snap == null || snap.Gear == null || (Object)(object)snap.Upgrade == (Object)null)
{
return false;
}
try
{
UpgradeInstance val = PlayerData.CreateUpgradeInstance(snap.Gear, snap.Upgrade, false);
if (val == null)
{
return false;
}
if (seedProp != null)
{
seedProp.SetValue(val, snap.Seed);
}
if (flagsField != null)
{
flagsField.SetValue(val, snap.Flags);
}
if (removeOnQuitProp != null)
{
removeOnQuitProp.SetValue(val, snap.RemoveOnQuit);
}
val.OverriddenPattern = snap.OverriddenPattern;
PlayerData.CollectInstance(val, (UnlockFlags)1);
if (snap.IsUnlocked)
{
val.Unlock(true);
}
else
{
val.Lock();
}
if (hasBeenSeenProp != null)
{
hasBeenSeenProp.SetValue(val, snap.HasBeenSeen);
}
if (flagsField != null)
{
flagsField.SetValue(val, snap.Flags);
}
if (snap.WasEquipped && snap.EquippedOnGear != null)
{
try
{
val.Equip(snap.EquippedOnGear, snap.EquipX, snap.EquipY, snap.EquipRotation, true, false);
}
catch (Exception ex)
{
SparrohPlugin.Logger.LogWarning((object)("ScrapUndo: re-equip failed: " + ex.Message));
}
}
return true;
}
catch (Exception ex2)
{
SparrohPlugin.Logger.LogError((object)("ScrapUndo: restore upgrade failed: " + ex2.Message));
return false;
}
}
private static void RefreshOpenWindows()
{
try
{
if ((Object)(object)Menu.Instance != (Object)null && Menu.Instance.IsOpen)
{
Window top = Menu.Instance.WindowSystem.GetTop();
if ((Object)(object)top != (Object)null)
{
top.OnOpen(Menu.Instance.WindowSystem);
}
}
}
catch (Exception ex)
{
SparrohPlugin.Logger.LogWarning((object)("ScrapUndo: window refresh failed: " + ex.Message));
}
}
}
public static class ScrapUndoPatches
{
private struct Pending
{
public UpgradeInstance Instance;
public UndoPatches.UpgradeSnapshot Snap;
public Dictionary<PlayerResource, int> ResourcesBefore;
}
private static readonly Stack<Pending> pendingStack = new Stack<Pending>();
public static void DismantlePrefix(GearUpgradeUI __instance)
{
try
{
if ((Object)(object)__instance == (Object)null)
{
return;
}
UpgradeInstance upgrade = ((HoverInfoUpgrade)__instance).Upgrade;
if (upgrade != null && !upgrade.Favorite)
{
UndoPatches.UpgradeSnapshot upgradeSnapshot = UndoPatches.CaptureUpgrade(upgrade);
if (upgradeSnapshot != null)
{
pendingStack.Push(new Pending
{
Instance = upgrade,
Snap = upgradeSnapshot,
ResourcesBefore = UndoPatches.SnapshotResources()
});
}
}
}
catch (Exception ex)
{
SparrohPlugin.Logger.LogError((object)("ScrapUndo DismantlePrefix: " + ex.Message));
}
}
public static void DismantlePostfix(GearUpgradeUI __instance)
{
try
{
if (pendingStack.Count == 0)
{
return;
}
Pending pending = pendingStack.Pop();
if (pending.Instance != null && pending.Snap != null)
{
bool flag;
try
{
flag = pending.Instance.IsDestroyed();
}
catch
{
flag = true;
}
if (flag)
{
UndoPatches.QueueSingleDeferred(pending.Snap, pending.ResourcesBefore);
}
}
}
catch (Exception ex)
{
SparrohPlugin.Logger.LogError((object)("ScrapUndo DismantlePostfix: " + ex.Message));
if (pendingStack.Count > 0)
{
try
{
pendingStack.Pop();
return;
}
catch
{
return;
}
}
}
}
}
namespace BatchScrapping
{
public static class MyPluginInfo
{
public const string PLUGIN_GUID = "BatchScrapping";
public const string PLUGIN_NAME = "BatchScrapping";
public const string PLUGIN_VERSION = "2.1.3";
}
}
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
internal sealed class IgnoresAccessChecksToAttribute : Attribute
{
public IgnoresAccessChecksToAttribute(string assemblyName)
{
}
}
}