Decompiled source of BatchScrapping v2.1.0

BepInEx/plugins/BatchScrapping.dll

Decompiled a day ago
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(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("BatchScrapping")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("BatchScrapping")]
[assembly: AssemblyTitle("BatchScrapping")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
public static class InstantPatches
{
	internal static ConfigEntry<bool> EnableFixedTimer;

	internal static ConfigEntry<float> FixedTimerDuration;

	internal static ConfigEntry<bool> EnableInstantScrapping;

	internal static bool s_EnableInstantScrapping;

	internal static bool s_EnableFixedTimer;

	internal static float s_FixedTimerDuration;

	private static FileSystemWatcher configWatcher;

	private static Harmony _harmony;

	private const float InstantDuration = 0.05f;

	public static bool ShouldModifyDuration
	{
		get
		{
			if (EnableInstantScrapping == null || !EnableInstantScrapping.Value)
			{
				if (EnableFixedTimer != null)
				{
					return EnableFixedTimer.Value;
				}
				return false;
			}
			return true;
		}
	}

	public static void Initialize(ConfigEntry<bool> enableFixed, ConfigEntry<float> fixedDur, ConfigEntry<bool> enableInst)
	{
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		//IL_0072: Expected O, but got Unknown
		//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cd: Expected O, but got Unknown
		try
		{
			EnableFixedTimer = enableFixed;
			FixedTimerDuration = fixedDur;
			EnableInstantScrapping = enableInst;
			s_EnableInstantScrapping = EnableInstantScrapping.Value;
			s_EnableFixedTimer = EnableFixedTimer.Value;
			s_FixedTimerDuration = FixedTimerDuration.Value;
			try
			{
				SetupFileWatchers();
			}
			catch (Exception ex)
			{
				SparrohPlugin.Logger.LogError((object)("Failed to setup file watchers: " + ex.Message));
			}
			_harmony = new Harmony("sparroh.instantscrapping");
			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)"InstantScrapping: patched GearUpgradeUI.HasUnlockAction");
					return;
				}
				catch (Exception ex2)
				{
					SparrohPlugin.Logger.LogError((object)("Failed to patch HasUnlockAction: " + ex2.Message));
					return;
				}
			}
			SparrohPlugin.Logger.LogError((object)"InstantScrapping: could not find GearUpgradeUI.HasUnlockAction(out UnlockActionParams). Instant scrap disabled.");
		}
		catch (Exception ex3)
		{
			SparrohPlugin.Logger.LogError((object)("Critical error during InstantScrapping initialization: " + ex3.Message));
		}
	}

	private static void SetupFileWatchers()
	{
		configWatcher = new FileSystemWatcher(Paths.ConfigPath, "sparroh.batchscrapping.cfg");
		configWatcher.Changed += delegate
		{
			try
			{
				((ConfigEntryBase)EnableInstantScrapping).ConfigFile.Reload();
				s_EnableInstantScrapping = EnableInstantScrapping.Value;
				s_EnableFixedTimer = EnableFixedTimer.Value;
				s_FixedTimerDuration = FixedTimerDuration.Value;
			}
			catch (Exception ex)
			{
				SparrohPlugin.Logger.LogWarning((object)("Config reload failed: " + ex.Message));
			}
		};
		configWatcher.EnableRaisingEvents = true;
	}

	public static void Destroy()
	{
		if (configWatcher != null)
		{
			configWatcher.EnableRaisingEvents = false;
			configWatcher.Dispose();
			configWatcher = null;
		}
		Harmony harmony = _harmony;
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
		_harmony = null;
	}

	public static float GetModifiedDuration(float original)
	{
		if (EnableInstantScrapping != null && EnableInstantScrapping.Value)
		{
			return 0.05f;
		}
		if (EnableFixedTimer != null && EnableFixedTimer.Value)
		{
			return Mathf.Max(0.05f, 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.0")]
[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.0";

	private ConfigEntry<Key> TrashMarkKey;

	private ConfigEntry<bool> EnableInstantScrapping;

	private ConfigEntry<bool> EnableFixedTimer;

	private ConfigEntry<float> FixedTimerDuration;

	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_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ae: Expected O, but got Unknown
		//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f2: Expected O, but got Unknown
		//IL_024d: Unknown result type (might be due to invalid IL or missing references)
		//IL_025a: Expected O, but got Unknown
		//IL_02a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b0: Expected O, but got Unknown
		//IL_02e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_02fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0308: Expected O, but got Unknown
		//IL_0308: Expected O, but got Unknown
		try
		{
			Logger = ((BaseUnityPlugin)this).Logger;
			Instance = this;
			Harmony val = new Harmony("sparroh.batchscrapping");
			try
			{
				TrashMarkKey = ((BaseUnityPlugin)this).Config.Bind<Key>("Keybinds", "Mark for Trash", (Key)34, "Key to toggle trash mark on upgrades");
				ScrapHandlingMod.currentTrashKey = TrashMarkKey.Value;
				TrashMarkKey.SettingChanged += delegate
				{
					//IL_0006: Unknown result type (might be due to invalid IL or missing references)
					//IL_000b: Unknown result type (might be due to invalid IL or missing references)
					ScrapHandlingMod.currentTrashKey = TrashMarkKey.Value;
				};
				ScrapHandlingMod.ScrapMarkedAction = delegate
				{
					ScrapHandlingMod.TryScrapMarkedUpgrades((MonoBehaviour)(object)this);
				};
				ScrapHandlingMod.ScrapNonFavoriteAction = delegate
				{
					ScrapHandlingMod.TryScrapNonFavoriteUpgrades((MonoBehaviour)(object)this);
				};
				EnableInstantScrapping = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Instant Scrap", false, "Enable instant scrapping without hold timer");
				EnableFixedTimer = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Fixed Scrap Time", false, "Use fixed scrap timer instead of default (ignored when Instant Scrap is on)");
				FixedTimerDuration = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Scrap Duration", 1f, "Duration in seconds for fixed scrap timer");
			}
			catch (Exception ex)
			{
				Logger.LogError((object)("Failed to setup configuration bindings: " + ex.Message));
			}
			try
			{
				InstantPatches.Initialize(EnableFixedTimer, FixedTimerDuration, EnableInstantScrapping);
			}
			catch (Exception ex2)
			{
				Logger.LogError((object)("Failed to initialize InstantScrapping: " + 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(Patches), "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(Patches), "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(Patches), "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(Patches), "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.");
				}
			}
			catch (Exception ex4)
			{
				Logger.LogError((object)("Failed to apply scrap patches: " + ex4.Message));
			}
		}
		catch (Exception ex5)
		{
			Logger.LogError((object)("Critical error during mod initialization: " + ex5.Message + "\n" + ex5.StackTrace));
		}
		Logger.LogInfo((object)"BatchScrapping v2.1.0 loaded successfully.");
	}

	private void Update()
	{
		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
		{
			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 static bool isScrapping;

	private static bool wasScrappingSkins;

	private const float HOLD_DURATION = 1f;

	private const int BATCH_SIZE = 1000000;

	private const float BATCH_INTERVAL = 0f;

	private const float SPECIAL_REWARD_CHANCE = 0.02f;

	public static Key currentTrashKey;

	public static Action ScrapMarkedAction;

	public static Action ScrapNonFavoriteAction;

	public static Sprite starSprite;

	private const byte FavoriteFlag = 1;

	private const byte TrashMarkFlag = 32;

	public static bool IsScrapping => isScrapping;

	public static IEnumerator ScrapMarkedUpgrades()
	{
		Dictionary<PlayerResource, int> dictionary = new Dictionary<PlayerResource, int>();
		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;
			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.");
				isScrapping = false;
				yield break;
			}
			IUpgradable upgradablePrefab = val.UpgradablePrefab;
			if (upgradablePrefab == null)
			{
				SparrohPlugin.Logger.LogError((object)"ScrapMarkedUpgrades: UpgradablePrefab is null.");
				isScrapping = false;
				yield break;
			}
			bool num2 = (bool)AccessTools.Field(typeof(GearDetailsWindow), "inSkinMode").GetValue(val);
			flag = num2;
			IEnumerable<UpgradeInfo> enumerable = (num2 ? PlayerData.GetAllSkins(upgradablePrefab, true) : PlayerData.GetAllUpgrades(upgradablePrefab, true));
			int num3 = 0;
			foreach (UpgradeInfo item in enumerable)
			{
				if (item?.Instances != null)
				{
					num3 += item.Instances.Count;
				}
			}
			list = new List<UpgradeInstance>(num3);
			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.");
				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.");
			PlayerResource key2 = default(PlayerResource);
			PlayerResource key3 = default(PlayerResource);
			foreach (KeyValuePair<Rarity, List<UpgradeInstance>> item4 in (from inst in list
				group inst by inst.Upgrade.Rarity).ToDictionary((IGrouping<Rarity, UpgradeInstance> g) => g.Key, (IGrouping<Rarity, UpgradeInstance> g) => g.ToList()))
			{
				Rarity key = item4.Key;
				int count = item4.Value.Count;
				ref RarityData rarity = ref Global.GetRarity(key);
				int num4 = rarity.upgradeScripCost / 6;
				dictionary.TryGetValue(Global.Instance.ScripResource, out var value);
				dictionary[Global.Instance.ScripResource] = value + num4 * count;
				if (!flag)
				{
					dictionary.TryGetValue(rarity.scrapResource, out var value2);
					dictionary[rarity.scrapResource] = value2 + 2 * count;
					if (PlayerResource.TryGetResource("strange_comp", ref key2))
					{
						int num5 = Mathf.FloorToInt((float)count * 0.02f);
						dictionary.TryGetValue(key2, out var value3);
						dictionary[key2] = value3 + num5;
					}
				}
				else if (PlayerResource.TryGetResource("oyster", ref key3))
				{
					int num6 = Mathf.FloorToInt((float)count * 0.02f);
					dictionary.TryGetValue(key3, out var value4);
					dictionary[key3] = value4 + num6;
				}
			}
			flag2 = true;
		}
		catch (Exception ex)
		{
			SparrohPlugin.Logger.LogError((object)("ScrapMarkedUpgrades: Setup failed: " + ex.Message));
			UndoPatches.CancelBatch();
			isScrapping = false;
			yield break;
		}
		if (flag2 && list != null)
		{
			for (int num7 = 0; num7 < list.Count; num7 += 1000000)
			{
				int num8 = Mathf.Min(num7 + 1000000, list.Count);
				for (int num9 = num7; num9 < num8; num9++)
				{
					UpgradeInstance val2 = list[num9];
					if (val2 != null && !((Object)(object)val2.Upgrade == (Object)null))
					{
						try
						{
							PlayerData.UnequipFromAll(val2);
						}
						catch
						{
						}
						val2.Destroy();
						num++;
					}
				}
			}
		}
		try
		{
			foreach (KeyValuePair<PlayerResource, int> item5 in dictionary)
			{
				PlayerData.Instance.AddResource(item5.Key, item5.Value, true);
			}
		}
		catch (Exception ex2)
		{
			SparrohPlugin.Logger.LogError((object)("ScrapMarkedUpgrades: Failed to add resources: " + ex2.Message));
		}
		if (num > 0)
		{
			UndoPatches.EndBatch();
			try
			{
				wasScrappingSkins = flag;
				RefreshOpenWindows();
			}
			catch (Exception ex3)
			{
				SparrohPlugin.Logger.LogError((object)("ScrapMarkedUpgrades: Failed to refresh windows: " + ex3.Message));
			}
		}
		else
		{
			UndoPatches.CancelBatch();
		}
		isScrapping = false;
	}

	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()
	{
		Dictionary<PlayerResource, int> dictionary = new Dictionary<PlayerResource, int>();
		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;
			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.");
				isScrapping = false;
				yield break;
			}
			IUpgradable upgradablePrefab = val.UpgradablePrefab;
			if (upgradablePrefab == null)
			{
				SparrohPlugin.Logger.LogError((object)"ScrapNonFavoriteUpgrades: UpgradablePrefab is null.");
				isScrapping = false;
				yield break;
			}
			bool num2 = (bool)AccessTools.Field(typeof(GearDetailsWindow), "inSkinMode").GetValue(val);
			flag = num2;
			IEnumerable<UpgradeInfo> enumerable = (num2 ? PlayerData.GetAllSkins(upgradablePrefab, true) : PlayerData.GetAllUpgrades(upgradablePrefab, true));
			int num3 = 0;
			foreach (UpgradeInfo item in enumerable)
			{
				if (item?.Instances != null)
				{
					num3 += item.Instances.Count;
				}
			}
			list = new List<UpgradeInstance>(num3);
			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.");
				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.");
			PlayerResource key2 = default(PlayerResource);
			PlayerResource key3 = default(PlayerResource);
			foreach (KeyValuePair<Rarity, List<UpgradeInstance>> item4 in (from inst in list
				group inst by inst.Upgrade.Rarity).ToDictionary((IGrouping<Rarity, UpgradeInstance> g) => g.Key, (IGrouping<Rarity, UpgradeInstance> g) => g.ToList()))
			{
				Rarity key = item4.Key;
				int count = item4.Value.Count;
				ref RarityData rarity = ref Global.GetRarity(key);
				int num4 = rarity.upgradeScripCost / 6;
				dictionary.TryGetValue(Global.Instance.ScripResource, out var value);
				dictionary[Global.Instance.ScripResource] = value + num4 * count;
				if (!flag)
				{
					dictionary.TryGetValue(rarity.scrapResource, out var value2);
					dictionary[rarity.scrapResource] = value2 + 2 * count;
					if (PlayerResource.TryGetResource("strange_comp", ref key2))
					{
						int num5 = Mathf.FloorToInt((float)count * 0.02f);
						dictionary.TryGetValue(key2, out var value3);
						dictionary[key2] = value3 + num5;
					}
				}
				else if (PlayerResource.TryGetResource("oyster", ref key3))
				{
					int num6 = Mathf.FloorToInt((float)count * 0.02f);
					dictionary.TryGetValue(key3, out var value4);
					dictionary[key3] = value4 + num6;
				}
			}
			flag2 = true;
		}
		catch (Exception ex)
		{
			SparrohPlugin.Logger.LogError((object)("ScrapNonFavoriteUpgrades: Setup failed: " + ex.Message));
			UndoPatches.CancelBatch();
			isScrapping = false;
			yield break;
		}
		if (flag2 && list != null)
		{
			for (int num7 = 0; num7 < list.Count; num7 += 1000000)
			{
				int num8 = Mathf.Min(num7 + 1000000, list.Count);
				for (int num9 = num7; num9 < num8; num9++)
				{
					UpgradeInstance val2 = list[num9];
					if (val2 != null && !((Object)(object)val2.Upgrade == (Object)null))
					{
						try
						{
							PlayerData.UnequipFromAll(val2);
						}
						catch
						{
						}
						val2.Destroy();
						num++;
					}
				}
			}
		}
		try
		{
			foreach (KeyValuePair<PlayerResource, int> item5 in dictionary)
			{
				PlayerData.Instance.AddResource(item5.Key, item5.Value, true);
			}
		}
		catch (Exception ex2)
		{
			SparrohPlugin.Logger.LogError((object)("ScrapNonFavoriteUpgrades: Failed to add resources: " + ex2.Message));
		}
		if (num > 0)
		{
			UndoPatches.EndBatch();
			try
			{
				wasScrappingSkins = flag;
				RefreshOpenWindows();
			}
			catch (Exception ex3)
			{
				SparrohPlugin.Logger.LogError((object)("ScrapNonFavoriteUpgrades: Failed to refresh windows: " + ex3.Message));
			}
		}
		else
		{
			UndoPatches.CancelBatch();
		}
		isScrapping = false;
	}

	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 Patches
{
	private static 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
		//IL_0070: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
		if (ScrapHandlingMod.IsScrapping)
		{
			return;
		}
		Image val = (Image)AccessTools.Field(typeof(GearUpgradeUI), "favoriteIcon").GetValue(__instance);
		if ((Object)(object)val != (Object)null && ((HoverInfoUpgrade)__instance).Upgrade != null)
		{
			bool num = ScrapHandlingMod.IsFavorite(((HoverInfoUpgrade)__instance).Upgrade);
			bool flag = ScrapHandlingMod.IsTrashMarked(((HoverInfoUpgrade)__instance).Upgrade);
			if (num)
			{
				ScrapHandlingMod.starSprite = val.sprite;
				((Component)val).gameObject.SetActive(true);
				((Graphic)val).color = Color.white;
			}
			else if (flag)
			{
				val.sprite = ScrapHandlingMod.starSprite ?? Resources.Load<Sprite>("favorite star");
				((Component)val).gameObject.SetActive(true);
				((Graphic)val).color = Color.red;
				Canvas.ForceUpdateCanvases();
			}
			else
			{
				((Component)val).gameObject.SetActive(false);
			}
		}
	}

	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
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		if (!ScrapHandlingMod.IsScrapping && ((HoverInfoUpgrade)__instance).Upgrade != null)
		{
			Image val = (Image)AccessTools.Field(typeof(GearUpgradeUI), "favoriteIcon").GetValue(__instance);
			if (((Component)val).gameObject.activeSelf)
			{
				((Graphic)val).color = (ScrapHandlingMod.IsFavorite(((HoverInfoUpgrade)__instance).Upgrade) ? Color.white : Color.red);
			}
		}
	}

	public static void UpdatePrefix(GearDetailsWindow __instance)
	{
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ba: Expected O, but got Unknown
		//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
		//IL_010f: Unknown result type (might be due to invalid IL or missing references)
		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))
		{
			return;
		}
		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)
		{
			if (ScrapHandlingMod.IsFavorite(upgrade))
			{
				val2.sprite = ScrapHandlingMod.starSprite;
				((Component)val2).gameObject.SetActive(true);
				((Graphic)val2).color = Color.white;
			}
			else if (ScrapHandlingMod.IsTrashMarked(upgrade))
			{
				val2.sprite = ScrapHandlingMod.starSprite;
				((Component)val2).gameObject.SetActive(true);
				((Graphic)val2).color = Color.red;
			}
			else
			{
				((Component)val2).gameObject.SetActive(false);
			}
			Canvas.ForceUpdateCanvases();
		}
	}
}
public class HoldButtonHandler : MonoBehaviour, IPointerDownHandler, IEventSystemHandler, IPointerUpHandler
{
	private bool isHolding;

	private float holdTimer;

	private const float HOLD_DURATION = 1f;

	public Action onHoldComplete;

	public void OnPointerDown(PointerEventData eventData)
	{
		isHolding = true;
		holdTimer = 0f;
	}

	public void OnPointerUp(PointerEventData eventData)
	{
		isHolding = false;
	}

	private void Update()
	{
		if (isHolding)
		{
			holdTimer += Time.deltaTime;
			if (holdTimer >= 1f)
			{
				onHoldComplete?.Invoke();
				isHolding = false;
			}
		}
	}
}
public static class UndoPatches
{
	public sealed class UpgradeSnapshot
	{
		public IUpgradable Gear;

		public Upgrade Upgrade;

		public int Seed;

		public byte Flags;

		public bool IsUnlocked;

		public bool HasBeenSeen;

		public bool RemoveOnQuit;

		public UpgradeID OverriddenPattern;

		public bool WasEquipped;

		public IUpgradable EquippedOnGear;

		public sbyte EquipX;

		public sbyte EquipY;

		public byte EquipRotation;
	}

	public sealed class ScrapAction
	{
		public List<UpgradeSnapshot> Upgrades = new List<UpgradeSnapshot>();

		public Dictionary<PlayerResource, int> ResourcesGranted = new Dictionary<PlayerResource, int>();

		public string Description;
	}

	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 void EndBatch()
	{
		if (pendingBatch == null)
		{
			return;
		}
		try
		{
			Dictionary<PlayerResource, int> after = SnapshotResources();
			pendingBatch.ResourcesGranted = DiffResources(resourceBaseline, after);
			if (pendingBatch.Upgrades.Count > 0)
			{
				PushAction(pendingBatch);
			}
		}
		catch (Exception ex)
		{
			SparrohPlugin.Logger.LogError((object)("ScrapUndo EndBatch failed: " + ex.Message));
		}
		pendingBatch = null;
		resourceBaseline = null;
	}

	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 = "1.0.0";
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}