Decompiled source of Kitbag v0.1.0

plugins/Kitbag.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
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 System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Managers;
using Jotunn.Utils;
using Kitbag.Ui;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_guiutils")]
[assembly: IgnoresAccessChecksTo("assembly_utils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyCompany("Kitbag")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0")]
[assembly: AssemblyProduct("Kitbag")]
[assembly: AssemblyTitle("Kitbag")]
[assembly: AssemblyMetadata("AI_Assisted_Creation", "Written with the assistance of generative AI.")]
[assembly: AssemblyMetadata("AI_Model_Vendor", "Anthropic (Claude)")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class IsReadOnlyAttribute : Attribute
	{
	}
	[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 Kitbag
{
	internal sealed class ChestSkips
	{
		public int InUse;

		public int NoAccess;

		public int Total => InUse + NoAccess;
	}
	internal static class ChestRegistry
	{
		private static readonly HashSet<Container> All = new HashSet<Container>();

		public static void Add(Container container)
		{
			All.Add(container);
		}

		public static List<Container> Find(Player player, float range, ChestSkips skips)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: 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_00ee: Unknown result type (might be due to invalid IL or missing references)
			All.RemoveWhere((Container c) => (Object)(object)c == (Object)null);
			List<Container> list = new List<Container>();
			Vector3 origin = ((Component)player).transform.position;
			long playerID = player.GetPlayerID();
			Container val = (((Object)(object)InventoryGui.instance != (Object)null) ? InventoryGui.instance.m_currentContainer : null);
			foreach (Container item in All)
			{
				ZNetView nview = item.m_nview;
				if (!((Object)(object)nview == (Object)null) && nview.IsValid() && item.GetInventory() != null && !(Vector3.Distance(origin, ((Component)item).transform.position) > range) && IsPlayerChest(item))
				{
					if (!item.CheckAccess(playerID) || (item.m_checkGuardStone && !PrivateArea.CheckAccess(((Component)item).transform.position, 0f, false, false)))
					{
						skips.NoAccess++;
					}
					else if ((Object)(object)item != (Object)(object)val && OpenBySomeone(item))
					{
						skips.InUse++;
					}
					else
					{
						list.Add(item);
					}
				}
			}
			return list.OrderBy((Container c) => Vector3.Distance(origin, ((Component)c).transform.position)).ToList();
		}

		public static bool Owned(Container container)
		{
			if ((Object)(object)container.m_nview != (Object)null && container.m_nview.IsValid())
			{
				return container.m_nview.IsOwner();
			}
			return false;
		}

		public static bool StillUsable(Container container)
		{
			if ((Object)(object)container == (Object)null || !Owned(container))
			{
				return false;
			}
			Container val = (((Object)(object)InventoryGui.instance != (Object)null) ? InventoryGui.instance.m_currentContainer : null);
			if (container.IsInUse())
			{
				return (Object)(object)container == (Object)(object)val;
			}
			return true;
		}

		private static bool OpenBySomeone(Container container)
		{
			if (container.m_nview.IsOwner())
			{
				return container.IsInUse();
			}
			return container.m_nview.GetZDO().GetInt(ZDOVars.s_inUse, 0) == 1;
		}

		private static bool IsPlayerChest(Container container)
		{
			if ((Object)(object)container.m_wagon != (Object)null || (Object)(object)((Component)container).GetComponent<TombStone>() != (Object)null || (Object)(object)((Component)container).GetComponentInParent<Ship>() != (Object)null)
			{
				return false;
			}
			Piece piece = container.m_piece;
			if ((Object)(object)piece != (Object)null)
			{
				return piece.GetCreator() != 0;
			}
			return false;
		}
	}
	internal static class Commands
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static ConsoleEvent <0>__Run;
		}

		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static ConsoleOptionsFetcher <>9__2_0;

			public static Func<LoadoutSlot, bool> <>9__5_0;

			public static Func<LoadoutSlot, bool> <>9__6_0;

			public static Func<LoadoutSlot, int> <>9__8_0;

			public static Func<LoadoutSlot, int> <>9__8_1;

			internal List<string> <Register>b__2_0()
			{
				return Subcommands;
			}

			internal bool <List>b__5_0(LoadoutSlot s)
			{
				return s.Worn;
			}

			internal bool <Save>b__6_0(LoadoutSlot s)
			{
				return s.Worn;
			}

			internal int <Show>b__8_0(LoadoutSlot s)
			{
				return s.Pos.y;
			}

			internal int <Show>b__8_1(LoadoutSlot s)
			{
				return s.Pos.x;
			}
		}

		private static bool _registered;

		private static readonly List<string> Subcommands = new List<string> { "help", "list", "save", "use", "show", "delete", "run", "test", "reload" };

		public static void Register()
		{
			//IL_0028: 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_0033: Expected O, but got Unknown
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected O, but got Unknown
			if (_registered)
			{
				return;
			}
			_registered = true;
			object obj = <>O.<0>__Run;
			if (obj == null)
			{
				ConsoleEvent val = Run;
				<>O.<0>__Run = val;
				obj = (object)val;
			}
			object obj2 = <>c.<>9__2_0;
			if (obj2 == null)
			{
				ConsoleOptionsFetcher val2 = () => Subcommands;
				<>c.<>9__2_0 = val2;
				obj2 = (object)val2;
			}
			new ConsoleCommand("kitbag", "Kitbag loadouts. kitbag help lists what it can do.", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)obj2, false, false, false);
		}

		private static void Run(ConsoleEventArgs args)
		{
			Terminal context = args.Context;
			if (!Plugin.Enabled.Value)
			{
				context.AddString("Kitbag is turned off in its settings.");
				return;
			}
			string text = ((args.Length > 1) ? args[1].ToLowerInvariant() : "help");
			string name = string.Join(" ", args.Args.Skip(2)).Trim();
			if (text == "help")
			{
				Help(context);
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			LoadoutBook current = LoadoutStore.Current;
			if ((Object)(object)localPlayer == (Object)null || current == null)
			{
				context.AddString("Kitbag: load into a world first.");
				return;
			}
			switch (text)
			{
			case "list":
				List(context, current);
				break;
			case "save":
				Save(context, localPlayer, current, name);
				break;
			case "use":
				Use(context, current, name);
				break;
			case "show":
				Show(context, current, name);
				break;
			case "delete":
				Delete(context, current, name);
				break;
			case "run":
				Restocker.Run(localPlayer, Plugin.Preview.Value);
				break;
			case "test":
				Restocker.Run(localPlayer, preview: true);
				break;
			case "reload":
				Reload(context);
				break;
			default:
				context.AddString("Kitbag doesn't know \"" + text + "\". Try kitbag help.");
				break;
			}
		}

		private static void Help(Terminal output)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			output.AddString("Kitbag commands. In chat, put a / in front: /kitbag save Main");
			output.AddString("  kitbag list: your loadouts; * marks the active one");
			output.AddString("  kitbag save <name>: save your inventory as it is now as a loadout, replacing one with that name");
			output.AddString("  kitbag use <name>: make a loadout the active one");
			output.AddString("  kitbag show [name]: list a loadout's slots (the active one if you leave out the name)");
			output.AddString("  kitbag delete <name>: delete a loadout");
			output.AddString($"  kitbag run: the same as pressing {Plugin.Key.Value}");
			output.AddString("  kitbag test: a preview run, which reports but moves nothing");
			output.AddString("  kitbag reload: read the loadout file again after editing it");
			if (Plugin.Preview.Value)
			{
				output.AddString("The Preview setting is on, so runs only report. Turn it off in the config to move items.");
			}
			output.AddString("Loadouts are in " + LoadoutStore.Folder + ".");
		}

		private static void List(Terminal output, LoadoutBook book)
		{
			if (book.Loadouts.Count == 0)
			{
				output.AddString("No loadouts yet. kitbag save <name> saves your inventory as one.");
				return;
			}
			foreach (Loadout loadout in book.Loadouts)
			{
				string text = ((loadout == book.ActiveLoadout) ? "*" : " ");
				output.AddString($"{text} {loadout.Name}: {loadout.Slots.Count} slots, {loadout.Slots.Count((LoadoutSlot s) => s.Worn)} worn");
			}
		}

		private static void Save(Terminal output, Player player, LoadoutBook book, string name)
		{
			if (name.Length == 0)
			{
				output.AddString("Name the loadout: kitbag save <name>");
				return;
			}
			int skipped;
			Loadout loadout = LoadoutStore.Capture(player, name, out skipped);
			Loadout loadout2 = book.Find(name);
			if (loadout2 != null)
			{
				if (book.Active == loadout2.Name)
				{
					book.Active = loadout.Name;
				}
				book.Loadouts[book.Loadouts.IndexOf(loadout2)] = loadout;
			}
			else
			{
				book.Loadouts.Add(loadout);
			}
			if (book.ActiveLoadout == null)
			{
				book.Active = loadout.Name;
			}
			if (SaveOrSay(output))
			{
				string arg = ((book.ActiveLoadout == loadout) ? "It's the active loadout." : ("The active loadout is still " + book.Active + "; kitbag use " + loadout.Name + " switches."));
				output.AddString(string.Format("{0} {1}: {2} slots, ", (loadout2 != null) ? "Replaced" : "Saved", loadout.Name, loadout.Slots.Count) + $"{loadout.Slots.Count((LoadoutSlot s) => s.Worn)} worn. {arg}");
				if (skipped > 0)
				{
					output.AddString($"Left out {skipped} items that have no item ID.");
				}
			}
		}

		private static void Use(Terminal output, LoadoutBook book, string name)
		{
			Loadout loadout = Find(output, book, name);
			if (loadout != null)
			{
				bool recovering = book.Recovering;
				book.Use(loadout.Name);
				if (SaveOrSay(output))
				{
					output.AddString("Active loadout: " + loadout.Name + (recovering ? ". That ended the death run." : ""));
				}
			}
		}

		private static void Show(Terminal output, LoadoutBook book, string name)
		{
			Loadout loadout = ((name.Length == 0) ? book.ActiveLoadout : Find(output, book, name));
			if (loadout == null)
			{
				if (name.Length == 0)
				{
					output.AddString("No loadout is active. kitbag show <name> shows another one.");
				}
				return;
			}
			output.AddString(loadout.Name + " (column,row from the top left; row 1 is the hotbar):");
			foreach (LoadoutSlot item in from s in loadout.Slots
				orderby s.Pos.y, s.Pos.x
				select s)
			{
				List<string> list = new List<string> { $"x{item.Amount}" };
				if (item.Worn)
				{
					list.Add("worn");
				}
				if (item.Quality > 0)
				{
					list.Add($"level {item.Quality}");
				}
				if (item.Style >= 0)
				{
					list.Add($"style {item.Style}");
				}
				if (item.Crafter.Length > 0)
				{
					list.Add("by " + item.Crafter);
				}
				output.AddString(string.Format("  {0},{1}  {2} ({3}) {4}", item.Pos.x + 1, item.Pos.y + 1, Items.Name(item.Prefab), item.Prefab, string.Join(", ", list)));
			}
		}

		private static void Delete(Terminal output, LoadoutBook book, string name)
		{
			Loadout loadout = Find(output, book, name);
			if (loadout != null)
			{
				book.Loadouts.Remove(loadout);
				if (book.Active == loadout.Name)
				{
					book.Active = "";
				}
				if (string.Equals(book.DeathRun, loadout.Name, StringComparison.OrdinalIgnoreCase))
				{
					book.DeathRun = "";
				}
				if (string.Equals(book.ReturnTo, loadout.Name, StringComparison.OrdinalIgnoreCase))
				{
					book.ReturnTo = "";
				}
				if (SaveOrSay(output))
				{
					output.AddString("Deleted " + loadout.Name + ".");
				}
			}
		}

		private static void Reload(Terminal output)
		{
			List<string> list = LoadoutStore.Reload();
			LoadoutBook current = LoadoutStore.Current;
			output.AddString(string.Format("Read {0} loadouts. Active: {1}.", current.Loadouts.Count, (current.ActiveLoadout == null) ? "none" : current.ActiveLoadout.Name));
			foreach (string item in list)
			{
				output.AddString("  Skipped " + item);
			}
		}

		private static Loadout Find(Terminal output, LoadoutBook book, string name)
		{
			if (name.Length == 0)
			{
				output.AddString("Which loadout? kitbag list shows them.");
				return null;
			}
			Loadout loadout = book.Find(name);
			if (loadout == null)
			{
				output.AddString("There's no loadout called " + name + ". kitbag list shows them.");
			}
			return loadout;
		}

		private static bool SaveOrSay(Terminal output)
		{
			string text = LoadoutStore.Save();
			if (text != null)
			{
				output.AddString("Kitbag couldn't save the loadout file: " + text);
			}
			return text == null;
		}
	}
	internal static class DeathRun
	{
		public static void OnGraveCreated(TombStone grave, long ownerId)
		{
			if (!Plugin.Enabled.Value || (Object)(object)Game.instance == (Object)null || ownerId != Game.instance.GetPlayerProfile().GetPlayerID())
			{
				return;
			}
			LoadoutBook current = LoadoutStore.Current;
			if (current == null || current.Recovering)
			{
				return;
			}
			Loadout loadout = current.Find(current.DeathRun);
			Loadout activeLoadout = current.ActiveLoadout;
			if (loadout != null && activeLoadout != null && activeLoadout != loadout)
			{
				current.Grave = ((object)Unsafe.As<ZDOID, ZDOID>(ref grave.m_nview.GetZDO().m_uid)/*cast due to .constrained prefix*/).ToString();
				current.ReturnTo = activeLoadout.Name;
				current.GraveWorld = WorldId();
				current.GraveWorldName = (((Object)(object)ZNet.instance != (Object)null) ? (ZNet.instance.GetWorldName() ?? "") : "");
				current.Active = loadout.Name;
				if (LoadoutStore.Save() == null)
				{
					Plugin.Log.LogInfo((object)("Death run: switched from " + activeLoadout.Name + " to " + loadout.Name + ", waiting for grave " + current.Grave));
				}
				KitbagPanel.Refresh();
			}
		}

		public static void OnSpawned(Player player)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			LoadoutBook current = LoadoutStore.Current;
			if (current != null && current.Recovering)
			{
				((Character)player).Message((MessageType)1, $"Kitbag: {current.Active} is active. Press {Plugin.Key.Value} near your chests to kit up; {current.ReturnTo} comes back when you loot your grave{Where(current)}.", 0, (Sprite)null, false);
			}
		}

		public static string Where(LoadoutBook book)
		{
			if (book.GraveWorld.Length == 0 || book.GraveWorld == WorldId())
			{
				return "";
			}
			if (book.GraveWorldName.Length <= 0)
			{
				return " in another world";
			}
			return " in " + book.GraveWorldName;
		}

		private static string WorldId()
		{
			ZNet instance = ZNet.instance;
			if (!((Object)(object)instance != (Object)null) || instance.GetWorld() == null)
			{
				return "";
			}
			return instance.GetWorldUID().ToString();
		}

		public static void OnGraveEmptied(TombStone grave)
		{
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			LoadoutBook current = LoadoutStore.Current;
			if (current == null || !current.Recovering || (Object)(object)grave.m_nview == (Object)null || !grave.m_nview.IsValid() || ((object)Unsafe.As<ZDOID, ZDOID>(ref grave.m_nview.GetZDO().m_uid)/*cast due to .constrained prefix*/).ToString() != current.Grave || (current.GraveWorld.Length > 0 && current.GraveWorld != WorldId()))
			{
				return;
			}
			Loadout loadout = current.Find(current.ReturnTo);
			string active = current.Active;
			current.EndRecovery();
			if (loadout != null)
			{
				current.Active = loadout.Name;
			}
			if (LoadoutStore.Save() != null)
			{
				return;
			}
			KitbagPanel.Refresh();
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || loadout == null)
			{
				return;
			}
			string text = "";
			if (Plugin.AutoEquip.Value)
			{
				int num = Restocker.EquipOnly(localPlayer, loadout);
				if (num > 0)
				{
					text = $" Put on {num} items.";
				}
			}
			((Character)localPlayer).Message((MessageType)1, $"Kitbag: you got your gear back. {loadout.Name} is active again.{text} Press {Plugin.Key.Value} near your chests to put the {active} gear away.", 0, (Sprite)null, false);
		}
	}
	internal sealed class HandoverResult
	{
		public int Asked;

		public int TookOver;

		public int Refused;

		public int NoAnswer;
	}
	internal static class Handover
	{
		private enum State
		{
			Waiting,
			Granted,
			Refused
		}

		private sealed class Request
		{
			public State State;

			public float Sent;
		}

		private const float Timeout = 3f;

		private const float Forget = 60f;

		private static readonly Dictionary<Container, Request> Requests = new Dictionary<Container, Request>();

		public static bool Busy { get; private set; }

		public static bool OnResponse(Container container, bool granted)
		{
			if (!Requests.TryGetValue(container, out var value))
			{
				return false;
			}
			Requests.Remove(container);
			value.State = (granted ? State.Granted : State.Refused);
			return true;
		}

		public static IEnumerator Ask(List<Container> chests, Action<HandoverResult> done)
		{
			HandoverResult result = new HandoverResult
			{
				Asked = chests.Count
			};
			Busy = true;
			try
			{
				Prune();
				long playerID = Game.instance.GetPlayerProfile().GetPlayerID();
				float sent = Time.time;
				Dictionary<Container, Request> asked = new Dictionary<Container, Request>();
				foreach (Container chest in chests)
				{
					Request value = (asked[chest] = new Request
					{
						Sent = sent
					});
					if (chest.m_nview.GetZDO().HasOwner())
					{
						Requests[chest] = value;
						chest.m_nview.InvokeRPC("RPC_RequestStack", new object[1] { playerID });
					}
				}
				while (Time.time < sent + 3f && !asked.All((KeyValuePair<Container, Request> a) => Settled(a.Key, a.Value)))
				{
					yield return null;
				}
				foreach (KeyValuePair<Container, Request> item in asked)
				{
					Container key = item.Key;
					Request value2 = item.Value;
					if ((Object)(object)key != (Object)null && value2.State == State.Refused)
					{
						result.Refused++;
					}
					else if ((Object)(object)key != (Object)null && value2.State == State.Granted && ChestRegistry.Owned(key))
					{
						key.Load();
						result.TookOver++;
					}
					else
					{
						result.NoAnswer++;
					}
				}
			}
			finally
			{
				Busy = false;
			}
			done(result);
		}

		private static bool Settled(Container chest, Request request)
		{
			if ((Object)(object)chest == (Object)null || request.State == State.Refused)
			{
				return true;
			}
			if (request.State == State.Granted)
			{
				return ChestRegistry.Owned(chest);
			}
			return false;
		}

		private static void Prune()
		{
			float time = Time.time;
			foreach (Container item in Requests.Keys.ToList())
			{
				if ((Object)(object)item == (Object)null || time - Requests[item].Sent > 60f)
				{
					Requests.Remove(item);
				}
			}
		}
	}
	internal static class Items
	{
		public static string Prefab(ItemData item)
		{
			if (!((Object)(object)item.m_dropPrefab != (Object)null))
			{
				return null;
			}
			return ((Object)item.m_dropPrefab).name;
		}

		public static bool IsGear(SharedData shared)
		{
			return shared.m_maxStackSize <= 1;
		}

		public static SharedData Shared(string prefab)
		{
			if ((Object)(object)ObjectDB.instance == (Object)null)
			{
				return null;
			}
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(prefab);
			if ((Object)(object)itemPrefab == (Object)null)
			{
				return null;
			}
			ItemDrop component = itemPrefab.GetComponent<ItemDrop>();
			if (!((Object)(object)component == (Object)null))
			{
				return component.m_itemData.m_shared;
			}
			return null;
		}

		public static string Name(string prefab)
		{
			SharedData val = Shared(prefab);
			if (val != null)
			{
				return Localization.instance.Localize(val.m_name);
			}
			return prefab;
		}

		public static string Name(ItemData item)
		{
			return Localization.instance.Localize(item.m_shared.m_name);
		}

		public static string Describe(ItemData item)
		{
			List<string> list = new List<string>();
			if (item.m_shared.m_maxQuality > 1)
			{
				list.Add($"level {item.m_quality}");
			}
			if (item.m_shared.m_variants > 1)
			{
				list.Add($"style {item.m_variant}");
			}
			if (!string.IsNullOrEmpty(item.m_crafterName))
			{
				list.Add("by " + item.m_crafterName);
			}
			if (list.Count != 0)
			{
				return Name(item) + " (" + string.Join(", ", list) + ")";
			}
			return Name(item);
		}

		public static int Count(Inventory inventory, string prefab)
		{
			int num = 0;
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (Prefab(allItem) == prefab)
				{
					num += allItem.m_stack;
				}
			}
			return num;
		}

		public static bool Matches(LoadoutSlot slot, ItemData item)
		{
			if (slot.Quality > 0 && item.m_quality < slot.Quality)
			{
				return false;
			}
			if (slot.Style >= 0 && item.m_variant != slot.Style)
			{
				return false;
			}
			if (slot.Crafter.Length > 0 && !string.Equals(item.m_crafterName, slot.Crafter, StringComparison.OrdinalIgnoreCase))
			{
				return false;
			}
			return true;
		}

		public static int Compare(LoadoutSlot slot, ItemData a, ItemData b)
		{
			int num = Matches(slot, a).CompareTo(Matches(slot, b));
			if (num != 0)
			{
				return num;
			}
			int num2 = a.m_quality.CompareTo(b.m_quality);
			if (num2 != 0)
			{
				return num2;
			}
			return a.m_durability.CompareTo(b.m_durability);
		}
	}
	internal sealed class Journal : IDisposable
	{
		private const long MaxBytes = 2097152L;

		private readonly StreamWriter _writer;

		private Journal(StreamWriter writer)
		{
			_writer = writer;
		}

		public static Journal Open(string characterKey)
		{
			try
			{
				Directory.CreateDirectory(LoadoutStore.Folder);
				string text = Path.Combine(LoadoutStore.Folder, characterKey + ".journal.txt");
				if (File.Exists(text) && new FileInfo(text).Length > 2097152)
				{
					string text2 = text + ".old";
					if (File.Exists(text2))
					{
						File.Delete(text2);
					}
					File.Move(text, text2);
				}
				return new Journal(new StreamWriter(text, append: true, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)));
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Couldn't open the journal: " + ex.Message));
				return null;
			}
		}

		public void Line(string text)
		{
			_writer.WriteLine(text);
		}

		public void Dispose()
		{
			_writer.Dispose();
		}
	}
	internal sealed class LoadoutSlot
	{
		public Vector2i Pos;

		public string Prefab;

		public int Amount = 1;

		public bool Worn;

		public int Quality;

		public int Style = -1;

		public string Crafter = "";
	}
	internal sealed class Loadout
	{
		public string Name;

		public readonly List<LoadoutSlot> Slots = new List<LoadoutSlot>();
	}
	internal sealed class LoadoutBook
	{
		public string Active = "";

		public readonly List<Loadout> Loadouts = new List<Loadout>();

		public string DeathRun = "";

		public string Grave = "";

		public string ReturnTo = "";

		public string GraveWorld = "";

		public string GraveWorldName = "";

		public bool Recovering => Grave.Length > 0;

		public Loadout ActiveLoadout
		{
			get
			{
				if (!string.IsNullOrEmpty(Active))
				{
					return Find(Active);
				}
				return null;
			}
		}

		public void Use(string name)
		{
			Active = name;
			EndRecovery();
		}

		public void EndRecovery()
		{
			Grave = "";
			ReturnTo = "";
			GraveWorld = "";
			GraveWorldName = "";
		}

		public Loadout Find(string name)
		{
			return Loadouts.Find((Loadout l) => string.Equals(l.Name, name, StringComparison.OrdinalIgnoreCase));
		}
	}
	internal static class LoadoutFile
	{
		public static string Write(LoadoutBook book, string characterName)
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("# Kitbag loadouts for " + characterName + ".");
			stringBuilder.AppendLine("# Each loadout starts with a \"loadout:\" line, followed by one line per inventory slot:");
			stringBuilder.AppendLine("#   <column>,<row>  <item ID>  x<amount>  [worn]  [level <n>]  [style <n>]  [by <crafter>]");
			stringBuilder.AppendLine("# Columns and rows count from 1 at the top left; row 1 is the hotbar. Item IDs are the names the spawn");
			stringBuilder.AppendLine("# command uses. \"worn\" equips the item after a run if AutoEquip is on. Level, style and crafter pick which copy of a piece of");
			stringBuilder.AppendLine("# gear to fetch; a higher level still counts. \"by\" must come last on its line.");
			stringBuilder.AppendLine("# \"deathrun:\" names the loadout Kitbag switches to when you die. While you're on a death run, \"recovering:\" holds");
			stringBuilder.AppendLine("# the grave with your gear and the loadout to switch back to once you loot it, and \"grave world:\" the world");
			stringBuilder.AppendLine("# it's in. End death run in the panel clears them, for a grave that's gone (after a save rollback, say).");
			stringBuilder.AppendLine("# Edit this file with the game closed, or type /kitbag reload in chat after saving it.");
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("active: " + book.Active);
			if (book.DeathRun.Length > 0)
			{
				stringBuilder.AppendLine("deathrun: " + book.DeathRun);
			}
			if (book.Recovering)
			{
				stringBuilder.AppendLine("recovering: " + book.Grave + " " + book.ReturnTo);
				if (book.GraveWorld.Length > 0)
				{
					stringBuilder.AppendLine("grave world: " + book.GraveWorld + " " + book.GraveWorldName);
				}
			}
			foreach (Loadout loadout in book.Loadouts)
			{
				stringBuilder.AppendLine();
				stringBuilder.AppendLine("loadout: " + loadout.Name);
				int totalWidth = ((loadout.Slots.Count != 0) ? loadout.Slots.Max((LoadoutSlot s) => s.Prefab.Length) : 0);
				foreach (LoadoutSlot slot in loadout.Slots)
				{
					stringBuilder.Append($"  {slot.Pos.x + 1},{slot.Pos.y + 1}  {slot.Prefab.PadRight(totalWidth)}  x{slot.Amount}");
					if (slot.Worn)
					{
						stringBuilder.Append("  worn");
					}
					if (slot.Quality > 0)
					{
						stringBuilder.Append($"  level {slot.Quality}");
					}
					if (slot.Style >= 0)
					{
						stringBuilder.Append($"  style {slot.Style}");
					}
					if (slot.Crafter.Length > 0)
					{
						stringBuilder.Append("  by " + slot.Crafter);
					}
					stringBuilder.AppendLine();
				}
			}
			return stringBuilder.ToString();
		}

		public static LoadoutBook Read(string text, List<string> errors)
		{
			LoadoutBook loadoutBook = new LoadoutBook();
			Loadout loadout = null;
			string[] array = text.Replace("\r\n", "\n").Split(new char[1] { '\n' });
			for (int i = 0; i < array.Length; i++)
			{
				string text2 = array[i].Trim();
				int num = i + 1;
				if (text2.Length == 0 || text2.StartsWith("#"))
				{
					continue;
				}
				if (StartsWithWord(text2, "active:"))
				{
					loadoutBook.Active = text2.Substring("active:".Length).Trim();
				}
				else if (StartsWithWord(text2, "deathrun:"))
				{
					loadoutBook.DeathRun = text2.Substring("deathrun:".Length).Trim();
				}
				else if (StartsWithWord(text2, "recovering:"))
				{
					string[] array2 = text2.Substring("recovering:".Length).Trim().Split(new char[1] { ' ' }, 2, StringSplitOptions.RemoveEmptyEntries);
					if (array2.Length == 2)
					{
						loadoutBook.Grave = array2[0];
						loadoutBook.ReturnTo = array2[1].Trim();
					}
					else
					{
						errors.Add($"line {num}: expected the grave and a loadout name after \"recovering:\"");
					}
				}
				else if (StartsWithWord(text2, "grave world:"))
				{
					string[] array3 = text2.Substring("grave world:".Length).Trim().Split(new char[1] { ' ' }, 2, StringSplitOptions.RemoveEmptyEntries);
					if (array3.Length >= 1)
					{
						loadoutBook.GraveWorld = array3[0];
						loadoutBook.GraveWorldName = ((array3.Length == 2) ? array3[1].Trim() : "");
					}
				}
				else if (StartsWithWord(text2, "loadout:"))
				{
					string text3 = text2.Substring("loadout:".Length).Trim();
					if (text3.Length == 0)
					{
						errors.Add($"line {num}: a loadout needs a name");
						loadout = null;
					}
					else if (loadoutBook.Find(text3) != null)
					{
						errors.Add($"line {num}: there's already a loadout called {text3}; skipping this one");
						loadout = null;
					}
					else
					{
						loadout = new Loadout
						{
							Name = text3
						};
						loadoutBook.Loadouts.Add(loadout);
					}
				}
				else if (loadout == null)
				{
					errors.Add($"line {num}: an item line needs a \"loadout:\" line above it");
				}
				else
				{
					string error;
					LoadoutSlot slot = ReadSlot(text2, out error);
					if (slot == null)
					{
						errors.Add($"line {num}: {error}");
					}
					else if (loadout.Slots.Any((LoadoutSlot s) => s.Pos.x == slot.Pos.x && s.Pos.y == slot.Pos.y))
					{
						errors.Add($"line {num}: slot {slot.Pos.x + 1},{slot.Pos.y + 1} is already used in {loadout.Name}");
					}
					else
					{
						loadout.Slots.Add(slot);
					}
				}
			}
			return loadoutBook;
		}

		private static LoadoutSlot ReadSlot(string line, out string error)
		{
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			error = null;
			string[] array = line.Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries);
			if (array.Length < 2)
			{
				error = "expected a slot and an item ID, like 1,1 SwordIron x1";
				return null;
			}
			string[] array2 = array[0].Split(new char[1] { ',' });
			if (array2.Length != 2 || !TryInt(array2[0], out var value) || !TryInt(array2[1], out var value2) || value < 1 || value2 < 1)
			{
				error = "\"" + array[0] + "\" isn't a slot; write column,row counting from 1, like 3,1";
				return null;
			}
			LoadoutSlot loadoutSlot = new LoadoutSlot
			{
				Pos = new Vector2i(value - 1, value2 - 1),
				Prefab = array[1]
			};
			for (int i = 2; i < array.Length; i++)
			{
				string text = array[i];
				if (text.Length > 1 && (text[0] == 'x' || text[0] == 'X') && TryInt(text.Substring(1), out var value3) && value3 > 0)
				{
					loadoutSlot.Amount = value3;
					continue;
				}
				if (Is(text, "worn"))
				{
					loadoutSlot.Worn = true;
					continue;
				}
				if (Is(text, "level") && i + 1 < array.Length && TryInt(array[i + 1], out var value4) && value4 > 0)
				{
					loadoutSlot.Quality = value4;
					i++;
					continue;
				}
				if (Is(text, "style") && i + 1 < array.Length && TryInt(array[i + 1], out var value5) && value5 >= 0)
				{
					loadoutSlot.Style = value5;
					i++;
					continue;
				}
				if (Is(text, "by") && i + 1 < array.Length)
				{
					loadoutSlot.Crafter = string.Join(" ", array, i + 1, array.Length - i - 1);
					break;
				}
				error = "didn't understand \"" + text + "\"";
				return null;
			}
			return loadoutSlot;
		}

		private static bool StartsWithWord(string line, string word)
		{
			return line.StartsWith(word, StringComparison.OrdinalIgnoreCase);
		}

		private static bool Is(string word, string expected)
		{
			return string.Equals(word, expected, StringComparison.OrdinalIgnoreCase);
		}

		private static bool TryInt(string s, out int value)
		{
			return int.TryParse(s, NumberStyles.Integer, CultureInfo.InvariantCulture, out value);
		}
	}
	internal static class LoadoutStore
	{
		private static string _key;

		private static string _characterName;

		private static LoadoutBook _book;

		public static string Folder => Path.Combine(Paths.ConfigPath, "Kitbag");

		public static string CharacterKey
		{
			get
			{
				if ((Object)(object)Game.instance == (Object)null)
				{
					return null;
				}
				PlayerProfile playerProfile = Game.instance.GetPlayerProfile();
				if (playerProfile != null)
				{
					return playerProfile.GetFilename();
				}
				return null;
			}
		}

		public static LoadoutBook Current
		{
			get
			{
				string characterKey = CharacterKey;
				if (characterKey == null)
				{
					return null;
				}
				if (_book == null || characterKey != _key)
				{
					foreach (string item in Load(characterKey))
					{
						Plugin.Log.LogWarning((object)(FilePath(characterKey) + ", " + item));
					}
				}
				return _book;
			}
		}

		public static string FilePath(string key)
		{
			return Path.Combine(Folder, key + ".txt");
		}

		public static List<string> Reload()
		{
			string characterKey = CharacterKey;
			if (characterKey != null)
			{
				return Load(characterKey);
			}
			return new List<string> { "not in a game" };
		}

		private static List<string> Load(string key)
		{
			List<string> list = new List<string>();
			_key = key;
			_characterName = Game.instance.GetPlayerProfile().GetName();
			string path = FilePath(key);
			try
			{
				_book = (File.Exists(path) ? LoadoutFile.Read(File.ReadAllText(path), list) : new LoadoutBook());
			}
			catch (Exception ex)
			{
				list.Add("couldn't be read: " + ex.Message);
				_book = new LoadoutBook();
			}
			return list;
		}

		public static string Save()
		{
			if (_book == null || _key == null)
			{
				return "not in a game";
			}
			try
			{
				Directory.CreateDirectory(Folder);
				File.WriteAllText(FilePath(_key), LoadoutFile.Write(_book, _characterName), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
				return null;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)$"Couldn't save {FilePath(_key)}: {ex}");
				return ex.Message;
			}
		}

		public static Loadout Capture(Player player, string name, out int skipped)
		{
			Loadout loadout = new Loadout
			{
				Name = name
			};
			skipped = 0;
			foreach (ItemData item in from i in ((Humanoid)player).GetInventory().GetAllItems()
				orderby i.m_gridPos.y, i.m_gridPos.x
				select i)
			{
				LoadoutSlot loadoutSlot = SlotFor(player, item);
				if (loadoutSlot == null)
				{
					skipped++;
				}
				else
				{
					loadout.Slots.Add(loadoutSlot);
				}
			}
			return loadout;
		}

		public static LoadoutSlot SlotFor(Player player, ItemData item)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			string text = Items.Prefab(item);
			if (text == null)
			{
				return null;
			}
			LoadoutSlot loadoutSlot = new LoadoutSlot
			{
				Pos = item.m_gridPos,
				Prefab = text,
				Amount = item.m_stack,
				Worn = ((Humanoid)player).IsItemEquiped(item)
			};
			if (Items.IsGear(item.m_shared))
			{
				if (item.m_shared.m_maxQuality > 1)
				{
					loadoutSlot.Quality = item.m_quality;
				}
				if (item.m_shared.m_variants > 1)
				{
					loadoutSlot.Style = item.m_variant;
				}
				if (!string.IsNullOrEmpty(item.m_crafterName))
				{
					loadoutSlot.Crafter = item.m_crafterName;
				}
			}
			return loadoutSlot;
		}
	}
	internal sealed class NameFilter
	{
		private readonly Regex _regex;

		public NameFilter(string list)
		{
			string[] array = (from n in (list ?? "").Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries)
				select n.Trim() into n
				where n.Length > 0
				select n).ToArray();
			if (array.Length != 0)
			{
				string text = string.Join("|", array.Select((string n) => Regex.Escape(n).Replace("\\*", ".*")));
				_regex = new Regex("^(?:" + text + ")$", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
			}
		}

		public bool Matches(string name)
		{
			if (_regex != null && name != null)
			{
				return _regex.IsMatch(name);
			}
			return false;
		}
	}
	[BepInPlugin("com.adapperbadger.kitbag", "Kitbag", "0.1.0")]
	[BepInProcess("valheim.exe")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		internal static ManualLogSource Log;

		internal static Plugin Instance;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<KeyCode> Key;

		internal static ConfigEntry<bool> Preview;

		internal static ConfigEntry<bool> AutoEquip;

		internal static ConfigEntry<float> Range;

		internal static ConfigEntry<bool> MixedChests;

		internal static ConfigEntry<string> NeverPutAway;

		internal static ConfigEntry<int> MessageLines;

		internal static ConfigEntry<bool> LogReports;

		private const string GeneralSection = "1 - General";

		private const string ChestsSection = "2 - Chests";

		private const string ReportSection = "3 - Report";

		private readonly Harmony _harmony = new Harmony("com.adapperbadger.kitbag");

		private void Awake()
		{
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Expected O, but got Unknown
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Instance = this;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Enabled", true, "Turn Kitbag on or off: the key, the Kitbag and Dump extras buttons, the chat commands and starting death runs.");
			Key = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("1 - General", "Key", (KeyCode)111, "Puts away what your active loadout doesn't need, refills what's missing from nearby chests and puts each item in its slot.");
			Preview = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Preview", false, "While on, the key and Dump extras work out what they would do and show the report, but move nothing.");
			AutoEquip = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "AutoEquip", false, "Also put on what your loadout wears (the items marked worn in its file) at the end of each run. Off: Kitbag fetches and arranges that gear but never changes what you're wearing.");
			Range = ((BaseUnityPlugin)this).Config.Bind<float>("2 - Chests", "Range", 100f, new ConfigDescription("How far away a chest can be, in meters, and still be used. Only chests you or another player built count; carts, ships and graves never do.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 2000f), Array.Empty<object>()));
			MixedChests = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Chests", "MixedChests", true, "Items are put away into chests that already hold them, the ones holding nothing but that item (like a box of wood) first. On: chests that hold it among other things come next. Off: only chests that hold nothing else.");
			NeverPutAway = ((BaseUnityPlugin)this).Config.Bind<string>("2 - Chests", "NeverPutAway", "", "Comma-separated item IDs (the names the spawn command uses) that are never put away, even when they're not in your loadout. * is a wildcard: Trophy* keeps every trophy.");
			MessageLines = ((BaseUnityPlugin)this).Config.Bind<int>("3 - Report", "MessageLines", 6, new ConfigDescription("How many lines of the report (what's missing, what had no room and what had no chest) show at the top left of the screen. The full report is always in the journal file, BepInEx/config/Kitbag/<character>.journal.txt.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 20), Array.Empty<object>()));
			LogReports = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - Report", "LogReports", false, "Also write each report to BepInEx/LogOutput.log. Leave off unless you're reporting a problem.");
			((BaseUnityPlugin)this).Config.Save();
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = true;
			_harmony.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Kitbag 0.1.0 loaded");
		}

		private void Update()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			KitbagPanel.Update();
			LoadoutWindow.Update();
			if (Enabled.Value && ZInput.GetKeyDown(Key.Value, false))
			{
				Player localPlayer = Player.m_localPlayer;
				if (!((Object)(object)localPlayer == (Object)null) && !((Character)localPlayer).IsDead() && !((Character)localPlayer).IsTeleporting() && !Console.IsVisible() && !Menu.IsVisible() && !TextInput.IsVisible() && !StoreGui.IsVisible() && !Minimap.InTextInput() && (!((Object)(object)Chat.instance != (Object)null) || !Chat.instance.HasFocus()))
				{
					Restocker.Run(localPlayer, Preview.Value);
				}
			}
		}

		private void OnDestroy()
		{
			_harmony.UnpatchSelf();
		}
	}
	internal sealed class Report
	{
		private sealed class Tally
		{
			private readonly List<string> _order = new List<string>();

			private readonly Dictionary<string, int> _counts = new Dictionary<string, int>();

			public int Total => _counts.Values.Sum();

			public bool Any => _order.Count > 0;

			public void Add(string prefab, int amount)
			{
				if (amount > 0)
				{
					if (!_counts.ContainsKey(prefab))
					{
						_order.Add(prefab);
						_counts[prefab] = 0;
					}
					_counts[prefab] += amount;
				}
			}

			public string Join(int maxEntries)
			{
				IEnumerable<string> values = from p in _order.Take(maxEntries)
					select $"{Items.Name(p)} x{_counts[p]}";
				string text = string.Join(", ", values);
				if (_order.Count <= maxEntries)
				{
					return text;
				}
				return $"{text} and {_order.Count - maxEntries} more";
			}
		}

		private readonly string _loadout;

		private readonly bool _preview;

		private readonly int _chests;

		private readonly ChestSkips _skips;

		private readonly Tally _took = new Tally();

		private readonly Tally _putAway = new Tally();

		private readonly Tally _dumped = new Tally();

		private readonly Tally _missing = new Tally();

		private readonly Tally _noRoom = new Tally();

		private readonly Tally _noChest = new Tally();

		private readonly Tally _chestsFull = new Tally();

		private readonly List<string> _equipped = new List<string>();

		private readonly List<string> _notes = new List<string>();

		private int _arranged;

		private string _stopped;

		private int _wouldAsk;

		private HandoverResult _handover;

		public bool WasStopped => _stopped != null;

		public int EquippedCount => _equipped.Count;

		public Report(string loadout, bool preview, int chests, ChestSkips skips)
		{
			_loadout = loadout;
			_preview = preview;
			_chests = chests;
			_skips = skips;
		}

		public void Took(string prefab, int amount)
		{
			_took.Add(prefab, amount);
		}

		public void PutAway(string prefab, int amount)
		{
			_putAway.Add(prefab, amount);
		}

		public void Dumped(string prefab, int amount)
		{
			_dumped.Add(prefab, amount);
		}

		public void Missing(string prefab, int amount)
		{
			_missing.Add(prefab, amount);
		}

		public void NoRoom(string prefab, int amount)
		{
			_noRoom.Add(prefab, amount);
		}

		public void NoChest(string prefab, int amount)
		{
			_noChest.Add(prefab, amount);
		}

		public void ChestsFull(string prefab, int amount)
		{
			_chestsFull.Add(prefab, amount);
		}

		public void Equipped(ItemData item)
		{
			_equipped.Add(Items.Name(item));
		}

		public void Arranged(int moves)
		{
			_arranged += moves;
		}

		public void Note(string text)
		{
			_notes.Add(text);
		}

		public void Stopped(string reason)
		{
			if (_stopped == null)
			{
				_stopped = reason;
			}
		}

		public void WouldAsk(int chests)
		{
			_wouldAsk = chests;
		}

		public void Handover(HandoverResult result)
		{
			_handover = result;
		}

		public string Summary()
		{
			if (_stopped != null)
			{
				if (!_preview)
				{
					return "Kitbag stopped to keep your items safe. See the journal.";
				}
				return "Kitbag preview stopped: " + _stopped;
			}
			List<string> list = new List<string>();
			if (_took.Any)
			{
				list.Add(string.Format("{0} {1}", _preview ? "would take" : "took", _took.Total));
			}
			if (_putAway.Any)
			{
				list.Add(string.Format("{0} {1}", _preview ? "would put away" : "put away", _putAway.Total));
			}
			if (_dumped.Any)
			{
				list.Add(string.Format("{0} {1}", _preview ? "would dump" : "dumped", _dumped.Total));
			}
			if (_arranged > 0)
			{
				list.Add(string.Format("{0} {1} into place", _preview ? "would move" : "moved", _arranged));
			}
			if (_equipped.Count > 0)
			{
				list.Add(string.Format("{0} {1}", _preview ? "would equip" : "equipped", _equipped.Count));
			}
			int num = _missing.Total + _noRoom.Total;
			int num2 = _noChest.Total + _chestsFull.Total;
			string text = (_preview ? "Kitbag preview, nothing moved" : "Kitbag");
			List<string> list2 = new List<string>();
			if (num > 0)
			{
				list2.Add($"{num} short");
			}
			if (num2 > 0)
			{
				list2.Add(string.Format("{0} {1} with you, see top left", num2, _preview ? "would stay" : "stayed"));
			}
			string text2 = ((list2.Count > 0) ? (" " + string.Join("; ", list2) + ".") : "");
			if (list.Count == 0 && num == 0)
			{
				return text + ": " + _loadout + " is all set." + text2;
			}
			string text3 = ((list.Count == 0) ? "nothing to do" : string.Join(", ", list));
			return text + ": " + text3 + "." + text2;
		}

		public List<string> Details(int maxEntries = 5)
		{
			List<string> list = new List<string>();
			if (_stopped != null)
			{
				list.Add("Stopped: " + _stopped);
			}
			AddList(list, "Missing", _missing, maxEntries);
			AddList(list, "No room in your inventory for", _noRoom, maxEntries);
			AddList(list, "Chests full for", _chestsFull, maxEntries);
			AddList(list, "No chest for", _noChest, maxEntries);
			list.AddRange(_notes);
			List<string> list2 = new List<string>();
			if (_skips.InUse > 0)
			{
				list2.Add($"{_skips.InUse} someone has open");
			}
			if (_skips.NoAccess > 0)
			{
				list2.Add($"{_skips.NoAccess} you can't open");
			}
			if (_handover != null && _handover.Refused > 0)
			{
				list2.Add($"{_handover.Refused} another player's game wouldn't hand over (usually because it's open)");
			}
			if (_handover != null && _handover.NoAnswer > 0)
			{
				list2.Add($"{_handover.NoAnswer} not handed over in time");
			}
			if (list2.Count > 0)
			{
				list.Add("Skipped chests: " + string.Join(", ", list2));
			}
			return list;
		}

		public List<string> Full()
		{
			List<string> list = new List<string>
			{
				Summary(),
				$"Used {_chests} chests in range."
			};
			if (_wouldAsk > 0)
			{
				list.Add($"Would ask for {_wouldAsk} chests another player's game controls.");
			}
			if (_handover != null && _handover.TookOver > 0)
			{
				list.Add($"Took over {_handover.TookOver} of {_handover.Asked} chests another player's game controlled.");
			}
			AddList(list, _preview ? "Would take" : "Took", _took, int.MaxValue);
			AddList(list, _preview ? "Would put away" : "Put away", _putAway, int.MaxValue);
			AddList(list, _preview ? "Would dump" : "Dumped", _dumped, int.MaxValue);
			if (_equipped.Count > 0)
			{
				list.Add((_preview ? "Would equip" : "Equipped") + ": " + string.Join(", ", _equipped));
			}
			list.AddRange(Details(int.MaxValue));
			return list;
		}

		private static void AddList(List<string> lines, string label, Tally tally, int maxEntries)
		{
			if (tally.Any)
			{
				lines.Add(label + ": " + tally.Join(maxEntries));
			}
		}
	}
	internal static class Restocker
	{
		private readonly struct Spare
		{
			public readonly ItemData Item;

			public readonly int Amount;

			public Spare(ItemData item, int amount)
			{
				Item = item;
				Amount = amount;
			}
		}

		private sealed class Pass
		{
			private enum Holding
			{
				None,
				Only,
				AmongOthers
			}

			private readonly Workspace _ws;

			private readonly Loadout _loadout;

			private readonly Report _report;

			private readonly NameFilter _neverPutAway = new NameFilter(Plugin.NeverPutAway.Value);

			private readonly List<LoadoutSlot> _gearSlots = new List<LoadoutSlot>();

			private readonly List<LoadoutSlot> _stackSlots = new List<LoadoutSlot>();

			private readonly Dictionary<string, int> _targets = new Dictionary<string, int>();

			private readonly Dictionary<string, List<Vector2i>> _stackPositions = new Dictionary<string, List<Vector2i>>();

			private readonly HashSet<Vector2i> _reserved = new HashSet<Vector2i>();

			private readonly Dictionary<LoadoutSlot, ItemData> _assigned = new Dictionary<LoadoutSlot, ItemData>();

			private Inventory Inv => _ws.Me.Inv;

			public Pass(Workspace workspace, Loadout loadout, Report report)
			{
				_ws = workspace;
				_loadout = loadout;
				_report = report;
			}

			public void Execute()
			{
				Classify();
				AssignGear();
				if (Plugin.AutoEquip.Value)
				{
					EquipWorn(final: false);
				}
				List<ItemData> takenOff = TakeOffDeathRunGear();
				PutAway(Candidates());
				PutBackOn(takenOff);
				Refill();
				Arrange();
				Distribute();
				if (Plugin.AutoEquip.Value)
				{
					List<ItemData> source = Inv.GetAllItems().Where(_ws.IsWorn).ToList();
					EquipWorn(final: true);
					HashSet<ItemData> kept = new HashSet<ItemData>(_assigned.Values);
					PutAway((from i in source
						where !_ws.IsWorn(i) && !kept.Contains(i) && Items.IsGear(i.m_shared) && !_neverPutAway.Matches(Items.Prefab(i))
						select new Spare(i, i.m_stack)).ToList());
				}
			}

			public void EquipOnly()
			{
				Classify();
				AssignGear();
				EquipWorn(final: true);
			}

			public void Dump(Store chest)
			{
				Classify();
				AssignGear();
				foreach (Spare item2 in PutAway(Candidates(), reportLeftOver: false))
				{
					ItemData item = item2.Item;
					string prefab = Items.Prefab(item);
					ItemData placed;
					int num = ((chest != null) ? _ws.Transfer(_ws.Me, chest, item, item2.Amount, null, null, out placed) : 0);
					_report.Dumped(prefab, num);
					if (num < item2.Amount)
					{
						_report.ChestsFull(prefab, item2.Amount - num);
					}
				}
			}

			private void Classify()
			{
				//IL_0079: Unknown result type (might be due to invalid IL or missing references)
				//IL_008c: Unknown result type (might be due to invalid IL or missing references)
				//IL_017f: Unknown result type (might be due to invalid IL or missing references)
				foreach (LoadoutSlot slot in _loadout.Slots)
				{
					SharedData val = Items.Shared(slot.Prefab);
					if (val == null)
					{
						_report.Note("Unknown item ID " + slot.Prefab + " in " + _loadout.Name + ", skipped");
						continue;
					}
					if (InBounds(slot.Pos))
					{
						_reserved.Add(slot.Pos);
					}
					else
					{
						_report.Note($"{Items.Name(slot.Prefab)}: slot {slot.Pos.x + 1},{slot.Pos.y + 1} is past the {Inv.GetHeight()} rows your inventory has, so it goes in any free slot");
					}
					if (Items.IsGear(val))
					{
						_gearSlots.Add(slot);
						continue;
					}
					_stackSlots.Add(slot);
					_targets.TryGetValue(slot.Prefab, out var value);
					_targets[slot.Prefab] = value + slot.Amount;
					if (!_stackPositions.TryGetValue(slot.Prefab, out var value2))
					{
						value2 = (_stackPositions[slot.Prefab] = new List<Vector2i>());
					}
					value2.Add(slot.Pos);
				}
			}

			private void AssignGear()
			{
				//IL_0084: Unknown result type (might be due to invalid IL or missing references)
				//IL_008a: Unknown result type (might be due to invalid IL or missing references)
				_assigned.Clear();
				HashSet<ItemData> hashSet = new HashSet<ItemData>();
				foreach (LoadoutSlot gearSlot in _gearSlots)
				{
					ItemData val = null;
					foreach (ItemData allItem in Inv.GetAllItems())
					{
						if (!hashSet.Contains(allItem) && !(Items.Prefab(allItem) != gearSlot.Prefab))
						{
							int num = ((val == null) ? 1 : Rank(gearSlot, allItem, val));
							if (num > 0 || (num == 0 && allItem.m_gridPos == gearSlot.Pos))
							{
								val = allItem;
							}
						}
					}
					if (val != null)
					{
						_assigned[gearSlot] = val;
						hashSet.Add(val);
					}
				}
			}

			private int Rank(LoadoutSlot slot, ItemData a, ItemData b)
			{
				if (!_ws.DeathRun && Items.Matches(slot, a) == Items.Matches(slot, b))
				{
					int num = Workspace.IsDeathRunGear(b).CompareTo(Workspace.IsDeathRunGear(a));
					if (num != 0)
					{
						return num;
					}
				}
				return Items.Compare(slot, a, b);
			}

			private List<ItemData> TakeOffDeathRunGear()
			{
				List<ItemData> list = new List<ItemData>();
				if (_ws.DeathRun)
				{
					return list;
				}
				HashSet<ItemData> hashSet = new HashSet<ItemData>(_assigned.Values);
				foreach (ItemData item in Inv.GetAllItems().ToList())
				{
					if (Workspace.IsDeathRunGear(item) && _ws.IsWorn(item) && !hashSet.Contains(item) && !_neverPutAway.Matches(Items.Prefab(item)) && _ws.SourceOf(item) != null && _ws.Unequip(item))
					{
						list.Add(item);
						_ws.Journal?.Line($"  take off {Items.Prefab(item)} (death run gear) in slot {item.m_gridPos.x + 1},{item.m_gridPos.y + 1}");
					}
				}
				return list;
			}

			private void PutBackOn(List<ItemData> takenOff)
			{
				foreach (ItemData item in takenOff)
				{
					if (Inv.ContainsItem(item) && _ws.Equip(item))
					{
						_ws.Journal?.Line("  put " + Items.Prefab(item) + " back on: no room for it");
						_report.Note("Still wearing " + Items.Describe(item) + ": no room for it in its chest");
					}
				}
			}

			private void EquipWorn(bool final)
			{
				foreach (LoadoutSlot item in _loadout.Slots.Where((LoadoutSlot s) => s.Worn))
				{
					ItemData val = WornItemFor(item);
					if (val != null && !_ws.IsWorn(val))
					{
						if (_ws.Equip(val))
						{
							_report.Equipped(val);
							_ws.Journal?.Line($"  equip {Items.Prefab(val)} in slot {val.m_gridPos.x + 1},{val.m_gridPos.y + 1}");
						}
						else if (final)
						{
							_report.Note("Couldn't equip " + Items.Describe(val));
						}
					}
				}
			}

			private ItemData WornItemFor(LoadoutSlot slot)
			{
				if (_gearSlots.Contains(slot))
				{
					if (!_assigned.TryGetValue(slot, out var value))
					{
						return null;
					}
					return value;
				}
				if (!_stackSlots.Contains(slot))
				{
					return null;
				}
				List<ItemData> list = (from i in Inv.GetAllItems()
					where Items.Prefab(i) == slot.Prefab
					select i).ToList();
				if (list.Count == 0 || list.Any(_ws.IsWorn))
				{
					return null;
				}
				return ((IEnumerable<ItemData>)list).FirstOrDefault((Func<ItemData, bool>)((ItemData s) => s.m_gridPos == slot.Pos)) ?? list.OrderByDescending((ItemData s) => s.m_stack).First();
			}

			private List<Spare> Candidates()
			{
				HashSet<ItemData> hashSet = new HashSet<ItemData>(_assigned.Values);
				List<Spare> list = new List<Spare>();
				foreach (ItemData item in Inv.GetAllItems().ToList())
				{
					string text = Items.Prefab(item);
					if (text != null && !_ws.IsWorn(item) && !_neverPutAway.Matches(text) && (Items.IsGear(item.m_shared) ? (!hashSet.Contains(item)) : (!_targets.ContainsKey(text))))
					{
						list.Add(new Spare(item, item.m_stack));
					}
				}
				foreach (KeyValuePair<string, int> target in _targets)
				{
					if (_neverPutAway.Matches(target.Key))
					{
						continue;
					}
					List<ItemData> source = (from i in Inv.GetAllItems()
						where Items.Prefab(i) == target.Key
						select i).ToList();
					int num = source.Sum((ItemData s) => s.m_stack) - target.Value;
					List<Vector2i> own = _stackPositions[target.Key];
					foreach (ItemData item2 in from s in source
						orderby own.Contains(s.m_gridPos) ? 1 : 0, s.m_stack
						select s)
					{
						if (num <= 0)
						{
							break;
						}
						int num2 = Math.Min(num, item2.m_stack - (_ws.IsWorn(item2) ? 1 : 0));
						if (num2 > 0)
						{
							list.Add(new Spare(item2, num2));
							num -= num2;
						}
					}
				}
				return list;
			}

			private List<Spare> PutAway(List<Spare> candidates, bool reportLeftOver = true)
			{
				List<Spare> list = new List<Spare>();
				foreach (Spare candidate in candidates)
				{
					ItemData item = candidate.Item;
					if (!Inv.ContainsItem(item))
					{
						continue;
					}
					string prefab = Items.Prefab(item);
					int num = Math.Min(candidate.Amount, item.m_stack);
					bool flag = false;
					Store store = _ws.SourceOf(item);
					ItemData placed;
					if (store != null)
					{
						flag = true;
						int num2 = _ws.Transfer(_ws.Me, store, item, num, null, null, out placed);
						_report.PutAway(prefab, num2);
						num -= num2;
					}
					Holding[] array = new Holding[2]
					{
						Holding.Only,
						Holding.AmongOthers
					};
					foreach (Holding holding in array)
					{
						if (holding == Holding.AmongOthers && !Plugin.MixedChests.Value)
						{
							break;
						}
						foreach (Store chest in _ws.Chests)
						{
							if (num <= 0)
							{
								break;
							}
							if (chest != store && Holds(chest, prefab) == holding)
							{
								flag = true;
								int num3 = _ws.Transfer(_ws.Me, chest, item, num, null, null, out placed);
								_report.PutAway(prefab, num3);
								num -= num3;
							}
						}
					}
					if (num <= 0)
					{
						continue;
					}
					list.Add(new Spare(item, num));
					if (reportLeftOver)
					{
						if (flag)
						{
							_report.ChestsFull(prefab, num);
						}
						else
						{
							_report.NoChest(prefab, num);
						}
					}
				}
				return list;
			}

			private static Holding Holds(Store chest, string prefab)
			{
				bool flag = false;
				bool flag2 = false;
				foreach (ItemData allItem in chest.Inv.GetAllItems())
				{
					if (Items.Prefab(allItem) == prefab)
					{
						flag = true;
					}
					else
					{
						flag2 = true;
					}
				}
				if (flag)
				{
					if (!flag2)
					{
						return Holding.Only;
					}
					return Holding.AmongOthers;
				}
				return Holding.None;
			}

			private void Refill()
			{
				foreach (LoadoutSlot gearSlot in _gearSlots)
				{
					RefillGear(gearSlot);
				}
				foreach (KeyValuePair<string, int> target in _targets)
				{
					string prefab = target.Key;
					int num = target.Value - Items.Count(Inv, prefab);
					if (num <= 0)
					{
						continue;
					}
					int num2 = 0;
					bool flag = false;
					foreach (Store chest in _ws.Chests)
					{
						foreach (ItemData item in (from i in chest.Inv.GetAllItems()
							where Items.Prefab(i) == prefab
							orderby i.m_stack descending
							select i).ToList())
						{
							int num3 = Math.Min(num, item.m_stack);
							ItemData placed;
							int num4 = _ws.Transfer(chest, _ws.Me, item, num3, _stackPositions[prefab], _reserved, out placed);
							num2 += num4;
							num -= num4;
							if (num4 < num3)
							{
								flag = true;
							}
							if (num <= 0 || flag)
							{
								break;
							}
						}
						if (num <= 0 || flag)
						{
							break;
						}
					}
					_report.Took(prefab, num2);
					if (num > 0)
					{
						if (flag)
						{
							_report.NoRoom(prefab, num);
						}
						else
						{
							_report.Missing(prefab, num);
						}
					}
				}
			}

			private void RefillGear(LoadoutSlot slot)
			{
				//IL_014d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0152: Unknown result type (might be due to invalid IL or missing references)
				_assigned.TryGetValue(slot, out var value);
				if (value != null && Items.Matches(slot, value))
				{
					return;
				}
				Store store = null;
				ItemData val = null;
				foreach (Store chest in _ws.Chests)
				{
					foreach (ItemData allItem in chest.Inv.GetAllItems())
					{
						if (Items.Prefab(allItem) == slot.Prefab && (val == null || Items.Compare(slot, allItem, val) > 0))
						{
							val = allItem;
							store = chest;
						}
					}
				}
				if (val == null)
				{
					if (value == null)
					{
						_report.Missing(slot.Prefab, 1);
					}
				}
				else
				{
					if (value != null && !Items.Matches(slot, val))
					{
						return;
					}
					bool flag = false;
					ItemData placed;
					if (value != null && !_ws.IsWorn(value) && !HasEmptySlot(Inv))
					{
						if (_ws.Transfer(_ws.Me, store, value, 1, null, null, out placed) == 0)
						{
							_report.NoRoom(slot.Prefab, 1);
							return;
						}
						flag = true;
					}
					if (_ws.Transfer(store, _ws.Me, val, 1, (IList<Vector2i>)(object)new Vector2i[1] { slot.Pos }, _reserved, out var placed2) == 0 || placed2 == null)
					{
						_report.NoRoom(slot.Prefab, 1);
						return;
					}
					_assigned[slot] = placed2;
					_report.Took(slot.Prefab, 1);
					if (!Items.Matches(slot, placed2))
					{
						_report.Note(Items.Name(placed2) + ": the one in your loadout wasn't found, took " + Items.Describe(placed2));
					}
					if (!(value == null || flag))
					{
						if (Plugin.AutoEquip.Value && slot.Worn && _ws.IsWorn(value) && _ws.Equip(placed2))
						{
							_report.Equipped(placed2);
						}
						if (_ws.IsWorn(value))
						{
							_report.Note("Kept your other " + Items.Describe(value) + ": you're wearing it");
						}
						else if (_ws.Transfer(_ws.Me, store, value, 1, null, null, out placed) == 0)
						{
							_report.Note("Kept your other " + Items.Describe(value) + ": no room to put it back");
						}
					}
				}
			}

			private void Arrange()
			{
				//IL_00de: Unknown result type (might be due to invalid IL or missing references)
				//IL_0150: Unknown result type (might be due to invalid IL or missing references)
				//IL_0157: Unknown result type (might be due to invalid IL or missing references)
				//IL_0168: 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_019c: Unknown result type (might be due to invalid IL or missing references)
				//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
				//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
				//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
				//IL_01e5: 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_01c6: Unknown result type (might be due to invalid IL or missing references)
				//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
				//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
				//IL_0281: 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_01f3: Unknown result type (might be due to invalid IL or missing references)
				//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
				//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
				//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
				//IL_0203: Unknown result type (might be due to invalid IL or missing references)
				HashSet<ItemData> hashSet = new HashSet<ItemData>();
				HashSet<ItemData> hashSet2 = new HashSet<ItemData>(_assigned.Values);
				foreach (ItemData allItem in Inv.GetAllItems())
				{
					string text = Items.Prefab(allItem);
					if (text != null && _targets.ContainsKey(text))
					{
						hashSet2.Add(allItem);
					}
				}
				int num = 0;
				foreach (LoadoutSlot item in from s in _loadout.Slots
					orderby s.Pos.y, s.Pos.x
					select s)
				{
					if (!InBounds(item.Pos))
					{
						continue;
					}
					ItemData value;
					ItemData val = ((!_gearSlots.Contains(item)) ? (_stackSlots.Contains(item) ? StackFor(item, hashSet) : null) : (_assigned.TryGetValue(item, out value) ? value : null));
					if (val == null || hashSet.Contains(val))
					{
						continue;
					}
					hashSet.Add(val);
					if (val.m_gridPos == item.Pos)
					{
						continue;
					}
					Vector2i gridPos = val.m_gridPos;
					ItemData itemAt = Inv.GetItemAt(item.Pos.x, item.Pos.y);
					if (itemAt == null)
					{
						val.m_gridPos = item.Pos;
					}
					else
					{
						if (hashSet.Contains(itemAt))
						{
							continue;
						}
						if (hashSet2.Contains(itemAt))
						{
							itemAt.m_gridPos = gridPos;
							val.m_gridPos = item.Pos;
						}
						else
						{
							Vector2i val2 = FreeSlotOutsideLoadout();
							itemAt.m_gridPos = ((val2.x >= 0) ? val2 : gridPos);
							val.m_gridPos = item.Pos;
							_ws.Journal?.Line($"  move {Items.Prefab(itemAt)} out of the way to slot {itemAt.m_gridPos.x + 1},{itemAt.m_gridPos.y + 1}");
						}
					}
					num++;
					_ws.Journal?.Line($"  move {item.Prefab} from slot {gridPos.x + 1},{gridPos.y + 1} to {item.Pos.x + 1},{item.Pos.y + 1}");
				}
				_report.Arranged(num);
			}

			private void Distribute()
			{
				//IL_014b: Unknown result type (might be due to invalid IL or missing references)
				//IL_019e: Unknown result type (might be due to invalid IL or missing references)
				//IL_028b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0312: Unknown result type (might be due to invalid IL or missing references)
				//IL_0317: Unknown result type (might be due to invalid IL or missing references)
				foreach (KeyValuePair<string, List<Vector2i>> stackPosition in _stackPositions)
				{
					string prefab = stackPosition.Key;
					List<LoadoutSlot> list = (from s in _stackSlots
						where s.Prefab == prefab && InBounds(s.Pos)
						orderby s.Pos.y, s.Pos.x
						select s).ToList();
					List<ItemData> list2 = (from i in Inv.GetAllItems()
						where Items.Prefab(i) == prefab
						select i).ToList();
					if (list.Count == 0 || list2.Count == 0)
					{
						continue;
					}
					int maxStackSize = list2[0].m_shared.m_maxStackSize;
					Dictionary<Vector2i, int> share = new Dictionary<Vector2i, int>();
					int num = list2.Sum((ItemData s) => s.m_stack);
					foreach (LoadoutSlot item in list)
					{
						int num2 = Math.Min(Math.Min(item.Amount, maxStackSize), num);
						share[item.Pos] = num2;
						num -= num2;
					}
					Dictionary<ItemData, int> spare = new Dictionary<ItemData, int>();
					foreach (ItemData item2 in list2)
					{
						int value;
						int num3 = (share.TryGetValue(item2.m_gridPos, out value) ? value : 0);
						if (_ws.IsWorn(item2))
						{
							num3 = Math.Max(num3, 1);
						}
						spare[item2] = Math.Max(0, item2.m_stack - num3);
					}
					List<ItemData> list3 = (from s in list2
						where spare[s] > 0
						orderby share.ContainsKey(s.m_gridPos) ? 1 : 0
						select s).ToList();
					foreach (LoadoutSlot item3 in list)
					{
						ItemData val = Inv.GetItemAt(item3.Pos.x, item3.Pos.y);
						if (val != null && Items.Prefab(val) != prefab)
						{
							continue;
						}
						int num4 = share[item3.Pos] - (val?.m_stack ?? 0);
						foreach (ItemData item4 in list3)
						{
							if (num4 <= 0)
							{
								break;
							}
							if (item4 != val && spare[item4] > 0)
							{
								int num5 = Math.Min(num4, spare[item4]);
								if (val == null)
								{
									val = item4.Clone();
									val.m_stack = 0;
									val.m_gridPos = item3.Pos;
									val.m_equipped = false;
									Inv.m_inventory.Add(val);
								}
								ItemData obj = val;
								obj.m_stack += num5;
								item4.m_stack -= num5;
								spare[item4] -= num5;
								num4 -= num5;
								_ws.Journal?.Line($"  split {prefab} x{num5} from slot {item4.m_gridPos.x + 1},{item4.m_gridPos.y + 1} " + $"to {item3.Pos.x + 1},{item3.Pos.y + 1}");
								_report.Arranged(1);
								if (item4.m_stack <= 0)
								{
									Inv.m_inventory.Remove(item4);
								}
							}
						}
					}
					SplitOff(prefab, share, maxStackSize);
				}
			}

			private void SplitOff(string prefab, Dictionary<Vector2i, int> share, int max)
			{
				//IL_0085: 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_0107: Unknown result type (might be due to invalid IL or missing references)
				//IL_0109: Unknown result type (might be due to invalid IL or missing references)
				//IL_012d: Unknown result type (might be due to invalid IL or missing references)
				//IL_012f: Unknown result type (might be due to invalid IL or missing references)
				foreach (ItemData stack in (from i in Inv.GetAllItems()
					where Items.Prefab(i) == prefab
					select i).ToList())
				{
					if (!share.TryGetValue(stack.m_gridPos, out var value))
					{
						continue;
					}
					int num = (_ws.IsWorn(stack) ? Math.Max(value, 1) : value);
					int num2 = stack.m_stack - num;
					while (num2 > 0)
					{
						ItemData val = ((IEnumerable<ItemData>)Inv.GetAllItems()).FirstOrDefault((Func<ItemData, bool>)((ItemData i) => i != stack && Items.Prefab(i) == prefab && !share.ContainsKey(i.m_gridPos) && i.m_stack < max));
						if (val == null)
						{
							Vector2i val2 = FreeSlotOutsideLoadout();
							if (val2.x < 0)
							{
								break;
							}
							val = stack.Clone();
							val.m_stack = 0;
							val.m_gridPos = val2;
							val.m_equipped = false;
							Inv.m_inventory.Add(val);
						}
						int num3 = Math.Min(num2, max - val.m_stack);
						ItemData obj = val;
						obj.m_stack += num3;
						ItemData obj2 = stack;
						obj2.m_stack -= num3;
						num2 -= num3;
						_ws.Journal?.Line($"  split {prefab} x{num3} from slot {stack.m_gridPos.x + 1},{stack.m_gridPos.y + 1} " + $"to {val.m_gridPos.x + 1},{val.m_gridPos.y + 1}, outside the loadout");
						_report.Arranged(1);
					}
				}
			}

			private ItemData StackFor(LoadoutSlot slot, HashSet<ItemData> placed)
			{
				List<ItemData> source = (from i in Inv.GetAllItems()
					where Items.Prefab(i) == slot.Prefab && !placed.Contains(i)
					select i).ToList();
				ItemData val = ((IEnumerable<ItemData>)source).FirstOrDefault((Func<ItemData, bool>)((ItemData s) => s.m_gridPos == slot.Pos));
				if (val != null)
				{
					return val;
				}
				List<Vector2i> own = _stackPositions[slot.Prefab];
				return (from s in source
					orderby own.Contains(s.m_gridPos) ? 1 : 0, s.m_stack descending
					select s).FirstOrDefault();
			}

			private Vector2i FreeSlotOutsideLoadout()
			{
				//IL_0058: Unknown result type (might be due to invalid IL or missing references)
				//IL_0023: 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)
				Vector2i val = default(Vector2i);
				for (int num = Inv.GetHeight() - 1; num >= 0; num--)
				{
					for (int i = 0; i < Inv.GetWidth(); i++)
					{
						((Vector2i)(ref val))..ctor(i, num);
						if (!_reserved.Contains(val) && Inv.GetItemAt(i, num) == null)
						{
							return val;
						}
					}
				}
				return new Vector2i(-1, -1);
			}

			private bool InBounds(Vector2i pos)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0009: Unknown result type (might be due to invalid IL or missing references)
				//IL_0012: Unknown result type (might be due to invalid IL or missing references)
				//IL_0025: Unknown result type (might be due to invalid IL or missing references)
				if (pos.x >= 0 && pos.y >= 0 && pos.x < Inv.GetWidth())
				{
					return pos.y < Inv.GetHeight();
				}
				return false;
			}

			private static bool HasEmptySlot(Inventory inventory)
			{
				return inventory.NrOfItems() < inventory.GetWidth() * inventory.GetHeight();
			}
		}

		public static void Run(Player player, bool preview)
		{
			Start(player, preview, null);
		}

		public static void Dump(Player player, Container chest)
		{
			if (!Plugin.Preview.Value && !ChestRegistry.StillUsable(chest))
			{
				((Character)player).Message((MessageType)2, "Kitbag: that chest can't be changed right now.", 0, (Sprite)null, false);
			}
			else
			{
				Start(player, Plugin.Preview.Value, chest);
			}
		}

		private static void Start(Player player, bool preview, Container dumpInto)
		{
			if (Handover.Busy)
			{
				((Character)player).Message((MessageType)2, "Kitbag: still waiting for chests to be handed over", 0, (Sprite)null, false);
				return;
			}
			LoadoutBook current = LoadoutStore.Current;
			if (current == null)
			{
				return;
			}
			Loadout loadout = current.ActiveLoadout;
			if (loadout == null)
			{
				((Character)player).Message((MessageType)2, (current.Loadouts.Count == 0) ? "Kitbag: no loadout yet. Open your inventory, press Kitbag and save one." : "Kitbag: no loadout is active. Pick one in the Kitbag panel and press Use.", 0, (Sprite)null, false);
				return;
			}
			bool deathRun = current.Recovering;
			string what = (((Object)(object)dumpInto == (Object)null) ? "run" : "dump");
			Action<Pass, Workspace> body = null;
			if ((Object)(object)dumpInto != (Object)null)
			{
				body = delegate(Pass pass, Workspace workspace)
				{
					pass.Dump(workspace.StoreFor(dumpInto));
				};
			}
			ChestSkips skips = new ChestSkips();
			List<Container> list = WithDumpChest(ChestRegistry.Find(player, Plugin.Range.Value, skips), dumpInto);
			if (preview)
			{
				Execute(player, loadout, list, preview: true, deathRun, skips, null, what, body);
				return;
			}
			List<Container> list2 = list.Where(ChestRegistry.Owned).ToList();
			List<Container> list3 = ((list2.Count == list.Count) ? new List<Container>() : Needed(player, loadout, list, deathRun, body));
			if (list3.Count == 0)
			{
				Execute(player, loadout, list2, preview: false, deathRun, skips, null, what, body);
				return;
			}
			((MonoBehaviour)Plugin.Instance).StartCoroutine(Handover.Ask(list3, delegate(HandoverResult handover)
			{
				Player localPlayer = Player.m_localPlayer;
				if (!((Object)(object)localPlayer == (Object)null) && !((Character)localPlayer).IsDead() && !((Character)localPlayer).IsTeleporting())
				{
					ChestSkips skips2 = new ChestSkips();
					List<Container> chests = WithDumpChest(ChestRegistry.Find(localPlayer, Plugin.Range.Value, skips2), dumpInto).Where(ChestRegistry.Owned).ToList();
					Execute(localPlayer, loadout, chests, preview: false, deathRun, skips2, handover, what, body);
				}
			}));
		}

		private static List<Container> WithDumpChest(List<Container> chests, Container dumpInto)
		{
			if ((Object)(object)dumpInto != (Object)null && !chests.Contains(dumpInto))
			{
				chests.Insert(0, dumpInto);
			}
			return chests;
		}

		private static List<Container> Needed(Player player, Loadout loadout, List<Container> chests, bool deathRun, Action<Pass, Workspace> body)
		{
			Workspace plan = new Workspace(player, chests, preview: true, deathRun);
			try
			{
				Pass pass = new Pass(plan, loadout, new Report(loadout.Name, preview: true, chests.Count, new ChestSkips()));
				if (body == null)
				{
					pass.Execute();
				}
				else
				{
					body(pass, plan);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Kitbag couldn't work out which chests to ask for: " + ex.Message));
			}
			return chests.Where((Container c) => !ChestRegistry.Owned(c) && plan.Touched.Contains(c)).ToList();
		}

		private static void Execute(Player player, Loadout loadout, List<Container> chests, bool preview, bool deathRun, ChestSkips skips, HandoverResult handover, string what = "run", Action<Pass, Workspace> body = null)
		{
			//IL_005a: 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_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			Stopwatch stopwatch = Stopwatch.StartNew();
			Workspace workspace = new Workspace(player, chests, preview, deathRun);
			Report report = new Report(loadout.Name, preview, chests.Count, skips);
			if (handover != null)
			{
				report.Handover(handover);
			}
			using Journal journal = Journal.Open(LoadoutStore.CharacterKey);
			workspace.Journal = journal;
			Vector3 position = ((Component)player).transform.position;
			journal?.Line("");
			journal?.Line(string.Format("=== {0:yyyy-MM-dd HH:mm:ss} {1} {2} of \"{3}\" ", DateTime.Now, preview ? "Preview" : "Live", what, loadout.Name) + $"at ({position.x:0}, {position.y:0}, {position.z:0}), {chests.Count} chests in range, {skips.Total} skipped");
			Dictionary<string, int> before = workspace.Totals();
			try
			{
				Pass pass = new Pass(workspace, loadout, report);
				if (body == null)
				{
					pass.Execute();
				}
				else
				{
					body(pass, workspace);
				}
			}
			catch (KitbagSafetyException ex)
			{
				report.Stopped(ex.Message);
				Plugin.Log.LogError((object)("Kitbag stopped a run: " + ex.Message));
			}
			catch (Exception ex2)
			{
				report.Stopped("unexpected error: " + ex2.Message);
				Plugin.Log.LogError((object)$"Kitbag run failed: {ex2}");
			}
			if (!preview)
			{
				workspace.Me.Inv.Changed(false, false);
			}
			string text = Drift(before, workspace.Totals());
			if (text != null)
			{
				report.Stopped("item totals changed: " + text);
				Plugin.Log.LogError((object)("Kitbag: item totals changed during a run: " + text));
			}
			if (preview)
			{
				report.WouldAsk(chests.Count((Container c) => !ChestRegistry.Owned(c) && workspace.Touched.Contains(c)));
			}
			stopwatch.Stop();
			Show(player, report);
			foreach (string item in report.Full())
			{
				journal?.Line(item);
				if (Plugin.LogReports.Value)
				{
					Plugin.Log.LogInfo((object)item);
				}
			}
			journal?.Line($"The run took {stopwatch.Elapsed.TotalMilliseconds:0.0} ms.");
		}

		public static int EquipOnly(Player player, Loadout loadout)
		{
			Report report = new Report(loadout.Name, preview: false, 0, new ChestSkips());
			try
			{
				new Pass(new Workspace(player, new List<Container>(), preview: false, deathRun: false), loadout, report).EquipOnly();
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Kitbag couldn't put on {loadout.Name}'s gear: {arg}");
			}
			return report.EquippedCount;
		}

		private static void Show(Player player, Report report)
		{
			((Character)player).Message((MessageType)2, report.Summary(), 0, (Sprite)null, false);
			KitbagPanel.ShowResult(report.Summary(), report.Details(3));
			MessageHud instance = MessageHud.instance;
			if (!((Object)(object)instance == (Object)null))
			{
				List<string> list = report.Details();
				int value = Plugin.MessageLines.Value;
				for (int i = 0; i < list.Count && i < value; i++)
				{
					instance.ShowMessage((MessageType)1, list[i], 0, (Sprite)null, false, true);
				}
				if (value > 0 && list.Count > value)
				{
					instance.ShowMessage((MessageType)1, $"And {list.Count - value} more in the journal", 0, (Sprite)null, false, true);
				}
			}
		}

		private static string Drift(Dictionary<string, int> before, Dictionary<string, int> after)
		{
			List<string> list = new List<string>();
			foreach (string item in before.Keys.Union(after.Keys))
			{
				before.TryGetValue(item, out var value);
				after.TryGetValue(item, out var value2);
				if (value != value2)
				{
					list.Add($"{item} {value} to {value2}");
				}
			}
			if (list.Count != 0)
			{
				return string.Join(", ", list);
			}
			return null;
		}
	}
	internal sealed class KitbagSafetyException : Exception
	{
		public KitbagSafetyException(string message)
			: base(message)
		{
		}
	}
	internal sealed class Store
	{
		public readonly Inventory Inv;

		public readonly Container Container;

		public readonly string Id;

		public bool IsPlayer => (Object)(object)Container == (Object)null;

		public string Label
		{
			get
			{
				//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_005e: Unknown result type (might be due to invalid IL or missing references)
				//IL_006b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0079: Unknown result type (might be due to invalid IL or missing references)
				//IL_0087: Unknown result type (might be due to invalid IL or missing references)
				if (IsPlayer)
				{
					return "inventory";
				}
				Vector3 position = ((Component)Container).transform.position;
				return string.Format("{0} {1} at ({2:0}, {3:0}, {4:0})", ((Object)((Component)Container).gameObject).name.Replace("(Clone)", ""), Container.m_nview.GetZDO().m_uid, position.x, position.y, position.z);
			}
		}

		public Store(Inventory inventory, Container container)
		{
			Inv = inventory;
			Container = container;
			Id = (((Object)(object)container == (Object)null) ? "" : ((object)Unsafe.As<ZDOID, ZDOID>(ref container.m_nview.GetZDO().m_uid)/*cast due to .constrained prefix*/).ToString());
		}
	}
	internal sealed class Workspace
	{
		public readonly bool Preview;

		public readonly bool DeathRun;

		public readonly Player Player;

		public readonly Store Me;

		public readonly List<Store> Chests = new List<Store>();

		public readonly HashSet<Container> Touched = new HashSet<Container>();

		public Journal Journal;

		public const string SourceKey = "kitbag.from";

		public const string DeathRunKey = "kitbag.deathrun";

		public Workspace(Player player, List<Container> chests, bool preview, bool deathRun)
		{
			Preview = preview;
			DeathRun = deathRun;
			Player = player;
			Inventory inventory = ((Humanoid)player).GetInventory();
			Me = new Store(preview ? Copy(inventory) : inventory, null);
			foreach (Container chest in chests)
			{
				Inventory inventory2 = chest.GetInventory();
				Chests.Add(new Store(preview ? Copy(inventory2) : inventory2, chest));
			}
		}

		public static bool IsDeathRunGear(ItemData item)
		{
			return item.m_customData.ContainsKey("kitbag.deathrun");
		}

		public bool IsWorn(ItemData item)
		{
			if (!Preview)
			{
				return ((Humanoid)Player).IsItemEquiped(item);
			}
			return item.m_equipped;
		}

		public Store StoreFor(Container container)
		{
			return Chests.Find((Store c) => (Object)(object)c.Container == (Object)(object)container);
		}

		public Store SourceOf(ItemData item)
		{
			if (!item.m_customData.TryGetValue("kitbag.from", out var id))
			{
				return null;
			}
			return Chests.Find((Store c) => c.Id == id);
		}

		public int Transfer(Store from, Store to, ItemData item, int amount, IList<Vector2i> preferred, ICollection<Vector2i> avoid, out ItemData placed)
		{
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01df: Unknown result type (might be due to invalid IL or missing references)
			//IL_020a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0211: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b0: Unknown result type (might be due to invalid IL or missing references)
			placed = null;
			amount = Math.Min(amount, item.m_stack);
			if (amount <= 0 || !from.Inv.ContainsItem(item))
			{
				return 0;
			}
			if (!Preview && ((!from.IsPlayer && !ChestRegistry.StillUsable(from.Container)) || (!to.IsPlayer && !ChestRegistry.StillUsable(to.Container))))
			{
				throw new KitbagSafetyException("a chest stopped being usable mid-run (its owner changed or someone opened it)");
			}
			string text = Items.Prefab(item);
			int num = Items.Count(from.Inv, text);
			int num2 = Items.Count(to.Inv, text);
			Vector2i gridPos = item.m_gridPos;
			int maxStackSize = item.m_shared.m_maxStackSize;
			int num3 = amount;
			string value = null;
			string value2 = null;
			if (maxStackSize <= 1 && !to.IsPlayer)
			{
				if (item.m_customData.TryGetValue("kitbag.from", out value))
				{
					item.m_customData.Remove("kitbag.from");
				}
				if (item.m_customData.TryGetValue("kitbag.deathrun", out value2))
				{
					item.m_customData.Remove("kitbag.deathrun");
				}
			}
			if (maxStackSize > 1)
			{
				foreach (ItemData item2 in StacksToTopUp(to.Inv, item, preferred))
				{
					int stack = item.m_stack;
					to.Inv.AddItem(item, Math.Min(num3, maxStackSize - item2.m_stack), item2.m_gridPos.x, item2.m_gridPos.y, false);
					num3 -= stack - item.m_stack;
					placed = item2;
					if (num3 <= 0)
					{
						break;
					}
				}
			}
			while (num3 > 0)
			{
				Vector2i val = EmptySlot(to.Inv, preferred, avoid, to.Inv.TopFirst(item));
				if (val.x < 0)
				{
					break;
				}
				int stack2 = item.m_stack;
				to.Inv.AddItem(item, Math.Min(num3, maxStackSize), val.x, val.y, false);
				int num4 = stack2 - item.m_stack;
				if (num4 <= 0)
				{
					break;
				}
				num3 -= num4;
				placed = to.Inv.GetItemAt(val.x, val.y);
				if (placed != null)
				{
					placed.m_equipped = false;
				}
			}
			int num5 = amount - num3;
			if (maxStackSize <= 1)
			{
				if (num5 == 0)
				{
					if (value != null)
					{
						item.m_customData["kitbag.from"] = value;
					}
					if (value2 != null)
					{
						item.m_customData["kitbag.deathrun"] = value2;
					}
				}
				else if (!from.IsPlayer && to.IsPlayer && placed != null)
				{
					placed.m_customData["kitbag.from"] = from.Id;
					if (DeathRun)
					{
						placed.m_customData["kitbag.deathrun"] = "1";
					}
					else
					{
						placed.m_customData.Remove("kitbag.deathrun");
					}
				}
			}
			if (num5 > 0)
			{
				if (!from.IsPlayer)
				{
					Touched.Add(from.Container);
				}
				if (!to.IsPlayer)
				{
					Touched.Add(to.Container);
				}
				if (item.m_stack <= 0)
				{
					from.Inv.RemoveItem(item);
				}
				else
				{
					from.Inv.Changed(false, false);
				}
			}
			if (num5 > 0 && Journal != null)
			{
				string text2 = ((placed != null && to.IsPlayer) ? $" slot {placed.m_gridPos.x + 1},{placed.m_gridPos.y + 1}" : "");
				string text3 = (from.IsPlayer ? $" slot {gridPos.x + 1},{gridPos.y + 1}" : "");
				Journal.Line($"  move {text} x{num5}: {from.Label}{text3} to {to.Label}{text2}");
			}
			int num6 = Items.Count(from.Inv, text);
			int num7 = Items.Count(to.Inv, text);
			if (num - num6 != num5 || num7 - num2 != num5)
			{
				throw new KitbagSafetyException($"moving {num5} {text} from {from.Label} to {to.Label} didn't add up: " + $"the source went from {num} to {num6} and the destination from {num2} to {num7}");
			}
			return num5;
		}

		public bool Equip(ItemData item)
		{
			if (!Preview)
			{
				return ((Humanoid)Player).EquipItem(item, true);
			}
			string text = EquipSlot(item);
			if (text == null)
			{
				return false;
			}
			foreach (ItemData allItem in Me.Inv.GetAllItems())
			{
				if (allItem != item && allItem.m_equipped && Conflicts(text, EquipSlot(allItem)))
				{
					allItem.m_equipped = false;
				}
			}
			item.m_equipped = true;
			return true;
		}

		public bool Unequip(ItemData item)
		{
			if (Preview)
			{
				item.m_equipped = false;
				return true;
			}
			((Humanoid)Player).UnequipItem(item, true);
			return !((Humanoid)Player).IsItemEquiped(item);
		}

		public Dictionary<string, int> Totals()
		{
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			foreach (Store item in new Store[1] { Me }.Concat(Chests))
			{
				foreach (ItemData allItem in item.Inv.GetAllItems())
				{
					string key = Items.Prefab(allItem) ?? allItem.m_shared.m_name;
					dictionary.TryGetValue(key, out var value);
					dictionary[key] = value + allItem.m_stack;
				}
			}
			return dictionary;
		}

		private static Inventory Copy(Inventory source)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Expected O, but got Unknown
			Inventory val = new Inventory(source.m_name, (Sprite)null, source.GetWidth(), source.GetHeight());
			foreach (ItemData allItem in source.GetAllItems())
			{
				val.m_inventory.Add(allItem.Clone());
			}
			return val;
		}

		private static List<ItemData> StacksToTopUp(Inventory inventory, ItemData item, IList<Vector2i> preferred)
		{
			return (from s in inventory.GetAllItems()
				where s != item && s.IsSameType(item) && s.m_stack < s.m_shared.m_maxStackSize
				orderby (preferred == null || !preferred.Contains(s.m_gridPos)) ? 1 : 0
				select s).ToList();
		}

		private static Vector2i EmptySlot(Inventory inventory, IList<Vector2i> preferred, ICollection<Vector2i> avoid, bool topFirst)
		{
			//IL_00e5: 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_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: 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_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: 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)
			int width = inventory.GetWidth();
			int height = inventory.GetHeight();
			if (preferred != null)
			{
				foreach (Vector2i item in preferred)
				{
					if (item.x >= 0 && item.y >= 0 && item.x < width && item.y < height && inventory.GetItemAt(item.x, item.y) == null)
					{
						return item;
					}
				}
			}
			Vector2i val = default(Vector2i);
			for (int i = 0; i < 2; i++)
			{
				for (int j = 0; j < height; j++)
				{
					int num = (topFirst ? j : (height - 1 - j));
					for (int k = 0; k < width; k++)
					{
						((Vector2i)(ref val))..ctor(k, num);
						if (inventory.GetItemAt(k, num) == null && (i == 1 || avoid == null || !avoid.Contains(val)))
						{
							return val;
						}
					}
				}
				if (avoid == null)
				{
					break;
				}
			}
			return new Vector2i(-1, -1);
		}

		private static string EquipSlot(ItemData item)
		{
			//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)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Expected I4, but got Unknown
			ItemType itemType = item.m_shared.m_itemType;
			switch (itemType - 3)
			{
			case 3:
				return "helmet";
			case 4:
				return "chest";
			case 8:
				return "legs";
			case 14:
				return "shoulder";
			case 15:
				return "utility";
			case 21:
				return "trinket";
			case 6:
				return "ammo";
			case 0:
			case 12:
			case 16:
				return "right";
			case 2:
				return "left";
			case 1:
			case 11:
			case 19:
				return "both";
			default:
				return null;
			}
		}

		private static bool Conflicts(string a, string b)
		{
			if (a == null || b == null)
			{
				return false;
			}
			if (a == b)
			{
				return true;
			}
			bool num = a == "right" || a == "left" || a == "both";
			bool flag = b == "right" || b == "left" || b == "both";
			if (num && flag)
			{
				if (!(a == "both"))
				{
					return b == "both";
				}
				return true;
			}
			return false;
		}
	}
	internal static class ModInfo
	{
		public const string Guid = "com.adapperbadger.kitbag";

		public const string Name = "Kitbag";

		public const string Version = "0.1.0";
	}
}
namespace Kitbag.Ui
{
	internal static class DumpButton
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static UnityAction <0>__OnClick;
		}

		public const string Name = "KitbagDump";

		private const float Padding = 8f;

		private const float Gap = 6f;

		private static GameObject _box;

		private static Container _shownFor;

		private static bool _shownEnabled;

		public static void Ensure()
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected O, but got Unknown
			//IL_0059: 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_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_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: 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_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Expected O, but got Unknown
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: 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_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: 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_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_0196: Unknown result type (might be due to invalid IL or missing references)
			//IL_019d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Expected O, but got Unknown
			//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: Expected O, but got Unknown
			InventoryGui instance = InventoryGui.instance;
			if (!((Object)(object)_box != (Object)null) && !((Object)(object)instance == (Object)null) && !((Object)(object)instance.m_container == (Object)null) && !((Object)(object)instance.m_stackAllButton == (Object)null) && GUIManager.Instance != null)
			{
				RectTransform container = instance.m_container;
				RectTransform val = (RectTransform)((Component)instance.m_stackAllButton).transform;
				Rect rect = val.rect;
				Vector2 size = ((Rect)(ref rect)).size;
				_box = GUIManager.Instance.CreateWoodpanel((Transform)(object)container, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, size.x + 16f, size.y + 16f, false);
				((Object)_box).name = "KitbagDump";
				RectTransform val2 = (RectTransform)_box.transform;
				val2.pivot = new Vector2(0f, 0.5f);
				float num;
				if (!((Object)(object)((Transform)val).parent == (Object)(object)container))
				{
					rect = container.rect;
					num = ((Rect)(ref rect)).yMax - size.y / 2f - 8f;
				}
				else
				{
					num = RowCenterY(val);
				}
				float num2 = num;
				rect = container.rect;
				((Transform)val2).localPosition = new Vector3(((Rect)(ref rect)).xMax + 6f, num2, 0f);
				GameObject val3 = Object.Instantiate<GameObject>(((Component)instance.m_stackAllButton).gameObject, (Transform)(object)val2);
				((Object)val3).name = "Button";
				RectTransform val4 = (RectTransform)val3.transform;
				Vector2 val5 = default(Vector2);
				((Vector2)(ref val5))..ctor(0.5f, 0.5f);
				val4.pivot = val5;
				Vector2 anchorMin = (val4.anchorMax = val5);
				val4.anchorMin = anchorMin;
				val4.anchoredPosition = Vector2.zero;
				((Transform)val4).localScale = Vector3.one;
				Button component = val3.GetComponent<Button>();
				component.onClick = new ButtonClickedEvent();
				ButtonClickedEvent onClick = component.onClick;
				object obj = <>O.<0>__OnClick;
				if (obj == null)
				{
					UnityAction val7 = OnClick;
					<>O.<0>__OnClick = val7;
					obj = (object)val7;
				}
				((UnityEvent)onClick).AddListener((UnityAction)obj);
				Localize[] componentsInChildren = val3.GetComponentsInChildren<Localize>(true);
				for (int i = 0; i < componentsInChildren.Length; i++)
				{
					Object.DestroyImmediate((Object)(object)componentsInChildren[i]);
				}
				TMP_Text componentInChildren = val3.GetComponentInChildren<TMP_Text>(true);
				if ((Object)(object)componentInChildren != (Object)null)
				{
					componentInChildren.text = "Dump extras";
				}
			}
		}

		public static void UpdateFor(Container container)
		{
			bool value = Plugin.Enabled.Value;
			if (!((Object)(object)_box == (Object)null) && (!((Object)(object)container == (Object)(object)_shownFor) || value != _shownEnabled))
			{
				_shownFor = container;
				_shownEnabled = value;
				_box.SetActive(value && (Object)(object)container != (Object)null && (Object)(object)((Component)container).GetComponent<TombStone>() == (Object)null);
			}
		}

		private static float RowCenterY(RectTransform rect)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			float y = ((Transform)rect).localPosition.y;
			Rect rect2 = rect.rect;
			return y + ((Rect)(ref rect2)).center.y * ((Transform)rect).localScale.y;
		}

		private static void OnClick()
		{
			InventoryGui instance = InventoryGui.instance;
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)instance == (Object)null) && !((Object)(object)localPlayer == (Object)null) && !((Object)(object)instance.m_currentContainer == (Object)null) && !((Character)localPlayer).IsTeleporting())
			{
				instance.SetupDragItem((ItemData)null, (Inventory)null, 1);
				Restocker.Dump(localPlayer, instance.m_currentContainer);
			}
		}
	}
	internal static class InventoryButton
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static UnityAction <0>__Toggle;
		}

		private const float Width = 100f;

		private const float Height = 32f;

		private static GameObject _button;

		public static void Ensure()
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invali