Decompiled source of LootWindow v1.0.1

BepInEx/plugins/dannanboi_LootWindow/dannanboi_LootWindow.dll

Decompiled 3 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1")]
[assembly: AssemblyVersion("1.0.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace LootWindow
{
	internal static class InventoryTransfer
	{
		private static bool Matches(ItemData existing, ItemData incoming)
		{
			return existing.m_shared.m_name == incoming.m_shared.m_name && existing.m_quality == incoming.m_quality && existing.m_worldLevel == incoming.m_worldLevel;
		}

		internal static int Move(Inventory inventory, ItemData item)
		{
			int stack = item.m_stack;
			int num = 0;
			long num2 = 0L;
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (Matches(allItem, item))
				{
					num2 += allItem.m_stack;
					num += Math.Max(0, allItem.m_shared.m_maxStackSize - allItem.m_stack);
				}
			}
			num += Math.Max(0, inventory.GetWidth() * inventory.GetHeight() - inventory.GetAllItems().Count) * item.m_shared.m_maxStackSize;
			if (num < stack)
			{
				return 0;
			}
			inventory.AddItem(item);
			long num3 = 0L;
			foreach (ItemData allItem2 in inventory.GetAllItems())
			{
				if (Matches(allItem2, item))
				{
					num3 += allItem2.m_stack;
				}
			}
			return (int)Math.Min(stack, Math.Max(0L, num3 - num2));
		}
	}
	internal sealed class LootUi : IDisposable
	{
		private readonly Plugin plugin;

		private const float Width = 204f;

		private const float Height = 396f;

		private const float HeaderHeight = 36f;

		private GameObject root;

		private GameObject body;

		private GameObject shade;

		private GameObject dialog;

		private RectTransform canvasRect;

		private RectTransform panel;

		private InventoryGui owner;

		private TMP_Text title;

		private TMP_Text allText;

		private TMP_Text discardText;

		private TMP_Text smallText;

		private Button all;

		private Button discard;

		private InventoryGrid grid;

		private Canvas nativeCanvas;

		private CanvasGroup bodyGroup;

		private bool dirty = true;

		private bool failed;

		private bool visible;

		private bool fallbackPromptShown;

		private string language;

		private float scale;

		private Vector2 screen;

		private Vector2 configuredPosition;

		private static readonly FieldInfo CurrentContainer = AccessTools.Field(typeof(InventoryGui), "m_currentContainer");

		private static readonly FieldInfo Elements = AccessTools.Field(typeof(InventoryGrid), "m_elements");

		private static readonly FieldInfo DragInventory = AccessTools.Field(typeof(InventoryGui), "m_dragInventory");

		private static readonly FieldInfo DragObject = AccessTools.Field(typeof(InventoryGui), "m_dragGo");

		private static readonly FieldInfo DragItem = AccessTools.Field(typeof(InventoryGui), "m_dragItem");

		private static readonly FieldInfo Split = AccessTools.Field(typeof(InventoryGui), "m_splitDialog");

		private static readonly MethodInfo SetupDrag = AccessTools.Method(typeof(InventoryGui), "SetupDragItem", (Type[])null, (Type[])null);

		private static readonly MethodInfo HideSplit = AccessTools.Method(typeof(InventoryGui), "HideSplitDialog", (Type[])null, (Type[])null);

		private static readonly MethodInfo Outside = AccessTools.Method(typeof(InventoryGui), "OnDropOutside", (Type[])null, (Type[])null);

		internal bool Busy
		{
			get
			{
				int result;
				if (Object.op_Implicit((Object)(object)owner))
				{
					if (DragItem.GetValue(owner) == null)
					{
						object? value = Split.GetValue(owner);
						SplitDialog val = (SplitDialog)((value is SplitDialog) ? value : null);
						result = ((val != null && val.IsActive) ? 1 : 0);
					}
					else
					{
						result = 1;
					}
				}
				else
				{
					result = 0;
				}
				return (byte)result != 0;
			}
		}

		internal bool PreserveContainerDrag(InventoryGui gui)
		{
			if (!Object.op_Implicit((Object)(object)gui) || !plugin.Loot.Owns((Inventory)/*isinst with value type is only supported in some contexts*/))
			{
				return false;
			}
			object? value = CurrentContainer.GetValue(gui);
			Container val = (Container)((value is Container) ? value : null);
			return !Object.op_Implicit((Object)(object)val) || !val.IsOwner();
		}

		internal Inventory SuspendContainerDrag(InventoryGui gui)
		{
			if (!Object.op_Implicit((Object)(object)gui))
			{
				return null;
			}
			object? value = DragInventory.GetValue(gui);
			Inventory val = (Inventory)((value is Inventory) ? value : null);
			if (!plugin.Loot.Owns(val))
			{
				return null;
			}
			DragInventory.SetValue(gui, null);
			return val;
		}

		internal void RestoreContainerDrag(InventoryGui gui, Inventory inventory)
		{
			if (Object.op_Implicit((Object)(object)gui) && inventory != null && plugin.Loot.Owns(inventory) && DragItem.GetValue(gui) != null && DragInventory.GetValue(gui) == null)
			{
				DragInventory.SetValue(gui, inventory);
			}
		}

		internal bool OwnsGrid(InventoryGrid value)
		{
			return Object.op_Implicit((Object)(object)value) && (Object)(object)value == (Object)(object)grid;
		}

		internal LootUi(Plugin value)
		{
			plugin = value;
		}

		internal void Invalidate()
		{
			dirty = true;
		}

		internal void Reset()
		{
			CancelDrag();
			dirty = true;
			if (Object.op_Implicit((Object)(object)root))
			{
				root.SetActive(false);
			}
			visible = false;
		}

		internal void CancelDrag()
		{
			InventoryGui val = (Object.op_Implicit((Object)(object)owner) ? owner : InventoryGui.instance);
			if (!Object.op_Implicit((Object)(object)val))
			{
				return;
			}
			LootController loot = plugin.Loot;
			if (loot != null && loot.Owns((Inventory)/*isinst with value type is only supported in some contexts*/))
			{
				SetupDrag.Invoke(val, new object[3] { null, null, 1 });
			}
			object? value = Split.GetValue(val);
			SplitDialog val2 = (SplitDialog)((value is SplitDialog) ? value : null);
			if (val2 != null && val2.IsActive)
			{
				LootController loot2 = plugin.Loot;
				if (loot2 != null && loot2.NeedsDecision)
				{
					HideSplit.Invoke(val, null);
				}
			}
		}

		internal void CloseInventory()
		{
			if (Object.op_Implicit((Object)(object)InventoryGui.instance))
			{
				InventoryGui.instance.Hide();
			}
		}

		internal static bool OtherInput()
		{
			return (Object.op_Implicit((Object)(object)Chat.instance) && Chat.instance.HasFocus()) || Console.IsVisible() || TextInput.IsVisible() || Menu.IsVisible() || Minimap.IsOpen() || UnifiedPopup.IsVisible();
		}

		internal void Tick()
		{
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_021c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0259: Unknown result type (might be due to invalid IL or missing references)
			//IL_0283: Unknown result type (might be due to invalid IL or missing references)
			//IL_0373: Unknown result type (might be due to invalid IL or missing references)
			//IL_0384: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_03cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ef: Unknown result type (might be due to invalid IL or missing references)
			if (Application.isBatchMode)
			{
				return;
			}
			LootController loot = plugin.Loot;
			if (!Object.op_Implicit((Object)(object)loot.Player) || ((Character)loot.Player).IsDead() || ((Character)loot.Player).IsTeleporting() || ((Character)loot.Player).InCutscene() || !loot.ShowWindow)
			{
				if (Object.op_Implicit((Object)(object)loot.Player))
				{
					LootState state = loot.State;
					if (((state != null && state.Waiting) || loot.TriggerSlot.Filled) && InventoryGui.IsVisible() && !OtherInput() && plugin.Input.Pressed(plugin.DiscardKey.Value))
					{
						plugin.Input.Consume(plugin.DiscardKey.Value);
						loot.Discard();
					}
				}
				if (visible)
				{
					Reset();
				}
				return;
			}
			InventoryGui instance = InventoryGui.instance;
			if (!Object.op_Implicit((Object)(object)instance) || !Object.op_Implicit((Object)(object)instance.m_container) || !Object.op_Implicit((Object)(object)instance.m_takeAllButton) || !Object.op_Implicit((Object)(object)instance.m_playerGrid?.m_elementPrefab) || !Object.op_Implicit((Object)(object)instance.m_containerName))
			{
				return;
			}
			if (owner != instance)
			{
				Dispose();
				owner = instance;
				failed = false;
			}
			if (!InventoryGui.IsVisible())
			{
				instance.Show((Container)null, 1);
			}
			if (!OtherInput() && !ZInput.GetKeyDown((KeyCode)27, true) && !ZInput.GetKeyDown((KeyCode)9, true))
			{
				if (!loot.Confirming && plugin.Input.Pressed(plugin.DiscardKey.Value))
				{
					plugin.Input.Consume(plugin.DiscardKey.Value);
					loot.Discard();
					return;
				}
				if (!loot.Confirming && !Busy && plugin.Input.Pressed(plugin.LootAllKey.Value))
				{
					plugin.Input.Consume(plugin.LootAllKey.Value);
					loot.TakeAll();
				}
			}
			if (!loot.ShowWindow)
			{
				Reset();
				return;
			}
			if (failed)
			{
				FallbackPrompt();
				return;
			}
			try
			{
				if (!Object.op_Implicit((Object)(object)root))
				{
					Create(instance);
				}
				bool active = InventoryGui.IsVisible() && !OtherInput();
				root.SetActive(active);
				visible = active;
				Localization instance2 = Localization.instance;
				string text = ((instance2 != null) ? instance2.GetSelectedLanguage() : null);
				if (language != text)
				{
					language = text;
					dirty = true;
				}
				Vector2 val = default(Vector2);
				((Vector2)(ref val))..ctor(plugin.PositionX.Value, plugin.PositionY.Value);
				if (screen != new Vector2((float)Screen.width, (float)Screen.height) || scale != plugin.WindowScale.Value || configuredPosition != val)
				{
					screen = new Vector2((float)Screen.width, (float)Screen.height);
					scale = plugin.WindowScale.Value;
					configuredPosition = val;
					Layout();
					dirty = true;
				}
				bodyGroup.interactable = !loot.Confirming;
				Button obj = all;
				LootState state2 = loot.State;
				((Selectable)obj).interactable = state2 != null && state2.HasLoot && !loot.Confirming && !Busy;
				shade.SetActive(loot.Confirming);
				dialog.SetActive(loot.Confirming);
				Clamp();
				if (!loot.Confirming)
				{
					object? value = DragObject.GetValue(instance);
					GameObject val2 = (GameObject)((value is GameObject) ? value : null);
					if (val2 != null && Object.op_Implicit((Object)(object)val2))
					{
						val2.transform.SetAsLastSibling();
					}
				}
				if (Object.op_Implicit((Object)(object)grid) && loot.PendingInventory() != null)
				{
					? val3 = grid;
					Inventory obj2 = loot.PendingInventory();
					object? value2 = DragItem.GetValue(instance);
					((InventoryGrid)val3).UpdateInventory(obj2, (Player)null, (ItemData)((value2 is ItemData) ? value2 : null));
					foreach (InventoryElement item in (List<InventoryElement>)Elements.GetValue(grid))
					{
						Transform val4 = ((Component)item).transform.Find("binding");
						if (Object.op_Implicit((Object)(object)val4) && ((Component)val4).gameObject.activeSelf)
						{
							((Component)val4).gameObject.SetActive(false);
						}
					}
				}
				if (dirty)
				{
					Refresh();
					dirty = false;
				}
				loot.AutoTick(Time.realtimeSinceStartup, visible, Busy);
			}
			catch (Exception ex)
			{
				Dispose();
				failed = true;
				owner = instance;
				plugin.Warn("Loot UI could not use native chest templates. Loot remains saved. " + ex);
				plugin.Notice("Loot window could not open. Check the log.");
				FallbackPrompt();
			}
		}

		private void Create(InventoryGui gui)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Expected O, but got Unknown
			//IL_0079: 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_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: 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_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: 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_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0208: Unknown result type (might be due to invalid IL or missing references)
			//IL_0229: Unknown result type (might be due to invalid IL or missing references)
			//IL_0238: Unknown result type (might be due to invalid IL or missing references)
			//IL_0277: Unknown result type (might be due to invalid IL or missing references)
			//IL_0286: Unknown result type (might be due to invalid IL or missing references)
			//IL_0393: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0459: Unknown result type (might be due to invalid IL or missing references)
			//IL_0468: Unknown result type (might be due to invalid IL or missing references)
			//IL_0497: Unknown result type (might be due to invalid IL or missing references)
			//IL_049c: Unknown result type (might be due to invalid IL or missing references)
			//IL_04bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_04cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_04db: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0514: Unknown result type (might be due to invalid IL or missing references)
			//IL_053e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0543: Unknown result type (might be due to invalid IL or missing references)
			//IL_057b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0583: Unknown result type (might be due to invalid IL or missing references)
			//IL_0585: Unknown result type (might be due to invalid IL or missing references)
			//IL_0586: Unknown result type (might be due to invalid IL or missing references)
			//IL_058e: 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_05bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_05ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_060e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0645: Unknown result type (might be due to invalid IL or missing references)
			//IL_0654: Unknown result type (might be due to invalid IL or missing references)
			nativeCanvas = ((Component)gui).GetComponentInParent<Canvas>();
			if (!Object.op_Implicit((Object)(object)nativeCanvas))
			{
				throw new InvalidOperationException("Native inventory canvas is unavailable.");
			}
			root = new GameObject("DannanboiLootWindow", new Type[1] { typeof(RectTransform) });
			canvasRect = root.GetComponent<RectTransform>();
			((Transform)canvasRect).SetParent(((Component)gui).transform, false);
			canvasRect.anchorMin = Vector2.zero;
			canvasRect.anchorMax = Vector2.one;
			Vector2 val = (canvasRect.offsetMin = (canvasRect.offsetMax = Vector2.zero));
			panel = Rect("LootChest", (Transform)(object)canvasRect, new Vector2(204f, 396f), Vector2.zero);
			RectTransform obj = panel;
			RectTransform obj2 = panel;
			((Vector2)(ref val))..ctor(0.5f, 0.5f);
			obj2.anchorMax = val;
			obj.anchorMin = val;
			Image val3 = ((Component)panel).gameObject.AddComponent<Image>();
			((Graphic)val3).color = Color.clear;
			((Graphic)val3).raycastTarget = true;
			Background(gui, panel);
			RectTransform val4 = Rect("DragHeader", (Transform)(object)panel, new Vector2(204f, 36f), Vector2.zero);
			Image val5 = ((Component)val4).gameObject.AddComponent<Image>();
			((Graphic)val5).color = Color.clear;
			((Graphic)val5).raycastTarget = true;
			LootDrag lootDrag = ((Component)val4).gameObject.AddComponent<LootDrag>();
			lootDrag.Ui = this;
			title = Text(gui.m_containerName, (Transform)(object)val4, "Loot", 20f, (TextAlignmentOptions)4098);
			title.textWrappingMode = (TextWrappingModes)0;
			title.overflowMode = (TextOverflowModes)1;
			title.rectTransform.offsetMin = new Vector2(10f, 0f);
			title.rectTransform.offsetMax = new Vector2(-10f, 0f);
			body = ((Component)Rect("LootBody", (Transform)(object)panel, new Vector2(204f, 360f), new Vector2(0f, -36f))).gameObject;
			bodyGroup = body.AddComponent<CanvasGroup>();
			RectTransform val6 = Rect("LootGrid", body.transform, new Vector2(128f, 256f), new Vector2(38f, -8f));
			grid = ((Component)val6).gameObject.AddComponent<InventoryGrid>();
			grid.m_gridRoot = val6;
			grid.m_elementPrefab = gui.m_playerGrid.m_elementPrefab;
			grid.m_elementSpace = 64f;
			grid.m_tooltipAnchor = gui.m_playerGrid.m_tooltipAnchor;
			grid.m_uiGroup = gui.m_playerGrid.m_uiGroup;
			grid.m_onSelected = Select;
			grid.m_onRightClick = delegate(InventoryGrid _, ItemData item, Vector2i __)
			{
				if (!Busy && item != null)
				{
					plugin.Loot.Take(plugin.Loot.Slot(item));
				}
			};
			grid.m_onReleased = delegate(InventoryGrid target, ItemData item, Vector2i pos)
			{
				//IL_002f: Unknown result type (might be due to invalid IL or missing references)
				if (!Object.op_Implicit((Object)(object)target))
				{
					Outside.Invoke(owner, null);
				}
				else if (Busy)
				{
					Select(target, item, pos, (Modifier)0);
				}
			};
			grid.CanDropDragOntoItem = (ItemData _) => false;
			all = Button(gui.m_takeAllButton, body.transform, "Loot All [F]", new Vector2(180f, 32f), new Vector2(12f, -276f), delegate
			{
				if (!Busy)
				{
					plugin.Loot.TakeAll();
				}
			});
			allText = ((Component)all).GetComponentInChildren<TMP_Text>(true);
			discard = Button(gui.m_takeAllButton, body.transform, "Discard Loot [O]", new Vector2(180f, 32f), new Vector2(12f, -312f), delegate
			{
				plugin.Loot.Discard();
			});
			TintRed(discard);
			discardText = ((Component)discard).GetComponentInChildren<TMP_Text>(true);
			smallText = Text(gui.m_containerName, (Transform)(object)Rect("Status", body.transform, new Vector2(180f, 18f), new Vector2(12f, -346f)), "", 12f, (TextAlignmentOptions)4098);
			shade = ((Component)Rect("ConfirmShade", (Transform)(object)canvasRect, Vector2.zero, Vector2.zero)).gameObject;
			RectTransform component = shade.GetComponent<RectTransform>();
			component.anchorMin = Vector2.zero;
			component.anchorMax = Vector2.one;
			val = (component.offsetMin = (component.offsetMax = Vector2.zero));
			Image val8 = shade.AddComponent<Image>();
			((Graphic)val8).color = new Color(0f, 0f, 0f, 0.55f);
			((Graphic)val8).raycastTarget = true;
			dialog = ((Component)Rect("DiscardConfirmation", (Transform)(object)canvasRect, new Vector2(350f, 158f), Vector2.zero)).gameObject;
			RectTransform component2 = dialog.GetComponent<RectTransform>();
			Vector2 val9 = default(Vector2);
			((Vector2)(ref val9))..ctor(0.5f, 0.5f);
			component2.pivot = val9;
			val = (component2.anchorMin = (component2.anchorMax = val9));
			Background(gui, component2);
			RectTransform parent = Rect("Question", (Transform)(object)component2, new Vector2(318f, 70f), new Vector2(16f, -10f));
			Text(gui.m_containerName, (Transform)(object)parent, "Discard loot?", 24f, (TextAlignmentOptions)4098);
			Button button = Button(gui.m_takeAllButton, (Transform)(object)component2, "Discard", new Vector2(151f, 38f), new Vector2(16f, -100f), delegate
			{
				plugin.Loot.Discard();
			});
			TintRed(button);
			Button(gui.m_takeAllButton, (Transform)(object)component2, "Cancel", new Vector2(151f, 38f), new Vector2(183f, -100f), delegate
			{
				plugin.Loot.CancelDiscard();
			});
			Layout();
			Refresh();
			dirty = false;
		}

		private static RectTransform Rect(string name, Transform parent, Vector2 size, Vector2 position)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) });
			RectTransform component = val.GetComponent<RectTransform>();
			((Transform)component).SetParent(parent, false);
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(0f, 1f);
			component.anchorMax = val2;
			component.anchorMin = val2;
			component.pivot = new Vector2(0f, 1f);
			component.sizeDelta = size;
			component.anchoredPosition = position;
			return component;
		}

		private static void Background(InventoryGui gui, RectTransform parent)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: 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_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: 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_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
			RectTransform container = gui.m_container;
			Rect rect = container.rect;
			float num = Mathf.Abs(((Rect)(ref rect)).width * ((Rect)(ref rect)).height);
			if (num <= 0f)
			{
				throw new InvalidOperationException("Native chest layout is not ready.");
			}
			int num2 = 0;
			Vector3[] array = (Vector3[])(object)new Vector3[4];
			Image[] componentsInChildren = ((Component)gui.m_container).GetComponentsInChildren<Image>(true);
			foreach (Image val in componentsInChildren)
			{
				Rect rect2 = ((Graphic)val).rectTransform.rect;
				float width = ((Rect)(ref rect2)).width;
				rect2 = ((Graphic)val).rectTransform.rect;
				float num3 = Mathf.Abs(width * ((Rect)(ref rect2)).height);
				if (!(num3 < num * 0.45f) && ((Behaviour)val).enabled && !InsideControl(((Component)val).transform, (Transform)(object)container) && ActiveLayer(((Component)val).transform, (Transform)(object)container) && !GamepadLayer(gui, ((Component)val).transform))
				{
					((Graphic)val).rectTransform.GetWorldCorners(array);
					Vector3 val2 = ((Transform)container).InverseTransformPoint(array[0]);
					Vector3 val3 = ((Transform)container).InverseTransformPoint(array[2]);
					RectTransform val4 = Rect("ChestFrameLayer" + num2++, (Transform)(object)parent, Vector2.zero, Vector2.zero);
					val4.anchorMin = Vector2.zero;
					val4.anchorMax = Vector2.one;
					val4.offsetMin = new Vector2(val2.x - ((Rect)(ref rect)).xMin, val2.y - ((Rect)(ref rect)).yMin);
					val4.offsetMax = new Vector2(val3.x - ((Rect)(ref rect)).xMax, val3.y - ((Rect)(ref rect)).yMax);
					Image val5 = ((Component)val4).gameObject.AddComponent<Image>();
					val5.sprite = val.sprite;
					val5.type = val.type;
					((Graphic)val5).color = ((Graphic)val).color;
					((Graphic)val5).material = ((Graphic)val).material;
					val5.fillCenter = val.fillCenter;
					val5.pixelsPerUnitMultiplier = val.pixelsPerUnitMultiplier;
					((Graphic)val5).raycastTarget = false;
				}
			}
			if (num2 == 0)
			{
				throw new InvalidOperationException("Native chest background is unavailable.");
			}
		}

		private static bool GamepadLayer(InventoryGui gui, Transform value)
		{
			if (gui.m_uiGroups == null)
			{
				return false;
			}
			UIGroupHandler[] uiGroups = gui.m_uiGroups;
			foreach (UIGroupHandler val in uiGroups)
			{
				if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val.m_enableWhenActiveAndGamepad) && value.IsChildOf(val.m_enableWhenActiveAndGamepad.transform))
				{
					return true;
				}
			}
			return false;
		}

		private static bool ActiveLayer(Transform value, Transform root)
		{
			Transform val = value;
			while (Object.op_Implicit((Object)(object)val) && (Object)(object)val != (Object)(object)root)
			{
				if (!((Component)val).gameObject.activeSelf)
				{
					return false;
				}
				val = val.parent;
			}
			return true;
		}

		private static bool InsideControl(Transform value, Transform root)
		{
			Transform val = value;
			while (Object.op_Implicit((Object)(object)val) && (Object)(object)val != (Object)(object)root)
			{
				if (Object.op_Implicit((Object)(object)((Component)val).GetComponent<Button>()) || Object.op_Implicit((Object)(object)((Component)val).GetComponent<InventoryGrid>()) || Object.op_Implicit((Object)(object)((Component)val).GetComponent<InventoryElement>()))
				{
					return true;
				}
				val = val.parent;
			}
			return false;
		}

		private static TMP_Text Text(TMP_Text template, Transform parent, string text, float size, TextAlignmentOptions alignment)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: 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_008e: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = Rect("Label", parent, Vector2.zero, Vector2.zero);
			val.anchorMin = Vector2.zero;
			val.anchorMax = Vector2.one;
			Vector2 offsetMin = (val.offsetMax = Vector2.zero);
			val.offsetMin = offsetMin;
			TextMeshProUGUI val2 = ((Component)val).gameObject.AddComponent<TextMeshProUGUI>();
			((TMP_Text)val2).font = template.font;
			((TMP_Text)val2).fontSharedMaterial = template.fontSharedMaterial;
			((Graphic)val2).color = ((Graphic)template).color;
			((TMP_Text)val2).fontSize = size;
			((TMP_Text)val2).fontStyle = template.fontStyle;
			((TMP_Text)val2).alignment = alignment;
			((TMP_Text)val2).text = text;
			((Graphic)val2).raycastTarget = false;
			((TMP_Text)val2).richText = false;
			return (TMP_Text)(object)val2;
		}

		private static Button Button(Button template, Transform parent, string text, Vector2 size, Vector2 position, Action click)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: 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_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Expected O, but got Unknown
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Expected O, but got Unknown
			//IL_00b6: 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)
			Button val = Object.Instantiate<Button>(template, parent, false);
			((Component)val).gameObject.SetActive(true);
			CleanScripts(((Component)val).gameObject, null);
			RectTransform component = ((Component)val).GetComponent<RectTransform>();
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(0f, 1f);
			component.anchorMax = val2;
			component.anchorMin = val2;
			component.pivot = new Vector2(0f, 1f);
			component.sizeDelta = size;
			component.anchoredPosition = position;
			((Transform)component).localScale = Vector3.one;
			val.onClick = new ButtonClickedEvent();
			((UnityEvent)val.onClick).AddListener((UnityAction)delegate
			{
				click();
			});
			Navigation navigation = default(Navigation);
			((Navigation)(ref navigation)).mode = (Mode)0;
			((Selectable)val).navigation = navigation;
			TMP_Text componentInChildren = ((Component)val).GetComponentInChildren<TMP_Text>(true);
			if (Object.op_Implicit((Object)(object)componentInChildren))
			{
				componentInChildren.text = text;
				componentInChildren.richText = false;
				componentInChildren.alignment = (TextAlignmentOptions)4098;
				componentInChildren.textWrappingMode = (TextWrappingModes)0;
				componentInChildren.overflowMode = (TextOverflowModes)1;
				componentInChildren.enableAutoSizing = true;
				componentInChildren.fontSizeMin = 12f;
				componentInChildren.fontSizeMax = 20f;
			}
			UITooltip[] componentsInChildren = ((Component)val).GetComponentsInChildren<UITooltip>(true);
			foreach (UITooltip val3 in componentsInChildren)
			{
				val3.m_topic = "";
				val3.m_text = "";
			}
			return val;
		}

		private static void CleanScripts(GameObject go, InventoryElement keep)
		{
			MonoBehaviour[] componentsInChildren = go.GetComponentsInChildren<MonoBehaviour>(true);
			foreach (MonoBehaviour val in componentsInChildren)
			{
				if (!(val is Graphic) && !(val is Button) && !(val is UITooltip) && !((Object)(object)val == (Object)(object)keep))
				{
					((Behaviour)val).enabled = false;
					Object.Destroy((Object)(object)val);
				}
			}
		}

		private static void TintRed(Button button)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: 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_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: 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)
			ColorBlock colors = ((Selectable)button).colors;
			((ColorBlock)(ref colors)).normalColor = new Color(1f, 0.7f, 0.7f);
			((ColorBlock)(ref colors)).highlightedColor = new Color(1f, 0.84f, 0.84f);
			((ColorBlock)(ref colors)).pressedColor = new Color(0.8f, 0.45f, 0.45f);
			((Selectable)button).colors = colors;
			if (Object.op_Implicit((Object)(object)((Selectable)button).targetGraphic))
			{
				((Selectable)button).targetGraphic.color = new Color(1f, 0.78f, 0.78f);
			}
		}

		private void Select(InventoryGrid selected, ItemData item, Vector2i pos, Modifier modifier)
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Invalid comparison between Unknown and I4
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Invalid comparison between Unknown and I4
			if (plugin.Loot.Confirming)
			{
				return;
			}
			if (DragItem.GetValue(owner) != null)
			{
				LootController loot = plugin.Loot;
				object? value = DragInventory.GetValue(owner);
				if (loot.Owns((Inventory)((value is Inventory) ? value : null)))
				{
					CancelDrag();
				}
			}
			else if (item != null)
			{
				if ((int)modifier == 2)
				{
					plugin.Loot.Take(plugin.Loot.Slot(item));
					return;
				}
				if ((int)modifier == 3)
				{
					((Humanoid)plugin.Loot.Player).DropItem(plugin.Loot.PendingInventory(), item, item.m_stack);
					return;
				}
				SetupDrag.Invoke(owner, new object[3]
				{
					item,
					plugin.Loot.PendingInventory(),
					item.m_stack
				});
			}
		}

		private void Refresh()
		{
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			LootController loot = plugin.Loot;
			title.text = (loot.Corrupt ? "Saved loot" : Localize(loot.State?.Name ?? "Loot"));
			allText.text = Label(plugin.LootAllLabel.Value, plugin.LootAllKey.Value);
			discardText.text = Label(plugin.DiscardLabel.Value, plugin.DiscardKey.Value);
			Button obj = all;
			LootState state = loot.State;
			((Selectable)obj).interactable = state != null && state.HasLoot && !loot.Confirming && !Busy;
			((Selectable)discard).interactable = !loot.Confirming;
			smallText.text = (loot.Corrupt ? "Saved loot could not be loaded." : "");
		}

		private unsafe static string Label(string text, KeyboardShortcut binding)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			string text2 = Localize(text);
			object obj;
			if ((int)((KeyboardShortcut)(ref binding)).MainKey != 0)
			{
				KeyboardShortcut val = binding;
				obj = " [" + ((object)(*(KeyboardShortcut*)(&val))/*cast due to .constrained prefix*/).ToString() + "]";
			}
			else
			{
				obj = "";
			}
			return text2 + (string?)obj;
		}

		private static string Localize(string text)
		{
			return (Localization.instance != null) ? Localization.instance.Localize(text) : text;
		}

		private void Layout()
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: 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_009c: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)panel) && Object.op_Implicit((Object)(object)canvasRect))
			{
				((Transform)panel).localScale = Vector3.one * plugin.WindowScale.Value;
				Rect rect = canvasRect.rect;
				panel.anchoredPosition = new Vector2((plugin.PositionX.Value - 0.5f) * ((Rect)(ref rect)).width, (plugin.PositionY.Value - 0.5f) * ((Rect)(ref rect)).height);
				Clamp();
			}
		}

		private void Clamp()
		{
			//IL_003a: 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_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: 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_009c: 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_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)panel) && Object.op_Implicit((Object)(object)canvasRect))
			{
				panel.pivot = new Vector2(0.5f, 1f);
				Vector2 val = panel.sizeDelta * plugin.WindowScale.Value;
				Rect rect = canvasRect.rect;
				float num = Mathf.Max(0f, (((Rect)(ref rect)).width - val.x) / 2f);
				Vector2 anchoredPosition = panel.anchoredPosition;
				float num2 = Mathf.Min(((Rect)(ref rect)).height / 2f, (0f - ((Rect)(ref rect)).height) / 2f + val.y);
				panel.anchoredPosition = new Vector2(Mathf.Clamp(anchoredPosition.x, 0f - num, num), Mathf.Clamp(anchoredPosition.y, num2, ((Rect)(ref rect)).height / 2f));
			}
		}

		internal void Drag(Vector2 delta)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)panel) && !plugin.Loot.Confirming)
			{
				RectTransform obj = panel;
				obj.anchoredPosition += delta / nativeCanvas.scaleFactor;
				Clamp();
			}
		}

		internal void EndDrag()
		{
			//IL_003b: 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_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)panel) && Object.op_Implicit((Object)(object)canvasRect))
			{
				ConfigEntry<float> positionX = plugin.PositionX;
				float x = panel.anchoredPosition.x;
				Rect rect = canvasRect.rect;
				positionX.Value = 0.5f + x / ((Rect)(ref rect)).width;
				ConfigEntry<float> positionY = plugin.PositionY;
				float y = panel.anchoredPosition.y;
				rect = canvasRect.rect;
				positionY.Value = 0.5f + y / ((Rect)(ref rect)).height;
			}
		}

		private void FallbackPrompt()
		{
			//IL_0044: 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_005c: Expected O, but got Unknown
			//IL_005c: Expected O, but got Unknown
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Expected O, but got Unknown
			if (plugin.Loot.Confirming && !fallbackPromptShown && UnifiedPopup.IsAvailable())
			{
				fallbackPromptShown = true;
				UnifiedPopup.Push((PopupBase)new YesNoPopup("Loot Window", "Discard loot?", (PopupButtonCallback)delegate
				{
					UnifiedPopup.Pop();
					fallbackPromptShown = false;
					plugin.Loot.Discard();
				}, (PopupButtonCallback)delegate
				{
					UnifiedPopup.Pop();
					fallbackPromptShown = false;
					plugin.Loot.CancelDiscard();
				}, false, true));
			}
		}

		public void Dispose()
		{
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			CancelDrag();
			if (Object.op_Implicit((Object)(object)root))
			{
				Object.Destroy((Object)(object)root);
			}
			root = (body = (shade = (dialog = null)));
			panel = (canvasRect = null);
			owner = null;
			grid = null;
			bodyGroup = null;
			nativeCanvas = null;
			dirty = true;
			visible = false;
			screen = Vector2.zero;
		}
	}
	public sealed class LootDrag : MonoBehaviour, IBeginDragHandler, IEventSystemHandler, IDragHandler, IEndDragHandler
	{
		internal LootUi Ui;

		public void OnBeginDrag(PointerEventData eventData)
		{
		}

		public void OnDrag(PointerEventData eventData)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Invalid comparison between Unknown and I4
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			if ((int)eventData.button == 0)
			{
				Ui?.Drag(eventData.delta);
			}
		}

		public void OnEndDrag(PointerEventData eventData)
		{
			Ui?.EndDrag();
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "Hide")]
	internal static class LootCloseInventory
	{
		private static bool Prefix()
		{
			LootController lootController = Plugin.Instance?.Loot;
			if (lootController == null || !lootController.NeedsDecision || !Object.op_Implicit((Object)(object)lootController.Player) || ((Character)lootController.Player).IsDead() || ((Character)lootController.Player).IsTeleporting() || ((Character)lootController.Player).InCutscene())
			{
				return true;
			}
			lootController.AskDiscard();
			return false;
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "UpdateContainer")]
	internal static class LootPreserveRewardDrag
	{
		private static bool Prefix(InventoryGui __instance)
		{
			Plugin instance = Plugin.Instance;
			if (instance == null || instance.Ui?.PreserveContainerDrag(__instance) != true)
			{
				return true;
			}
			((Component)__instance.m_container).gameObject.SetActive(false);
			return false;
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "CloseContainer")]
	internal static class LootCloseWorldContainer
	{
		private static void Prefix(InventoryGui __instance, out Inventory __state)
		{
			__state = Plugin.Instance?.Ui?.SuspendContainerDrag(__instance);
		}

		private static Exception Finalizer(InventoryGui __instance, Inventory __state, Exception __exception)
		{
			Plugin.Instance?.Ui?.RestoreContainerDrag(__instance, __state);
			return __exception;
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "ShowSplitDialog")]
	internal static class LootNoRewardSplitting
	{
		private static bool Prefix(Inventory __1)
		{
			Plugin instance = Plugin.Instance;
			return instance == null || instance.Loot?.Owns(__1) != true;
		}
	}
	[HarmonyPatch(typeof(InventoryGrid), "DropItem")]
	internal static class LootGridDrop
	{
		private static bool Prefix(InventoryGrid __instance, Inventory __0, ItemData __1, Vector2i __3, ref bool __result)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			LootController lootController = Plugin.Instance?.Loot;
			if (lootController != null)
			{
				Inventory inventory = __instance.GetInventory();
				Inventory inventory2 = __instance.GetInventory();
				if (!lootController.CanDrop(__0, inventory, __1, (inventory2 != null) ? inventory2.GetItemAt(__3.x, __3.y) : null))
				{
					__result = false;
					return false;
				}
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "OnSelectedItem")]
	internal static class LootModalSelection
	{
		private static bool Prefix()
		{
			Plugin instance = Plugin.Instance;
			return instance == null || instance.Loot?.Confirming != true;
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "OnDropOutside")]
	internal static class LootModalDrop
	{
		private static bool Prefix()
		{
			Plugin instance = Plugin.Instance;
			return instance == null || instance.Loot?.Confirming != true;
		}
	}
	[HarmonyPatch(typeof(InventoryGrid), "UpdateInventory")]
	internal static class LootHiddenTokens
	{
		private static readonly FieldInfo Elements = AccessTools.Field(typeof(InventoryGrid), "m_elements");

		private static void Postfix(InventoryGrid __instance, Inventory __0)
		{
			//IL_0055: 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_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			TriggerRegistry triggerRegistry = Plugin.Instance?.Triggers;
			if (triggerRegistry == null || __0 == null || !triggerRegistry.HasOwnedItems(__0))
			{
				return;
			}
			foreach (InventoryElement item in (List<InventoryElement>)Elements.GetValue(__instance))
			{
				Vector2i position = item.Position;
				if (triggerRegistry.Owns(__0.GetItemAt(position.x, position.y)))
				{
					if (Object.op_Implicit((Object)(object)item.m_icon))
					{
						((Behaviour)item.m_icon).enabled = false;
					}
					if (Object.op_Implicit((Object)(object)item.m_amount))
					{
						((Component)item.m_amount).gameObject.SetActive(false);
					}
					if (Object.op_Implicit((Object)(object)item.m_tooltip))
					{
						item.m_tooltip.m_topic = "";
						item.m_tooltip.m_text = "";
					}
				}
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGrid), "UpdateGamepad")]
	internal static class LootGridGamepad
	{
		private static bool Prefix(InventoryGrid __instance)
		{
			Plugin instance = Plugin.Instance;
			return instance == null || instance.Ui?.OwnsGrid(__instance) != true;
		}
	}
	[HarmonyPatch(typeof(PlayerController), "TakeInput")]
	internal static class LootInventoryInput
	{
		private static void Postfix(ref bool __result)
		{
			LootController lootController = Plugin.Instance?.Loot;
			if (lootController != null && lootController.NeedsDecision && InventoryGui.IsVisible())
			{
				lootController.Player.m_autoRun = false;
				__result = false;
			}
		}
	}
	internal sealed class LootState
	{
		internal string Id = "";

		internal string Trigger = "";

		internal string Name = "Loot";

		internal string EmptyLootText = "No loot.";

		internal long World;

		internal long GroundUser;

		internal uint GroundID;

		internal bool Waiting;

		internal bool Hidden;

		internal readonly List<Reward> Rewards = new List<Reward>();

		internal bool Ground => Waiting && GroundID != 0;

		internal bool HasLoot => !Waiting && Rewards.Exists((Reward r) => r.Amount > 0);

		internal bool Active => Waiting || HasLoot;

		internal LootState Copy()
		{
			LootState lootState = new LootState
			{
				Id = Id,
				Trigger = Trigger,
				Name = Name,
				World = World,
				Waiting = Waiting,
				Hidden = Hidden,
				EmptyLootText = EmptyLootText,
				GroundUser = GroundUser,
				GroundID = GroundID
			};
			foreach (Reward reward in Rewards)
			{
				lootState.Rewards.Add(reward.Copy());
			}
			return lootState;
		}

		internal string Encode()
		{
			using MemoryStream memoryStream = new MemoryStream();
			using (BinaryWriter binaryWriter = new BinaryWriter(memoryStream, Encoding.UTF8, leaveOpen: true))
			{
				binaryWriter.Write(5);
				binaryWriter.Write(Id);
				binaryWriter.Write(Trigger);
				binaryWriter.Write(Name);
				binaryWriter.Write(World);
				binaryWriter.Write(Waiting);
				binaryWriter.Write(EmptyLootText);
				binaryWriter.Write(GroundUser);
				binaryWriter.Write(GroundID);
				binaryWriter.Write(Hidden);
				binaryWriter.Write(Rewards.Count);
				foreach (Reward reward in Rewards)
				{
					binaryWriter.Write(reward.Item);
					binaryWriter.Write(reward.Amount);
					binaryWriter.Write(reward.Quality);
				}
			}
			return Convert.ToBase64String(memoryStream.ToArray());
		}

		internal static LootState Decode(string text)
		{
			if (text == null || text.Length > 8192)
			{
				throw new FormatException("Invalid saved loot size.");
			}
			using MemoryStream memoryStream = new MemoryStream(Convert.FromBase64String(text));
			using BinaryReader binaryReader = new BinaryReader(memoryStream, Encoding.UTF8);
			int num = binaryReader.ReadInt32();
			if (num < 1 || num > 5)
			{
				throw new FormatException("Unsupported saved loot format.");
			}
			LootState lootState = new LootState
			{
				Id = binaryReader.ReadString(),
				Trigger = binaryReader.ReadString(),
				Name = binaryReader.ReadString(),
				World = binaryReader.ReadInt64(),
				Waiting = binaryReader.ReadBoolean()
			};
			if (num >= 2)
			{
				lootState.EmptyLootText = binaryReader.ReadString();
			}
			if (num >= 3)
			{
				lootState.GroundUser = binaryReader.ReadInt64();
				lootState.GroundID = binaryReader.ReadUInt32();
			}
			if (num >= 4)
			{
				lootState.Hidden = binaryReader.ReadBoolean();
			}
			if (lootState.Hidden && (!lootState.Waiting || lootState.GroundID != 0 || lootState.GroundUser != 0L || !lootState.Trigger.StartsWith("dblw_", StringComparison.Ordinal)))
			{
				throw new FormatException("Invalid hidden trigger source.");
			}
			if ((!lootState.Waiting && (lootState.GroundUser != 0L || lootState.GroundID != 0)) || (lootState.GroundID == 0 && lootState.GroundUser != 0))
			{
				throw new FormatException("Invalid saved trigger source.");
			}
			if (lootState.EmptyLootText.Length > 256)
			{
				throw new FormatException("Invalid empty loot text.");
			}
			if (!Guid.TryParseExact(lootState.Id, "N", out var _) || lootState.Trigger.Length > 128 || lootState.Name.Length > 128)
			{
				throw new FormatException("Invalid saved loot identity.");
			}
			int num2 = binaryReader.ReadInt32();
			if (num2 < 0 || num2 > 8 || (lootState.Waiting && num2 != 0))
			{
				throw new FormatException("Invalid saved slot count.");
			}
			for (int i = 0; i < num2; i++)
			{
				Reward reward = new Reward
				{
					Item = binaryReader.ReadString(),
					Amount = binaryReader.ReadInt32()
				};
				if (num >= 5)
				{
					reward.Quality = binaryReader.ReadInt32();
				}
				if (reward.Item.Length == 0 || reward.Item.Length > 128 || reward.Amount < 0 || reward.Amount > 10000 || reward.Quality < 1 || reward.Quality > 10000)
				{
					throw new FormatException("Invalid saved reward.");
				}
				lootState.Rewards.Add(reward);
			}
			if (memoryStream.Position != memoryStream.Length)
			{
				throw new FormatException("Trailing saved loot data.");
			}
			return lootState;
		}
	}
	internal sealed class LootEntry
	{
		internal string Item;

		internal int Min;

		internal int Max;

		internal int Quality = 1;

		internal double Chance;

		internal bool Rare;
	}
	internal sealed class LootTable
	{
		internal string Trigger;

		internal string Name;

		internal string DisplayTitle = "";

		internal string TriggerCall = "";

		internal string EmptyLootText = "No loot.";

		internal bool HasDisplayTitle;

		internal bool AlwaysLoot;

		internal int MinRares;

		internal int MaxRares = 1;

		internal readonly List<LootEntry> Entries = new List<LootEntry>();
	}
	internal sealed class Reward
	{
		internal string Item;

		internal int Amount;

		internal int Quality = 1;

		internal Reward Copy()
		{
			return new Reward
			{
				Item = Item,
				Amount = Amount,
				Quality = Quality
			};
		}
	}
	internal sealed class Catalog
	{
		internal const int MaxBytes = 65536;

		internal const int Capacity = 8;

		internal readonly Dictionary<string, LootTable> Tables = new Dictionary<string, LootTable>(StringComparer.Ordinal);

		private static readonly Regex Prefab = new Regex("^[A-Za-z0-9_.\\-]{1,128}$", RegexOptions.CultureInvariant);

		private static readonly Regex Spec = new Regex("^([A-Za-z0-9_.\\-]{1,128})(?:\\((\\d+)\\))?\\[(\\d+)(?:-(\\d+))?(?::(\\d+(?:[.,]\\d+)?))?\\]$", RegexOptions.CultureInvariant);

		internal const string Example = "# Missing trigger prefabs are created from Wisp; reward prefabs must already exist.\n# Amount may be fixed or a range: Coins[20] or Coins[10-20].\n# Chance is a percentage and works with either: Ruby[1:5] or Ruby[1-2:5].\n# Optional (level): BronzeSword(2)[1:0.1] is one level-2 sword at 0.1% chance.\n# No range is required: [1:0.1] and [1-1:0.1] mean the same thing.\n# Without :chance, chance defaults to 100%.\n# Use one reward per indented list line; the old comma-separated string still works.\n# Prefix ! marks a rare entry; quotes are optional: !BronzeSword(2)[1:0.1].\n# minRares/maxRares count selected rare entries, not units or native stacks.\n# Entries roll once in random order at their stated chances. Rare minimums reserve slots first.\n# Defaults: minRares 0, maxRares 1. Set maxRares 0 to disable rare rewards.\n# Use 0 <= minRares <= maxRares; guaranteed rare minimums must fit the eight-slot window.\n# Any number of entries may be listed within 64 KiB. Stop at eight stacks or whole-reward overflow.\n# triggerCall is reserved for future function calls; it has no active behavior yet.\n# For a quick solo test, replace dblw_Test2 with TrophyBoar and pick one up.\n- triggerItem: dblw_Test2\n  itemDisplayTitle: \"\"\n  triggerCall:\n  alwaysLoot: false\n  emptyLootText: \"Nothing was found.\"\n  lootTableName: \"Boss reward\"\n  minRares: 0\n  maxRares: 1\n  lootTable:\n    - Coins[50-100]\n    - Ruby[1-2:5]\n    - Amber[2]\n    - !BronzeSword(2)[1:0.1]\n";

		internal static Catalog Parse(string yaml)
		{
			if (yaml == null || Encoding.UTF8.GetByteCount(yaml) > 65536)
			{
				throw new FormatException("Loot tables exceed 64 KiB.");
			}
			Catalog catalog = new Catalog();
			Dictionary<string, string> row = null;
			List<string> listedLoot = null;
			bool flag = false;
			Action action = delegate
			{
				if (row != null)
				{
					if (!row.TryGetValue("triggerItem", out var value) || !Prefab.IsMatch(value))
					{
						throw new FormatException("triggerItem must be an exact item prefab name.");
					}
					if (!row.TryGetValue("lootTable", out var value2) || ((listedLoot == null) ? string.IsNullOrWhiteSpace(value2) : (listedLoot.Count == 0)))
					{
						throw new FormatException("lootTable must contain Item[amount] entries as a list or comma-separated string.");
					}
					if (catalog.Tables.Count >= 128 || catalog.Tables.ContainsKey(value))
					{
						throw new FormatException("Duplicate trigger or more than 128 tables: " + value);
					}
					string value3;
					LootTable lootTable = new LootTable
					{
						Trigger = value,
						Name = ((row.TryGetValue("lootTableName", out value3) && value3.Length > 0) ? value3 : "Loot")
					};
					lootTable.DisplayTitle = (row.TryGetValue("itemDisplayTitle", out var value4) ? value4 : "");
					lootTable.HasDisplayTitle = row.ContainsKey("itemDisplayTitle");
					if (lootTable.DisplayTitle.Length > 128)
					{
						throw new FormatException("itemDisplayTitle exceeds 128 characters.");
					}
					lootTable.TriggerCall = (row.TryGetValue("triggerCall", out var value5) ? value5 : "");
					if (lootTable.TriggerCall.Length > 128)
					{
						throw new FormatException("triggerCall exceeds 128 characters.");
					}
					if (row.TryGetValue("alwaysLoot", out var value6))
					{
						if (!bool.TryParse(value6, out var result))
						{
							throw new FormatException("alwaysLoot must be true or false.");
						}
						lootTable.AlwaysLoot = result;
					}
					lootTable.EmptyLootText = ((row.TryGetValue("emptyLootText", out var value7) && !string.IsNullOrWhiteSpace(value7)) ? value7 : "No loot.");
					if (lootTable.EmptyLootText.Length > 256)
					{
						throw new FormatException("emptyLootText exceeds 256 characters.");
					}
					if (lootTable.Name.Length > 128)
					{
						throw new FormatException("lootTableName exceeds 128 characters.");
					}
					lootTable.MinRares = ReadRareLimit(row, "minRares", 0);
					lootTable.MaxRares = ReadRareLimit(row, "maxRares", 1);
					HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
					int num4 = 0;
					IEnumerable<string> enumerable;
					if (listedLoot == null)
					{
						enumerable = SplitEntries(value2);
					}
					else
					{
						IEnumerable<string> enumerable2 = listedLoot;
						enumerable = enumerable2;
					}
					foreach (string item in enumerable)
					{
						string text5 = item.Trim().Replace('@', ':');
						bool flag3 = text5.StartsWith("!", StringComparison.Ordinal);
						if (flag3)
						{
							text5 = text5.Substring(1);
						}
						Match match = Spec.Match(text5);
						if (!match.Success)
						{
							throw new FormatException("Invalid loot entry: " + item + ". Use Item[amount:chance] or Item(level)[min-max:chance], optionally prefixed with !.");
						}
						int result2 = 1;
						if (match.Groups[2].Success && (!int.TryParse(match.Groups[2].Value, out result2) || result2 < 1 || result2 > 10000))
						{
							throw new FormatException("Item levels must be 1–10000 and within the prefab's native maximum.");
						}
						if (!int.TryParse(match.Groups[3].Value, out var result3) || result3 < 1 || result3 > 10000)
						{
							throw new FormatException("Amounts must be 1–10000.");
						}
						int result4;
						if (!match.Groups[4].Success)
						{
							result4 = result3;
						}
						else if (!int.TryParse(match.Groups[4].Value, out result4) || result4 < result3 || result4 > 10000)
						{
							throw new FormatException("Invalid amount range.");
						}
						double num5 = (match.Groups[5].Success ? double.Parse(match.Groups[5].Value.Replace(',', '.'), CultureInfo.InvariantCulture) : 100.0);
						if (num5 <= 0.0 || num5 > 100.0)
						{
							throw new FormatException("Chance must be above 0 and at most 100 percent.");
						}
						string value8 = match.Groups[1].Value;
						if (!hashSet.Add(value8 + ":" + result2.ToString(CultureInfo.InvariantCulture)))
						{
							throw new FormatException("Duplicate item/level entry: " + value8 + "(" + result2 + ").");
						}
						lootTable.Entries.Add(new LootEntry
						{
							Item = value8,
							Quality = result2,
							Min = result3,
							Max = result4,
							Chance = num5,
							Rare = flag3
						});
						if (flag3)
						{
							num4++;
						}
					}
					ValidateRareLimits(lootTable, num4);
					catalog.Tables.Add(value, lootTable);
					row = null;
				}
			};
			int num = 0;
			string[] array = yaml.Replace("\r", "").TrimStart('\ufeff').Split('\n');
			foreach (string text in array)
			{
				num++;
				try
				{
					if (text.Contains("\t"))
					{
						throw new FormatException("Indent with spaces, not tabs.");
					}
					bool flag2 = text.StartsWith("    - ", StringComparison.Ordinal) || text == "    -";
					string text2 = YamlScalar.Uncomment(text, flag2 ? 6 : (-1)).TrimEnd();
					if (string.IsNullOrWhiteSpace(text2))
					{
						continue;
					}
					if (flag2)
					{
						if (!flag || listedLoot == null)
						{
							throw new FormatException("Nested entries are only allowed immediately under an empty lootTable: field.");
						}
						listedLoot.Add(YamlScalar.Read((text2.Length > 6) ? text2.Substring(6) : ""));
						continue;
					}
					flag = false;
					if ((text2 == "---" && row == null && catalog.Tables.Count == 0) || (text2 == "[]" && row == null && catalog.Tables.Count == 0))
					{
						continue;
					}
					if (text2.StartsWith("- ", StringComparison.Ordinal))
					{
						action();
						row = new Dictionary<string, string>(StringComparer.Ordinal);
						listedLoot = null;
						text2 = text2.Substring(2);
					}
					else
					{
						if (row == null || !text2.StartsWith("  ", StringComparison.Ordinal) || text2.StartsWith("   ", StringComparison.Ordinal))
						{
							throw new FormatException("Use two spaces before table fields and four spaces before lootTable list entries.");
						}
						text2 = text2.Substring(2);
					}
					int num3 = text2.IndexOf(':');
					if (num3 < 1 || (num3 + 1 < text2.Length && !char.IsWhiteSpace(text2[num3 + 1])))
					{
						throw new FormatException("Expected field: value.");
					}
					string text3 = text2.Substring(0, num3);
					switch (text3)
					{
					case "name":
						text3 = "lootTableName";
						break;
					case "loot":
						text3 = "lootTable";
						break;
					case "displayTitle":
						text3 = "itemDisplayTitle";
						break;
					}
					if (text3 != "triggerItem" && text3 != "lootTableName" && text3 != "lootTable" && text3 != "emptyLootText" && text3 != "itemDisplayTitle" && text3 != "triggerCall" && text3 != "alwaysLoot" && text3 != "minRares" && text3 != "maxRares")
					{
						throw new FormatException("Unknown field: " + text3);
					}
					if (row.ContainsKey(text3))
					{
						throw new FormatException("Duplicate field: " + text3);
					}
					string text4 = YamlScalar.Read(text2.Substring(num3 + 1), allowEmpty: true);
					row.Add(text3, text4);
					if (text3 == "lootTable" && text4.Length == 0)
					{
						listedLoot = new List<string>();
						flag = true;
					}
				}
				catch (FormatException ex)
				{
					throw new FormatException("loottables.yaml:" + num + ": " + ex.Message);
				}
			}
			action();
			foreach (LootTable value9 in catalog.Tables.Values)
			{
				foreach (LootEntry entry in value9.Entries)
				{
					if (catalog.Tables.ContainsKey(entry.Item))
					{
						throw new FormatException("Loot cannot include a mapped trigger item: " + entry.Item);
					}
				}
			}
			return catalog;
		}

		private static int ReadRareLimit(Dictionary<string, string> row, string field, int fallback)
		{
			if (!row.TryGetValue(field, out var value) || value.Length == 0)
			{
				return fallback;
			}
			if (!int.TryParse(value, NumberStyles.None, CultureInfo.InvariantCulture, out var result) || result < 0)
			{
				throw new FormatException(field + " must be a nonnegative whole number.");
			}
			return result;
		}

		private static void ValidateRareLimits(LootTable table, int rareCount)
		{
			if (table.MinRares < 0 || table.MaxRares < table.MinRares)
			{
				throw new FormatException("Use 0 <= minRares <= maxRares.");
			}
			if (table.MinRares > 8)
			{
				throw new FormatException("The guaranteed minRares cannot fit the eight-slot window.");
			}
			if (table.MinRares > rareCount)
			{
				throw new FormatException("minRares exceeds the number of ! rare entries.");
			}
			if (table.AlwaysLoot && table.MaxRares == 0 && rareCount == table.Entries.Count)
			{
				throw new FormatException("alwaysLoot cannot guarantee a reward when maxRares disables every entry.");
			}
		}

		private static IEnumerable<string> SplitEntries(string text)
		{
			int start = 0;
			int bracket = 0;
			for (int i = 0; i < text.Length; i++)
			{
				if (text[i] == '[')
				{
					bracket++;
				}
				else if (text[i] == ']')
				{
					bracket--;
				}
				else if (text[i] == ',' && bracket == 0)
				{
					yield return text.Substring(start, i - start);
					start = i + 1;
				}
			}
			yield return text.Substring(start);
		}

		internal static void Validate(LootTable table, Func<string, int> maxStack, Func<string, int> maxQuality = null)
		{
			int num = 0;
			foreach (LootEntry entry in table.Entries)
			{
				if (entry.Rare)
				{
					num++;
				}
			}
			ValidateRareLimits(table, num);
			int[] array = ((table.MinRares > 0) ? new int[table.MinRares + 1] : null);
			foreach (LootEntry entry2 in table.Entries)
			{
				int num2 = maxStack(entry2.Item);
				if (num2 < 1)
				{
					throw new FormatException("Reward prefab is unavailable: " + entry2.Item);
				}
				if (entry2.Quality < 1 || entry2.Quality > 10000 || (maxQuality != null && entry2.Quality > maxQuality(entry2.Item)))
				{
					throw new FormatException("Reward item level is unavailable: " + entry2.Item + "(" + entry2.Quality + ").");
				}
				int num3 = (entry2.Max + num2 - 1) / num2;
				if (array != null && entry2.Rare)
				{
					for (int num4 = table.MinRares; num4 > 0; num4--)
					{
						array[num4] = Math.Max(array[num4], array[num4 - 1] + num3);
					}
				}
				if (table.AlwaysLoot && table.MinRares == 0 && (!entry2.Rare || table.MaxRares > 0) && num3 > 8)
				{
					throw new FormatException("alwaysLoot requires each eligible reward to fit the eight-slot window.");
				}
			}
			if (array != null && array[table.MinRares] > 8)
			{
				throw new FormatException("The guaranteed minRares can exceed eight native stacks. Reduce rare quantities or minRares.");
			}
		}

		internal static List<Reward> Roll(LootTable table, Func<string, int> maxStack, Func<double> random, Func<string, int> maxQuality = null)
		{
			Validate(table, maxStack, maxQuality);
			List<Reward> list = new List<Reward>(8);
			int[] rewardOrder = new int[8];
			byte[] array = null;
			int i = 0;
			if (table.MinRares > 0)
			{
				array = new byte[table.Entries.Count];
				int[] array2 = new int[table.Entries.Count];
				int num = 0;
				for (int j = 0; j < table.Entries.Count; j++)
				{
					if (table.Entries[j].Rare)
					{
						array2[num++] = j;
					}
				}
				for (int k = 0; k < num; k++)
				{
					if (i >= table.MinRares)
					{
						break;
					}
					int num2 = PickNext(array2, k, num, random);
					LootEntry lootEntry = table.Entries[num2];
					array[num2] = 1;
					if (!(lootEntry.Chance < 100.0) || !(random() * 100.0 >= lootEntry.Chance))
					{
						if (!AddReward(list, rewardOrder, lootEntry, num2, maxStack, random))
						{
							throw new FormatException("Guaranteed rare reward exceeds available capacity.");
						}
						array[num2] = 2;
						i++;
					}
				}
				for (; i < table.MinRares; i++)
				{
					double num3 = 0.0;
					int num4 = -1;
					for (int l = 0; l < num; l++)
					{
						int num5 = array2[l];
						if (array[num5] == 1)
						{
							num3 += table.Entries[num5].Chance;
							num4 = num5;
						}
					}
					double num6 = random() * num3;
					int num7 = num4;
					for (int m = 0; m < num; m++)
					{
						int num8 = array2[m];
						if (array[num8] == 1)
						{
							double chance = table.Entries[num8].Chance;
							if (num6 < chance)
							{
								num7 = num8;
								break;
							}
							num6 -= chance;
						}
					}
					if (!AddReward(list, rewardOrder, table.Entries[num7], num7, maxStack, random))
					{
						throw new FormatException("Guaranteed rare reward exceeds available capacity.");
					}
					array[num7] = 2;
				}
				if (list.Count == 8)
				{
					return list;
				}
			}
			int[] array3 = new int[table.Entries.Count];
			int num9 = 0;
			for (int n = 0; n < table.Entries.Count; n++)
			{
				LootEntry lootEntry2 = table.Entries[n];
				if (!lootEntry2.Rare || (i < table.MaxRares && (array == null || array[n] == 0)))
				{
					array3[num9++] = n;
				}
			}
			bool flag = table.AlwaysLoot && list.Count == 0;
			double[] array4 = null;
			if (flag)
			{
				for (int num10 = 0; num10 < num9; num10++)
				{
					PickNext(array3, num10, num9, random);
				}
				array4 = new double[num9 + 1];
				for (int num11 = num9 - 1; num11 >= 0; num11--)
				{
					double chance2 = table.Entries[array3[num11]].Chance;
					array4[num11] = chance2 + (1.0 - chance2 / 100.0) * array4[num11 + 1];
				}
			}
			for (int num12 = 0; num12 < num9; num12++)
			{
				if (list.Count >= 8)
				{
					break;
				}
				int index = (flag ? array3[num12] : PickNext(array3, num12, num9, random));
				LootEntry lootEntry3 = table.Entries[index];
				double num13 = ((flag && list.Count == 0) ? array4[num12] : 100.0);
				if (lootEntry3.Chance < num13 && random() * num13 >= lootEntry3.Chance)
				{
					continue;
				}
				if (!AddReward(list, rewardOrder, lootEntry3, index, maxStack, random))
				{
					break;
				}
				if (!lootEntry3.Rare || ++i != table.MaxRares)
				{
					continue;
				}
				int num14 = num12 + 1;
				for (int num15 = num14; num15 < num9; num15++)
				{
					if (!table.Entries[array3[num15]].Rare)
					{
						array3[num14++] = array3[num15];
					}
				}
				num9 = num14;
			}
			return list;
		}

		private static int PickNext(int[] order, int position, int count, Func<double> random)
		{
			if (position + 1 < count)
			{
				int num = position + (int)(random() * (double)(count - position));
				int num2 = order[position];
				order[position] = order[num];
				order[num] = num2;
			}
			return order[position];
		}

		private static bool AddReward(List<Reward> result, int[] rewardOrder, LootEntry entry, int index, Func<string, int> maxStack, Func<double> random)
		{
			int num = entry.Min + (int)(random() * (double)(entry.Max - entry.Min + 1));
			int num2 = maxStack(entry.Item);
			int num3 = (num + num2 - 1) / num2;
			if (num3 > 8 - result.Count)
			{
				return false;
			}
			int i;
			for (i = 0; i < result.Count && rewardOrder[i] < index; i++)
			{
			}
			while (num > 0)
			{
				for (int num4 = result.Count; num4 > i; num4--)
				{
					rewardOrder[num4] = rewardOrder[num4 - 1];
				}
				rewardOrder[i] = index;
				int num5 = Math.Min(num, num2);
				result.Insert(i++, new Reward
				{
					Item = entry.Item,
					Amount = num5,
					Quality = entry.Quality
				});
				num -= num5;
			}
			return true;
		}
	}
	[BepInPlugin("dannanboi.lootwindow", "Loot Window", "1.0.1")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Guid = "dannanboi.lootwindow";

		public const string ModName = "Loot Window";

		public const string Version = "1.0.1";

		public const string BuildVersion = "1.0.1";

		internal static Plugin Instance;

		internal ConfigEntry<bool> EnableMod;

		internal ConfigEntry<bool> AutoLoot;

		internal ConfigEntry<bool> AutoLootAutoclose;

		internal ConfigEntry<bool> Autoclose;

		internal ConfigEntry<float> AutoLootInterval;

		internal ConfigEntry<float> AutoLootSleep;

		internal ConfigEntry<float> InputBlockInterval;

		internal ConfigEntry<float> TriggerPickupRadius;

		internal LootInput Input;

		internal TriggerRegistry Triggers;

		internal LootTokenPickup Tokens;

		internal ConfigEntry<KeyboardShortcut> LootAllKey;

		internal ConfigEntry<KeyboardShortcut> DiscardKey;

		internal ConfigEntry<float> WindowScale;

		internal ConfigEntry<float> PositionX;

		internal ConfigEntry<float> PositionY;

		internal ConfigEntry<string> LootAllLabel;

		internal ConfigEntry<string> DiscardLabel;

		internal Catalog LocalCatalog = new Catalog();

		internal string LocalYaml = "[]\n";

		internal LootController Loot;

		internal LootNetwork Network;

		internal LootUi Ui;

		private Harmony harmony;

		private string yamlPath;

		private DateTime yamlWrite;

		private DateTime cfgWrite;

		private long yamlLength = -1L;

		private float nextFiles;

		private string lastError;

		private bool reloading;

		private void Awake()
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Expected O, but got Unknown
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Expected O, but got Unknown
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Expected O, but got Unknown
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Expected O, but got Unknown
			//IL_0211: Unknown result type (might be due to invalid IL or missing references)
			//IL_021b: Expected O, but got Unknown
			//IL_024f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0259: Expected O, but got Unknown
			//IL_028d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0297: Expected O, but got Unknown
			//IL_03a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ab: Expected O, but got Unknown
			Instance = this;
			EnableMod = ((BaseUnityPlugin)this).Config.Bind<bool>("Settings", "Enable Mod", true, "Consume configured trigger items and open their loot tables. Server-controlled in multiplayer. Existing pending loot can still be collected or discarded when disabled.");
			TriggerPickupRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Settings", "Trigger pickup radius", 3f, new ConfigDescription("Horizontal pickup distance in metres for dblw_ tokens. Allows up to 2 metres of height difference for spawned/floating items. Server-controlled in multiplayer.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 6f), Array.Empty<object>()));
			LootAllKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Keybindings", "Loot All", new KeyboardShortcut((KeyCode)102, Array.Empty<KeyCode>()), "Collect all items that fit in inventory. Local binding; the button shows this binding live.");
			DiscardKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Keybindings", "Discard Loot", new KeyboardShortcut((KeyCode)111, Array.Empty<KeyCode>()), "Ask for confirmation before destroying remaining loot. Local binding; the button shows this binding live.");
			AutoLoot = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "Auto Loot", false, "Automatically collect one reward stack at a time while the window is visible. Local UI preference.");
			AutoLootInterval = ((BaseUnityPlugin)this).Config.Bind<float>("UI", "Auto Loot interval", 1f, new ConfigDescription("Seconds before the first automatic pickup and between subsequent stacks. Local UI preference.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 2f), Array.Empty<object>()));
			AutoLootSleep = ((BaseUnityPlugin)this).Config.Bind<float>("UI", "Sleep before auto-loot", 0.5f, new ConfigDescription("Additional seconds to wait once per loot window before the first pickup interval. Local UI preference.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 60f), Array.Empty<object>()));
			RemoveLegacyAutoLoot();
			InputBlockInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Keybindings", "Input-block interval", 0.25f, new ConfigDescription("Seconds to reserve loot keys after an action or closing the window. Held keys stay reserved until released; the interval then applies. Local setting.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>()));
			AutoLootAutoclose = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "Auto Loot Autoclose", true, "Close inventory when loot is emptied or discarded while Auto Loot is enabled. Local UI preference.");
			Autoclose = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "Autoclose", true, "Close inventory when loot is emptied or discarded while Auto Loot is disabled. Local UI preference.");
			WindowScale = ((BaseUnityPlugin)this).Config.Bind<float>("UI", "Window scale", 1f, new ConfigDescription("Local scale relative to the native inventory canvas.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.7f, 2f), Array.Empty<object>()));
			PositionX = ((BaseUnityPlugin)this).Config.Bind<float>("UI", "Window X", 0.35f, new ConfigDescription("Local horizontal position as a fraction of the screen. Dragging updates this value.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			PositionY = ((BaseUnityPlugin)this).Config.Bind<float>("UI", "Window Y", 0.62f, new ConfigDescription("Local position of the window's top edge as a fraction of the screen. Dragging updates this value.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			LootAllLabel = ((BaseUnityPlugin)this).Config.Bind<string>("UI", "Loot All text", "Loot All", "Local button text. The current key binding is appended automatically.");
			DiscardLabel = ((BaseUnityPlugin)this).Config.Bind<string>("UI", "Discard Loot text", "Discard Loot", "Local button text. The current key binding is appended automatically.");
			yamlPath = Path.Combine(Paths.ConfigPath, "LootWindow", "loottables.yaml");
			Directory.CreateDirectory(Path.GetDirectoryName(yamlPath));
			if (!File.Exists(yamlPath))
			{
				File.WriteAllText(yamlPath, "# Missing trigger prefabs are created from Wisp; reward prefabs must already exist.\n# Amount may be fixed or a range: Coins[20] or Coins[10-20].\n# Chance is a percentage and works with either: Ruby[1:5] or Ruby[1-2:5].\n# Optional (level): BronzeSword(2)[1:0.1] is one level-2 sword at 0.1% chance.\n# No range is required: [1:0.1] and [1-1:0.1] mean the same thing.\n# Without :chance, chance defaults to 100%.\n# Use one reward per indented list line; the old comma-separated string still works.\n# Prefix ! marks a rare entry; quotes are optional: !BronzeSword(2)[1:0.1].\n# minRares/maxRares count selected rare entries, not units or native stacks.\n# Entries roll once in random order at their stated chances. Rare minimums reserve slots first.\n# Defaults: minRares 0, maxRares 1. Set maxRares 0 to disable rare rewards.\n# Use 0 <= minRares <= maxRares; guaranteed rare minimums must fit the eight-slot window.\n# Any number of entries may be listed within 64 KiB. Stop at eight stacks or whole-reward overflow.\n# triggerCall is reserved for future function calls; it has no active behavior yet.\n# For a quick solo test, replace dblw_Test2 with TrophyBoar and pick one up.\n- triggerItem: dblw_Test2\n  itemDisplayTitle: \"\"\n  triggerCall:\n  alwaysLoot: false\n  emptyLootText: \"Nothing was found.\"\n  lootTableName: \"Boss reward\"\n  minRares: 0\n  maxRares: 1\n  lootTable:\n    - Coins[50-100]\n    - Ruby[1-2:5]\n    - Amber[2]\n    - !BronzeSword(2)[1:0.1]\n");
			}
			ReadLocal();
			Triggers = new TriggerRegistry(this);
			Loot = new LootController(this);
			Network = new LootNetwork(this);
			Tokens = new LootTokenPickup(this);
			Input = new LootInput(this);
			Ui = new LootUi(this);
			((BaseUnityPlugin)this).Config.SettingChanged += delegate(object _, SettingChangedEventArgs args)
			{
				Ui?.Invalidate();
				if (!reloading && args.ChangedSetting.Definition.Section == "Settings")
				{
					Network?.Publish();
				}
			};
			harmony = new Harmony("dannanboi.lootwindow");
			try
			{
				harmony.PatchAll(typeof(Plugin).Assembly);
			}
			catch (Exception ex)
			{
				harmony.UnpatchSelf();
				((Behaviour)this).enabled = false;
				((BaseUnityPlugin)this).Logger.LogError((object)("Unsupported game API; patches removed: " + ex));
				return;
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Loot Window 1.0.1 loaded.");
		}

		private void RemoveLegacyAutoLoot()
		{
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Expected O, but got Unknown
			Dictionary<ConfigDefinition, string> dictionary = AccessTools.Property(typeof(ConfigFile), "OrphanedEntries")?.GetValue(((BaseUnityPlugin)this).Config) as Dictionary<ConfigDefinition, string>;
			bool flag = false;
			string[] array = new string[3] { "Auto Loot", "Auto Loot interval", "Sleep before auto-loot" };
			foreach (string text in array)
			{
				ConfigDefinition val = new ConfigDefinition("Settings", text);
				if (dictionary != null)
				{
					flag |= dictionary.Remove(val);
				}
				flag |= ((BaseUnityPlugin)this).Config.Remove(val);
			}
			if (flag)
			{
				((BaseUnityPlugin)this).Config.Save();
			}
		}

		private void Update()
		{
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			Network.Tick(realtimeSinceStartup);
			Triggers.Tick(realtimeSinceStartup);
			if (realtimeSinceStartup >= nextFiles)
			{
				nextFiles = realtimeSinceStartup + 1f;
				try
				{
					FileInfo fileInfo = new FileInfo(((BaseUnityPlugin)this).Config.ConfigFilePath);
					if (fileInfo.Exists && fileInfo.LastWriteTimeUtc != cfgWrite)
					{
						cfgWrite = fileInfo.LastWriteTimeUtc;
						reloading = true;
						try
						{
							((BaseUnityPlugin)this).Config.Reload();
						}
						finally
						{
							reloading = false;
						}
						if (Network.Authority)
						{
							Network.Publish();
						}
						Ui.Invalidate();
					}
				}
				catch (Exception ex)
				{
					Warn("Config reload failed: " + ex.Message);
				}
				if (Network.Authority)
				{
					ReadLocal();
				}
			}
			if (!Application.isBatchMode)
			{
				Input.Tick();
				Loot.Tick(realtimeSinceStartup);
				Tokens.Tick(realtimeSinceStartup);
				Ui.Tick();
			}
		}

		private void ReadLocal()
		{
			try
			{
				FileInfo fileInfo = new FileInfo(yamlPath);
				if (!fileInfo.Exists)
				{
					throw new IOException("Loot table file is missing.");
				}
				if (!(fileInfo.LastWriteTimeUtc == yamlWrite) || fileInfo.Length != yamlLength)
				{
					yamlWrite = fileInfo.LastWriteTimeUtc;
					yamlLength = fileInfo.Length;
					if (fileInfo.Length > 65536)
					{
						throw new FormatException("Loot tables exceed 64 KiB.");
					}
					string text = File.ReadAllText(yamlPath);
					Catalog localCatalog = Catalog.Parse(text);
					LocalCatalog = localCatalog;
					LocalYaml = text;
					lastError = null;
					Triggers?.Invalidate();
					Network?.Publish();
					Loot?.InventoryChanged();
				}
			}
			catch (Exception ex)
			{
				if (ex is IOException)
				{
					yamlLength = -1L;
				}
				if (lastError != ex.Message)
				{
					lastError = ex.Message;
					Warn("Last valid loot tables retained. " + ex.Message);
				}
			}
		}

		internal static ItemDrop FindItem(string name)
		{
			if (!Object.op_Implicit((Object)(object)ObjectDB.instance) || string.IsNullOrEmpty(name))
			{
				return null;
			}
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(name);
			return Object.op_Implicit((Object)(object)itemPrefab) ? itemPrefab.GetComponent<ItemDrop>() : null;
		}

		internal static int MaxStack(string name)
		{
			ItemDrop val = FindItem(name);
			return (Object.op_Implicit((Object)(object)val) && val.m_itemData?.m_shared != null) ? val.m_itemData.m_shared.m_maxStackSize : 0;
		}

		internal static int MaxQuality(string name)
		{
			ItemDrop val = FindItem(name);
			return (Object.op_Implicit((Object)(object)val) && val.m_itemData?.m_shared != null) ? val.m_itemData.m_shared.m_maxQuality : 0;
		}

		internal void Warn(string message)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)message);
		}

		internal void Notice(string message)
		{
			if (Object.op_Implicit((Object)(object)MessageHud.instance))
			{
				MessageHud.instance.ShowMessage((MessageType)2, message, 0, (Sprite)null, false, true);
			}
		}

		private void OnDestroy()
		{
			Ui?.Dispose();
			Loot?.Dispose();
			Tokens?.Dispose();
			Input?.Clear();
			Network?.Dispose();
			Triggers?.Dispose();
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}
	}
	internal static class YamlScalar
	{
		public static string Uncomment(string text, int valueStart = -1)
		{
			if (text.TrimStart().StartsWith("#", StringComparison.Ordinal))
			{
				return "";
			}
			int i;
			for (i = ((valueStart >= 0) ? valueStart : (text.IndexOf(':') + 1)); i < text.Length && char.IsWhiteSpace(text[i]); i++)
			{
			}
			char c = ((i < text.Length && (text[i] == '\'' || text[i] == '"')) ? text[i] : '\0');
			bool flag = c == '\0';
			for (int j = ((!flag) ? (i + 1) : 0); j < text.Length; j++)
			{
				char c2 = text[j];
				if (!flag)
				{
					if (c == '"' && c2 == '\\')
					{
						j++;
					}
					else if (c == '\'' && c2 == '\'' && j + 1 < text.Length && text[j + 1] == '\'')
					{
						j++;
					}
					else if (c2 == c)
					{
						flag = true;
					}
				}
				else if (c2 == '#' && (j == 0 || char.IsWhiteSpace(text[j - 1])))
				{
					return text.Substring(0, j);
				}
			}
			if (!flag)
			{
				throw new FormatException("Unclosed YAML quote.");
			}
			return text;
		}

		public static string Read(string text, bool allowEmpty = false)
		{
			text = text.Trim();
			if (text.Length == 0)
			{
				if (allowEmpty)
				{
					return "";
				}
				throw new FormatException("Missing value.");
			}
			char c = text[0];
			if (c != '\'' && c != '"')
			{
				if (allowEmpty && (text == "~" || string.Equals(text, "null", StringComparison.OrdinalIgnoreCase)))
				{
					return "";
				}
				if (text == "|" || text == ">")
				{
					throw new FormatException("Use a quoted description with \\n for line breaks.");
				}
				return text;
			}
			if (text.Length < 2 || text[text.Length - 1] != c)
			{
				throw new FormatException("Invalid quoted value.");
			}
			StringBuilder stringBuilder = new StringBuilder();
			for (int i = 1; i < text.Length - 1; i++)
			{
				char c2 = text[i];
				if (c2 == c)
				{
					if (c == '\'' && i + 1 < text.Length - 1 && text[i + 1] == '\'')
					{
						stringBuilder.Append('\'');
						i++;
						continue;
					}
					throw new FormatException("Escape quotes inside a quoted value.");
				}
				if (c == '"' && c2 == '\\')
				{
					if (++i >= text.Length - 1)
					{
						throw new FormatException("Incomplete escape.");
					}
					c2 = text[i];
					switch (c2)
					{
					case 'n':
						c2 = '\n';
						break;
					case 't':
						c2 = '\t';
						break;
					case 'r':
						c2 = '\r';
						break;
					default:
						if (c2 != '"')
						{
							throw new FormatException("Unsupported escape; use \\n, \\t, \\r, \\\\ or \\\".");
						}
						break;
					case '\\':
						break;
					}
				}
				stringBuilder.Append(c2);
			}
			if (stringBuilder.Length == 0 && !allowEmpty)
			{
				throw new FormatException("Missing value.");
			}
			return stringBuilder.ToString();
		}
	}
	internal sealed class RollLedger
	{
		private readonly string folder;

		private readonly Dictionary<string, LootState> cache = new Dictionary<string, LootState>();

		private readonly Queue<string> cachedPaths = new Queue<string>();

		internal RollLedger(string path)
		{
			folder = path;
			Directory.CreateDirectory(folder);
		}

		private string PathFor(long player, string id)
		{
			if (player == 0L || !Guid.TryParseExact(id, "N", out var _))
			{
				throw new FormatException("Invalid roll identity.");
			}
			return Path.Combine(folder, player.ToString(CultureInfo.InvariantCulture) + "_" + id + ".receipt");
		}

		internal LootState GetOrCreate(long player, string id, string trigger, Func<LootState> make)
		{
			string text = PathFor(player, id);
			if (!cache.TryGetValue(text, out var value))
			{
				if (File.Exists(text))
				{
					value = LootState.Decode(File.ReadAllText(text));
				}
				else
				{
					value = make();
					string s = value.Encode();
					string text2 = text + ".tmp";
					using (FileStream fileStream = new FileStream(text2, FileMode.Create, FileAccess.Write, FileShare.None))
					{
						byte[] bytes = Encoding.UTF8.GetBytes(s);
						fileStream.Write(bytes, 0, bytes.Length);
						fileStream.Flush(flushToDisk: true);
					}
					File.Move(text2, text);
				}
				if (cache.Count >= 256)
				{
					cache.Remove(cachedPaths.Dequeue());
				}
				cache.Add(text, value);
				cachedPaths.Enqueue(text);
			}
			if (value.Trigger != trigger)
			{
				throw new FormatException("A roll id cannot be used with a different trigger.");
			}
			return value.Copy();
		}

		internal static List<Reward> Roll(LootTable table, Func<string, int> maxStack, Func<string, int> maxQuality)
		{
			byte[] bytes = new byte[4];
			RandomNumberGenerator rng = RandomNumberGenerator.Create();
			try
			{
				return Catalog.Roll(table, maxStack, delegate
				{
					rng.GetBytes(bytes);
					return (double)BitConverter.ToUInt32(bytes, 0) / 4294967296.0;
				}, maxQuality);
			}
			finally
			{
				if (rng != null)
				{
					((IDisposable)rng).Dispose();
				}
			}
		}
	}
	internal sealed class LootNetwork : IDisposable
	{
		internal sealed class Connection
		{
			internal ZNetPeer Peer;

			internal bool Compatible;

			internal bool HelloReceived;

			internal bool Rejected;

			internal float Started;

			internal float CloseAt;

			internal float NextRoll;
		}

		private const string Hello = "dannanboi.lootwindow.Hello";

		private const string Settings = "dannanboi.lootwindow.Settings";

		private const string Roll = "dannanboi.lootwindow.Roll";

		private const string Reply = "dannanboi.lootwindow.Reply";

		private readonly Plugin plugin;

		private ZNet network;

		private ZRpc server;

		private readonly Dictionary<ZRpc, Connection> connections = new Dictionary<ZRpc, Connection>();

		private readonly List<ZRpc> stale = new List<ZRpc>();

		private RollLedger ledger;

		private long ledgerWorld;

		private float nextCleanup;

		private bool serverEnabled;

		private bool publishedEnabled;

		private bool hasServerCatalog;

		private float serverPickupRadius = 3f;

		private float publishedPickupRadius;

		private string publishedYaml;

		private string serverYaml;

		private Catalog remoteCatalog = new Catalog();

		internal bool Authority => !Object.op_Implicit((Object)(object)network) || network.IsServer();

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

		internal bool Enabled => Authority ? plugin.EnableMod.Value : serverEnabled;

		internal Catalog Catalog => Authority ? plugin.LocalCatalog : remoteCatalog;

		internal float PickupRadius => Authority ? LootTokenPickup.ClampRadius(plugin.TriggerPickupRadius.Value) : serverPickupRadius;

		internal long World => Object.op_Implicit((Object)(object)network) ? network.GetWorldUID() : 0;

		internal LootNetwork(Plugin value)
		{
			plugin = value;
		}

		internal void Begin(ZNet value)
		{
			if (network == value)
			{
				return;
			}
			foreach (ZRpc key in connections.Keys)
			{
				Unregister(key);
			}
			connections.Clear();
			server = null;
			ledger = null;
			ledgerWorld = 0L;
			network = value;
			Ready = Authority;
			serverEnabled = (hasServerCatalog = false);
			serverYaml = null;
			serverPickupRadius = 3f;
			publishedEnabled = plugin.EnableMod.Value;
			publishedYaml = plugin.LocalYaml;
			publishedPickupRadius = PickupRadius;
			remoteCatalog = new Catalog();
			plugin.Loot?.InventoryChanged();
			plugin.Triggers?.Invalidate();
		}

		internal void Register(ZNet value, ZNetPeer peer)
		{
			if (Object.op_Implicit((Object)(object)value) && peer != null && peer.m_rpc != null)
			{
				Begin(value);
				if ((Authority || peer.m_server) && !connections.ContainsKey(peer.m_rpc))
				{
					connections.Add(peer.m_rpc, new Connection
					{
						Peer = peer,
						Started = Time.realtimeSinceStartup
					});
					peer.m_rpc.Register<string>("dannanboi.lootwindow.Hello", (Action<ZRpc, string>)ReceiveHello);
					peer.m_rpc.Register<ZPackage>("dannanboi.lootwindow.Settings", (Action<ZRpc, ZPackage>)ReceiveSettings);
					peer.m_rpc.Register<ZPackage>("dannanboi.lootwindow.Roll", (Action<ZRpc, ZPackage>)ReceiveRoll);
					peer.m_rpc.Register<ZPackage>("dannanboi.lootwindow.Reply", (Action<ZRpc, ZPackage>)ReceiveReply);
					peer.m_rpc.Invoke("dannanboi.lootwindow.Hello", new object[1] { "1.0.1" });
				}
			}
		}

		private void ReceiveHello(ZRpc rpc, string version)
		{
			if (connections.TryGetValue(rpc, out var value) && !value.HelloReceived)
			{
				value.HelloReceived = true;
				value.Compatible = version == "1.0.1";
				if (!value.Compatible)
				{
					Reject(rpc, value, "Loot Window versions differ. Install 1.0.1 on clients and server.");
				}
				else if (Authority)
				{
					SendSettings(rpc);
				}
				else
				{
					server = rpc;
				}
			}
		}

		private void Reject(ZRpc rpc, Connection c, string message)
		{
			c.Rejected = true;
			c.CloseAt = Time.realtimeSinceStartup + 2f;
			plugin.Warn(message);
			if (!Authority)
			{
				Ready = false;
				plugin.Notice(message);
			}
			if (rpc.IsConnected())
			{
				rpc.Invoke("Error", new object[1] { 3 });
			}
		}

		private void SendSettings(ZRpc rpc)
		{
			rpc.Invoke("dannanboi.lootwindow.Settings", new object[1] { SettingsPacket(includeTables: true) });
		}

		private ZPackage SettingsPacket(bool includeTables)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write(6);
			val.Write(plugin.EnableMod.Value);
			val.Write(PickupRadius);
			val.Write(includeTables);
			if (includeTables)
			{
				val.Write(plugin.LocalYaml);
			}
			return val;
		}

		internal void Publish()
		{
			if (!Authority)
			{
				return;
			}
			bool flag = publishedYaml != plugin.LocalYaml;
			bool flag2 = publishedEnabled != plugin.EnableMod.Value;
			if (!flag2 && !flag && publishedPickupRadius == PickupRadius)
			{
				return;
			}
			publishedEnabled = plugin.EnableMod.Value;
			publishedYaml = plugin.LocalYaml;
			publishedPickupRadius = PickupRadius;
			ZPackage val = null;
			foreach (KeyValuePair<ZRpc, Connection> connection in connections)
			{
				if (connection.Value.Compatible && !connection.Value.Rejected && connection.Key.IsConnected())
				{
					if (val == null)
					{
						val = SettingsPacket(flag);
					}
					connection.Key.Invoke("dannanboi.lootwindow.Settings", new object[1] { val });
				}
			}
			if (flag)
			{
				plugin.Triggers?.Invalidate();
			}
			if (flag || flag2)
			{
				plugin.Loot?.InventoryChanged();
			}
		}

		private bool FromServer(ZRpc rpc)
		{
			Connection value;
			return !Authority && connections.TryGetValue(rpc, out value) && value.Peer.m_server && value.Compatible && !value.Rejected && rpc.IsConnected();
		}

		private void ReceiveSettings(ZRpc rpc, ZPackage packet)
		{
			if (!FromServer(rpc))
			{
				return;
			}
			try
			{
				if (packet == null || packet.Size() > 65604 || packet.ReadInt() != 6)
				{
					throw new FormatException("Invalid server settings packet.");
				}
				bool flag = packet.ReadBool();
				float num = packet.ReadSingle();
				bool flag2 = packet.ReadBool();
				if (float.IsNaN(num) || float.IsInfinity(num) || num < 1f || num > 6f)
				{
					throw new FormatException("Invalid server trigger pickup radius.");
				}
				if (!flag2 && !hasServerCatalog)
				{
					throw new FormatException("Initial server settings must include loot tables.");
				}
				bool flag3 = false;
				if (flag2)
				{
					string text = packet.ReadString();
					flag3 = !hasServerCatalog || text != serverYaml;
					if (flag3)
					{
						Catalog catalog = Catalog.Parse(text);
						remoteCatalog = catalog;
						serverYaml = text;
						hasServerCatalog = true;
					}
				}
				bool flag4 = serverEnabled != flag;
				serverEnabled = flag;
				serverPickupRadius = num;
				server = rpc;
				Ready = true;
				if (flag3)
				{
					plugin.Triggers?.Invalidate();
					plugin.Triggers?.Apply();
				}
				if (flag3 || flag4)
				{
					plugin.Loot.InventoryChanged();
				}
			}
			catch (Exception ex)
			{
				Ready = false;
				plugin.Warn("Server loot settings rejected; triggers kept in inventory. " + ex.Message);
			}
		}

		internal void Request(LootState state, long player)
		{
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Expected O, but got Unknown
			if (!Ready || !Enabled)
			{
				plugin.Loot.RollFailed(state.Id, "Loot Window is disabled or server settings are unavailable.");
				return;
			}
			if (Authority)
			{
				try
				{
					plugin.Loot.Receive(Generate(player, state.Id, state.Trigger));
					return;
				}
				catch (Exception ex)
				{
					plugin.Loot.RollFailed(state.Id, ex.Message);
					return;
				}
			}
			if (server != null && server.IsConnected())
			{
				ZPackage val = new ZPackage();
				val.Write(state.Id);
				val.Write(state.Trigger);
				val.Write(player);
				server.Invoke("dannanboi.lootwindow.Roll", new object[1] { val });
			}
		}

		private LootState Generate(long player, string id, string trigger)
		{
			if (ledger == null || ledgerWorld != World)
			{
				ledgerWorld = World;
				ledger = new RollLedger(Path.Combine(Paths.ConfigPath, "LootWindow", "state", "server", World.ToString(CultureInfo.InvariantCulture)));
			}
			return ledger.GetOrCreate(player, id, trigger, delegate
			{
				if (!Catalog.Tables.TryGetValue(trigger, out var value))
				{
					throw new FormatException("Trigger has no active loot table: " + trigger);
				}
				LootState lootState = new LootState
				{
					Id = id,
					Trigger = trigger,
					Name = value.Name,
					EmptyLootText = value.EmptyLootText,
					World = World
				};
				lootState.Rewards.AddRange(RollLedger.Roll(value, Plugin.MaxStack, Plugin.MaxQuality));
				return lootState;
			});
		}

		private void ReceiveRoll(ZRpc rpc, ZPackage packet)
		{
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Expected O, but got Unknown
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Expected O, but got Unknown
			if (!Authority || !Ready || !connections.TryGetValue(rpc, out var value) || !value.Compatible || value.Rejected || !rpc.IsConnected() || packet == null || packet.Size() > 512 || Time.realtimeSinceStartup < value.NextRoll)
			{
				return;
			}
			value.NextRoll = Time.realtimeSinceStartup + 0.5f;
			string text = null;
			Guid result;
			try
			{
				text = packet.ReadString();
				string text2 = packet.ReadString();
				long num = packet.ReadLong();
				if (Guid.TryParseExact(text, "N", out result) && text2.Length != 0 && text2.Length <= 128)
				{
					if (!Enabled)
					{
						throw new FormatException("Loot Window is disabled on the server.");
					}
					ZDOMan instance = ZDOMan.instance;
					ZDO val = ((instance != null) ? instance.GetZDO(value.Peer.m_characterID) : null);
					if (val != null && val.GetOwner() == value.Peer.m_uid && val.GetLong("playerID", 0L) == num && num != 0)
					{
						LootState lootState = Generate(num, text, text2);
						ZPackage val2 = new ZPackage();
						val2.Write(text);
						val2.Write(true);
						val2.Write(lootState.Encode());
						rpc.Invoke("dannanboi.lootwindow.Reply", new object[1] { val2 });
					}
				}
			}
			catch (Exception ex)
			{
				if (text != null && Guid.TryParseExact(text, "N", out result))
				{
					ZPackage val3 = new ZPackage();
					val3.Write(text);
					val3.Write(false);
					val3.Write("The server could not prepare this loot table.");
					rpc.Invoke("dannanboi.lootwindow.Reply", new object[1] { val3 });
					plugin.Warn("Loot roll failed: " + ex.Message);
				}
			}
		}

		private void ReceiveReply(ZRpc rpc, ZPackage packet)
		{
			if (!FromServer(rpc) || rpc != server || packet == null || packet.Size() > 12288)
			{
				return;
			}
			try
			{
				string text = packet.ReadString();
				bool flag = packet.ReadBool();
				string text2 = packet.ReadString();
				if (!flag)
				{
					plugin.Loot.RollFailed(text, text2);
					return;
				}
				LootState lootState = LootState.Decode(text2);
				if (!(text != lootState.Id) && lootState.World == World)
				{
					plugin.Loot.Receive(lootState);
				}
			}
			catch (Exception ex)
			{
				plugin.Warn("Loot reply rejected; trigger kept. " + ex.Message);
			}
		}

		internal void Tick(float now)
		{