Decompiled source of Computer AutoLootbag v0.0.27

Computer-AutoLootbag.dll

Decompiled 3 days ago
using System;
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.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using ComputerMods.ConfigSync;
using HarmonyLib;
using Jotunn;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using Splatform;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Computer-AutoLootbag")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.0.27.0")]
[assembly: AssemblyInformationalVersion("0.0.27")]
[assembly: AssemblyProduct("Computer-AutoLootbag")]
[assembly: AssemblyTitle("Computer-AutoLootbag")]
[assembly: AssemblyVersion("0.0.27.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 ComputerMods.ConfigSync
{
	internal sealed class ServerConfigSync : IDisposable
	{
		private static readonly List<ServerConfigSync> instances = new List<ServerConfigSync>();

		private static Harmony? hooks;

		private readonly ConfigFile config;

		private readonly string guid;

		private readonly Dictionary<ConfigEntryBase, ConfigurationManagerAttributes> gameplay = new Dictionary<ConfigEntryBase, ConfigurationManagerAttributes>();

		private readonly Dictionary<ConfigEntryBase, object> local = new Dictionary<ConfigEntryBase, object>();

		private readonly Dictionary<ConfigEntryBase, object> remote = new Dictionary<ConfigEntryBase, object>();

		private readonly Dictionary<ConfigurationManagerAttributes, (bool Admin, bool? ReadOnly, bool? HideDefault)> originalTags = new Dictionary<ConfigurationManagerAttributes, (bool, bool?, bool?)>();

		private bool client;

		private bool ready;

		private bool receiving;

		private bool writing;

		private bool leaving;

		private bool disposed;

		private bool remoteEnabled = true;

		public const float PushDelay = 0.5f;

		private static MethodInfo? pushMethod;

		private static bool pushChecked;

		private static bool pushMissingLogged;

		private static bool pushPending;

		private static float pushDue;

		internal static Func<float> Clock = () => Time.realtimeSinceStartup;

		public ConfigEntry<bool> Enabled { get; }

		public bool IsServerControlled
		{
			get
			{
				if (client)
				{
					if (ready)
					{
						return remoteEnabled;
					}
					return true;
				}
				return false;
			}
		}

		private static bool PlayerIsAdmin => SynchronizationManager.Instance.PlayerIsAdmin;

		public event Action? Applied;

		private static MethodInfo? FindPush()
		{
			if (pushChecked)
			{
				return pushMethod;
			}
			pushChecked = true;
			pushMethod = AccessTools.Method(typeof(SynchronizationManager), "SynchronizeChangedConfig", (Type[])null, (Type[])null);
			if (pushMethod == null)
			{
				Logger.LogWarning((object)"ServerConfigSync: Jotunn's config push (SynchronizeChangedConfig) was not found; in-game admin edits sync when the config window closes.");
			}
			return pushMethod;
		}

		public static ServerConfigSync Attach(BaseUnityPlugin plugin)
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Expected O, but got Unknown
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Expected O, but got Unknown
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Expected O, but got Unknown
			//IL_00f8: Expected O, but got Unknown
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Expected O, but got Unknown
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Expected O, but got Unknown
			ServerConfigSync serverConfigSync = new ServerConfigSync(plugin.Config, plugin.Info.Metadata.GUID);
			FindPush();
			bool flag = false;
			try
			{
				if (hooks == null)
				{
					hooks = new Harmony(plugin.Info.Metadata.GUID + ".computer-config-sync");
					flag = true;
					hooks.Patch((MethodBase)AccessTools.Method(typeof(ZNet), "Start", (Type[])null, (Type[])null), new HarmonyMethod(typeof(ServerConfigSync), "BeforeStart", (Type[])null)
					{
						priority = 800
					}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					hooks.Patch((MethodBase)AccessTools.Method(typeof(ZNet), "OnDestroy", (Type[])null, (Type[])null), new HarmonyMethod(typeof(ServerConfigSync), "BeforeDestroy", (Type[])null)
					{
						priority = 800
					}, new HarmonyMethod(typeof(ServerConfigSync), "AfterDestroy", (Type[])null)
					{
						priority = 0
					}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					hooks.Patch((MethodBase)AccessTools.Method(typeof(ConfigEntryBase), "GetSerializedValue", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(ServerConfigSync), "AfterSerialize", (Type[])null)
					{
						priority = 0
					}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					hooks.Patch((MethodBase)AccessTools.Method(typeof(ZNet), "Update", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(ServerConfigSync), "AfterNetUpdate", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
				return serverConfigSync;
			}
			catch
			{
				if (flag)
				{
					Harmony? obj2 = hooks;
					if (obj2 != null)
					{
						obj2.UnpatchSelf();
					}
					hooks = null;
				}
				serverConfigSync.Dispose();
				throw;
			}
		}

		internal ServerConfigSync(ConfigFile config, string guid)
		{
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Expected O, but got Unknown
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Expected O, but got Unknown
			if (instances.Any((ServerConfigSync p) => p.config == config))
			{
				throw new InvalidOperationException("Config already has a server sync policy.");
			}
			this.config = config;
			this.guid = guid;
			Enabled = config.Bind<bool>("ServerSync", "Enabled", true, new ConfigDescription("Server-owned policy. True enforces server gameplay settings; false lets each player use local gameplay settings. Change on the host/server and reload its config. Client values cannot override the server policy.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} }));
			Refresh();
			RememberTags(Tags((ConfigEntryBase)(object)Enabled));
			config.SettingChanged += EntryChanged;
			instances.Add(this);
			SynchronizationManager.OnSyncingConfiguration += BeforeSync;
			SynchronizationManager.OnConfigurationSynchronized += AfterSync;
			SynchronizationManager.OnAdminStatusChanged += AdminChanged;
		}

		public void Refresh()
		{
			if (client)
			{
				throw new InvalidOperationException("Bind gameplay settings before connecting.");
			}
			foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in config)
			{
				ConfigEntryBase value = item.Value;
				if ((object)value != Enabled && !gameplay.ContainsKey(value))
				{
					ConfigurationManagerAttributes val = Tags(value);
					if (val != null && val.IsAdminOnly)
					{
						gameplay.Add(value, val);
						RememberTags(val);
					}
				}
			}
		}

		internal void BeginSession(bool isClient)
		{
			client = isClient;
			ready = false;
			receiving = false;
			leaving = false;
			local.Clear();
			remote.Clear();
			remoteEnabled = true;
			foreach (ConfigEntryBase key in gameplay.Keys)
			{
				local[key] = key.BoxedValue;
			}
			local[(ConfigEntryBase)(object)Enabled] = ((ConfigEntryBase)Enabled).BoxedValue;
			SetTags(synchronizeGameplay: true);
		}

		private void BeforeSync(object? sender, SyncingConfigurationEventArgs args)
		{
			if (!client || leaving)
			{
				return;
			}
			receiving = true;
			if (ready && !remoteEnabled)
			{
				foreach (ConfigEntryBase key in gameplay.Keys)
				{
					local[key] = key.BoxedValue;
				}
			}
			SetTags(synchronizeGameplay: true);
			Write(delegate
			{
				if (ready)
				{
					Enabled.Value = remoteEnabled;
					foreach (KeyValuePair<ConfigEntryBase, object> item in remote)
					{
						item.Key.BoxedValue = item.Value;
					}
				}
			});
		}

		private void AfterSync(object? sender, ConfigurationSynchronizationEventArgs args)
		{
			if (!client)
			{
				if (args.UpdatedPluginGUIDs.Contains(guid))
				{
					Notify();
				}
			}
			else
			{
				if (!receiving || leaving)
				{
					return;
				}
				if (!args.UpdatedPluginGUIDs.Contains(guid))
				{
					if (ready)
					{
						if (!remoteEnabled)
						{
							Write(delegate
							{
								foreach (ConfigEntryBase key in gameplay.Keys)
								{
									key.BoxedValue = local[key];
								}
							});
						}
						SetTags(remoteEnabled);
					}
					receiving = false;
					return;
				}
				remoteEnabled = Enabled.Value;
				foreach (ConfigEntryBase key2 in gameplay.Keys)
				{
					remote[key2] = key2.BoxedValue;
				}
				ready = true;
				if (!remoteEnabled)
				{
					Write(delegate
					{
						foreach (ConfigEntryBase key3 in gameplay.Keys)
						{
							key3.BoxedValue = local[key3];
						}
					});
				}
				SetTags(remoteEnabled);
				receiving = false;
				Notify();
			}
		}

		private void EntryChanged(object? sender, SettingChangedEventArgs args)
		{
			if (writing || leaving || receiving)
			{
				return;
			}
			ConfigEntryBase entry = args.ChangedSetting;
			if ((object)entry != Enabled && !gameplay.ContainsKey(entry))
			{
				return;
			}
			if (client)
			{
				if (!ready)
				{
					return;
				}
				if ((object)entry == Enabled)
				{
					Write(delegate
					{
						Enabled.Value = remoteEnabled;
					});
				}
				else if (remoteEnabled && PlayerIsAdmin)
				{
					remote[entry] = entry.BoxedValue;
					SchedulePush();
				}
				else if (remoteEnabled)
				{
					Write(delegate
					{
						entry.BoxedValue = remote[entry];
					});
				}
				else
				{
					local[entry] = entry.BoxedValue;
				}
			}
			Notify();
		}

		private void AdminChanged()
		{
			if (client && ready && !leaving)
			{
				SetTags(remoteEnabled);
			}
		}

		private void SetTags(bool synchronizeGameplay)
		{
			foreach (KeyValuePair<ConfigEntryBase, ConfigurationManagerAttributes> item in gameplay)
			{
				item.Value.IsAdminOnly = !client || synchronizeGameplay;
				if (client)
				{
					item.Value.ReadOnly = synchronizeGameplay && !PlayerIsAdmin;
				}
			}
			ConfigurationManagerAttributes val = Tags((ConfigEntryBase)(object)Enabled);
			val.IsAdminOnly = true;
			if (client)
			{
				val.ReadOnly = true;
			}
		}

		private static ConfigurationManagerAttributes? Tags(ConfigEntryBase entry)
		{
			return entry.Description.Tags.OfType<ConfigurationManagerAttributes>().FirstOrDefault();
		}

		private void RememberTags(ConfigurationManagerAttributes tag)
		{
			if (!originalTags.ContainsKey(tag))
			{
				originalTags.Add(tag, (tag.IsAdminOnly, tag.ReadOnly, tag.HideDefaultButton));
			}
		}

		private void RestoreTags()
		{
			foreach (KeyValuePair<ConfigurationManagerAttributes, (bool, bool?, bool?)> originalTag in originalTags)
			{
				originalTag.Key.IsAdminOnly = originalTag.Value.Item1;
				originalTag.Key.ReadOnly = originalTag.Value.Item2;
				originalTag.Key.HideDefaultButton = originalTag.Value.Item3;
			}
		}

		private void Write(Action action)
		{
			bool flag = writing;
			bool saveOnConfigSet = config.SaveOnConfigSet;
			writing = true;
			config.SaveOnConfigSet = false;
			try
			{
				action();
			}
			finally
			{
				config.SaveOnConfigSet = saveOnConfigSet;
				writing = flag;
			}
		}

		internal void BeginDisconnect()
		{
			pushPending = false;
			if (!client)
			{
				return;
			}
			if (ready && !remoteEnabled)
			{
				foreach (ConfigEntryBase key in gameplay.Keys)
				{
					local[key] = key.BoxedValue;
				}
			}
			leaving = true;
			SetTags(synchronizeGameplay: true);
		}

		internal void EndDisconnect()
		{
			if (client)
			{
				Write(delegate
				{
					foreach (KeyValuePair<ConfigEntryBase, object> item in local)
					{
						item.Key.BoxedValue = item.Value;
					}
				});
			}
			client = false;
			ready = false;
			leaving = false;
			receiving = false;
			RestoreTags();
			local.Clear();
			remote.Clear();
			Notify();
		}

		internal string SerializeLocal(ConfigEntryBase entry, string original)
		{
			if (!client || !local.TryGetValue(entry, out object value))
			{
				return original;
			}
			if (ready && !remoteEnabled && !receiving && !writing && !leaving && (object)entry != Enabled)
			{
				value = entry.BoxedValue;
			}
			return TomlTypeConverter.ConvertToString(value, entry.SettingType);
		}

		public static bool PushAdminChanges()
		{
			pushPending = false;
			if (!PlayerIsAdmin || !instances.Any((ServerConfigSync p) => p.client && p.ready && !p.leaving))
			{
				return false;
			}
			try
			{
				MethodInfo methodInfo = FindPush();
				if (methodInfo == null)
				{
					if (!pushMissingLogged)
					{
						pushMissingLogged = true;
						Logger.LogError((object)"ServerConfigSync: Jotunn config push not found; admin edits stay local until the config window closes.");
					}
					return false;
				}
				methodInfo.Invoke(SynchronizationManager.Instance, null);
				return true;
			}
			catch (Exception ex)
			{
				Logger.LogError((object)("ServerConfigSync admin push failed: " + ex));
				return false;
			}
		}

		private static void SchedulePush()
		{
			pushPending = true;
			pushDue = Clock() + 0.5f;
		}

		internal static void AfterNetUpdate()
		{
			if (pushPending && Clock() >= pushDue)
			{
				PushAdminChanges();
			}
		}

		private void Notify()
		{
			if (this.Applied == null)
			{
				return;
			}
			Delegate[] invocationList = this.Applied.GetInvocationList();
			for (int i = 0; i < invocationList.Length; i++)
			{
				Action action = (Action)invocationList[i];
				try
				{
					action();
				}
				catch (Exception ex)
				{
					Logger.LogError((object)("ServerConfigSync apply callback failed for " + guid + " (" + (action.Method.DeclaringType?.FullName ?? "?") + "): " + ex));
				}
			}
		}

		private static void BeforeStart(ZNet __instance)
		{
			ServerConfigSync[] array = instances.ToArray();
			for (int i = 0; i < array.Length; i++)
			{
				array[i].BeginSession(!__instance.IsServer());
			}
		}

		private static void BeforeDestroy()
		{
			ServerConfigSync[] array = instances.ToArray();
			for (int i = 0; i < array.Length; i++)
			{
				array[i].BeginDisconnect();
			}
		}

		private static void AfterDestroy()
		{
			ServerConfigSync[] array = instances.ToArray();
			for (int i = 0; i < array.Length; i++)
			{
				array[i].EndDisconnect();
			}
		}

		private static void AfterSerialize(ConfigEntryBase __instance, ref string __result)
		{
			foreach (ServerConfigSync instance in instances)
			{
				__result = instance.SerializeLocal(__instance, __result);
			}
		}

		public void Dispose()
		{
			if (disposed)
			{
				return;
			}
			BeginDisconnect();
			EndDisconnect();
			disposed = true;
			config.SettingChanged -= EntryChanged;
			SynchronizationManager.OnSyncingConfiguration -= BeforeSync;
			SynchronizationManager.OnConfigurationSynchronized -= AfterSync;
			SynchronizationManager.OnAdminStatusChanged -= AdminChanged;
			instances.Remove(this);
			if (instances.Count == 0)
			{
				Harmony? obj = hooks;
				if (obj != null)
				{
					obj.UnpatchSelf();
				}
				hooks = null;
			}
		}
	}
}
namespace Computer.AutoLootbag
{
	internal static class AzothAction
	{
		internal static string Refusal(bool available, bool selected, bool known, bool busy, bool deleting, int held, int shownAmount, int shownAzoth, int freshAmount, int freshAzoth, string providerReason)
		{
			if (busy)
			{
				return "An Azoth conversion is already running.";
			}
			if (deleting)
			{
				return "Finish or cancel the delete question first.";
			}
			if (!available)
			{
				if (!string.IsNullOrEmpty(providerReason))
				{
					return providerReason;
				}
				return "Azoth conversion is unavailable on this server.";
			}
			if (!selected)
			{
				return "Select an item's name in the bag first. Only that row is converted.";
			}
			if (!known)
			{
				return "This item's mod is not loaded. Its loot stays in the bag.";
			}
			if (held <= 0 || shownAmount > held)
			{
				return "The selected loot changed. Select it again; nothing was used.";
			}
			if (freshAmount <= 0 || freshAzoth <= 0)
			{
				if (!string.IsNullOrEmpty(providerReason))
				{
					return providerReason;
				}
				return "Alchemy won't accept this item or quantity under the current rates and batch limits.";
			}
			if (freshAmount > held || freshAmount != shownAmount || freshAzoth != shownAzoth)
			{
				return "The Alchemy quote changed. Check the new amount and click again; nothing was used.";
			}
			return "";
		}
	}
	internal readonly struct AzothQuote
	{
		internal readonly int Amount;

		internal readonly int Azoth;

		internal readonly string Reason;

		internal AzothQuote(int amount, int azoth, string reason = "")
		{
			Amount = amount;
			Azoth = azoth;
			Reason = reason;
		}
	}
	internal sealed class AzothBatch
	{
		internal delegate bool Pay(string name, int amount, int expectedAzoth, Func<bool> consume, Action<bool, int, float, string> done);

		private readonly Func<BagModel?> read;

		private readonly Func<BagModel, BagModel, bool> replace;

		private readonly Func<string, int, AzothQuote> quote;

		private readonly Pay pay;

		private readonly Action<string, int, int, BagModel>? intent;

		private readonly Queue<KeyValuePair<string, int>> rows;

		private BagModel? expected;

		private int expectedRevision;

		private int remaining;

		private int batchIndex;

		private readonly AzothPlan? approved;

		private string item = "";

		private bool stepping;

		private bool paying;

		private bool stopped;

		private readonly HashSet<string> used = new HashSet<string>(StringComparer.Ordinal);

		internal bool Done { get; private set; }

		internal long Pieces { get; private set; }

		internal long Azoth { get; private set; }

		internal float Xp { get; private set; }

		internal int Skipped { get; private set; }

		internal string Reason { get; private set; } = "";

		internal bool Uncertain { get; private set; }

		internal string Warning { get; private set; } = "";

		internal string Summary
		{
			get
			{
				if (Pieces <= 0)
				{
					if (Reason.Length <= 0)
					{
						return "Nothing eligible in your active lootbag. Whole-batch leftovers and excluded items were kept.";
					}
					return "Nothing converted. " + Reason;
				}
				return "Converted " + Pieces.ToString("N0", CultureInfo.InvariantCulture) + " items (" + used.Count + " kinds) into " + Azoth.ToString("N0", CultureInfo.InvariantCulture) + " Azoth" + ((Xp > 0f) ? (" (+" + Xp.ToString("0.##", CultureInfo.InvariantCulture) + " Alchemy XP)") : "") + ". " + (stopped ? ("Stopped: " + Reason) : ((Skipped > 0) ? (Skipped + " ineligible/leftover rows kept. " + Reason) : "")) + ((Warning.Length > 0) ? (" Warning: " + Warning) : "");
			}
		}

		internal AzothBatch(Func<BagModel?> read, Func<BagModel, BagModel, bool> replace, Func<string, int, AzothQuote> quote, Pay pay, string? selected = null, Action<string, int, int, BagModel>? intent = null, AzothPlan? approved = null)
		{
			this.read = read;
			this.replace = replace;
			this.quote = quote;
			this.pay = pay;
			this.intent = intent;
			this.approved = approved;
			expected = read();
			expectedRevision = expected?.Revision ?? 0;
			IEnumerable<KeyValuePair<string, int>> collection;
			if (expected != null)
			{
				collection = from i in expected.Items.Values.Where((BagItem i) => selected == null || i.Name == selected).OrderBy<BagItem, string>((BagItem i) => i.Name, StringComparer.Ordinal)
					select new KeyValuePair<string, int>(i.Name, i.Total);
			}
			else
			{
				IEnumerable<KeyValuePair<string, int>> enumerable = Array.Empty<KeyValuePair<string, int>>();
				collection = enumerable;
			}
			rows = new Queue<KeyValuePair<string, int>>(collection);
		}

		internal void Step()
		{
			if (Done || stepping)
			{
				return;
			}
			stepping = true;
			BagModel live;
			string frozen;
			BagModel staged;
			bool consumed;
			bool answered;
			try
			{
				if (expected == null || expected != read() || expected.Revision != expectedRevision)
				{
					Stop("The active bag changed. Remaining loot was kept; click again after checking it.");
					return;
				}
				if (remaining <= 0)
				{
					if (rows.Count == 0)
					{
						Done = true;
						return;
					}
					KeyValuePair<string, int> keyValuePair = rows.Dequeue();
					item = keyValuePair.Key;
					remaining = keyValuePair.Value;
					batchIndex = 0;
				}
				live = expected;
				frozen = live.Serialize();
				int num = Math.Min(remaining, live.Count(item));
				AzothQuote offered = quote(item, num);
				if (approved != null)
				{
					bool num2 = offered.Amount > 0 && offered.Azoth > 0;
					AzothPlan.Row value;
					bool flag = approved.Rows.TryGetValue(item, out value) && batchIndex < value.Batches.Count;
					if ((num2 || flag) && !approved.Matches(item, num, batchIndex, offered))
					{
						Stop("The displayed Azoth quote changed. Remaining loot was kept; check the refreshed amounts.");
						return;
					}
				}
				if (offered.Amount <= 0 || offered.Azoth <= 0)
				{
					Skipped++;
					remaining = 0;
					if (Reason.Length == 0)
					{
						Reason = offered.Reason;
					}
					return;
				}
				if (offered.Amount > num)
				{
					Stop("Alchemy returned an invalid quantity. Nothing further was used.");
					return;
				}
				if (!BagModel.TryParse(frozen, out staged) || staged.Take(item, offered.Amount) != offered.Amount)
				{
					Stop("The selected loot changed. Nothing further was used.");
					return;
				}
				staged.Revision = live.Revision + 1;
				string text = staged.Serialize();
				consumed = false;
				answered = false;
				bool accepted = false;
				int paid = 0;
				float xp = 0f;
				string message = "";
				intent?.Invoke(item, offered.Amount, offered.Azoth, live);
				paying = true;
				bool num3 = pay(item, offered.Amount, offered.Azoth, Consume, delegate(bool ok, int got, float earned, string why)
				{
					if (!answered)
					{
						answered = true;
						accepted = ok;
						paid = got;
						xp = earned;
						message = why ?? "";
					}
				});
				paying = false;
				if (!num3 || !answered || accepted != consumed || (accepted && paid != offered.Azoth))
				{
					Uncertain = true;
					Stop("Alchemy did not confirm a safe exchange. Conversion stopped; do not retry until its result is checked.");
					return;
				}
				if (message.StartsWith("UNCERTAIN:", StringComparison.Ordinal))
				{
					Uncertain = true;
					Stop(message);
					return;
				}
				if (!accepted)
				{
					Stop((message.Length > 0) ? message : "Alchemy refused the exchange. Remaining loot was kept.");
					return;
				}
				Pieces += offered.Amount;
				Azoth += paid;
				if (xp > 0f && !float.IsNaN(xp) && !float.IsInfinity(xp))
				{
					Xp += xp;
				}
				if (message.Length > 0)
				{
					Warning = message;
				}
				used.Add(item);
				remaining -= offered.Amount;
				batchIndex++;
				if (read() != staged || staged.Serialize() != text)
				{
					Stop("Your bag changed after a committed exchange. Earlier results are kept; remaining rows were not retried.");
					return;
				}
				expected = read();
				expectedRevision = expected?.Revision ?? 0;
			}
			catch (Exception ex)
			{
				Uncertain |= paying;
				Stop("Alchemy conversion failed: " + ex.Message + ". Conversion stopped; no automatic retry.");
			}
			finally
			{
				stepping = false;
			}
			bool Consume()
			{
				if (consumed || answered || live != read() || live.Serialize() != frozen)
				{
					return false;
				}
				consumed = replace(live, staged);
				return consumed;
			}
		}

		private void Stop(string why)
		{
			Reason = why;
			Done = true;
			stopped = true;
		}
	}
	internal static class BagStore
	{
		internal const string AzothIntentKey = "ComputerAutoLootbag.azoth.v1";

		private static Player? azothOperationPlayer;

		private static string azothInitial = "";

		internal const string SaveKey = "ComputerAutoLootbag.v1";

		internal const string LeftKey = "ComputerAutoLootbag.left.v1";

		internal const string InventoryFull = "Inventory full";

		private static BagModel? bag;

		private static long owner;

		private static readonly List<Gone> gone = new List<Gone>();

		private static readonly List<KeyValuePair<string, int>> gains = new List<KeyValuePair<string, int>>();

		private static readonly List<KeyValuePair<string, int>> spilled = new List<KeyValuePair<string, int>>();

		private static readonly List<KeyValuePair<string, int>> lines = new List<KeyValuePair<string, int>>();

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

		private static float nextSweep;

		private static float nextGains;

		private static float nextSpot;

		private static List<LeftBag> left = new List<LeftBag>();

		internal static int LeftRevision;

		private static long tombUser;

		private static uint tombId;

		private static int tombFrame = -100;

		internal const string CarryKey = "ComputerAutoLootbag.carry.v1";

		private static List<SealedBag> seals = new List<SealedBag>();

		private static bool carryReadable = true;

		internal static IReadOnlyList<LeftBag> Left => left;

		internal static bool Ready
		{
			get
			{
				Player localPlayer = Player.m_localPlayer;
				if (bag != null && Object.op_Implicit((Object)(object)localPlayer))
				{
					return owner == localPlayer.GetPlayerID();
				}
				return false;
			}
		}

		internal static BagModel? Bag
		{
			get
			{
				if (!Ready)
				{
					return null;
				}
				return bag;
			}
		}

		internal static IReadOnlyList<SealedBag> Seals
		{
			get
			{
				if (!Ready)
				{
					return Array.Empty<SealedBag>();
				}
				return seals;
			}
		}

		internal static bool BeginAzothOperation(Player player, string? selected, out string why)
		{
			why = "";
			if (!Ready || (Object)(object)azothOperationPlayer != (Object)null)
			{
				why = "Your lootbag is busy or not ready.";
				return false;
			}
			if (player.m_customData.ContainsKey("ComputerAutoLootbag.azoth.v1"))
			{
				why = "An earlier Azoth exchange needs inspection. Conversion is held; nothing will be retried automatically.";
				return false;
			}
			string s = bag.Serialize();
			azothInitial = "CAAZ1\nowner=" + player.GetPlayerID() + "\nworld=" + ZNet.instance.GetWorldUID() + "\nsource=active bag only\nselection=" + (selected ?? "all eligible") + "\noriginal=" + Convert.ToBase64String(Encoding.UTF8.GetBytes(s)) + "\n";
			player.m_customData["ComputerAutoLootbag.azoth.v1"] = azothInitial + "state=started\n";
			azothOperationPlayer = player;
			return true;
		}

		internal static void StageAzothIntent(string item, int amount, int value, BagModel original)
		{
			Player val = azothOperationPlayer;
			if (!Ready || !Object.op_Implicit((Object)(object)val) || (Object)(object)val != (Object)(object)Player.m_localPlayer || bag != original)
			{
				throw new InvalidOperationException("Original active bag owner changed.");
			}
			val.m_customData["ComputerAutoLootbag.azoth.v1"] = azothInitial + "state=pending\nitem=" + item + "\namount=" + amount + "\nexpectedAzoth=" + value + "\nbefore=" + Convert.ToBase64String(Encoding.UTF8.GetBytes(original.Serialize())) + "\n";
		}

		internal static bool CommitAzoth(BagModel expected, BagModel replacement)
		{
			Player val = azothOperationPlayer;
			if (!Ready || !Object.op_Implicit((Object)(object)val) || (Object)(object)val != (Object)(object)Player.m_localPlayer || bag != expected)
			{
				return false;
			}
			string text = replacement.Serialize();
			string value = val.m_customData["ComputerAutoLootbag.azoth.v1"] + "state=source committed\nafter=" + Convert.ToBase64String(Encoding.UTF8.GetBytes(text)) + "\n";
			val.m_customData["ComputerAutoLootbag.v1"] = text;
			val.m_customData["ComputerAutoLootbag.azoth.v1"] = value;
			bag = replacement;
			return true;
		}

		internal static void EndAzothOperation(Player player, bool uncertain, string result)
		{
			if ((Object)(object)player != (Object)(object)azothOperationPlayer)
			{
				return;
			}
			try
			{
				if (uncertain)
				{
					player.m_customData["ComputerAutoLootbag.azoth.v1"] = player.m_customData["ComputerAutoLootbag.azoth.v1"] + "state=UNCERTAIN\nresult=" + result.Replace('\n', ' ') + "\n";
				}
				else
				{
					player.m_customData.Remove("ComputerAutoLootbag.azoth.v1");
				}
			}
			finally
			{
				azothOperationPlayer = null;
				azothInitial = "";
			}
		}

		internal static void Loaded(Player player)
		{
			long playerID = player.GetPlayerID();
			if (playerID == 0L || (bag != null && owner == playerID))
			{
				return;
			}
			player.m_customData.TryGetValue("ComputerAutoLootbag.v1", out var value);
			if (!BagModel.TryParse(value, out BagModel model))
			{
				if (!player.m_customData.ContainsKey("ComputerAutoLootbag.v1.unreadable"))
				{
					player.m_customData["ComputerAutoLootbag.v1.unreadable"] = value ?? "";
				}
				Plugin.Log.LogError((object)"Computer Auto Lootbag: the saved loot bag of this character could not be read. It was kept under 'ComputerAutoLootbag.v1.unreadable' and a new bag was started.");
			}
			bag = model;
			owner = playerID;
			LoadCarry(player);
			LootNet.Load(player);
			OverflowStorage.LoadPending(player);
			announced.Clear();
			gains.Clear();
			spilled.Clear();
			player.m_customData.TryGetValue("ComputerAutoLootbag.left.v1", out var value2);
			if (!LeftBags.TryParse(value2, out left))
			{
				if (!player.m_customData.ContainsKey("ComputerAutoLootbag.left.v1.unreadable"))
				{
					player.m_customData["ComputerAutoLootbag.left.v1.unreadable"] = value2 ?? "";
				}
				Plugin.Log.LogError((object)"Computer Auto Lootbag: the bags this character left at its bodies could not be read. The text was kept under 'ComputerAutoLootbag.left.v1.unreadable'.");
			}
			LeftRevision++;
			if (left.Count > 0)
			{
				int num = 0;
				foreach (LeftBag item in left)
				{
					num += item.Bag.Pieces;
				}
				Plugin.Diag("left loaded", ("bags", left.Count), ("pieces", num));
			}
			int num2 = 0;
			foreach (BagItem value3 in model.Items.Values)
			{
				num2 += value3.Total;
			}
			Plugin.Diag("loaded", ("kinds", model.Items.Count), ("pieces", num2), ("playedMin", (int)(model.Clock / 60.0)));
		}

		internal static void Saving(Player player)
		{
			if (bag != null && !((Object)(object)player != (Object)(object)Player.m_localPlayer) && owner == player.GetPlayerID())
			{
				player.m_customData["ComputerAutoLootbag.v1"] = bag.Serialize();
				SaveCarry(player);
				LootNet.Save(player);
				OverflowStorage.SavePending(player);
				if (left.Count > 0)
				{
					player.m_customData["ComputerAutoLootbag.left.v1"] = LeftBags.Serialize(left);
				}
				else
				{
					player.m_customData.Remove("ComputerAutoLootbag.left.v1");
				}
			}
		}

		internal static void TombstoneMade(TombStone stone, long ownerId)
		{
			//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)
			ZNetView val = default(ZNetView);
			if (bag != null && ownerId == owner && ((Component)stone).TryGetComponent<ZNetView>(ref val) && val.IsValid())
			{
				ZDOID uid = val.GetZDO().m_uid;
				tombUser = ((ZDOID)(ref uid)).UserID;
				tombId = ((ZDOID)(ref uid)).ID;
				tombFrame = Time.frameCount;
			}
		}

		internal static void Died(Player player)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			if (Plugin.StayAtBody.Value && bag != null && !((Object)(object)player != (Object)(object)Player.m_localPlayer) && owner == player.GetPlayerID() && bag.Items.Count != 0)
			{
				ZNet instance = ZNet.instance;
				Vector3 position = ((Component)player).transform.position;
				LeftBag leftBag = new LeftBag
				{
					World = (Object.op_Implicit((Object)(object)instance) ? instance.GetWorldUID() : 0),
					WorldName = (Object.op_Implicit((Object)(object)instance) ? instance.GetWorldName() : ""),
					X = position.x,
					Y = position.y,
					Z = position.z,
					Bag = bag
				};
				if (Time.frameCount - tombFrame <= 1)
				{
					leftBag.TombUser = tombUser;
					leftBag.TombId = tombId;
				}
				tombFrame = -100;
				left.Add(leftBag);
				bag = new BagModel
				{
					Clock = leftBag.Bag.Clock
				};
				announced.Clear();
				gains.Clear();
				spilled.Clear();
				LeftRevision++;
				int pieces = leftBag.Bag.Pieces;
				Plugin.Diag("left", ("pieces", pieces), ("kinds", leftBag.Bag.Items.Count), ("tombstone", leftBag.HasTomb), ("at", ((Vector3)(ref position)).ToString("F0")), ("bagsLeft", left.Count));
				Feed.Post("Your loot bag stays at your body: " + LootRules.Number(pieces) + ((pieces == 1) ? " piece" : " pieces"));
			}
		}

		private static void Recover(int index, string by)
		{
			if (Ready && carryReadable && index >= 0 && index < left.Count)
			{
				LeftBag leftBag = left[index];
				SealRules.CollectDeath(bag, leftBag, Mathf.Clamp(Plugin.MaxPerItem.Value, 1, 100000), out BagModel restored, out LeftBag sealedBag);
				List<LeftBag> list = new List<LeftBag>(left);
				list.RemoveAt(index);
				List<SealedBag> list2 = new List<SealedBag>(seals);
				if (sealedBag != null)
				{
					list2.Add(new SealedBag
					{
						Token = Guid.NewGuid().ToString("N"),
						Owner = owner,
						Carrier = owner,
						OwnerName = Player.m_localPlayer.GetPlayerName(),
						CarrierName = Player.m_localPlayer.GetPlayerName(),
						Escrow = true,
						Record = sealedBag
					});
				}
				CommitCarry(restored, list, list2);
				Plugin.Diag("recovered", ("pieces", leftBag.Bag.Pieces), ("by", by), ("sealed", sealedBag != null), ("bagsLeft", left.Count));
				Feed.Post((sealedBag == null) ? "Your Computer Lootbag was recovered." : "Computer Lootbag collected: active bag full. Open J and use Unseal after making room; timers stay paused.");
			}
		}

		private static void CheckSpots(Player player)
		{
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			if (Time.unscaledTime < nextSpot)
			{
				return;
			}
			nextSpot = Time.unscaledTime + 0.5f;
			if (left.Count == 0 || ((Character)player).IsDead() || CarryNet.OpenCorpse(out Container _, out TombStone _))
			{
				return;
			}
			ZNet instance = ZNet.instance;
			long world = (Object.op_Implicit((Object)(object)instance) ? instance.GetWorldUID() : 0);
			Vector3 position = ((Component)player).transform.position;
			for (int i = 0; i < left.Count; i++)
			{
				if (LeftBags.AtSpot(left[i], world, position.x, position.y, position.z) && !OwnTombstoneNear(left[i]))
				{
					Recover(i, "spot");
					break;
				}
			}
		}

		private static bool OwnTombstoneNear(LeftBag record)
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			float num = 64f;
			TombStone[] array = Object.FindObjectsByType<TombStone>((FindObjectsSortMode)0);
			ZNetView val2 = default(ZNetView);
			foreach (TombStone val in array)
			{
				if (Object.op_Implicit((Object)(object)val) && ((Component)val).TryGetComponent<ZNetView>(ref val2) && val2.IsValid() && val2.GetZDO().GetLong(ZDOVars.s_owner, 0L) == owner)
				{
					Vector3 position = ((Component)val).transform.position;
					float num2 = position.x - record.X;
					float num3 = position.z - record.Z;
					if (num2 * num2 + num3 * num3 <= num)
					{
						return true;
					}
				}
			}
			return false;
		}

		internal static void Receive(List<KeyValuePair<string, int>> items, Vector3 at, LootSource source)
		{
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			bool flag = Ready && Plugin.UseBag.Value;
			int cap = Mathf.Clamp(Plugin.MaxPerItem.Value, 1, 100000);
			int value = Plugin.Minutes.Value;
			foreach (KeyValuePair<string, int> item in items)
			{
				int num = (flag ? bag.Add(item.Key, item.Value, cap, value) : 0);
				if (num > 0)
				{
					Count(gains, item.Key, num);
				}
				int num2 = item.Value - num;
				if (num2 > 0)
				{
					Ground.Spawn(item.Key, num2, at);
					if (flag)
					{
						Count(spilled, item.Key, num2);
					}
				}
			}
		}

		private static void Count(List<KeyValuePair<string, int>> list, string name, int amount)
		{
			for (int i = 0; i < list.Count; i++)
			{
				if (list[i].Key == name)
				{
					list[i] = new KeyValuePair<string, int>(name, list[i].Value + amount);
					return;
				}
			}
			list.Add(new KeyValuePair<string, int>(name, amount));
		}

		internal static int TakeOut(string name, int want, out string note)
		{
			int num = (Ready ? bag.Count(name) : 0);
			int num2 = TakeOutCore(name, want, out note);
			Plugin.Diag("take", ("item", name), ("asked", want), ("inBag", num), ("moved", num2), ("left", Ready ? bag.Count(name) : 0), ("result", (note.Length != 0) ? ((num2 > 0) ? ("partial: " + note) : ("refused: " + note)) : ((num2 > 0) ? "ok" : "nothing")));
			return num2;
		}

		private static int TakeOutCore(string name, int want, out string note)
		{
			note = "";
			Player localPlayer = Player.m_localPlayer;
			if (!Ready || !Object.op_Implicit((Object)(object)localPlayer) || want <= 0)
			{
				return 0;
			}
			ItemDrop val = RareItems.Find(name);
			SharedData val2 = ((!Object.op_Implicit((Object)(object)val)) ? null : val.m_itemData?.m_shared);
			if (val2 == null)
			{
				note = "This item's mod is not installed. It stays in the bag.";
				return 0;
			}
			Inventory inventory = ((Humanoid)localPlayer).GetInventory();
			int num = Math.Max(1, val2.m_maxStackSize);
			long room = inventory.FindFreeStackSpace(val2.m_name, (float)Game.m_worldLevel) + (long)inventory.GetEmptySlots() * (long)num;
			int num2 = Math.Min(want, bag.Count(name));
			int num3 = LootRules.Fit(want, bag.Count(name), room);
			if (num3 <= 0)
			{
				note = "Inventory full";
				return 0;
			}
			int num4 = inventory.CountItems(val2.m_name, -1, true);
			int num5 = num3;
			while (num5 > 0)
			{
				int num6 = Math.Min(num5, num);
				if (!inventory.AddItem(((Component)val).gameObject, num6))
				{
					break;
				}
				num5 -= num6;
			}
			int num7 = Mathf.Clamp(inventory.CountItems(val2.m_name, -1, true) - num4, 0, num3);
			bag.Take(name, num7);
			if (num7 < num2)
			{
				note = "Inventory full";
			}
			return num7;
		}

		internal static void SetUse(bool on, string by)
		{
			if (Plugin.UseBag.Value != on)
			{
				Plugin.UseBag.Value = on;
				LootNet.FlagNow();
				Plugin.Diag("use", ("state", on ? "on" : "off"), ("by", by));
				Feed.Post(on ? "Loot bag on: loot goes into your bag" : "Loot bag off: your share of new loot drops on the ground");
			}
		}

		internal static int Count(string name)
		{
			if (!Ready)
			{
				return 0;
			}
			return bag.Count(name);
		}

		internal static int Delete(string name)
		{
			if (!Ready)
			{
				return 0;
			}
			int num = bag.Take(name, bag.Count(name));
			if (num <= 0)
			{
				return 0;
			}
			announced.Remove(name);
			Plugin.Diag("deleted", ("item", name), ("pieces", num));
			return num;
		}

		internal static void Tick(float delta)
		{
			LootNet.Register();
			Ground.Drain();
			LootNet.Tick();
			Player localPlayer = Player.m_localPlayer;
			if (!Object.op_Implicit((Object)(object)localPlayer) || bag == null || owner != localPlayer.GetPlayerID())
			{
				return;
			}
			LootNet.PublishFlag(localPlayer);
			if (!Game.IsPaused())
			{
				bag.Clock += Mathf.Min(delta, 1f);
			}
			if (Time.unscaledTime >= nextGains)
			{
				nextGains = Time.unscaledTime + 1.5f;
				if (gains.Count > 0)
				{
					if (Plugin.ShowGains.Value)
					{
						Feed.Post(Feed.List(gains, "+"));
					}
					gains.Clear();
				}
				if (spilled.Count > 0)
				{
					string text = Feed.List(spilled);
					Feed.Post("Bag full: " + text + " fell on the ground");
					Plugin.Diag("bag full", ("onGround", text));
					spilled.Clear();
				}
			}
			CheckSpots(localPlayer);
			LeftPins.Sync(left, LeftRevision);
			if (Time.unscaledTime < nextSweep)
			{
				return;
			}
			nextSweep = Time.unscaledTime + 1f;
			if (!Plugin.Expires.Value)
			{
				return;
			}
			gone.Clear();
			bag.Expire(RareItems.NeverExpires, gone);
			if (gone.Count <= 0)
			{
				return;
			}
			lines.Clear();
			int num = 0;
			foreach (Gone item in gone)
			{
				lines.Add(new KeyValuePair<string, int>(item.Name, item.Amount));
				num += item.Amount;
				announced.Remove(item.Name);
			}
			Feed.Post("Expired: " + Feed.List(lines));
			Plugin.Diag("expired", ("kinds", gone.Count), ("pieces", num));
		}

		private static void LoadCarry(Player player)
		{
			player.m_customData.TryGetValue("ComputerAutoLootbag.carry.v1", out var value);
			carryReadable = SealRules.TryParse(value, out List<SealedBag> entries);
			seals = (carryReadable ? entries : new List<SealedBag>());
			if (!carryReadable)
			{
				if (!player.m_customData.ContainsKey("ComputerAutoLootbag.carry.v1.unreadable"))
				{
					player.m_customData["ComputerAutoLootbag.carry.v1.unreadable"] = value ?? "";
				}
				Plugin.Log.LogError((object)"Computer Auto Lootbag: sealed bag data is unreadable; retained unchanged. Carrier transfers are disabled for this character.");
			}
		}

		private static void SaveCarry(Player player)
		{
			if (carryReadable)
			{
				player.m_customData["ComputerAutoLootbag.carry.v1"] = SealRules.Serialize(seals);
			}
		}

		private static void CommitCarry(BagModel active, List<LeftBag> remaining, List<SealedBag> entries)
		{
			Player localPlayer = Player.m_localPlayer;
			string value = active.Serialize();
			string value2 = LeftBags.Serialize(remaining);
			string value3 = SealRules.Serialize(entries);
			Dictionary<string, string> dictionary = new Dictionary<string, string>(localPlayer.m_customData);
			try
			{
				localPlayer.m_customData["ComputerAutoLootbag.v1"] = value;
				localPlayer.m_customData["ComputerAutoLootbag.left.v1"] = value2;
				localPlayer.m_customData["ComputerAutoLootbag.carry.v1"] = value3;
			}
			catch
			{
				string[] array = new string[3] { "ComputerAutoLootbag.v1", "ComputerAutoLootbag.left.v1", "ComputerAutoLootbag.carry.v1" };
				foreach (string key in array)
				{
					if (dictionary.TryGetValue(key, out var value4))
					{
						localPlayer.m_customData[key] = value4;
					}
					else
					{
						localPlayer.m_customData.Remove(key);
					}
				}
				throw;
			}
			bag = active;
			left = remaining;
			seals = entries;
			LeftRevision++;
		}

		internal static bool SealCorpse(long world, ZDOID tomb, long carrier, string carrierName, out string receipt, out string note)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			receipt = "";
			note = "No Computer Lootbag remains at this corpse.";
			if (!Ready || !carryReadable)
			{
				note = "Owner's sealed bag storage is unavailable.";
				return false;
			}
			SealedBag sealedBag = seals.FirstOrDefault((SealedBag e) => e.Escrow && SealRules.SameCorpse(e.Record, world, ((ZDOID)(ref tomb)).UserID, ((ZDOID)(ref tomb)).ID));
			if (sealedBag != null)
			{
				if (sealedBag.Carrier != carrier)
				{
					note = "This loot bag is already carried by " + sealedBag.CarrierName + ".";
					return false;
				}
				receipt = Receipt(sealedBag);
				note = "Sealed bag already assigned to you.";
				return true;
			}
			int num = left.FindIndex((LeftBag r) => SealRules.SameCorpse(r, world, ((ZDOID)(ref tomb)).UserID, ((ZDOID)(ref tomb)).ID));
			if (num < 0)
			{
				return false;
			}
			LeftBag leftBag = left[num];
			List<LeftBag> list = new List<LeftBag>(left);
			list.RemoveAt(num);
			if (carrier == owner)
			{
				if (!SealRules.TryRestore(bag, leftBag.Bag, Mathf.Clamp(Plugin.MaxPerItem.Value, 1, 100000), out BagModel restored))
				{
					note = "Loot bag full. Make room; all corpse loot stays sealed.";
					Plugin.Diag("corpse button", ("owner", owner), ("carrier", carrier), ("pieces", leftBag.Bag.Pieces), ("result", "bag full, stays sealed"));
					return false;
				}
				CommitCarry(restored, list, new List<SealedBag>(seals));
				note = "Your Computer Lootbag was recovered.";
			}
			else
			{
				SealedBag sealedBag2 = new SealedBag
				{
					Token = Guid.NewGuid().ToString("N"),
					Owner = owner,
					OwnerName = Player.m_localPlayer.GetPlayerName(),
					Carrier = carrier,
					CarrierName = carrierName,
					Escrow = true,
					Record = leftBag
				};
				List<SealedBag> entries = new List<SealedBag>(seals) { sealedBag2 };
				CommitCarry(bag, list, entries);
				receipt = Receipt(sealedBag2);
				note = "Carrying " + sealedBag2.OwnerName + "'s sealed Computer Lootbag.";
				Feed.Post(carrierName + " is carrying your sealed Computer Lootbag.");
			}
			Plugin.Diag("corpse button", ("owner", owner), ("carrier", carrier), ("pieces", leftBag.Bag.Pieces), ("result", (carrier == owner) ? "restored to own bag" : "sealed, carried by friend"));
			return true;
		}

		private static string Receipt(SealedBag source)
		{
			return SealRules.Serialize(new SealedBag[1]
			{
				new SealedBag
				{
					Token = source.Token,
					Owner = source.Owner,
					OwnerName = source.OwnerName,
					Carrier = source.Carrier,
					CarrierName = source.CarrierName,
					Record = source.Record,
					Escrow = false
				}
			});
		}

		internal static bool AcceptReceipt(string receipt, long expectedOwner)
		{
			if (!Ready || !carryReadable || !SealRules.TryParse(receipt, out List<SealedBag> entries) || entries.Count != 1)
			{
				return false;
			}
			SealedBag e = entries[0];
			if (e.Escrow || e.Owner != expectedOwner || e.Carrier != owner)
			{
				return false;
			}
			if (seals.Any((SealedBag s) => s.Token == e.Token))
			{
				return true;
			}
			List<SealedBag> entries2 = new List<SealedBag>(seals) { e };
			CommitCarry(bag, new List<LeftBag>(left), entries2);
			return true;
		}

		internal static int ReturnSeal(string token, long carrier, out string note)
		{
			note = "This sealed bag is no longer assigned to you (already returned or recalled).";
			if (!Ready || !carryReadable || ((Character)Player.m_localPlayer).IsDead())
			{
				note = "Owner must be alive and have readable bag storage.";
				return 0;
			}
			SealedBag sealedBag = seals.FirstOrDefault((SealedBag e) => e.Escrow && e.Token == token && e.Owner == owner && e.Carrier == carrier);
			if (sealedBag == null)
			{
				return 2;
			}
			if (!SealRules.TryRestore(bag, sealedBag.Record.Bag, Mathf.Clamp(Plugin.MaxPerItem.Value, 1, 100000), out BagModel restored))
			{
				note = ((sealedBag.Carrier == owner) ? "Your active bag is full. Make room; this recovered bag stays sealed with paused timers." : "Owner's loot bag is full. Sealed contents remain with the carrier.");
				return 0;
			}
			List<SealedBag> list = new List<SealedBag>(seals);
			list.Remove(sealedBag);
			CommitCarry(restored, new List<LeftBag>(left), list);
			note = "Sealed Computer Lootbag returned to " + sealedBag.OwnerName + ".";
			Feed.Post("Your sealed Computer Lootbag was returned.");
			Plugin.Diag("sealed returned", ("token", token), ("carrier", carrier), ("pieces", sealedBag.Record.Bag.Pieces));
			return 1;
		}

		internal static void ForgetReceipt(string token)
		{
			if (Ready && carryReadable)
			{
				List<SealedBag> entries = seals.Where((SealedBag e) => e.Escrow || e.Token != token).ToList();
				CommitCarry(bag, new List<LeftBag>(left), entries);
			}
		}

		internal static void RecallSeal(SealedBag entry)
		{
			if (entry.Escrow && Ready && entry.Owner == owner)
			{
				string note;
				int num = ReturnSeal(entry.Token, entry.Carrier, out note);
				Plugin.Diag("recall", ("carrier", entry.Carrier), ("pieces", entry.Record.Bag.Pieces), ("result", num switch
				{
					2 => "already returned", 
					1 => "ok", 
					_ => "refused: " + note, 
				}));
				Feed.Post(note);
			}
		}

		internal static void RecoverOwnCorpse(Container? container)
		{
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			TombStone val = default(TombStone);
			ZNetView val2 = default(ZNetView);
			if (!Ready || !carryReadable || !Object.op_Implicit((Object)(object)localPlayer) || ((Character)localPlayer).IsDead() || !Object.op_Implicit((Object)(object)container) || !((Component)container).TryGetComponent<TombStone>(ref val) || !((Component)container).TryGetComponent<ZNetView>(ref val2) || !val2.IsValid() || !val2.IsOwner() || !CarryNet.CanAccessCorpse(container, localPlayer))
			{
				return;
			}
			ZDO zDO = val2.GetZDO();
			long num = zDO.GetLong(ZDOVars.s_owner, 0L);
			ZDOID id = zDO.m_uid;
			if (!SealRules.TryRecoverOwn(bag, left, owner, num, ZNet.instance.GetWorldUID(), ((ZDOID)(ref id)).UserID, ((ZDOID)(ref id)).ID, Mathf.Clamp(Plugin.MaxPerItem.Value, 1, 100000), out BagModel restored, out List<LeftBag> remaining))
			{
				if (num == owner)
				{
					int num2 = left.FindIndex((LeftBag r) => SealRules.SameCorpse(r, ZNet.instance.GetWorldUID(), ((ZDOID)(ref id)).UserID, ((ZDOID)(ref id)).ID));
					if (num2 >= 0)
					{
						Recover(num2, "own native corpse (sealed)");
					}
				}
			}
			else
			{
				int num3 = restored.Pieces - bag.Pieces;
				CommitCarry(restored, remaining, new List<SealedBag>(seals));
				Feed.Post("Your Computer Lootbag was recovered.");
				Plugin.Diag("recovered", ("pieces", num3), ("by", "own native corpse"), ("bagsLeft", left.Count));
			}
		}
	}
	internal static class AzothLink
	{
		internal const string ApiType = "Computer.Alchemy.AzothApi";

		internal const string Reason = "Lootbag:TurnToAzoth";

		internal const int Contract = 4;

		private static bool searched;

		private static Func<bool>? available;

		private static Func<string, int, int>? amountFor;

		private static Func<string, int, int>? valueOf;

		private static Action<Player, string, int, int, string, Func<bool>, Action<bool, int, float, string>>? convert;

		private static string quotedName = "";

		private static int quotedHeld = -1;

		private static int quotedAmount;

		private static int quotedAzoth;

		private static float quotedUntil;

		internal static string Status { get; private set; } = "Computer Alchemy is not loaded; Azoth conversion is unavailable.";

		internal static bool Available
		{
			get
			{
				Find();
				if (available == null)
				{
					return false;
				}
				try
				{
					bool num = available();
					Status = (num ? "" : "Azoth conversion is off on this server or Alchemy isn't ready yet.");
					return num;
				}
				catch (Exception error)
				{
					Status = "Alchemy availability check failed. Your loot is unchanged; try again.";
					Faults.Report("AzothLink.available", error);
					return false;
				}
			}
		}

		internal static bool CheckedAvailable
		{
			get
			{
				if (Available)
				{
					return convert != null;
				}
				return false;
			}
		}

		private static void Find()
		{
			if (searched)
			{
				return;
			}
			searched = true;
			try
			{
				Type type = null;
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				for (int i = 0; i < assemblies.Length; i++)
				{
					if ((type = assemblies[i].GetType("Computer.Alchemy.AzothApi", throwOnError: false)) != null)
					{
						break;
					}
				}
				if (type == null)
				{
					return;
				}
				if (!(type.GetField("ContractVersion", BindingFlags.Static | BindingFlags.Public)?.GetValue(null) is int num) || num < 4)
				{
					Status = "Update Computer Alchemy to 0.0.55 or newer for safe lootbag conversion.";
					return;
				}
				MethodInfo methodInfo = type.GetProperty("ConvertAvailable", BindingFlags.Static | BindingFlags.Public)?.GetGetMethod();
				MethodInfo method = type.GetMethod("ConvertAmount", BindingFlags.Static | BindingFlags.Public, null, new Type[2]
				{
					typeof(string),
					typeof(int)
				}, null);
				MethodInfo method2 = type.GetMethod("AzothValue", BindingFlags.Static | BindingFlags.Public, null, new Type[2]
				{
					typeof(string),
					typeof(int)
				}, null);
				MethodInfo method3 = type.GetMethod("BeginConvertChecked", BindingFlags.Static | BindingFlags.Public, null, new Type[7]
				{
					typeof(Player),
					typeof(string),
					typeof(int),
					typeof(int),
					typeof(string),
					typeof(Func<bool>),
					typeof(Action<bool, int, float, string>)
				}, null);
				if (methodInfo?.ReturnType != typeof(bool) || method?.ReturnType != typeof(int) || method2?.ReturnType != typeof(int) || method3?.ReturnType != typeof(void))
				{
					Status = "Computer Alchemy checked conversion API is incompatible; your loot is unchanged.";
					return;
				}
				amountFor = (Func<string, int, int>)Delegate.CreateDelegate(typeof(Func<string, int, int>), method);
				valueOf = (Func<string, int, int>)Delegate.CreateDelegate(typeof(Func<string, int, int>), method2);
				convert = (Action<Player, string, int, int, string, Func<bool>, Action<bool, int, float, string>>)Delegate.CreateDelegate(typeof(Action<Player, string, int, int, string, Func<bool>, Action<bool, int, float, string>>), method3);
				available = (Func<bool>)Delegate.CreateDelegate(typeof(Func<bool>), methodInfo);
				Plugin.Diag("azothLink", ("contract", num), ("result", "checked v4 bound"));
			}
			catch (Exception error)
			{
				Status = "Could not bind Computer Alchemy. Your loot is unchanged.";
				Faults.Report("AzothLink.find", error);
			}
		}

		internal static void Forget()
		{
			quotedHeld = -1;
		}

		internal static void Quote(string name, int held, out int amount, out int azoth)
		{
			amount = (azoth = 0);
			if (!Available || amountFor == null || valueOf == null || held <= 0)
			{
				return;
			}
			if (name == quotedName && held == quotedHeld && Time.unscaledTime < quotedUntil)
			{
				amount = quotedAmount;
				azoth = quotedAzoth;
				return;
			}
			try
			{
				amount = Math.Max(0, Math.Min(held, amountFor(name, held)));
				azoth = ((amount > 0) ? Math.Max(0, valueOf(name, amount)) : 0);
				if (azoth <= 0)
				{
					amount = 0;
				}
				Status = ((amount > 0) ? "" : "Alchemy won't accept this item or quantity under its current rates and whole-batch limits.");
			}
			catch (Exception error)
			{
				amount = (azoth = 0);
				Status = "Alchemy could not quote this item. Your loot is unchanged; try again.";
				Faults.Report("AzothLink.quote", error);
			}
			quotedName = name;
			quotedHeld = held;
			quotedAmount = amount;
			quotedAzoth = azoth;
			quotedUntil = Time.unscaledTime + 0.5f;
		}

		internal static bool ConvertChecked(Player player, string name, int amount, int expectedAzoth, Func<bool> consume, Action<bool, int, float, string> done)
		{
			Forget();
			if (!CheckedAvailable)
			{
				done(arg1: false, 0, 0f, Status);
				return true;
			}
			convert(player, name, amount, expectedAzoth, "Lootbag:TurnToAzoth", consume, done);
			return true;
		}
	}
	internal sealed class AzothPlan
	{
		internal sealed class Row
		{
			internal readonly int Held;

			internal int Pieces;

			internal long Azoth;

			internal readonly List<AzothQuote> Batches = new List<AzothQuote>();

			internal readonly List<int> HeldBefore = new List<int>();

			internal Row(int held)
			{
				Held = held;
			}
		}

		internal readonly BagModel Source;

		internal readonly int Revision;

		internal readonly Dictionary<string, Row> Rows = new Dictionary<string, Row>(StringComparer.Ordinal);

		private readonly Queue<string> pending;

		private readonly Func<string, int, AzothQuote> quote;

		private string item = "";

		private int remaining;

		internal bool Done { get; private set; }

		internal bool Valid { get; private set; } = true;

		internal long Total { get; private set; }

		internal AzothPlan(BagModel source, Func<string, int, AzothQuote> quote)
		{
			Source = source;
			Revision = source.Revision;
			this.quote = quote;
			foreach (KeyValuePair<string, BagItem> item in source.Items)
			{
				Rows.Add(item.Key, new Row(item.Value.Total));
			}
			pending = new Queue<string>(Rows.Keys.OrderBy<string, string>((string n) => n, StringComparer.Ordinal));
		}

		internal bool Current(BagModel? source)
		{
			if (Valid && source != null && Source == source)
			{
				return source.Revision == Revision;
			}
			return false;
		}

		internal void Step()
		{
			if (Done)
			{
				return;
			}
			if (!Current(Source))
			{
				Valid = false;
				Done = true;
				return;
			}
			if (remaining <= 0)
			{
				if (pending.Count == 0)
				{
					Done = true;
					return;
				}
				item = pending.Dequeue();
				remaining = Rows[item].Held;
			}
			try
			{
				AzothQuote azothQuote = quote(item, remaining);
				if (azothQuote.Amount <= 0 || azothQuote.Azoth <= 0)
				{
					remaining = 0;
					return;
				}
				if (azothQuote.Amount > remaining)
				{
					Valid = false;
					Done = true;
					return;
				}
				Row row = Rows[item];
				row.HeldBefore.Add(remaining);
				row.Batches.Add(azothQuote);
				row.Pieces += azothQuote.Amount;
				row.Azoth += azothQuote.Azoth;
				Total += azothQuote.Azoth;
				remaining -= azothQuote.Amount;
			}
			catch
			{
				Valid = false;
				Done = true;
			}
		}

		internal bool Matches(string name, int held, int batch, AzothQuote offered)
		{
			if (!Done || !Valid || !Rows.TryGetValue(name, out Row value) || batch < 0 || batch >= value.Batches.Count)
			{
				return false;
			}
			AzothQuote azothQuote = value.Batches[batch];
			if (value.HeldBefore[batch] == held && azothQuote.Amount == offered.Amount)
			{
				return azothQuote.Azoth == offered.Azoth;
			}
			return false;
		}
	}
	internal static class BagButtonsUi
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static UnityAction <>9__22_0;

			public static UnityAction <>9__22_1;

			internal void <Build>b__22_0()
			{
				bagClicked = true;
			}

			internal void <Build>b__22_1()
			{
				switchClicked = true;
			}
		}

		private static RectTransform? root;

		private static Image? bagEdge;

		private static Image? bagFill;

		private static Image? useEdge;

		private static Image? useFill;

		private static Image? track;

		private static Image? knob;

		private static RawImage? emblem;

		private static TMP_Text? label;

		private static TMP_Text? badgeText;

		private static Sprite? round;

		private static bool bagClicked;

		private static bool switchClicked;

		private static bool splitWasOpen;

		private static float nextSplitLog;

		internal static bool SplitOpen
		{
			get
			{
				InventoryGui instance = InventoryGui.instance;
				if (instance != null && Object.op_Implicit((Object)(object)instance) && Object.op_Implicit((Object)(object)instance.m_splitDialog))
				{
					return instance.m_splitDialog.IsActive;
				}
				return false;
			}
		}

		internal static void Hide()
		{
			bagClicked = (switchClicked = (splitWasOpen = false));
			if (Object.op_Implicit((Object)(object)root) && ((Component)root).gameObject.activeSelf)
			{
				((Component)root).gameObject.SetActive(false);
			}
		}

		internal static bool TakeBagClick()
		{
			bool result = bagClicked;
			bagClicked = false;
			return result;
		}

		internal static bool TakeSwitchClick()
		{
			bool result = switchClicked;
			switchClicked = false;
			return result;
		}

		internal static void Place(Rect bag, Rect use, float unit, bool highlight, bool on, string badge, bool urgent)
		{
			//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ea: 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_0214: Unknown result type (might be due to invalid IL or missing references)
			//IL_0243: Unknown result type (might be due to invalid IL or missing references)
			//IL_025d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0262: Unknown result type (might be due to invalid IL or missing references)
			//IL_0263: Unknown result type (might be due to invalid IL or missing references)
			//IL_026a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0280: Unknown result type (might be due to invalid IL or missing references)
			//IL_028f: Unknown result type (might be due to invalid IL or missing references)
			//IL_029b: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0323: Unknown result type (might be due to invalid IL or missing references)
			//IL_0328: Unknown result type (might be due to invalid IL or missing references)
			//IL_032d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0339: Unknown result type (might be due to invalid IL or missing references)
			//IL_034e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0363: Unknown result type (might be due to invalid IL or missing references)
			//IL_0379: Unknown result type (might be due to invalid IL or missing references)
			//IL_0400: Unknown result type (might be due to invalid IL or missing references)
			//IL_042f: Unknown result type (might be due to invalid IL or missing references)
			//IL_047f: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_04bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0501: Unknown result type (might be due to invalid IL or missing references)
			//IL_053f: Unknown result type (might be due to invalid IL or missing references)
			//IL_055b: Unknown result type (might be due to invalid IL or missing references)
			//IL_05ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_0590: Unknown result type (might be due to invalid IL or missing references)
			//IL_05b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_05cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_05fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_05f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0603: Unknown result type (might be due to invalid IL or missing references)
			//IL_062d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0692: Unknown result type (might be due to invalid IL or missing references)
			InventoryGui instance = InventoryGui.instance;
			RectTransform val = (RectTransform)(Object.op_Implicit((Object)(object)instance) ? (/*isinst with value type is only supported in some contexts*/ ?? /*isinst with value type is only supported in some contexts*/) : null);
			if (!Object.op_Implicit((Object)(object)val))
			{
				Hide();
				return;
			}
			if (!Object.op_Implicit((Object)(object)root))
			{
				Build(val);
			}
			else if ((Object)(object)((Transform)root).parent != (Object)(object)val)
			{
				((Transform)root).SetParent((Transform)(object)val, false);
			}
			SplitDialog splitDialog = instance.m_splitDialog;
			Transform val2 = (Object.op_Implicit((Object)(object)splitDialog) ? ((Component)splitDialog).transform : null);
			while (Object.op_Implicit((Object)(object)val2) && (Object)(object)val2.parent != (Object)(object)val)
			{
				val2 = val2.parent;
			}
			bool flag = Object.op_Implicit((Object)(object)val2);
			if (flag && ((Transform)root).GetSiblingIndex() > val2.GetSiblingIndex())
			{
				((Transform)root).SetSiblingIndex(val2.GetSiblingIndex());
			}
			bool splitOpen = SplitOpen;
			if (splitOpen && !splitWasOpen && Time.unscaledTime >= nextSplitLog)
			{
				nextSplitLog = Time.unscaledTime + 1f;
				Plugin.Diag("inventory footer", ("key", "split"), ("result", flag ? "behind dialog" : "hidden for dialog"));
			}
			splitWasOpen = splitOpen;
			if (splitOpen && !flag)
			{
				((Component)root).gameObject.SetActive(false);
				return;
			}
			Image? obj = bagEdge;
			bool raycastTarget = (((Graphic)useEdge).raycastTarget = !splitOpen);
			((Graphic)obj).raycastTarget = raycastTarget;
			Canvas componentInParent = ((Component)val).GetComponentInParent<Canvas>();
			Camera val3 = ((Object.op_Implicit((Object)(object)componentInParent) && (int)componentInParent.renderMode != 0) ? componentInParent.worldCamera : null);
			Rect area = Rect.MinMaxRect(((Rect)(ref bag)).x, ((Rect)(ref bag)).y, ((Rect)(ref use)).xMax, Mathf.Max(((Rect)(ref bag)).yMax, ((Rect)(ref use)).yMax));
			Vector2 val4 = default(Vector2);
			RectTransformUtility.ScreenPointToLocalPointInRectangle(val, new Vector2(((Rect)(ref area)).x, (float)Screen.height - ((Rect)(ref area)).y), val3, ref val4);
			Vector2 val5 = default(Vector2);
			RectTransformUtility.ScreenPointToLocalPointInRectangle(val, new Vector2(((Rect)(ref area)).xMax, (float)Screen.height - ((Rect)(ref area)).yMax), val3, ref val5);
			RectTransform? obj2 = root;
			Vector2 anchorMin = (root.anchorMax = val.pivot);
			obj2.anchorMin = anchorMin;
			root.pivot = new Vector2(0f, 1f);
			root.anchoredPosition = val4;
			root.sizeDelta = new Vector2(val5.x - val4.x, val4.y - val5.y);
			float k = ((((Rect)(ref area)).height > 0f) ? (root.sizeDelta.y / ((Rect)(ref area)).height) : 1f);
			float refPpu = (Object.op_Implicit((Object)(object)componentInParent) ? componentInParent.rootCanvas.referencePixelsPerUnit : 100f);
			Vector2 val6 = Vector2.op_Implicit(ZInput.pointerPosition);
			bool flag3 = RectTransformUtility.RectangleContainsScreenPoint(((Graphic)bagEdge).rectTransform, val6, val3);
			bool flag4 = RectTransformUtility.RectangleContainsScreenPoint(((Graphic)useEdge).rectTransform, val6, val3);
			Body(bagEdge, bagFill, bag, flag3 || highlight);
			Body(useEdge, useFill, use, flag4);
			float num = ((Rect)(ref bag)).height / unit;
			float num2 = num - 6f;
			Texture2D val7 = BagGui.Emblem;
			emblem.texture = (Texture)(object)val7;
			((Behaviour)emblem).enabled = Object.op_Implicit((Object)(object)val7);
			Put((Graphic)(object)emblem, new Rect(((Rect)(ref bag)).x + 8f * unit, ((Rect)(ref bag)).y + 3f * unit, num2 * unit, num2 * unit));
			label.fontSize = (float)BagGui.QolLine.Font * unit * k;
			((Graphic)label).color = BagGui.Text;
			Put((Graphic)(object)label, new Rect(((Rect)(ref bag)).x + (Object.op_Implicit((Object)(object)val7) ? (num2 + 14f) : 12f) * unit, ((Rect)(ref bag)).y, 100f * unit, ((Rect)(ref bag)).height));
			badgeText.text = badge;
			badgeText.fontSize = 14f * unit * k;
			((Graphic)badgeText).color = (urgent ? BagGui.Warn : BagGui.Muted);
			Put((Graphic)(object)badgeText, new Rect(((Rect)(ref bag)).xMax - 82f * unit, ((Rect)(ref bag)).y, 72f * unit, ((Rect)(ref bag)).height));
			float num3 = Mathf.Min(22f, num - 10f);
			float num4 = num3 * 54f / 28f;
			float num5 = num3 - 6f;
			Rect screen = default(Rect);
			((Rect)(ref screen))..ctor(((Rect)(ref use)).center.x - num4 * unit / 2f, ((Rect)(ref use)).center.y - num3 * unit / 2f, num4 * unit, num3 * unit);
			Color tint = (Color)(flag4 ? new Color(1f, 0.93f, 0.78f, 1f) : Color.white);
			Skin(track, on ? "switch_track_on" : "switch_track_off", on ? BagGui.Gold : BagGui.ButtonFlat, tint);
			Skin(knob, on ? "switch_knob_on" : "switch_knob_off", on ? BagGui.Text : BagGui.Muted, tint);
			Image? obj3 = track;
			float pixelsPerUnitMultiplier = (knob.pixelsPerUnitMultiplier = 1f);
			obj3.pixelsPerUnitMultiplier = pixelsPerUnitMultiplier;
			Put((Graphic)(object)track, screen);
			Put((Graphic)(object)knob, new Rect(on ? (((Rect)(ref screen)).xMax - (num5 + 3f) * unit) : (((Rect)(ref screen)).x + 3f * unit), ((Rect)(ref screen)).y + 3f * unit, num5 * unit, num5 * unit));
			if ((Object)(object)track.sprite == (Object)(object)round)
			{
				track.pixelsPerUnitMultiplier = 32f * refPpu / 100f / Mathf.Max(0.01f, num3 / 2f * unit * k);
			}
			if ((Object)(object)knob.sprite == (Object)(object)round)
			{
				knob.pixelsPerUnitMultiplier = 32f * refPpu / 100f / Mathf.Max(0.01f, num5 / 2f * unit * k);
			}
			if (!((Component)root).gameObject.activeSelf)
			{
				((Component)root).gameObject.SetActive(true);
			}
			void Body(Image edge, Image fill, Rect screen2, bool hover)
			{
				//IL_0045: Unknown result type (might be due to invalid IL or missing references)
				//IL_0082: Unknown result type (might be due to invalid IL or missing references)
				//IL_008f: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
				//IL_009d: Unknown result type (might be due to invalid IL or missing references)
				float num7 = 32f * refPpu / 100f / Mathf.Max(0.01f, 9f * unit * k);
				float pixelsPerUnitMultiplier2 = (fill.pixelsPerUnitMultiplier = num7);
				edge.pixelsPerUnitMultiplier = pixelsPerUnitMultiplier2;
				Put((Graphic)(object)edge, screen2);
				Put((Graphic)(object)fill, new Rect(((Rect)(ref screen2)).x + 1f, ((Rect)(ref screen2)).y + 1f, ((Rect)(ref screen2)).width - 2f, ((Rect)(ref screen2)).height - 2f));
				((Graphic)edge).color = BagGui.ControlEdge;
				((Graphic)fill).color = (hover ? Color.Lerp(BagGui.ButtonFlat, Color.white, 0.06f) : BagGui.ButtonFlat);
			}
			void Put(Graphic g, Rect val8)
			{
				//IL_003c: 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)
				RectTransform rectTransform = g.rectTransform;
				rectTransform.anchoredPosition = new Vector2((((Rect)(ref val8)).x - ((Rect)(ref area)).x) * k, (0f - (((Rect)(ref val8)).y - ((Rect)(ref area)).y)) * k);
				rectTransform.sizeDelta = new Vector2(((Rect)(ref val8)).width * k, ((Rect)(ref val8)).height * k);
			}
		}

		private static void Skin(Image image, string kit, Color flat, Color tint)
		{
			//IL_0036: 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_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			Sprite val = BagGui.Kit(kit);
			image.sprite = (Sprite)(Object.op_Implicit((Object)(object)val) ? ((object)val) : ((object)round));
			image.type = (Type)1;
			((Graphic)image).color = (Object.op_Implicit((Object)(object)val) ? tint : (flat * tint));
		}

		private static void Build(RectTransform parent)
		{
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Expected O, but got Unknown
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Expected O, but got Unknown
			//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cf: Expected O, but got Unknown
			if (!Object.op_Implicit((Object)(object)round))
			{
				Texture2D val = BagGui.Rounded();
				round = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f, 0u, (SpriteMeshType)0, new Vector4(32f, 32f, 32f, 32f));
				((Object)round).hideFlags = (HideFlags)61;
			}
			root = (RectTransform)new GameObject("ComputerAutoLootbag_Buttons", new Type[1] { typeof(RectTransform) }).transform;
			((Transform)root).SetParent((Transform)(object)parent, false);
			TMP_Text componentInChildren = ((Component)parent).GetComponentInChildren<TMP_Text>(true);
			bagEdge = NewImage("BagEdge", raycast: true);
			bagFill = NewImage("BagFill", raycast: false);
			emblem = NewChild("Emblem", typeof(RawImage)).GetComponent<RawImage>();
			((Graphic)emblem).raycastTarget = false;
			label = NewText("Label", componentInChildren, (TextAlignmentOptions)4097);
			label.text = "Loot bag";
			label.fontStyle = (FontStyles)1;
			badgeText = NewText("Badge", componentInChildren, (TextAlignmentOptions)4100);
			useEdge = NewImage("SwitchEdge", raycast: true);
			useFill = NewImage("SwitchFill", raycast: false);
			track = NewImage("Track", raycast: false);
			knob = NewImage("Knob", raycast: false);
			Image? target = bagEdge;
			object obj = <>c.<>9__22_0;
			if (obj == null)
			{
				UnityAction val2 = delegate
				{
					bagClicked = true;
				};
				<>c.<>9__22_0 = val2;
				obj = (object)val2;
			}
			Clickable(target, (UnityAction)obj);
			Image? target2 = useEdge;
			object obj2 = <>c.<>9__22_1;
			if (obj2 == null)
			{
				UnityAction val3 = delegate
				{
					switchClicked = true;
				};
				<>c.<>9__22_1 = val3;
				obj2 = (object)val3;
			}
			Clickable(target2, (UnityAction)obj2);
		}

		private static GameObject NewChild(string name, Type graphic)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: 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_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: 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_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Expected O, but got Unknown
			GameObject val = new GameObject(name, new Type[2]
			{
				typeof(RectTransform),
				graphic
			});
			RectTransform val2 = (RectTransform)val.transform;
			((Transform)val2).SetParent((Transform)(object)root, false);
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor(0f, 1f);
			val2.pivot = val3;
			Vector2 anchorMin = (val2.anchorMax = val3);
			val2.anchorMin = anchorMin;
			return val;
		}

		private static Image NewImage(string name, bool raycast)
		{
			Image component = NewChild(name, typeof(Image)).GetComponent<Image>();
			component.sprite = round;
			component.type = (Type)1;
			((Graphic)component).raycastTarget = raycast;
			return component;
		}

		private static TMP_Text NewText(string name, TMP_Text? vanilla, TextAlignmentOptions align)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: 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_0053: 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_0087: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) });
			val.SetActive(false);
			RectTransform val2 = (RectTransform)val.transform;
			((Transform)val2).SetParent((Transform)(object)root, false);
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor(0f, 1f);
			val2.pivot = val3;
			Vector2 anchorMin = (val2.anchorMax = val3);
			val2.anchorMin = anchorMin;
			TextMeshProUGUI val5 = val.AddComponent<TextMeshProUGUI>();
			if (Object.op_Implicit((Object)(object)vanilla))
			{
				((TMP_Text)val5).font = vanilla.font;
				((TMP_Text)val5).fontSharedMaterial = vanilla.fontSharedMaterial;
			}
			((TMP_Text)val5).alignment = align;
			((TMP_Text)val5).textWrappingMode = (TextWrappingModes)0;
			((TMP_Text)val5).overflowMode = (TextOverflowModes)0;
			((Graphic)val5).raycastTarget = false;
			val.SetActive(true);
			return (TMP_Text)(object)val5;
		}

		private static void Clickable(Image target, UnityAction click)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			Button obj = ((Component)target).gameObject.AddComponent<Button>();
			((Selectable)obj).targetGraphic = (Graphic)(object)target;
			((Selectable)obj).transition = (Transition)0;
			Navigation navigation = ((Selectable)obj).navigation;
			((Navigation)(ref navigation)).mode = (Mode)0;
			((Selectable)obj).navigation = navigation;
			((UnityEvent)obj.onClick).AddListener(click);
		}
	}
	internal static class BagGui
	{
		internal static class QolLine
		{
			private delegate bool NextLine(out float x, out float y, out float scale);

			private delegate bool UnitsLine(out float height, out float gapY, out int fontSize, out float nextX);

			private static bool searched;

			private static NextLine? next;

			private static Func<bool>? suppressed;

			internal static float Height = 34f;

			internal static float GapY = 2f;

			internal static float Gap = 8f;

			internal static int Font = 16;

			private static bool unitsSearched;

			private static UnitsLine? units;

			internal static bool ButtonsSuppressed
			{
				get
				{
					Find();
					if (suppressed == null)
					{
						return false;
					}
					try
					{
						return suppressed();
					}
					catch (Exception error)
					{
						Faults.Report("QolLine.suppressed", error);
						return true;
					}
				}
			}

			internal static bool Found
			{
				get
				{
					Find();
					return next != null;
				}
			}

			private static void Find()
			{
				if (searched)
				{
					return;
				}
				searched = true;
				try
				{
					Type type = null;
					Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
					for (int i = 0; i < assemblies.Length; i++)
					{
						type = assemblies[i].GetType("Computer.QOL.InventoryLineApi", throwOnError: false);
						if (type != null)
						{
							break;
						}
					}
					if (type == null)
					{
						return;
					}
					MethodInfo method = type.GetMethod("TryGetNext", BindingFlags.Static | BindingFlags.Public, null, new Type[3]
					{
						typeof(float).MakeByRefType(),
						typeof(float).MakeByRefType(),
						typeof(float).MakeByRefType()
					}, null);
					if (method == null || method.ReturnType != typeof(bool))
					{
						return;
					}
					float num = Constant(type, "Height");
					float num2 = Constant(type, "FontSize");
					float num3 = Constant(type, "GapY");
					float num4 = Constant(type, "Gap");
					if (num >= 20f && num <= 80f)
					{
						Height = num;
					}
					if (num2 >= 10f && num2 <= 40f)
					{
						Font = Mathf.RoundToInt(num2);
					}
					if (num3 >= 0f && num3 <= 20f)
					{
						GapY = num3;
					}
					if (num4 >= 0f && num4 <= 30f)
					{
						Gap = num4;
					}
					PropertyInfo property = type.GetProperty("ButtonsSuppressed", BindingFlags.Static | BindingFlags.Public);
					if (property?.PropertyType == typeof(bool) && property.GetIndexParameters().Length == 0)
					{
						MethodInfo getMethod = property.GetGetMethod();
						if ((object)getMethod != null && getMethod.IsStatic)
						{
							suppressed = (Func<bool>)Delegate.CreateDelegate(typeof(Func<bool>), getMethod);
						}
					}
					next = (NextLine)Delegate.CreateDelegate(typeof(NextLine), method);
				}
				catch (Exception error)
				{
					Faults.Report("QolLine.find", error);
				}
			}

			private static float Constant(Type type, string name)
			{
				FieldInfo field = type.GetField(name, BindingFlags.Static | BindingFlags.Public);
				if (field != null && field.IsLiteral)
				{
					object rawConstantValue = field.GetRawConstantValue();
					if (rawConstantValue is float)
					{
						return (float)rawConstantValue;
					}
				}
				return -1f;
			}

			internal static bool TryGetNext(out float x, out float y, out float scale)
			{
				x = (y = (scale = 0f));
				Find();
				if (next == null)
				{
					return false;
				}
				try
				{
					if (!next(out x, out y, out scale))
					{
						return false;
					}
					return scale > 0f;
				}
				catch (Exception error)
				{
					Faults.Report("QolLine.read", error);
					next = null;
					return false;
				}
			}

			internal static bool TryGetUnits(out float nextX)
			{
				nextX = 0f;
				if (!unitsSearched)
				{
					unitsSearched = true;
					try
					{
						Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
						for (int i = 0; i < assemblies.Length; i++)
						{
							Type type = assemblies[i].GetType("Computer.QOL.QolUiKit", throwOnError: false);
							if (!(type == null))
							{
								MethodInfo method = type.GetMethod("TryGetInventoryLine", BindingFlags.Static | BindingFlags.Public, null, new Type[4]
								{
									typeof(float).MakeByRefType(),
									typeof(float).MakeByRefType(),
									typeof(int).MakeByRefType(),
									typeof(float).MakeByRefType()
								}, null);
								if (method != null && method.ReturnType == typeof(bool))
								{
									units = (UnitsLine)Delegate.CreateDelegate(typeof(UnitsLine), method);
								}
								break;
							}
						}
					}
					catch (Exception error)
					{
						Faults.Report("QolLine.findUnits", error);
					}
				}
				if (units == null)
				{
					return false;
				}
				try
				{
					if (!units(out var height, out var gapY, out var fontSize, out var nextX2))
					{
						return false;
					}
					if (nextX2 <= 0f || nextX2 > 600f)
					{
						return false;
					}
					if (height >= 20f && height <= 80f)
					{
						Height = height;
					}
					if (gapY >= 0f && gapY <= 20f)
					{
						GapY = gapY;
					}
					if (fontSize >= 10 && fontSize <= 40)
					{
						Font = fontSize;
					}
					Gap = 6f;
					nextX = nextX2;
					return true;
				}
				catch (Exception error2)
				{
					Faults.Report("QolLine.readUnits", error2);
					units = null;
					return false;
				}
			}
		}

		internal static Color Gold = Hex("e6bd66");

		internal static Color Text = Hex("f1ebdf");

		internal static Color Muted = Hex("aaa293");

		internal static Color Soft = Hex("aaa293");

		internal static Color Dim = Hex("736c60");

		internal static Color Warn = Hex("f2a44b");

		internal static Color Danger = Hex("f07a6c");

		internal static Color Good = Hex("83c77e");

		internal static Color Panel = Hex("11151b", 0.98f);

		internal static readonly Color RowFlat = new Color(1f, 1f, 1f, 0.05f);

		internal static readonly Color RowOver = Hex("e6bd66", 0.1f);

		internal static readonly Color Well = Hex("0c0f14");

		internal static readonly Color ButtonFlat = Hex("1c222b");

		internal static readonly Color Line = Hex("e6bd66", 0.45f);

		internal static readonly Color Edge = Hex("4a3c26");

		internal static readonly Color Shadow = new Color(0f, 0f, 0f, 0.85f);

		internal static readonly Color ControlEdge = Hex("6d5632");

		internal static readonly Color PrimaryFill = Hex("2a2118");

		internal static readonly Color PrimaryEdge = Hex("91703d");

		internal static readonly Color DangerFill = Hex("421f1c");

		internal static readonly Color DangerEdge = Hex("7a3a33");

		internal static readonly Color RowEdge = Hex("272d36");

		internal static readonly Color WellEdge = Hex("323a45");

		internal static readonly Color YellowFill = Hex("e2b862");

		internal static readonly Color YellowEdge = Hex("3a2c16");

		internal static readonly Color YellowText = Hex("2a2118");

		internal const float WindowRadius = 20f;

		internal const float PanelRadius = 12f;

		internal const float ControlRadius = 9f;

		private static bool paletteRead;

		private static Func<float, float, float, float, float>? qolWindowScale;

		private static MethodInfo? kitSprite;

		private static bool kitSearched;

		private static readonly Dictionary<string, Sprite?> sprites = new Dictionary<string, Sprite>(StringComparer.Ordinal);

		private static readonly Dictionary<int, GUIStyle> styles = new Dictionary<int, GUIStyle>();

		private static Font? body;

		private static Font? bold;

		private static float nextFontSearch;

		private static Texture2D? rounded;

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

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

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

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

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

		private static Font? fitBody;

		private static Font? fitBold;

		private static Texture2D? emblem;

		private static bool emblemLoaded;

		internal static Texture2D? Emblem
		{
			get
			{
				//IL_003b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0040: Unknown result type (might be due to invalid IL or missing references)
				//IL_004b: 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_005b: Expected O, but got Unknown
				if (emblemLoaded)
				{
					return emblem;
				}
				emblemLoaded = true;
				try
				{
					using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Computer.AutoLootbag.Assets.bag.png");
					if (stream == null)
					{
						return null;
					}
					using MemoryStream memoryStream = new MemoryStream();
					stream.CopyTo(memoryStream);
					Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false)
					{
						name = "ComputerAutoLootbag_Bag",
						wrapMode = (TextureWrapMode)1,
						hideFlags = (HideFlags)61
					};
					if (ImageConversion.LoadImage(val, memoryStream.ToArray(), true))
					{
						emblem = val;
					}
				}
				catch (Exception error)
				{
					Faults.Report("BagGui.Emblem", error);
				}
				return emblem;
			}
		}

		private static void Palette()
		{
			//IL_00b9: 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_00c5: 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_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: 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_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			if (paletteRead)
			{
				return;
			}
			paletteRead = true;
			try
			{
				Type kit = null;
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				foreach (Assembly assembly in assemblies)
				{
					if ((kit = assembly.GetType("Computer.QOL.QolUiKit", throwOnError: false)) != null)
					{
						break;
					}
				}
				if (kit == null)
				{
					return;
				}
				FieldInfo field = kit.GetField("Version", BindingFlags.Static | BindingFlags.Public);
				if (((field != null && field.GetValue(null) is int num) ? num : 0) >= 2)
				{
					Gold = Read("Gold", Gold);
					Text = Read("Text", Text);
					Muted = (Soft = Read("Muted", Muted));
					Dim = Read("Dim", Dim);
					Good = Read("Ok", Good);
					Warn = Read("Warn", Warn);
					Danger = Read("DangerText", Danger);
					Color val = Read("Panel", Panel);
					Panel = new Color(val.r, val.g, val.b, 0.98f);
					MethodInfo method = kit.GetMethod("WindowScale", BindingFlags.Static | BindingFlags.Public, null, new Type[4]
					{
						typeof(float),
						typeof(float),
						typeof(float),
						typeof(float)
					}, null);
					if (method != null && method.ReturnType == typeof(float))
					{
						qolWindowScale = (Func<float, float, float, float, float>)Delegate.CreateDelegate(typeof(Func<float, float, float, float, float>), method);
					}
					styles.Clear();
				}
				Color Read(string name, Color fallback)
				{
					//IL_002d: 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)
					//IL_002a: Unknown result type (might be due to invalid IL or missing references)
					//IL_002f: Unknown result type (might be due to invalid IL or missing references)
					object obj = kit.GetProperty(name, BindingFlags.Static | BindingFlags.Public)?.GetValue(null);
					if (obj is Color)
					{
						return (Color)obj;
					}
					return fallback;
				}
			}
			catch (Exception error)
			{
				Faults.Report("BagGui.palette", error);
			}
		}

		internal static float WindowScale(float screenWidth, float screenHeight, float width, float height)
		{
			Palette();
			if (qolWindowScale != null)
			{
				try
				{
					return qolWindowScale(screenWidth, screenHeight, width, height);
				}
				catch (Exception error)
				{
					Faults.Report("BagGui.windowScale", error);
					qolWindowScale = null;
				}
			}
			return WindowRules.WindowScale(screenWidth, screenHeight, width, height);
		}

		private static Color Hex(string hex, float alpha = 1f)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			return new Color((float)Convert.ToInt32(hex.Substring(0, 2), 16) / 255f, (float)Convert.ToInt32(hex.Substring(2, 2), 16) / 255f, (float)Convert.ToInt32(hex.Substring(4, 2), 16) / 255f, alpha);
		}

		internal static Sprite? Kit(string name)
		{
			if (sprites.TryGetValue(name, out Sprite value))
			{
				return value;
			}
			if (!kitSearched)
			{
				kitSearched = true;
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				for (int i = 0; i < assemblies.Length; i++)
				{
					Type type = assemblies[i].GetType("Computer.QOL.QolUiKit", throwOnError: false);
					if (!(type == null))
					{
						kitSprite = type.GetMethod("TryGetSprite", BindingFlags.Static | BindingFlags.Public);
						break;
					}
				}
			}
			Sprite val = null;
			if (kitSprite != null)
			{
				try
				{
					object[] array = new object[2] { name, null };
					object obj = kitSprite.Invoke(null, array);
					bool flag = default(bool);
					int num;
					if (obj is bool)
					{
						flag = (bool)obj;
						num = 1;
					}
					else
					{
						num = 0;
					}
					if (((uint)num & (flag ? 1u : 0u)) != 0)
					{
						object obj2 = array[1];
						val = (Sprite)((obj2 is Sprite) ? obj2 : null);
					}
				}
				catch (Exception error)
				{
					Faults.Report("BagGui.Kit", error);
					kitSprite = null;
				}
			}
			return sprites[name] = val;
		}

		private static void Fonts()
		{
			if (Object.op_Implicit((Object)(object)body) || Time.realtimeSinceStartup < nextFontSearch)
			{
				return;
			}
			nextFontSearch = Time.realtimeSinceStartup + 10f;
			Font[] array = Resources.FindObjectsOfTypeAll<Font>();
			foreach (Font val in array)
			{
				if (((Object)val).name == "AveriaSerifLibre-Regular")
				{
					body = val;
				}
				else if (((Object)val).name == "AveriaSerifLibre-Bold")
				{
					bold = val;
				}
			}
			if (Object.op_Implicit((Object)(object)body))
			{
				styles.Clear();
			}
		}

		internal static GUIStyle Style(int size, bool strong = false, TextAnchor anchor = (TextAnchor)3, bool wrap = false)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: 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_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_0020: Expected I4, but got Unknown
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: 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_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Expected O, but got Unknown
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Expected O, but got Unknown
			//IL_00c3: Expected O, but got Unknown
			Palette();
			Fonts();
			int key = size * 64 + anchor * 4 + (strong ? 2 : 0) + wrap;
			if (styles.TryGetValue(key, out GUIStyle value))
			{
				return value;
			}
			value = new GUIStyle(GUI.skin.label)
			{
				font = ((!strong) ? body : (Object.op_Implicit((Object)(object)bold) ? bold : body)),
				fontSize = size,
				fontStyle = (FontStyle)((strong && !Object.op_Implicit((Object)(object)bold)) ? 1 : 0),
				alignment = anchor,
				wordWrap = wrap,
				richText = false,
				clipping = (TextClipping)0,
				padding = new RectOffset(0, 0, 0, 0),
				margin = new RectOffset(0, 0, 0, 0)
			};
			return styles[key] = value;
		}

		internal static GUIStyle Title(int size)
		{
			return Style(size, strong: true, (TextAnchor)3);
		}

		internal static Texture2D Rounded()
		{
			//IL_00c8: 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_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Expected O, but got Unknown
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)rounded))
			{
				return rounded;
			}
			Color32[] array = (Color32[])(object)new Color32[9216];
			for (int i = 0; i < 96; i++)
			{
				for (int j = 0; j < 96; j++)
				{
					float num = (float)j + 0.5f;
					float num2 = (float)i + 0.5f;
					float num3 = num - Mathf.Clamp(num, 32f, 64f);
					float num4 = num2 - Mathf.Clamp(num2, 32f, 64f);
					array[i * 96 + j] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, (byte)Mathf.RoundToInt(255f * Mathf.Clamp01(32.5f - Mathf.Sqrt(num3 * num3 + num4 * num4))));
				}
			}
			rounded = new Texture2D(96, 96, (TextureFormat)4, false)
			{
				hideFlags = (HideFl