Decompiled source of CommandQueueSaveLoad v1.0.0

BepInEx/plugins/CommandQueueSaveLoad.dll

Decompiled 16 hours ago
using System;
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.Permissions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using CommandQueue;
using Microsoft.CodeAnalysis;
using On.RoR2.UI;
using RiskOfOptions;
using RiskOfOptions.OptionConfigs;
using RiskOfOptions.Options;
using RoR2;
using RoR2.UI;
using SimpleJSON;
using UnityEngine;
using UnityEngine.Events;
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("CommandQueueSaveLoad")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+bacd851801530791c3e8d43de4e7f52c0009337e")]
[assembly: AssemblyProduct("CommandQueueSaveLoad")]
[assembly: AssemblyTitle("CommandQueueSaveLoad")]
[assembly: AssemblyMetadata("AI_Assisted_Creation", "This assembly was partially or fully created with the assistance of Generative AI (e.g., Code Suggestions, Refactoring, Documentation Generation).")]
[assembly: AssemblyMetadata("AI_Model_Vendor", "DeepSeek")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace TeamTayne.CommandQueueSaveLoad
{
	[BepInPlugin("TeamTayne.CommandQueueSaveLoad", "CommandQueueSaveLoad", "1.0.0")]
	[BepInDependency("com.kuberoot.commandqueue", "1.7.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class CommandQueueSaveLoadPlugin : BaseUnityPlugin
	{
		internal const string PluginGuid = "TeamTayne.CommandQueueSaveLoad";

		internal const string PluginName = "CommandQueueSaveLoad";

		internal const string PluginVersion = "1.0.0";

		internal const string CommandQueueGuid = "com.kuberoot.commandqueue";

		private void Awake()
		{
			Log.Init(((BaseUnityPlugin)this).Logger);
			ConfigState.Initialize(((BaseUnityPlugin)this).Config);
			SaveLoadController.Install();
			ScoreboardQueueButtons.Install();
			RiskOfOptionsIntegration.TryRegister();
			Log.Info("CommandQueueSaveLoad 1.0.0 is ready; saved queues live in " + SaveLoadController.QueueFilePath);
		}

		private void OnDestroy()
		{
			ScoreboardQueueButtons.Uninstall();
			SaveLoadController.Uninstall();
		}
	}
	internal static class ConfigState
	{
		private const string GeneralSection = "General";

		internal static ConfigEntry<bool> AutoLoadOnRunStart { get; private set; }

		internal static bool AutoLoadEnabled
		{
			get
			{
				if (AutoLoadOnRunStart != null)
				{
					return AutoLoadOnRunStart.Value;
				}
				return false;
			}
		}

		internal static void Initialize(ConfigFile config)
		{
			AutoLoadOnRunStart = config.Bind<bool>("General", "AutoLoadOnRunStart", true, "true: the saved queue for your survivor is applied when a run starts.\nfalse: nothing is loaded automatically; use the open button on the Command Queue tab or cq_queues_load.");
		}
	}
	internal static class ConsoleCommands
	{
		[ConCommand(/*Could not decode attribute arguments.*/)]
		private static void SaveQueue(ConCommandArgs args)
		{
			SaveLoadController.SaveNow();
		}

		[ConCommand(/*Could not decode attribute arguments.*/)]
		private static void LoadQueue(ConCommandArgs args)
		{
			SaveLoadController.LoadNow();
		}

		[ConCommand(/*Could not decode attribute arguments.*/)]
		private static void ClearQueues(ConCommandArgs args)
		{
			SaveLoadController.ClearSaved();
		}
	}
	internal static class Log
	{
		private static ManualLogSource _source;

		internal static void Init(ManualLogSource source)
		{
			_source = source;
		}

		internal static void Info(object message)
		{
			ManualLogSource source = _source;
			if (source != null)
			{
				source.LogInfo(message);
			}
		}

		internal static void Warning(object message)
		{
			ManualLogSource source = _source;
			if (source != null)
			{
				source.LogWarning(message);
			}
		}

		internal static void Error(object message)
		{
			ManualLogSource source = _source;
			if (source != null)
			{
				source.LogError(message);
			}
		}
	}
	internal readonly struct SavedQueueEntry
	{
		internal string Item { get; }

		internal int Count { get; }

		internal SavedQueueEntry(string item, int count)
		{
			Item = item;
			Count = count;
		}
	}
	internal static class QueueStore
	{
		internal const int Version = 1;

		internal const int MaxCount = 999;

		internal static Dictionary<string, List<SavedQueueEntry>> Read(string path, out string error)
		{
			error = null;
			Dictionary<string, List<SavedQueueEntry>> dictionary = new Dictionary<string, List<SavedQueueEntry>>(StringComparer.OrdinalIgnoreCase);
			if (!File.Exists(path))
			{
				return dictionary;
			}
			string json;
			try
			{
				json = File.ReadAllText(path);
			}
			catch (IOException ex)
			{
				error = ex.Message;
				return dictionary;
			}
			try
			{
				ParseInto(json, dictionary);
			}
			catch (Exception ex2)
			{
				error = ex2.Message;
				dictionary.Clear();
				Quarantine(path);
			}
			return dictionary;
		}

		internal static void Write(string path, Dictionary<string, List<SavedQueueEntry>> queues, Func<string, string> describe)
		{
			string directoryName = Path.GetDirectoryName(path);
			if (!string.IsNullOrEmpty(directoryName))
			{
				Directory.CreateDirectory(directoryName);
			}
			string text = path + ".tmp";
			File.WriteAllText(text, Serialize(queues, describe));
			if (File.Exists(path))
			{
				File.Replace(text, path, null);
			}
			else
			{
				File.Move(text, path);
			}
		}

		internal static string Serialize(Dictionary<string, List<SavedQueueEntry>> queues, Func<string, string> describe)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected O, but got Unknown
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Expected O, but got Unknown
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Expected O, but got Unknown
			JSONObject val = new JSONObject { ["version"] = JSONNode.op_Implicit(1) };
			JSONArray val2 = new JSONArray();
			foreach (string item in OrderedKeys(queues))
			{
				JSONArray val3 = new JSONArray();
				foreach (SavedQueueEntry item2 in queues[item])
				{
					((JSONNode)val3).Add((JSONNode)new JSONObject
					{
						["item"] = JSONNode.op_Implicit(item2.Item),
						["count"] = JSONNode.op_Implicit(item2.Count)
					});
				}
				JSONObject val4 = new JSONObject
				{
					["survivor"] = JSONNode.op_Implicit(item),
					["items"] = (JSONNode)(object)val3
				};
				string text = Describe(describe, item);
				if (!string.IsNullOrEmpty(text))
				{
					((JSONNode)val4)["name"] = JSONNode.op_Implicit(text);
				}
				((JSONNode)val2).Add((JSONNode)(object)val4);
			}
			((JSONNode)val)["queues"] = (JSONNode)(object)val2;
			return ((JSONNode)val).ToString(2);
		}

		internal static Dictionary<string, List<SavedQueueEntry>> Parse(string json)
		{
			Dictionary<string, List<SavedQueueEntry>> dictionary = new Dictionary<string, List<SavedQueueEntry>>(StringComparer.OrdinalIgnoreCase);
			ParseInto(json, dictionary);
			return dictionary;
		}

		internal static void ParseInto(string json, Dictionary<string, List<SavedQueueEntry>> queues)
		{
			JSONNode val = JSON.Parse(json);
			if (val == (object)null || !val.IsObject)
			{
				throw new InvalidDataException("root is not an object");
			}
			JSONNode val2 = val["queues"];
			if (val2 == (object)null || !val2.IsArray)
			{
				throw new InvalidDataException("no queues array");
			}
			foreach (JSONNode child in val2.Children)
			{
				if (child == (object)null || !child.IsObject)
				{
					continue;
				}
				JSONNode val3 = child["survivor"];
				string text = ((val3 != (object)null && val3.IsString) ? val3.Value.Trim() : null);
				if (string.IsNullOrEmpty(text))
				{
					continue;
				}
				if (!queues.TryGetValue(text, out var value))
				{
					value = (queues[text] = new List<SavedQueueEntry>());
				}
				JSONNode val4 = child["items"];
				if (val4 == (object)null || !val4.IsArray)
				{
					continue;
				}
				foreach (JSONNode child2 in val4.Children)
				{
					if (!(child2 == (object)null) && child2.IsObject)
					{
						JSONNode val5 = child2["item"];
						if (!(val5 == (object)null) && val5.IsString)
						{
							Add(value, val5.Value.Trim(), child2["count"].AsInt);
						}
					}
				}
			}
		}

		internal static bool TryGetSection(Dictionary<string, List<SavedQueueEntry>> queues, string key, out List<SavedQueueEntry> entries)
		{
			entries = null;
			if (key != null)
			{
				return queues.TryGetValue(key, out entries);
			}
			return false;
		}

		internal static List<SavedQueueEntry> Section(Dictionary<string, List<SavedQueueEntry>> queues, string key)
		{
			if (!TryGetSection(queues, key, out var entries))
			{
				return new List<SavedQueueEntry>();
			}
			return entries;
		}

		private static void Add(List<SavedQueueEntry> queue, string item, int count)
		{
			if (item.Length == 0 || count <= 0)
			{
				return;
			}
			if (count > 999)
			{
				count = 999;
			}
			for (int i = 0; i < queue.Count; i++)
			{
				if (string.Equals(queue[i].Item, item, StringComparison.OrdinalIgnoreCase))
				{
					int num = queue[i].Count + count;
					queue[i] = new SavedQueueEntry(queue[i].Item, (num > 999) ? 999 : num);
					return;
				}
			}
			queue.Add(new SavedQueueEntry(item, count));
		}

		private static IEnumerable<string> OrderedKeys(Dictionary<string, List<SavedQueueEntry>> queues)
		{
			return queues.Keys.OrderBy<string, string>((string key) => key, StringComparer.OrdinalIgnoreCase);
		}

		private static string Describe(Func<string, string> describe, string key)
		{
			if (describe == null)
			{
				return null;
			}
			try
			{
				return describe(key);
			}
			catch (Exception)
			{
				return null;
			}
		}

		private static void Quarantine(string path)
		{
			try
			{
				string text = path + ".invalid";
				if (File.Exists(text))
				{
					File.Delete(text);
				}
				File.Move(path, text);
			}
			catch (IOException)
			{
			}
		}
	}
	internal static class RiskOfOptionsIntegration
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static UnityAction <0>__ClearSaved;
		}

		internal const string PluginGuid = "com.rune580.riskofoptions";

		private const string IconResourceName = "TeamTayne.CommandQueueSaveLoad.icon.png";

		private const string Category = "Saved Queues";

		private const string ModGuid = "TeamTayne.CommandQueueSaveLoad";

		private const string ModName = "CommandQueueSaveLoad";

		private static bool? _available;

		internal static bool Available
		{
			get
			{
				bool valueOrDefault = _available == true;
				if (!_available.HasValue)
				{
					valueOrDefault = Chainloader.PluginInfos.ContainsKey("com.rune580.riskofoptions");
					_available = valueOrDefault;
				}
				return _available.Value;
			}
		}

		internal static void TryRegister()
		{
			if (!Available)
			{
				return;
			}
			try
			{
				Register();
				Log.Info("Risk of Options settings registered.");
			}
			catch (Exception ex)
			{
				Log.Warning("Risk of Options integration failed; the config file and console commands remain available: " + ex.Message);
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
		private static void Register()
		{
			//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_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Expected O, but got Unknown
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Expected O, but got Unknown
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Expected O, but got Unknown
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Expected O, but got Unknown
			ModSettingsManager.SetModDescription("Remembers the queue you save for each survivor and applies it when a run starts.", "TeamTayne.CommandQueueSaveLoad", "CommandQueueSaveLoad");
			Sprite val = LoadIcon();
			if ((Object)(object)val != (Object)null)
			{
				ModSettingsManager.SetModIcon(val, "TeamTayne.CommandQueueSaveLoad", "CommandQueueSaveLoad");
			}
			else
			{
				Log.Warning("Risk of Options icon resource was not found or could not be decoded.");
			}
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(ConfigState.AutoLoadOnRunStart, new CheckBoxConfig
			{
				name = "Load Saved Queue At Run Start",
				category = "Saved Queues",
				description = "Apply the saved queue for your survivor automatically when a run starts."
			}), "TeamTayne.CommandQueueSaveLoad", "CommandQueueSaveLoad");
			object obj = <>O.<0>__ClearSaved;
			if (obj == null)
			{
				UnityAction val2 = SaveLoadController.ClearSaved;
				<>O.<0>__ClearSaved = val2;
				obj = (object)val2;
			}
			ModSettingsManager.AddOption((BaseOption)new GenericButtonOption("Clear Saved Queues", "Saved Queues", "Deletes every saved queue from disk. Save, open, and reset happen in a run, from CommandQueue's button row.", "Clear", (UnityAction)obj), "TeamTayne.CommandQueueSaveLoad", "CommandQueueSaveLoad");
		}

		private static Sprite LoadIcon()
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("TeamTayne.CommandQueueSaveLoad.icon.png");
			if (stream == null)
			{
				return null;
			}
			using MemoryStream memoryStream = new MemoryStream();
			stream.CopyTo(memoryStream);
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
			if (!ImageConversion.LoadImage(val, memoryStream.ToArray(), true))
			{
				Object.Destroy((Object)(object)val);
				return null;
			}
			((Object)val).name = "CommandQueueSaveLoadIcon";
			return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f);
		}
	}
	internal static class SaveLoadController
	{
		private const string QueueFileName = "CommandQueueSaveLoad.queues.json";

		private static string _survivorKey;

		private static bool _loadedThisRun;

		private static bool _installed;

		internal static string QueueFilePath => Path.Combine(Paths.ConfigPath, "CommandQueueSaveLoad.queues.json");

		internal static void Install()
		{
			if (_installed)
			{
				return;
			}
			try
			{
				Run.onRunStartGlobal += OnRunStart;
				CharacterBody.onBodyStartGlobal += OnBodyStart;
				_installed = true;
			}
			catch (Exception arg)
			{
				Log.Error($"could not install game hooks; queue saving and loading is unavailable: {arg}");
			}
		}

		internal static void Uninstall()
		{
			if (_installed)
			{
				try
				{
					Run.onRunStartGlobal -= OnRunStart;
					CharacterBody.onBodyStartGlobal -= OnBodyStart;
				}
				catch (Exception ex)
				{
					Log.Warning("could not remove game hooks during shutdown: " + ex.Message);
				}
				_installed = false;
			}
		}

		internal static void SaveNow()
		{
			if (!TryGetSurvivorKey("save", out var key))
			{
				return;
			}
			try
			{
				List<SavedQueueEntry> list = SnapshotLiveQueue();
				Dictionary<string, List<SavedQueueEntry>> dictionary = ReadFile();
				dictionary[key] = list;
				QueueStore.Write(QueueFilePath, dictionary, DescribeSurvivor);
				Notify("CommandQueue: saved " + DescribeItemCount(list) + " for " + DescribeSurvivor(key) + ".");
			}
			catch (Exception arg)
			{
				Log.Error($"saving the queue failed: {arg}");
				Notify("CommandQueue: saving the queue failed, see the log for details.");
			}
		}

		internal static void LoadNow()
		{
			if (!TryGetSurvivorKey("load", out var key))
			{
				return;
			}
			try
			{
				ApplySaved(key, automatic: false);
			}
			catch (Exception arg)
			{
				Log.Error($"loading the saved queue failed: {arg}");
				Notify("CommandQueue: loading the saved queue failed, see the log for details.");
			}
		}

		internal static void ResetNow()
		{
			if (!TryGetSurvivorKey("reset", out var _))
			{
				return;
			}
			try
			{
				if (!TryClearLiveQueue(out var _))
				{
					Notify("CommandQueue: cannot reset, CommandQueue is not ready.");
				}
				else
				{
					Notify("CommandQueue: reset the current queue; saved queue unchanged.");
				}
			}
			catch (Exception arg)
			{
				Log.Error($"resetting the queue failed: {arg}");
				Notify("CommandQueue: resetting the queue failed, see the log for details.");
			}
		}

		internal static void ClearSaved()
		{
			try
			{
				if (!File.Exists(QueueFilePath))
				{
					Notify("CommandQueue: there are no saved queues to clear.");
					return;
				}
				File.Delete(QueueFilePath);
				Log.Info("deleted " + QueueFilePath);
				Notify("CommandQueue: saved queues cleared.");
			}
			catch (Exception arg)
			{
				Log.Error($"clearing saved queues failed: {arg}");
				Notify("CommandQueue: clearing saved queues failed, see the log for details.");
			}
		}

		private static void OnRunStart(Run run)
		{
			_loadedThisRun = false;
			_survivorKey = null;
		}

		private static void OnBodyStart(CharacterBody body)
		{
			try
			{
				if ((Object)(object)body == (Object)null || (Object)(object)Run.instance == (Object)null || !IsLocalPlayerBody(body))
				{
					return;
				}
				string text = SurvivorKeyFor(body);
				if (text != null)
				{
					_survivorKey = text;
					if (!_loadedThisRun && ConfigState.AutoLoadEnabled)
					{
						_loadedThisRun = true;
						ApplySaved(text, automatic: true);
					}
				}
			}
			catch (Exception arg)
			{
				Log.Error($"applying the saved queue at run start failed: {arg}");
			}
		}

		private static void ApplySaved(string key, bool automatic)
		{
			if (!QueueStore.TryGetSection(ReadFile(), key, out var entries))
			{
				if (!automatic)
				{
					Notify("CommandQueue: no saved queue for " + DescribeSurvivor(key) + ".");
				}
			}
			else
			{
				int num = Apply(entries);
				Notify($"CommandQueue: loaded {DescribeItemCount(entries)} ({num} queued) " + "from the saved queue for " + DescribeSurvivor(key) + ".");
			}
		}

		private static int Apply(List<SavedQueueEntry> entries)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			if (!TryClearLiveQueue(out var _))
			{
				return 0;
			}
			int num = 0;
			foreach (SavedQueueEntry entry in entries)
			{
				if (TryResolvePickup(entry.Item, out var pickupIndex))
				{
					for (int i = 0; i < entry.Count; i++)
					{
						QueueManager.Enqueue(pickupIndex);
						num++;
					}
				}
			}
			return num;
		}

		private static bool TryClearLiveQueue(out int removed)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: 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_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			removed = 0;
			Dictionary<ItemTier, List<QueueEntry>> mainQueues = QueueManager.mainQueues;
			if (mainQueues == null)
			{
				Log.Error("CommandQueue queues are unavailable, so the live queue cannot be reset.");
				return false;
			}
			ItemTier[] array = mainQueues.Keys.ToArray();
			foreach (ItemTier val in array)
			{
				if (mainQueues.TryGetValue(val, out var value) && value != null)
				{
					while (value.Count > 0)
					{
						QueueEntry val2 = value[0];
						QueueManager.Remove(val, 0, val2.count);
						removed += val2.count;
					}
				}
			}
			return true;
		}

		private static List<SavedQueueEntry> SnapshotLiveQueue()
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: 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)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Invalid comparison between Unknown and I4
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			List<SavedQueueEntry> list = new List<SavedQueueEntry>();
			Dictionary<ItemTier, List<QueueEntry>> mainQueues = QueueManager.mainQueues;
			if (mainQueues == null)
			{
				return list;
			}
			foreach (KeyValuePair<ItemTier, List<QueueEntry>> item in mainQueues)
			{
				if (item.Value == null)
				{
					continue;
				}
				foreach (QueueEntry item2 in item.Value)
				{
					PickupDef pickupDef = PickupCatalog.GetPickupDef(item2.pickupIndex);
					if (pickupDef != null && (int)pickupDef.itemIndex != -1)
					{
						ItemDef itemDef = ItemCatalog.GetItemDef(pickupDef.itemIndex);
						if (!((Object)(object)itemDef == (Object)null) && !string.IsNullOrEmpty(((Object)itemDef).name))
						{
							list.Add(new SavedQueueEntry(((Object)itemDef).name, item2.count));
						}
					}
				}
			}
			return list;
		}

		private static bool TryResolvePickup(string itemName, out PickupIndex pickupIndex)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Invalid comparison between Unknown and I4
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			pickupIndex = PickupIndex.none;
			if (string.IsNullOrEmpty(itemName))
			{
				return false;
			}
			ItemIndex val = ItemCatalog.FindItemIndex(itemName);
			if ((int)val == -1)
			{
				return false;
			}
			ItemDef itemDef = ItemCatalog.GetItemDef(val);
			if ((Object)(object)itemDef == (Object)null)
			{
				return false;
			}
			Dictionary<ItemTier, List<QueueEntry>> mainQueues = QueueManager.mainQueues;
			if (mainQueues == null || !mainQueues.ContainsKey(itemDef.tier))
			{
				return false;
			}
			pickupIndex = PickupCatalog.FindPickupIndex(val);
			return ((PickupIndex)(ref pickupIndex)).isValid;
		}

		private static bool TryGetSurvivorKey(string action, out string key)
		{
			key = null;
			if ((Object)(object)Run.instance == (Object)null)
			{
				Notify("CommandQueue: cannot " + action + ", start a run first.");
				return false;
			}
			if (QueueManager.mainQueues == null)
			{
				Notify("CommandQueue: cannot " + action + ", CommandQueue is not ready.");
				return false;
			}
			key = _survivorKey;
			if (key == null)
			{
				Notify("CommandQueue: cannot " + action + ", your survivor is not known yet.");
				return false;
			}
			return true;
		}

		private static Dictionary<string, List<SavedQueueEntry>> ReadFile()
		{
			string error;
			Dictionary<string, List<SavedQueueEntry>> result = QueueStore.Read(QueueFilePath, out error);
			if (error != null)
			{
				Log.Warning("could not read " + QueueFilePath + " (" + error + "); it was moved aside and an empty set is used.");
			}
			return result;
		}

		private static bool IsLocalPlayerBody(CharacterBody body)
		{
			CharacterMaster master = body.master;
			if ((Object)(object)master == (Object)null)
			{
				return false;
			}
			PlayerCharacterMasterController playerCharacterMasterController = master.playerCharacterMasterController;
			if ((Object)(object)playerCharacterMasterController != (Object)null && (Object)(object)playerCharacterMasterController.networkUser != (Object)null && playerCharacterMasterController.networkUser.localUser != null)
			{
				return true;
			}
			LocalUser firstLocalUser = LocalUserManager.GetFirstLocalUser();
			if (firstLocalUser != null)
			{
				if (!((Object)(object)firstLocalUser.cachedMaster == (Object)(object)master))
				{
					return (Object)(object)firstLocalUser.cachedBody == (Object)(object)body;
				}
				return true;
			}
			return false;
		}

		private static string SurvivorKeyFor(CharacterBody body)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Invalid comparison between Unknown and I4
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			BodyIndex bodyIndex = body.bodyIndex;
			if ((int)bodyIndex == -1)
			{
				return null;
			}
			string bodyName = BodyCatalog.GetBodyName(bodyIndex);
			if (!string.IsNullOrEmpty(bodyName))
			{
				return bodyName;
			}
			return null;
		}

		private static string DescribeSurvivor(string key)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Invalid comparison between Unknown and I4
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrEmpty(key))
			{
				return "unknown survivor";
			}
			try
			{
				BodyIndex val = BodyCatalog.FindBodyIndex(key);
				if ((int)val != -1)
				{
					SurvivorDef survivorDef = SurvivorCatalog.GetSurvivorDef(SurvivorCatalog.GetSurvivorIndexFromBodyIndex(val));
					string text = (((Object)(object)survivorDef != (Object)null) ? survivorDef.displayNameToken : null);
					if (!string.IsNullOrEmpty(text) && !Language.IsTokenInvalid(text))
					{
						string text2 = Language.GetString(text);
						if (!string.IsNullOrEmpty(text2))
						{
							return text2;
						}
					}
				}
			}
			catch (Exception ex)
			{
				Log.Warning("could not resolve a display name for " + key + ": " + ex.Message);
			}
			return key;
		}

		private static string DescribeItemCount(List<SavedQueueEntry> items)
		{
			int num = items.Sum((SavedQueueEntry entry) => entry.Count);
			return string.Format("{0} item{1}", num, (num == 1) ? string.Empty : "s");
		}

		private static void Notify(string message)
		{
			Log.Info(message);
			if ((Object)(object)Run.instance != (Object)null)
			{
				Chat.AddMessage(message);
			}
		}
	}
	internal sealed class ScoreboardQueueButtons : MonoBehaviour
	{
		private sealed class QueueButton
		{
			internal RectTransform Rect;

			internal int Slot;
		}

		[CompilerGenerated]
		private static class <>O
		{
			public static hook_Awake <0>__ScoreboardAwake;

			public static UnityAction <1>__SaveNow;

			public static UnityAction <2>__LoadNow;

			public static UnityAction <3>__ResetNow;
		}

		private const string SaveIconResource = "TeamTayne.CommandQueueSaveLoad.save.png";

		private const string OpenIconResource = "TeamTayne.CommandQueueSaveLoad.open.png";

		private const string ResetIconResource = "TeamTayne.CommandQueueSaveLoad.reset.png";

		private const string QueueContainerName = "CommandQueueContainer";

		private const string LoopGlyphMarker = "RepeatButtonImageHolder";

		private const float IconInset = 7f;

		private const float ButtonGap = 5f;

		private static readonly Dictionary<string, Texture2D> Icons = new Dictionary<string, Texture2D>(StringComparer.Ordinal);

		private static bool _installAttempted;

		private RectTransform _scoreboard;

		private RectTransform _queueContainer;

		private RectTransform _loopButton;

		private readonly List<QueueButton> _buttons = new List<QueueButton>();

		private bool _warnedNoLoopButton;

		internal static void Install()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected O, but got Unknown
			if (_installAttempted)
			{
				return;
			}
			try
			{
				object obj = <>O.<0>__ScoreboardAwake;
				if (obj == null)
				{
					hook_Awake val = ScoreboardAwake;
					<>O.<0>__ScoreboardAwake = val;
					obj = (object)val;
				}
				ScoreboardController.Awake += (hook_Awake)obj;
				HUD[] array = Object.FindObjectsOfType<HUD>();
				foreach (HUD val2 in array)
				{
					if ((Object)(object)val2 != (Object)null)
					{
						Attach(val2.scoreboardPanel);
					}
				}
				_installAttempted = true;
			}
			catch (Exception arg)
			{
				Log.Error($"could not hook the scoreboard, so the in-game queue buttons are unavailable: {arg}");
			}
		}

		internal static void Uninstall()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected O, but got Unknown
			if (!_installAttempted)
			{
				return;
			}
			try
			{
				object obj = <>O.<0>__ScoreboardAwake;
				if (obj == null)
				{
					hook_Awake val = ScoreboardAwake;
					<>O.<0>__ScoreboardAwake = val;
					obj = (object)val;
				}
				ScoreboardController.Awake -= (hook_Awake)obj;
			}
			catch (Exception ex)
			{
				Log.Warning("could not remove the scoreboard hook during shutdown: " + ex.Message);
			}
			_installAttempted = false;
		}

		private static void ScoreboardAwake(orig_Awake orig, ScoreboardController self)
		{
			orig.Invoke(self);
			try
			{
				Attach(((Component)self).gameObject);
			}
			catch (Exception arg)
			{
				Log.Error($"could not add the in-game queue buttons: {arg}");
			}
		}

		private static void Attach(GameObject scoreboardPanel)
		{
			if (!((Object)(object)scoreboardPanel == (Object)null) && !((Object)(object)scoreboardPanel.GetComponent<ScoreboardQueueButtons>() != (Object)null))
			{
				scoreboardPanel.AddComponent<ScoreboardQueueButtons>();
			}
		}

		private void Awake()
		{
			ref RectTransform scoreboard = ref _scoreboard;
			Transform transform = ((Component)this).transform;
			scoreboard = (RectTransform)(object)((transform is RectTransform) ? transform : null);
			if ((Object)(object)_scoreboard == (Object)null)
			{
				Object.Destroy((Object)(object)this);
			}
		}

		private void OnDestroy()
		{
			ClearButtons();
		}

		private void LateUpdate()
		{
			RectTransform val = ResolveLoopButton();
			if ((Object)(object)val == (Object)null)
			{
				SetButtonsActive(active: false);
				return;
			}
			try
			{
				SyncButtons(val);
				SetButtonsActive(active: true);
			}
			catch (Exception arg)
			{
				Log.Error($"could not place the saved-queue buttons on the scoreboard: {arg}");
				SetButtonsActive(active: false);
			}
		}

		private RectTransform ResolveLoopButton()
		{
			if ((Object)(object)_queueContainer == (Object)null)
			{
				ref RectTransform queueContainer = ref _queueContainer;
				Transform obj = FindDescendant((Transform)(object)_scoreboard, "CommandQueueContainer");
				queueContainer = (RectTransform)(object)((obj is RectTransform) ? obj : null);
			}
			if ((Object)(object)_queueContainer == (Object)null || !((Component)_queueContainer).gameObject.activeInHierarchy)
			{
				_loopButton = null;
				return null;
			}
			if ((Object)(object)_loopButton != (Object)null && ((Component)_loopButton).gameObject.activeInHierarchy)
			{
				return _loopButton;
			}
			_loopButton = FindLoopButton();
			if ((Object)(object)_loopButton == (Object)null && !_warnedNoLoopButton)
			{
				_warnedNoLoopButton = true;
				Log.Warning("CommandQueue's loop button was not found on the scoreboard, so the saved-queue buttons stay hidden. They expect the CommandQueue 1.7.0 layout.");
			}
			return _loopButton;
		}

		private RectTransform FindLoopButton()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			foreach (Transform item in (Transform)_queueContainer)
			{
				Transform val = item;
				if (((Component)val).gameObject.activeInHierarchy)
				{
					Transform val2 = FindDescendant(val, "RepeatButtonImageHolder");
					if ((Object)(object)val2 != (Object)null)
					{
						Transform parent = val2.parent;
						return (RectTransform)(object)((parent is RectTransform) ? parent : null);
					}
				}
			}
			return null;
		}

		private void SyncButtons(RectTransform loopButton)
		{
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Expected O, but got Unknown
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Expected O, but got Unknown
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Expected O, but got Unknown
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: 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_012e: 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_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			Transform parent = ((Transform)loopButton).parent;
			if (NeedsRebuild())
			{
				ClearButtons();
				object obj = <>O.<1>__SaveNow;
				if (obj == null)
				{
					UnityAction val = SaveLoadController.SaveNow;
					<>O.<1>__SaveNow = val;
					obj = (object)val;
				}
				CreateButton(loopButton, "CommandQueueSaveButton", "TeamTayne.CommandQueueSaveLoad.save.png", 3, (UnityAction)obj);
				object obj2 = <>O.<2>__LoadNow;
				if (obj2 == null)
				{
					UnityAction val2 = SaveLoadController.LoadNow;
					<>O.<2>__LoadNow = val2;
					obj2 = (object)val2;
				}
				CreateButton(loopButton, "CommandQueueOpenButton", "TeamTayne.CommandQueueSaveLoad.open.png", 2, (UnityAction)obj2);
				object obj3 = <>O.<3>__ResetNow;
				if (obj3 == null)
				{
					UnityAction val3 = SaveLoadController.ResetNow;
					<>O.<3>__ResetNow = val3;
					obj3 = (object)val3;
				}
				CreateButton(loopButton, "CommandQueueResetButton", "TeamTayne.CommandQueueSaveLoad.reset.png", 1, (UnityAction)obj3);
			}
			float num = loopButton.sizeDelta.x + 5f;
			foreach (QueueButton button in _buttons)
			{
				RectTransform rect = button.Rect;
				if ((Object)(object)((Transform)rect).parent != (Object)(object)parent)
				{
					((Transform)rect).SetParent(parent, false);
				}
				if (rect.anchorMin != loopButton.anchorMin)
				{
					rect.anchorMin = loopButton.anchorMin;
				}
				if (rect.anchorMax != loopButton.anchorMax)
				{
					rect.anchorMax = loopButton.anchorMax;
				}
				if (rect.pivot != loopButton.pivot)
				{
					rect.pivot = loopButton.pivot;
				}
				if (rect.sizeDelta != loopButton.sizeDelta)
				{
					rect.sizeDelta = loopButton.sizeDelta;
				}
				Vector2 anchoredPosition = loopButton.anchoredPosition;
				anchoredPosition.x -= num * (float)button.Slot;
				if (rect.anchoredPosition != anchoredPosition)
				{
					rect.anchoredPosition = anchoredPosition;
				}
			}
		}

		private bool NeedsRebuild()
		{
			if (_buttons.Count == 0)
			{
				return true;
			}
			foreach (QueueButton button in _buttons)
			{
				if ((Object)(object)button.Rect == (Object)null)
				{
					return true;
				}
			}
			return false;
		}

		private void SetButtonsActive(bool active)
		{
			foreach (QueueButton button in _buttons)
			{
				if ((Object)(object)button.Rect != (Object)null && ((Component)button.Rect).gameObject.activeSelf != active)
				{
					((Component)button.Rect).gameObject.SetActive(active);
				}
			}
		}

		private void ClearButtons()
		{
			foreach (QueueButton button in _buttons)
			{
				if ((Object)(object)button.Rect != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)button.Rect).gameObject);
				}
			}
			_buttons.Clear();
		}

		private void CreateButton(RectTransform loopButton, string name, string iconResource, int slot, UnityAction action)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Expected O, but got Unknown
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Expected O, but got Unknown
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Expected O, but got Unknown
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Expected O, but got Unknown
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = Icon(iconResource);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			GameObject val2 = Object.Instantiate<GameObject>(((Component)loopButton).gameObject, ((Transform)loopButton).parent);
			((Object)val2).name = name;
			RectTransform val3 = (RectTransform)val2.transform;
			HGButton component = val2.GetComponent<HGButton>();
			if ((Object)(object)component == (Object)null)
			{
				Object.Destroy((Object)(object)val2);
				Log.Warning("CommandQueue loop button " + ((Object)loopButton).name + " has no HGButton component.");
				return;
			}
			((MPButton)component).onSelect = new UnityEvent();
			((MPButton)component).onDeselect = new UnityEvent();
			((Button)component).onClick = new ButtonClickedEvent();
			((UnityEvent)((Button)component).onClick).AddListener(action);
			Transform val4 = FindDescendant((Transform)(object)val3, "RepeatButtonImageHolder");
			GameObject val5 = new GameObject("Icon", new Type[1] { typeof(RectTransform) });
			RectTransform val6 = (RectTransform)val5.transform;
			((Transform)val6).SetParent((Transform)(object)val3, false);
			RectTransform val7 = (RectTransform)(object)((val4 is RectTransform) ? val4 : null);
			if (val7 != null)
			{
				val6.anchorMin = val7.anchorMin;
				val6.anchorMax = val7.anchorMax;
				val6.anchoredPosition = val7.anchoredPosition;
				val6.sizeDelta = val7.sizeDelta;
				val6.pivot = val7.pivot;
				((Object)val4).name = "RemovedLoopGlyph";
				((Component)val4).gameObject.SetActive(false);
				Object.Destroy((Object)(object)((Component)val4).gameObject);
			}
			else
			{
				val6.anchorMin = Vector2.zero;
				val6.anchorMax = Vector2.one;
				val6.offsetMin = new Vector2(7f, 7f);
				val6.offsetMax = new Vector2(-7f, -7f);
			}
			RawImage obj = val5.AddComponent<RawImage>();
			obj.texture = (Texture)(object)val;
			((Graphic)obj).raycastTarget = false;
			_buttons.Add(new QueueButton
			{
				Rect = val3,
				Slot = slot
			});
		}

		private static Transform FindDescendant(Transform root, string name)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Expected O, but got Unknown
			foreach (Transform item in root)
			{
				Transform val = item;
				if (((Object)val).name.IndexOf(name, StringComparison.Ordinal) >= 0)
				{
					return val;
				}
				Transform val2 = FindDescendant(val, name);
				if ((Object)(object)val2 != (Object)null)
				{
					return val2;
				}
			}
			return null;
		}

		private static Texture2D Icon(string resourceName)
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Expected O, but got Unknown
			if (Icons.TryGetValue(resourceName, out var value))
			{
				return value;
			}
			try
			{
				using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName);
				if (stream == null)
				{
					Log.Warning("queue button icon " + resourceName + " was not found in the assembly");
					return null;
				}
				using MemoryStream memoryStream = new MemoryStream();
				stream.CopyTo(memoryStream);
				Texture2D val = new Texture2D(2, 2, (TextureFormat)4, true);
				if (!ImageConversion.LoadImage(val, memoryStream.ToArray(), true))
				{
					Object.Destroy((Object)(object)val);
					Log.Warning("queue button icon " + resourceName + " could not be decoded");
					return null;
				}
				((Object)val).name = Path.GetFileNameWithoutExtension(resourceName);
				((Texture)val).wrapMode = (TextureWrapMode)1;
				((Texture)val).filterMode = (FilterMode)2;
				Icons[resourceName] = val;
				return val;
			}
			catch (Exception arg)
			{
				Log.Error($"loading queue button icon {resourceName} failed: {arg}");
				return null;
			}
		}
	}
}