Decompiled source of Taschenhaendler v1.1.2

CBra.Taschenhaendler.dll

Decompiled 8 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using TMPro;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("CBra.Taschenhaendler")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.2.0")]
[assembly: AssemblyInformationalVersion("1.1.2")]
[assembly: AssemblyProduct("CBra.Taschenhaendler")]
[assembly: AssemblyTitle("CBra.Taschenhaendler")]
[assembly: AssemblyVersion("1.1.2.0")]
namespace CBra.Erenshor.Taschenhaendler;

internal sealed class ExtraInventorySlotMarker : MonoBehaviour
{
}
internal sealed class InventoryScrollView : MonoBehaviour
{
	private Inventory _inventory;

	private RectTransform _viewport;

	private RectTransform _content;

	private ScrollRect _scrollRect;

	private GameObject _scrollbarObject;

	private readonly List<float> _columnOffsets = new List<float>();

	private readonly HashSet<ItemIcon> _displayedSlots = new HashSet<ItemIcon>();

	private float _rowSpacing = 70.5f;

	private float _slotVisualHeight = 52f;

	private const float ToolbarHeight = 86f;

	private RectMask2D _mask;

	internal static InventoryScrollView Create(Inventory inventory, IReadOnlyList<ItemIcon> usableSlots)
	{
		//IL_0123: Unknown result type (might be due to invalid IL or missing references)
		//IL_0129: Expected O, but got Unknown
		if ((Object)(object)inventory == (Object)null || usableSlots == null || usableSlots.Count == 0)
		{
			return null;
		}
		List<RectTransform> list = (from root in usableSlots.Select(InventoryExpansionController.GetSlotRoot)
			where (Object)(object)root != (Object)null
			select root).Distinct().ToList();
		if (list.Count == 0)
		{
			return null;
		}
		RectTransform commonParent = default(RectTransform);
		ref RectTransform reference = ref commonParent;
		Transform parent = ((Transform)list[0]).parent;
		reference = (RectTransform)(object)((parent is RectTransform) ? parent : null);
		if ((Object)(object)commonParent == (Object)null || list.Any((RectTransform root) => (Object)(object)((Transform)root).parent != (Object)(object)commonParent))
		{
			TaschenhaendlerPlugin.ModLog.LogWarning((object)"Inventar-Scrollbereich konnte nicht gebaut werden: Slot-Container haben unterschiedliche Eltern.");
			return null;
		}
		InventoryScrollView componentInChildren = ((Component)commonParent).GetComponentInChildren<InventoryScrollView>(true);
		if ((Object)(object)componentInChildren != (Object)null)
		{
			componentInChildren._inventory = inventory;
			componentInChildren.LayoutSlots();
			return componentInChildren;
		}
		GameObject val = new GameObject("Taschenhaendler_ScrollViewport", new Type[5]
		{
			typeof(RectTransform),
			typeof(Image),
			typeof(RectMask2D),
			typeof(ScrollRect),
			typeof(InventoryScrollView)
		});
		RectTransform component = val.GetComponent<RectTransform>();
		((Transform)component).SetParent((Transform)(object)commonParent, false);
		((Transform)component).SetSiblingIndex(Math.Max(0, list.Min((RectTransform root) => ((Transform)root).GetSiblingIndex())));
		InventoryScrollView component2 = val.GetComponent<InventoryScrollView>();
		component2._inventory = inventory;
		component2._viewport = component;
		component2._scrollRect = val.GetComponent<ScrollRect>();
		component2._mask = val.GetComponent<RectMask2D>();
		component2.ConfigureGeometry(list);
		component2.BuildContent(commonParent);
		component2.BuildScrollbar(commonParent);
		component2.ConfigureScrollRect();
		component2.BuildTools(commonParent);
		component2.LayoutSlots();
		return component2;
	}

	internal void LayoutSlots(bool resetScroll = false)
	{
		//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_014f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0165: 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_0217: 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_01f5: Unknown result type (might be due to invalid IL or missing references)
		//IL_027a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0264: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)_inventory == (Object)null || (Object)(object)_content == (Object)null || (Object)(object)_viewport == (Object)null)
		{
			return;
		}
		List<ItemIcon> list = _inventory.StoredSlots.Where(InventoryExpansionController.IsScrollableInventorySlot).ToList();
		List<ItemIcon> displayedSlots = InventoryToolsState.GetDisplayedSlots(list, _inventory.Empty);
		_displayedSlots.Clear();
		foreach (ItemIcon item in displayedSlots)
		{
			_displayedSlots.Add(item);
		}
		int num = Math.Max(1, _columnOffsets.Count);
		int num2 = Math.Max(1, Mathf.CeilToInt((float)displayedSlots.Count / (float)num));
		Rect rect = _viewport.rect;
		float num3 = Math.Max(((Rect)(ref rect)).height, _slotVisualHeight + (float)Math.Max(0, num2 - 1) * _rowSpacing + 4f);
		_content.SetSizeWithCurrentAnchors((Axis)1, num3);
		foreach (ItemIcon item2 in list)
		{
			RectTransform slotRoot = InventoryExpansionController.GetSlotRoot(item2);
			if (!((Object)(object)slotRoot == (Object)null))
			{
				((Transform)slotRoot).SetParent((Transform)(object)_content, false);
				slotRoot.anchorMin = new Vector2(0.5f, 1f);
				slotRoot.anchorMax = new Vector2(0.5f, 1f);
				slotRoot.pivot = new Vector2(0.5f, 0.5f);
			}
		}
		for (int i = 0; i < displayedSlots.Count; i++)
		{
			RectTransform slotRoot2 = InventoryExpansionController.GetSlotRoot(displayedSlots[i]);
			if (!((Object)(object)slotRoot2 == (Object)null))
			{
				int index = i % num;
				int num4 = i / num;
				slotRoot2.anchoredPosition = new Vector2(_columnOffsets[index], 0f - (_slotVisualHeight * 0.5f + 2f + (float)num4 * _rowSpacing));
			}
		}
		rect = _viewport.rect;
		bool flag = num3 > ((Rect)(ref rect)).height + 1f;
		if ((Object)(object)_scrollbarObject != (Object)null)
		{
			_scrollbarObject.SetActive(flag);
		}
		if (resetScroll)
		{
			ScrollRect scrollRect = _scrollRect;
			if (scrollRect != null)
			{
				scrollRect.StopMovement();
			}
			_content.anchoredPosition = Vector2.zero;
		}
		else if (!flag)
		{
			_content.anchoredPosition = Vector2.zero;
		}
		Canvas.ForceUpdateCanvases();
		UpdateVisibility();
	}

	private void ConfigureGeometry(IReadOnlyList<RectTransform> roots)
	{
		//IL_0221: Unknown result type (might be due to invalid IL or missing references)
		//IL_0226: Unknown result type (might be due to invalid IL or missing references)
		//IL_0230: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f6: Unknown result type (might be due to invalid IL or missing references)
		//IL_035b: Unknown result type (might be due to invalid IL or missing references)
		//IL_036c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0386: 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_03bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_03e7: Unknown result type (might be due to invalid IL or missing references)
		List<float> list = (from root in roots
			select root.anchoredPosition.x into value
			orderby value
			select value).ToList();
		List<float> list2 = new List<float>();
		foreach (float x in list)
		{
			if (list2.All((float existing) => Mathf.Abs(existing - x) > 5f))
			{
				list2.Add(x);
			}
		}
		if (list2.Count == 0)
		{
			list2.AddRange(new float[3] { -67f, 0f, 67f });
		}
		float num = (list2.First() + list2.Last()) * 0.5f;
		_columnOffsets.Clear();
		foreach (float item in list2)
		{
			_columnOffsets.Add(item - num);
		}
		List<float> list3 = (from value in roots.Select((RectTransform root) => root.anchoredPosition.y).Distinct()
			orderby value descending
			select value).ToList();
		if (list3.Count > 1)
		{
			List<float> list4 = new List<float>();
			for (int num2 = 1; num2 < list3.Count; num2++)
			{
				float num3 = list3[num2 - 1] - list3[num2];
				if (num3 > 10f)
				{
					list4.Add(num3);
				}
			}
			if (list4.Count > 0)
			{
				_rowSpacing = list4.OrderBy((float value) => value).ElementAt(list4.Count / 2);
			}
		}
		RectTransform val = roots[0];
		Rect rect = val.rect;
		_slotVisualHeight = Math.Max(40f, ((Rect)(ref rect)).height * Math.Abs(((Transform)val).localScale.y));
		float num4 = roots.Min((RectTransform root) => root.anchoredPosition.x);
		float num5 = roots.Max((RectTransform root) => root.anchoredPosition.x);
		float num6 = roots.Min((RectTransform root) => root.anchoredPosition.y);
		float num7 = roots.Max((RectTransform root) => root.anchoredPosition.y);
		rect = val.rect;
		float num8 = ((Rect)(ref rect)).width * Math.Abs(((Transform)val).localScale.x);
		float num9 = num5 - num4 + num8 + 12f;
		float num10 = num7 - num6 + _slotVisualHeight + 8f;
		float num11 = Math.Max(_slotVisualHeight * 3f, num10 - 86f);
		float num12 = (num6 + num7) * 0.5f;
		_viewport.anchorMin = val.anchorMin;
		_viewport.anchorMax = val.anchorMax;
		_viewport.pivot = new Vector2(0.5f, 0.5f);
		_viewport.anchoredPosition = new Vector2((num4 + num5) * 0.5f, num12 - 43f);
		_viewport.sizeDelta = new Vector2(num9, num11);
		Image component = ((Component)_viewport).GetComponent<Image>();
		((Graphic)component).color = new Color(0f, 0f, 0f, 0.001f);
		((Graphic)component).raycastTarget = true;
	}

	private void BuildContent(RectTransform commonParent)
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Expected O, but got Unknown
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		//IL_0080: 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_00ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b0: 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)
		GameObject val = new GameObject("Content", new Type[1] { typeof(RectTransform) });
		_content = val.GetComponent<RectTransform>();
		((Transform)_content).SetParent((Transform)(object)_viewport, false);
		_content.anchorMin = new Vector2(0f, 1f);
		_content.anchorMax = new Vector2(1f, 1f);
		_content.pivot = new Vector2(0.5f, 1f);
		_content.anchoredPosition = Vector2.zero;
		RectTransform content = _content;
		Rect rect = _viewport.rect;
		content.sizeDelta = new Vector2(0f, ((Rect)(ref rect)).height);
	}

	private void BuildScrollbar(RectTransform commonParent)
	{
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		//IL_003d: Expected O, but got Unknown
		//IL_0058: Unknown result type (might be due to invalid IL or missing references)
		//IL_0069: 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_008f: 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)
		//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d0: 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_0103: Unknown result type (might be due to invalid IL or missing references)
		//IL_0132: Unknown result type (might be due to invalid IL or missing references)
		//IL_0137: Unknown result type (might be due to invalid IL or missing references)
		//IL_0147: Unknown result type (might be due to invalid IL or missing references)
		//IL_0152: Unknown result type (might be due to invalid IL or missing references)
		//IL_0167: Unknown result type (might be due to invalid IL or missing references)
		//IL_017c: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
		_scrollbarObject = new GameObject("Taschenhaendler_Scrollbar", new Type[3]
		{
			typeof(RectTransform),
			typeof(Image),
			typeof(Scrollbar)
		});
		RectTransform component = _scrollbarObject.GetComponent<RectTransform>();
		((Transform)component).SetParent((Transform)(object)commonParent, false);
		component.anchorMin = _viewport.anchorMin;
		component.anchorMax = _viewport.anchorMax;
		component.pivot = new Vector2(0.5f, 0.5f);
		component.anchoredPosition = _viewport.anchoredPosition + new Vector2(_viewport.sizeDelta.x * 0.5f - 5f, 0f);
		component.sizeDelta = new Vector2(9f, _viewport.sizeDelta.y);
		((Graphic)_scrollbarObject.GetComponent<Image>()).color = new Color(0.02f, 0.02f, 0.02f, 0.7f);
		GameObject val = new GameObject("Handle", new Type[2]
		{
			typeof(RectTransform),
			typeof(Image)
		});
		RectTransform component2 = val.GetComponent<RectTransform>();
		((Transform)component2).SetParent((Transform)(object)component, false);
		component2.anchorMin = Vector2.zero;
		component2.anchorMax = Vector2.one;
		component2.offsetMin = new Vector2(1f, 1f);
		component2.offsetMax = new Vector2(-1f, -1f);
		Image component3 = val.GetComponent<Image>();
		((Graphic)component3).color = new Color(0.72f, 0.55f, 0.22f, 0.95f);
		Scrollbar component4 = _scrollbarObject.GetComponent<Scrollbar>();
		component4.handleRect = component2;
		((Selectable)component4).targetGraphic = (Graphic)(object)component3;
		component4.direction = (Direction)2;
		component4.numberOfSteps = 0;
		_scrollRect.verticalScrollbar = component4;
	}

	private void BuildTools(RectTransform commonParent)
	{
		//IL_0008: 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_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_005d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		Vector2 anchoredPosition = default(Vector2);
		((Vector2)(ref anchoredPosition))..ctor(_viewport.anchoredPosition.x, _viewport.anchoredPosition.y + _viewport.sizeDelta.y * 0.5f + 43f);
		InventoryToolsPanel.Create(_inventory, this, commonParent, _viewport.anchorMin, _viewport.anchorMax, anchoredPosition, new Vector2(_viewport.sizeDelta.x, 82f));
	}

	private void ConfigureScrollRect()
	{
		_scrollRect.content = _content;
		_scrollRect.viewport = _viewport;
		_scrollRect.horizontal = false;
		_scrollRect.vertical = true;
		_scrollRect.movementType = (MovementType)2;
		_scrollRect.inertia = true;
		_scrollRect.decelerationRate = 0.135f;
		_scrollRect.scrollSensitivity = TaschenhaendlerPlugin.Instance.ScrollSensitivity.Value;
		_scrollRect.verticalScrollbarVisibility = (ScrollbarVisibility)0;
		((UnityEvent<Vector2>)(object)_scrollRect.onValueChanged).AddListener((UnityAction<Vector2>)delegate
		{
			UpdateVisibility();
		});
	}

	private void UpdateVisibility()
	{
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)_inventory == (Object)null || (Object)(object)_viewport == (Object)null)
		{
			return;
		}
		Rect worldRect = GetWorldRect(_viewport);
		foreach (ItemIcon item in _inventory.StoredSlots.Where(InventoryExpansionController.IsScrollableInventorySlot))
		{
			RectTransform slotRoot = InventoryExpansionController.GetSlotRoot(item);
			bool num = _displayedSlots.Contains(item);
			bool flag = num && (Object)(object)slotRoot != (Object)null && ((Rect)(ref worldRect)).Overlaps(GetWorldRect(slotRoot), true);
			bool flag2 = num && (flag || item.dragging);
			BoxCollider2D component = ((Component)item).GetComponent<BoxCollider2D>();
			if ((Object)(object)component != (Object)null)
			{
				((Behaviour)component).enabled = flag2;
			}
			Image component2 = ((Component)item).GetComponent<Image>();
			if ((Object)(object)component2 != (Object)null)
			{
				((Graphic)component2).raycastTarget = flag2;
			}
			if ((Object)(object)slotRoot != (Object)null)
			{
				CanvasGroup obj = ((Component)slotRoot).GetComponent<CanvasGroup>() ?? ((Component)slotRoot).gameObject.AddComponent<CanvasGroup>();
				obj.alpha = (flag2 ? 1f : 0f);
				obj.blocksRaycasts = flag2;
			}
		}
	}

	private void LateUpdate()
	{
		if (!((Object)(object)_inventory == (Object)null) && !((Object)(object)_mask == (Object)null))
		{
			bool flag = _inventory.StoredSlots.Where(InventoryExpansionController.IsScrollableInventorySlot).Any((ItemIcon slot) => slot.dragging);
			if (((Behaviour)_mask).enabled == flag)
			{
				((Behaviour)_mask).enabled = !flag;
				UpdateVisibility();
			}
		}
	}

	private static Rect GetWorldRect(RectTransform rectTransform)
	{
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		Vector3[] array = (Vector3[])(object)new Vector3[4];
		rectTransform.GetWorldCorners(array);
		return Rect.MinMaxRect(array[0].x, array[0].y, array[2].x, array[2].y);
	}
}
internal sealed class SaveIsolationState
{
	internal bool CanProceed = true;

	internal bool Prepared;

	internal List<ItemIcon> Extras;

	internal List<HiddenExpansionItemState> HiddenExpansionItems;

	internal Dictionary<Hotkeys, int> HotkeyIndices = new Dictionary<Hotkeys, int>();
}
internal sealed class HiddenExpansionItemState
{
	internal ItemIcon Slot;

	internal Item Item;

	internal int Quantity;
}
internal static class InventoryExpansionController
{
	private sealed class IOExceptionLikeException : Exception
	{
	}

	private static Inventory _inventory;

	private static ItemIcon _trashSlot;

	private static readonly List<ItemIcon> ExtraSlots = new List<ItemIcon>();

	private static InventoryScrollView _scrollView;

	private static CharacterProgress _loadedProgress;

	private static readonly HashSet<int> UnresolvedOverflowIndices = new HashSet<int>();

	private static bool _sidecarAlreadyCommittedForSave;

	private static bool _nativeSaveProbeActive;

	private static bool _nativeSaveSucceeded;

	private static bool _restoringPendingExpansionItems;

	private static bool _pendingRecoveryNoticeShown;

	internal static RectTransform GetSlotRoot(ItemIcon slot)
	{
		object obj;
		if (slot == null)
		{
			obj = null;
		}
		else
		{
			Transform transform = ((Component)slot).transform;
			obj = ((transform != null) ? transform.parent : null);
		}
		return (RectTransform)((obj is RectTransform) ? obj : null);
	}

	internal static bool IsReservedUnresolvedSlot(ItemIcon slot)
	{
		int num = ExtraSlots.IndexOf(slot);
		if (num >= 0)
		{
			return UnresolvedOverflowIndices.Contains(num);
		}
		return false;
	}

	internal static bool IsScrollableInventorySlot(ItemIcon slot)
	{
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)slot != (Object)null && (int)slot.ThisSlotType == 0 && !slot.TrashSlot && !slot.VendorSlot && !slot.LootSlot && !slot.BankSlot && !slot.DisplayOnly && !slot.NotInInventory)
		{
			return !slot.CosmeticOnly;
		}
		return false;
	}

	internal static void RefreshLayout()
	{
		_scrollView?.LayoutSlots();
	}

	internal static void RestorePendingExpansionItems()
	{
		if (_restoringPendingExpansionItems || (Object)(object)_inventory == (Object)null || _loadedProgress == null || (Object)(object)MerchantController.ExpansionItem == (Object)null)
		{
			return;
		}
		long num = Math.Max(0L, _loadedProgress.PurchasedCount - _loadedProgress.AppliedCount);
		long num2 = 0L;
		foreach (ItemIcon storedSlot in _inventory.StoredSlots)
		{
			if ((Object)(object)storedSlot != (Object)null && TaschenhaendlerPlugin.IsExpansion(storedSlot.MyItem))
			{
				num2 = checked(num2 + Math.Max(1L, storedSlot.Quantity));
			}
		}
		long num3 = Math.Max(0L, num - num2);
		if (num3 == 0L)
		{
			_pendingRecoveryNoticeShown = false;
			return;
		}
		_restoringPendingExpansionItems = true;
		long num4 = 0L;
		try
		{
			foreach (ItemIcon item in _inventory.StoredSlots.Where((ItemIcon entry) => (Object)(object)entry != (Object)null && TaschenhaendlerPlugin.IsExpansion(entry.MyItem)))
			{
				if (num3 <= 0)
				{
					break;
				}
				long val = int.MaxValue - Math.Max(0, item.Quantity);
				int num5 = (int)Math.Min(num3, val);
				if (num5 > 0)
				{
					item.Quantity += num5;
					num3 -= num5;
					num4 += num5;
					item.UpdateSlotImage();
				}
			}
			foreach (ItemIcon item2 in _inventory.StoredSlots.Where((ItemIcon entry) => IsUsableEmptyInventorySlot(entry)).ToList())
			{
				if (num3 <= 0)
				{
					break;
				}
				int num6 = (int)((num3 > int.MaxValue) ? int.MaxValue : num3);
				item2.MyItem = MerchantController.ExpansionItem;
				item2.Quantity = num6;
				num3 -= num6;
				num4 += num6;
				item2.UpdateSlotImage();
			}
			if (num4 > 0)
			{
				_inventory.UpdatePlayerInventory();
				TaschenhaendlerPlugin.NotifyPlayer(ModLocalization.Format("{0} bereits bezahlte Inventarerweiterung(en) wurden aus dem geschützten Kaufstand wiederhergestellt.", "{0} previously purchased inventory expansion(s) were restored from protected progress.", num4));
			}
			if (num3 > 0 && !_pendingRecoveryNoticeShown)
			{
				_pendingRecoveryNoticeShown = true;
				TaschenhaendlerPlugin.NotifyPlayer(ModLocalization.Format("Für {0} bezahlte Erweiterung(en) ist noch kein freier Inventarplatz vorhanden. Räume einen Platz frei; die Wiederherstellung wird beim nächsten Laden erneut versucht.", "There is no free inventory slot yet for {0} purchased expansion(s). Free one slot; restoration will be retried on the next load.", num3));
			}
		}
		finally
		{
			_restoringPendingExpansionItems = false;
		}
	}

	internal static bool SaveNativeAfterSidecarCommit()
	{
		_sidecarAlreadyCommittedForSave = true;
		_nativeSaveProbeActive = true;
		_nativeSaveSucceeded = false;
		try
		{
			if ((Object)(object)GameData.GM == (Object)null)
			{
				return false;
			}
			try
			{
				GameData.GM.SaveGameData(false);
			}
			catch (Exception ex)
			{
				if (!_nativeSaveSucceeded)
				{
					throw;
				}
				TaschenhaendlerPlugin.ModLog.LogWarning((object)("Der Charakterstand wurde gespeichert, aber ein nachgelagerter Erenshor-Speicherschritt meldete einen Fehler: " + ex.Message));
			}
			return _nativeSaveSucceeded;
		}
		finally
		{
			_nativeSaveProbeActive = false;
			_sidecarAlreadyCommittedForSave = false;
		}
	}

	internal static void ReportNativeSaveResult(bool succeeded)
	{
		if (_nativeSaveProbeActive)
		{
			_nativeSaveSucceeded = succeeded;
		}
	}

	internal static void Initialize(Inventory inventory)
	{
		if ((Object)(object)inventory == (Object)null || !inventory.isPlayer)
		{
			return;
		}
		if ((Object)(object)_inventory != (Object)(object)inventory)
		{
			_inventory = inventory;
			_trashSlot = ((IEnumerable<ItemIcon>)inventory.StoredSlots).FirstOrDefault((Func<ItemIcon, bool>)((ItemIcon slot) => (Object)(object)slot != (Object)null && slot.TrashSlot));
			ExtraSlots.Clear();
			ExtraSlots.AddRange(inventory.StoredSlots.Where(delegate(ItemIcon slot)
			{
				RectTransform slotRoot = GetSlotRoot(slot);
				return (Object)(object)slotRoot != (Object)null && (Object)(object)((Component)slotRoot).GetComponent<ExtraInventorySlotMarker>() != (Object)null;
			}));
			_scrollView = null;
			_loadedProgress = null;
			_pendingRecoveryNoticeShown = false;
		}
		List<ItemIcon> usableSlots = inventory.StoredSlots.Where(IsScrollableInventorySlot).ToList();
		if (_scrollView == null)
		{
			_scrollView = InventoryScrollView.Create(inventory, usableSlots);
		}
		RebuildAllSlots();
	}

	internal static bool CaptureCurrentBeforeCharacterSwitch()
	{
		if (_loadedProgress == null || (Object)(object)_inventory == (Object)null)
		{
			return true;
		}
		CaptureOverflow(_loadedProgress);
		bool num = ProgressStore.Save();
		if (!num)
		{
			TaschenhaendlerPlugin.NotifyPlayer(ModLocalization.Text("Charakterwechsel abgebrochen: Das Zusatzinventar konnte nicht gespeichert werden.", "Character switch cancelled: the extra inventory could not be saved."));
		}
		return num;
	}

	internal static void ClearLoadedCharacter()
	{
		if ((Object)(object)_inventory != (Object)null)
		{
			EnsureExtraSlotCount(0);
		}
		_loadedProgress = null;
		UnresolvedOverflowIndices.Clear();
		_pendingRecoveryNoticeShown = false;
	}

	internal static void LoadCharacter(CharacterProgress progress)
	{
		if ((Object)(object)GameData.PlayerInv == (Object)null || progress == null)
		{
			return;
		}
		Initialize(GameData.PlayerInv);
		_loadedProgress = progress;
		UnresolvedOverflowIndices.Clear();
		foreach (ItemIcon extraSlot in ExtraSlots)
		{
			if ((Object)(object)extraSlot != (Object)null)
			{
				extraSlot.assignedHotkey = null;
			}
		}
		EnsureExtraSlotCount(GetDesiredExtraSlotCount(progress));
		MerchantController.RefreshOffer();
	}

	internal static void FinishCharacterLoad(CharacterProgress progress)
	{
		if ((Object)(object)_inventory == (Object)null || progress == null)
		{
			return;
		}
		for (int i = 0; i < ExtraSlots.Count; i++)
		{
			StoredItem storedItem = ((i < progress.Overflow.Count) ? progress.Overflow[i] : null);
			ItemIcon slot = ExtraSlots[i];
			if (storedItem == null || string.IsNullOrEmpty(storedItem.Id))
			{
				SetSlot(slot, _inventory.Empty, 0);
				continue;
			}
			ItemDatabase itemDB = GameData.ItemDB;
			Item val = ((itemDB != null) ? itemDB.GetItemByID(storedItem.Id) : null);
			if ((Object)(object)val == (Object)null || (Object)(object)val == (Object)(object)_inventory.Empty)
			{
				TaschenhaendlerPlugin.ModLog.LogWarning((object)$"Zusatzslot {i} verweist auf unbekanntes Item '{storedItem.Id}'. Der Eintrag bleibt in der Sidecar-Datei erhalten.");
				UnresolvedOverflowIndices.Add(i);
				SetSlot(slot, _inventory.Empty, 0);
			}
			else
			{
				SetSlot(slot, val, Math.Max(1, storedItem.Quantity));
			}
		}
		_inventory.UpdatePlayerInventory();
		RestorePendingExpansionItems();
		_scrollView?.LayoutSlots();
	}

	internal static void FinishHotkeyLoad(CharacterProgress progress)
	{
		if (progress != null && !((Object)(object)GameData.GM?.HKManager == (Object)null))
		{
			RestoreHotkeyList(GameData.GM.HKManager.FirstHotkeys, progress.FirstExtraHotkeys);
			RestoreHotkeyList(GameData.GM.HKManager.SecondHotkeys, progress.SecondExtraHotkeys);
		}
	}

	internal static bool TryUseExpansion(ItemIcon sourceSlot)
	{
		CharacterProgress current = ProgressStore.Current;
		if ((Object)(object)_inventory == (Object)null || current == null || (Object)(object)sourceSlot == (Object)null)
		{
			TaschenhaendlerPlugin.NotifyPlayer(ModLocalization.Text("Kein aktiver Charakterfortschritt gefunden.", "No active character progress was found."));
			return false;
		}
		if (sourceSlot.VendorSlot || !_inventory.StoredSlots.Contains(sourceSlot) || !TaschenhaendlerPlugin.IsExpansion(sourceSlot.MyItem))
		{
			TaschenhaendlerPlugin.ModLog.LogWarning((object)"Benutzung eines Erweiterungsitems außerhalb des Spielerinventars wurde blockiert.");
			return false;
		}
		if (current.AppliedCount >= current.PurchasedCount)
		{
			TaschenhaendlerPlugin.NotifyPlayer(ModLocalization.Text("Dieses Erweiterungsitem gehört nicht zum gespeicherten Kaufstand und wurde nicht verbraucht.", "This expansion item is not part of the saved purchase progress and was not consumed."));
			return false;
		}
		long appliedCount = current.AppliedCount;
		long grantedSlots = current.GrantedSlots;
		Item myItem = sourceSlot.MyItem;
		int quantity = sourceSlot.Quantity;
		int count = ExtraSlots.Count;
		bool flag = false;
		try
		{
			current.AppliedCount++;
			checked
			{
				current.GrantedSlots += TaschenhaendlerPlugin.Instance.SlotsPerExpansion.Value;
				EnsureExtraSlotCount(GetDesiredExtraSlotCount(current));
			}
			if (quantity > 1)
			{
				sourceSlot.Quantity = quantity - 1;
			}
			else
			{
				sourceSlot.MyItem = _inventory.Empty;
				sourceSlot.Quantity = 0;
			}
			sourceSlot.UpdateSlotImage();
			CaptureOverflow(current);
			if (!ProgressStore.Save())
			{
				throw new IOExceptionLikeException();
			}
			flag = true;
			_inventory.UpdatePlayerInventory();
			if (!SaveNativeAfterSidecarCommit())
			{
				throw new InvalidOperationException("Erenshor hat den nativen Charakterstand nicht gespeichert.");
			}
			int value = TaschenhaendlerPlugin.Instance.SlotsPerExpansion.Value;
			TaschenhaendlerPlugin.NotifyPlayer(ModLocalization.Format("Inventar dauerhaft um {0} Plätze erweitert. Insgesamt: {1} Zusatzplätze.", "Inventory permanently expanded by {0} slots. Total: {1} extra slots.", value, current.GrantedSlots));
			return true;
		}
		catch (Exception ex)
		{
			current.AppliedCount = appliedCount;
			current.GrantedSlots = grantedSlots;
			EnsureExtraSlotCount(count);
			sourceSlot.MyItem = myItem;
			sourceSlot.Quantity = quantity;
			sourceSlot.UpdateSlotImage();
			CaptureOverflow(current);
			if (!(ex is IOExceptionLikeException))
			{
				TaschenhaendlerPlugin.ModLog.LogError((object)$"Inventarerweiterung wurde zurückgerollt: {ex}");
				TaschenhaendlerPlugin.NotifyPlayer(ModLocalization.Text("Die Inventarerweiterung ist fehlgeschlagen und wurde nicht verbraucht.", "The inventory expansion failed and was not consumed."));
			}
			if (flag)
			{
				CaptureOverflow(current);
				ProgressStore.Save();
			}
			return false;
		}
	}

	internal static SaveIsolationState PrepareVanillaSave()
	{
		SaveIsolationState saveIsolationState = new SaveIsolationState
		{
			CanProceed = true,
			Prepared = false,
			Extras = new List<ItemIcon>(),
			HiddenExpansionItems = new List<HiddenExpansionItemState>()
		};
		if ((Object)(object)_inventory == (Object)null)
		{
			return saveIsolationState;
		}
		if (ProgressStore.Current == null)
		{
			saveIsolationState.CanProceed = ExtraSlots.Count == 0;
			if (!saveIsolationState.CanProceed)
			{
				TaschenhaendlerPlugin.ModLog.LogError((object)"Vanilla-Speichern wurde abgebrochen, weil Zusatzslots ohne aktiven Charakterzustand vorhanden sind.");
			}
			return saveIsolationState;
		}
		if (!_sidecarAlreadyCommittedForSave)
		{
			CaptureOverflow(ProgressStore.Current);
			if (!ProgressStore.Save())
			{
				saveIsolationState.CanProceed = false;
				return saveIsolationState;
			}
		}
		saveIsolationState.Prepared = true;
		try
		{
			saveIsolationState.Extras.AddRange(ExtraSlots.Where((ItemIcon slot) => (Object)(object)slot != (Object)null));
			foreach (ItemIcon extra in saveIsolationState.Extras)
			{
				_inventory.StoredSlots.Remove(extra);
			}
			foreach (ItemIcon item in _inventory.StoredSlots.Where((ItemIcon entry) => (Object)(object)entry != (Object)null && TaschenhaendlerPlugin.IsExpansion(entry.MyItem)).ToList())
			{
				saveIsolationState.HiddenExpansionItems.Add(new HiddenExpansionItemState
				{
					Slot = item,
					Item = item.MyItem,
					Quantity = item.Quantity
				});
				item.MyItem = _inventory.Empty;
				item.Quantity = 0;
				item.UpdateSlotImage();
			}
			MaskUnsafeHotkeys(saveIsolationState);
			RebuildAllSlots();
			saveIsolationState.Prepared = saveIsolationState.Extras.Count > 0 || saveIsolationState.HiddenExpansionItems.Count > 0 || saveIsolationState.HotkeyIndices.Count > 0;
			return saveIsolationState;
		}
		catch (Exception arg)
		{
			saveIsolationState.CanProceed = false;
			TaschenhaendlerPlugin.ModLog.LogError((object)$"Vorbereitung des sicheren Vanilla-Speicherns ist fehlgeschlagen; Teilzustand wird wiederhergestellt: {arg}");
			try
			{
				RestoreAfterVanillaSave(saveIsolationState);
			}
			catch (Exception arg2)
			{
				TaschenhaendlerPlugin.ModLog.LogError((object)$"Wiederherstellung nach fehlgeschlagener Save-Vorbereitung ist ebenfalls fehlgeschlagen: {arg2}");
			}
			TaschenhaendlerPlugin.NotifyPlayer(ModLocalization.Text("Speichern wurde abgebrochen, weil die Zusatzslots nicht sicher isoliert werden konnten.", "Saving was cancelled because the extra slots could not be isolated safely."));
			return saveIsolationState;
		}
	}

	internal static void RestoreAfterVanillaSave(SaveIsolationState state)
	{
		if (state == null || !state.Prepared || (Object)(object)_inventory == (Object)null)
		{
			return;
		}
		int num = (((Object)(object)_trashSlot != (Object)null) ? _inventory.StoredSlots.IndexOf(_trashSlot) : _inventory.StoredSlots.Count);
		if (num < 0)
		{
			num = _inventory.StoredSlots.Count;
		}
		foreach (ItemIcon extra in state.Extras)
		{
			if ((Object)(object)extra != (Object)null && !_inventory.StoredSlots.Contains(extra))
			{
				_inventory.StoredSlots.Insert(num++, extra);
			}
		}
		foreach (HiddenExpansionItemState hiddenExpansionItem in state.HiddenExpansionItems)
		{
			if (!((Object)(object)hiddenExpansionItem?.Slot == (Object)null))
			{
				hiddenExpansionItem.Slot.MyItem = hiddenExpansionItem.Item;
				hiddenExpansionItem.Slot.Quantity = hiddenExpansionItem.Quantity;
				hiddenExpansionItem.Slot.UpdateSlotImage();
			}
		}
		RebuildAllSlots();
		foreach (KeyValuePair<Hotkeys, int> hotkeyIndex in state.HotkeyIndices)
		{
			if ((Object)(object)hotkeyIndex.Key != (Object)null)
			{
				hotkeyIndex.Key.InvSlotIndex = hotkeyIndex.Value;
			}
		}
		state.Prepared = false;
		_scrollView?.LayoutSlots();
	}

	internal static void CaptureOverflow(CharacterProgress progress)
	{
		if (progress == null || (Object)(object)_inventory == (Object)null)
		{
			return;
		}
		List<StoredItem> list = new List<StoredItem>(ExtraSlots.Count);
		for (int i = 0; i < ExtraSlots.Count; i++)
		{
			ItemIcon val = ExtraSlots[i];
			if ((Object)(object)val == (Object)null || (Object)(object)val.MyItem == (Object)null || (Object)(object)val.MyItem == (Object)(object)_inventory.Empty)
			{
				if (UnresolvedOverflowIndices.Contains(i) && i < progress.Overflow.Count && !string.IsNullOrEmpty(progress.Overflow[i]?.Id))
				{
					StoredItem storedItem = progress.Overflow[i];
					list.Add(new StoredItem
					{
						Id = storedItem.Id,
						Quantity = storedItem.Quantity
					});
				}
				else
				{
					list.Add(new StoredItem());
				}
			}
			else
			{
				UnresolvedOverflowIndices.Remove(i);
				list.Add(new StoredItem
				{
					Id = (((BaseScriptableObject)val.MyItem).Id ?? string.Empty),
					Quantity = Math.Max(1, val.Quantity)
				});
			}
		}
		progress.Overflow = list;
		CaptureExtraHotkeys(progress);
	}

	private static int GetDesiredExtraSlotCount(CharacterProgress progress)
	{
		long grantedSlots = progress.GrantedSlots;
		if (grantedSlots > int.MaxValue)
		{
			throw new InvalidOperationException("Die Anzahl der Zusatzplätze überschreitet die technische Listengrenze von Unity.");
		}
		return (int)grantedSlots;
	}

	private static void EnsureExtraSlotCount(int desired)
	{
		if ((Object)(object)_inventory == (Object)null)
		{
			throw new InvalidOperationException("Spielerinventar ist nicht initialisiert.");
		}
		while (ExtraSlots.Count > desired)
		{
			ItemIcon val = ExtraSlots[ExtraSlots.Count - 1];
			ExtraSlots.RemoveAt(ExtraSlots.Count - 1);
			_inventory.StoredSlots.Remove(val);
			RectTransform slotRoot = GetSlotRoot(val);
			if ((Object)(object)slotRoot != (Object)null)
			{
				((Component)slotRoot).gameObject.SetActive(false);
				Object.Destroy((Object)(object)((Component)slotRoot).gameObject);
			}
		}
		while (ExtraSlots.Count < desired)
		{
			AddExtraSlot();
		}
		RebuildAllSlots();
		_scrollView?.LayoutSlots();
	}

	private static void AddExtraSlot()
	{
		RectTransform slotRoot = GetSlotRoot(((IEnumerable<ItemIcon>)_inventory.StoredSlots).FirstOrDefault((Func<ItemIcon, bool>)delegate(ItemIcon slot)
		{
			if ((Object)(object)slot != (Object)null && !slot.TrashSlot)
			{
				RectTransform slotRoot2 = GetSlotRoot(slot);
				return (Object)(object)((slotRoot2 != null) ? ((Component)slotRoot2).GetComponent<ExtraInventorySlotMarker>() : null) == (Object)null;
			}
			return false;
		}));
		if ((Object)(object)slotRoot == (Object)null)
		{
			throw new InvalidOperationException("Kein normaler Inventarslot als Vorlage gefunden.");
		}
		Transform parent = ((Transform)slotRoot).parent;
		RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null);
		GameObject val2 = Object.Instantiate<GameObject>(((Component)slotRoot).gameObject, (Transform)(object)val, false);
		((Object)val2).name = $"Taschenhaendler_ExtraSlot_{ExtraSlots.Count + 1:D4}";
		val2.AddComponent<ExtraInventorySlotMarker>();
		ItemIcon componentInChildren = val2.GetComponentInChildren<ItemIcon>(true);
		if ((Object)(object)componentInChildren == (Object)null)
		{
			Object.Destroy((Object)(object)val2);
			throw new InvalidOperationException("Die Slot-Vorlage enthält kein ItemIcon.");
		}
		if ((Object)(object)componentInChildren.MySparkler != (Object)null)
		{
			GameObject gameObject = ((Component)componentInChildren.MySparkler).gameObject;
			componentInChildren.MySparkler = null;
			Object.Destroy((Object)(object)gameObject);
		}
		componentInChildren.TrashSlot = false;
		componentInChildren.VendorSlot = false;
		componentInChildren.LootSlot = false;
		componentInChildren.BankSlot = false;
		componentInChildren.NotInInventory = false;
		componentInChildren.dragging = false;
		componentInChildren.assignedHotkey = null;
		Traverse obj = Traverse.Create((object)componentInChildren);
		obj.Field("hotkey").SetValue((object)null);
		obj.Field("MouseHomeSlot").SetValue((object)null);
		obj.Field("SwapWith").SetValue((object)null);
		obj.Field("rButton").SetValue((object)false);
		obj.Field("lButton").SetValue((object)false);
		obj.Field("doHotkey").SetValue((object)false);
		componentInChildren.MyItem = _inventory.Empty;
		componentInChildren.Quantity = 0;
		componentInChildren.ForceInitInv();
		int num = (((Object)(object)_trashSlot != (Object)null) ? _inventory.StoredSlots.IndexOf(_trashSlot) : _inventory.StoredSlots.Count);
		if (num < 0)
		{
			num = _inventory.StoredSlots.Count;
		}
		_inventory.StoredSlots.Insert(num, componentInChildren);
		ExtraSlots.Add(componentInChildren);
	}

	private static void SetSlot(ItemIcon slot, Item item, int quantity)
	{
		slot.MyItem = item;
		slot.Quantity = quantity;
		slot.UpdateSlotImage();
	}

	private static void RebuildAllSlots()
	{
		if (!((Object)(object)_inventory == (Object)null))
		{
			_inventory.ALLSLOTS.Clear();
			_inventory.ALLSLOTS.AddRange(_inventory.EquipmentSlots.Where((ItemIcon slot) => (Object)(object)slot != (Object)null));
			_inventory.ALLSLOTS.AddRange(_inventory.StoredSlots.Where((ItemIcon slot) => (Object)(object)slot != (Object)null));
			_inventory.ALLSLOTS.AddRange(_inventory.TransmogSlots.Where((ItemIcon slot) => (Object)(object)slot != (Object)null));
			for (int num = 0; num < _inventory.ALLSLOTS.Count; num++)
			{
				_inventory.ALLSLOTS[num].ALLSLOTSINDEX = num;
			}
		}
	}

	private static void CaptureExtraHotkeys(CharacterProgress progress)
	{
		if (!((Object)(object)GameData.GM?.HKManager == (Object)null))
		{
			progress.FirstExtraHotkeys = CaptureHotkeyList(GameData.GM.HKManager.FirstHotkeys);
			progress.SecondExtraHotkeys = CaptureHotkeyList(GameData.GM.HKManager.SecondHotkeys);
		}
	}

	private static List<int> CaptureHotkeyList(List<Hotkeys> hotkeys)
	{
		List<int> list = new List<int>();
		if (hotkeys == null)
		{
			return list;
		}
		foreach (Hotkeys hotkey in hotkeys)
		{
			int item = (((Object)(object)hotkey?.AssignedItem != (Object)null) ? ExtraSlots.IndexOf(hotkey.AssignedItem) : (-1));
			list.Add(item);
		}
		return list;
	}

	private static void RestoreHotkeyList(List<Hotkeys> hotkeys, List<int> mappings)
	{
		if (hotkeys == null || mappings == null)
		{
			return;
		}
		int num = Math.Min(hotkeys.Count, mappings.Count);
		for (int i = 0; i < num; i++)
		{
			int num2 = mappings[i];
			if (num2 >= 0 && num2 < ExtraSlots.Count && !((Object)(object)hotkeys[i] == (Object)null) && !((Object)(object)ExtraSlots[num2] == (Object)null))
			{
				hotkeys[i].ClearMe();
				hotkeys[i].AssignItemFrominv(ExtraSlots[num2]);
				ExtraSlots[num2].assignedHotkey = hotkeys[i];
			}
		}
	}

	private static void MaskUnsafeHotkeys(SaveIsolationState state)
	{
		if (GameData.GM?.HKManager?.AllHotkeys == null)
		{
			return;
		}
		foreach (Hotkeys hotkey in GameData.GM.HKManager.AllHotkeys)
		{
			if ((Object)(object)hotkey != (Object)null && (Object)(object)hotkey.AssignedItem != (Object)null && (ExtraSlots.Contains(hotkey.AssignedItem) || state.HiddenExpansionItems.Any((HiddenExpansionItemState hidden) => hidden != null && (Object)(object)hidden.Slot == (Object)(object)hotkey.AssignedItem)))
			{
				state.HotkeyIndices[hotkey] = hotkey.InvSlotIndex;
				hotkey.InvSlotIndex = -1;
			}
		}
	}

	private static bool IsUsableEmptyInventorySlot(ItemIcon slot)
	{
		if (IsScrollableInventorySlot(slot) && (Object)(object)slot.MyItem == (Object)(object)_inventory.Empty)
		{
			return !IsReservedUnresolvedSlot(slot);
		}
		return false;
	}
}
[HarmonyPatch(typeof(CharSelectManager), "LoadHotkeys")]
internal static class ExtraHotkeyLoadPatch
{
	[HarmonyPostfix]
	private static void Postfix()
	{
		InventoryExpansionController.FinishHotkeyLoad(ProgressStore.Current);
	}
}
[HarmonyPatch(typeof(Inventory), "Start")]
internal static class InventoryStartPatch
{
	[HarmonyPostfix]
	[HarmonyPriority(0)]
	private static void Postfix(Inventory __instance)
	{
		try
		{
			InventoryExpansionController.Initialize(__instance);
		}
		catch (Exception arg)
		{
			TaschenhaendlerPlugin.ModLog.LogError((object)$"Inventarinitialisierung fehlgeschlagen: {arg}");
		}
	}
}
[HarmonyPatch(typeof(CharSelectManager), "LoadEquipment")]
internal static class CharacterLoadPatch
{
	[HarmonyPrefix]
	private static void Prefix()
	{
		InventoryExpansionController.LoadCharacter(ProgressStore.SelectCurrentCharacter());
	}

	[HarmonyPostfix]
	private static void Postfix()
	{
		InventoryExpansionController.FinishCharacterLoad(ProgressStore.Current);
	}
}
[HarmonyPatch(typeof(CharSelectManager), "SelectSlot")]
internal static class CharacterSelectionPatch
{
	[HarmonyPrefix]
	private static bool Prefix()
	{
		return InventoryExpansionController.CaptureCurrentBeforeCharacterSwitch();
	}

	[HarmonyPostfix]
	private static void Postfix()
	{
		if (GameData.CurrentCharacterSlot != null && string.IsNullOrEmpty(GameData.CurrentCharacterSlot.CharName))
		{
			InventoryExpansionController.ClearLoadedCharacter();
			ProgressStore.ClearCurrent();
		}
	}
}
[HarmonyPatch(typeof(CharSelectManager), "SelectBackupSlot")]
internal static class BackupCharacterSelectionPatch
{
	[HarmonyPrefix]
	private static bool Prefix()
	{
		return InventoryExpansionController.CaptureCurrentBeforeCharacterSwitch();
	}
}
[HarmonyPatch(typeof(ItemIcon), "UseConsumable")]
internal static class UseExpansionPatch
{
	[HarmonyPrefix]
	private static bool Prefix(ItemIcon __instance)
	{
		if (!TaschenhaendlerPlugin.IsExpansion(__instance?.MyItem))
		{
			return true;
		}
		InventoryExpansionController.TryUseExpansion(__instance);
		return false;
	}
}
[HarmonyPatch(typeof(GameManager), "SaveGameData")]
internal static class SafeInventorySavePatch
{
	[HarmonyPrefix]
	private static bool Prefix(out SaveIsolationState __state)
	{
		__state = InventoryExpansionController.PrepareVanillaSave();
		return __state.CanProceed;
	}

	[HarmonyPostfix]
	private static void Postfix(SaveIsolationState __state)
	{
		InventoryExpansionController.RestoreAfterVanillaSave(__state);
	}

	[HarmonyFinalizer]
	private static Exception Finalizer(Exception __exception, SaveIsolationState __state)
	{
		InventoryExpansionController.RestoreAfterVanillaSave(__state);
		return __exception;
	}
}
[HarmonyPatch(typeof(GameData), "SaveData")]
internal static class NativeCharacterSaveResultPatch
{
	[HarmonyPostfix]
	private static void Postfix(bool __result)
	{
		InventoryExpansionController.ReportNativeSaveResult(__result);
	}
}
internal sealed class DeletedCharacterKey
{
	internal int SlotIndex;

	internal string Name;
}
[HarmonyPatch(typeof(CharSelectManager), "EraseCharacter")]
internal static class CharacterErasePatch
{
	[HarmonyPrefix]
	private static void Prefix(CharSelectManager __instance, out DeletedCharacterKey __state)
	{
		__state = null;
		SaveGameData val = GameData.CurrentCharacterSlot;
		if (val == null)
		{
			int value = Traverse.Create((object)__instance).Field("curIndex").GetValue<int>();
			if (GameData.SaveSlots != null && value >= 0 && value < GameData.SaveSlots.Count)
			{
				val = GameData.SaveSlots[value];
			}
		}
		if (val != null)
		{
			__state = new DeletedCharacterKey
			{
				SlotIndex = val.index,
				Name = (val.CharName ?? string.Empty)
			};
		}
	}

	[HarmonyPostfix]
	private static void Postfix(DeletedCharacterKey __state)
	{
		if (__state != null)
		{
			InventoryExpansionController.ClearLoadedCharacter();
			ProgressStore.Remove(__state.SlotIndex, __state.Name);
		}
	}
}
internal enum InventoryFilterMode
{
	All,
	Weapons,
	ArmorAndJewelry,
	Consumables,
	Crafting,
	QuestAndBooks,
	Rare,
	Other
}
internal enum InventorySortMode
{
	Name,
	Type,
	Level,
	Value,
	Quantity
}
internal static class InventoryToolsState
{
	private static readonly string[] StableSortModes = new string[5] { "Name", "Type", "Level", "Value", "Quantity" };

	private static string _searchText = string.Empty;

	private static InventoryFilterMode _filter = InventoryFilterMode.All;

	internal static string SearchText => _searchText;

	internal static InventoryFilterMode Filter => _filter;

	internal static InventorySortMode PendingSortMode
	{
		get
		{
			if (!TryParseSortMode(TaschenhaendlerPlugin.Instance?.PreferredSortMode?.Value, out var mode))
			{
				return InventorySortMode.Type;
			}
			return mode;
		}
	}

	internal static bool PendingDescending => TaschenhaendlerPlugin.Instance?.SortDescending?.Value == true;

	internal static IReadOnlyList<string> FilterLabels
	{
		get
		{
			if (ModLocalization.IsGerman)
			{
				return new string[8] { "Alle", "Waffen", "Rüst./Schm.", "Verbrauch", "Handwerk", "Quest/Buch", "Selten", "Sonstiges" };
			}
			return new string[8] { "All", "Weapons", "Armor/Jewel.", "Consume", "Crafting", "Quest/Book", "Rare", "Other" };
		}
	}

	internal static IReadOnlyList<string> SortLabels
	{
		get
		{
			if (ModLocalization.IsGerman)
			{
				return new string[5] { "Name", "Typ", "Stufe", "Wert", "Menge" };
			}
			return new string[5] { "Name", "Type", "Level", "Value", "Quantity" };
		}
	}

	internal static void SetSearch(string value)
	{
		_searchText = value?.Trim() ?? string.Empty;
	}

	internal static void SetFilter(int index)
	{
		_filter = (InventoryFilterMode)Mathf.Clamp(index, 0, Enum.GetValues(typeof(InventoryFilterMode)).Length - 1);
	}

	internal static void SetPendingSortMode(int index)
	{
		index = Mathf.Clamp(index, 0, StableSortModes.Length - 1);
		if (TaschenhaendlerPlugin.Instance?.PreferredSortMode != null)
		{
			TaschenhaendlerPlugin.Instance.PreferredSortMode.Value = StableSortModes[index];
		}
	}

	internal static void TogglePendingDirection()
	{
		if (TaschenhaendlerPlugin.Instance?.SortDescending != null)
		{
			TaschenhaendlerPlugin.Instance.SortDescending.Value = !TaschenhaendlerPlugin.Instance.SortDescending.Value;
		}
	}

	internal static bool TryParseSortMode(string value, out InventorySortMode mode)
	{
		for (int i = 0; i < StableSortModes.Length; i++)
		{
			if (string.Equals(value, StableSortModes[i], StringComparison.OrdinalIgnoreCase))
			{
				mode = (InventorySortMode)i;
				return true;
			}
		}
		mode = InventorySortMode.Type;
		return false;
	}

	internal static List<ItemIcon> GetDisplayedSlots(IEnumerable<ItemIcon> source, Item empty)
	{
		return (from slot in source
			where (Object)(object)slot != (Object)null && !slot.TrashSlot
			where Matches(slot, empty)
			select slot).ToList();
	}

	private static bool Matches(ItemIcon slot, Item empty)
	{
		Item val = slot?.MyItem;
		if ((Object)(object)val == (Object)null || (Object)(object)val == (Object)(object)empty)
		{
			if (_filter == InventoryFilterMode.All)
			{
				return string.IsNullOrEmpty(_searchText);
			}
			return false;
		}
		if (!string.IsNullOrEmpty(_searchText))
		{
			bool num = (val.ItemName ?? string.Empty).IndexOf(_searchText, StringComparison.CurrentCultureIgnoreCase) >= 0;
			bool flag = (val.Lore ?? string.Empty).IndexOf(_searchText, StringComparison.CurrentCultureIgnoreCase) >= 0;
			if (!num && !flag)
			{
				return false;
			}
		}
		if (_filter != InventoryFilterMode.All && GetFilterCategory(val) != _filter)
		{
			if (_filter == InventoryFilterMode.Rare)
			{
				if (!val.RareItem && !val.Relic)
				{
					return val.Unique;
				}
				return true;
			}
			return false;
		}
		return true;
	}

	private static InventoryFilterMode GetFilterCategory(Item item)
	{
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0013: Invalid comparison between Unknown and I4
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Invalid comparison between Unknown and I4
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: Invalid comparison between Unknown and I4
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)item == (Object)null)
		{
			return InventoryFilterMode.Other;
		}
		if ((int)item.RequiredSlot == 13 || (int)item.RequiredSlot == 14 || (int)item.RequiredSlot == 15 || item.WeaponDmg != 0 || (int)item.ThisWeaponType != 0 || item.IsBow || item.IsWand)
		{
			return InventoryFilterMode.Weapons;
		}
		if ((int)item.RequiredSlot != 0)
		{
			return InventoryFilterMode.ArmorAndJewelry;
		}
		if ((Object)(object)item.AssignQuestOnRead != (Object)null || (Object)(object)item.CompleteOnRead != (Object)null || !string.IsNullOrWhiteSpace(item.BookTitle) || (Object)(object)item.TeachSpell != (Object)null || (Object)(object)item.TeachSkill != (Object)null)
		{
			return InventoryFilterMode.QuestAndBooks;
		}
		if ((Object)(object)item.ItemEffectOnClick != (Object)null || (Object)(object)item.ItemSkillUse != (Object)null || item.Disposable)
		{
			return InventoryFilterMode.Consumables;
		}
		if (item.Template || item.FuelSource || item.Mining > 0 || item.FurnitureSet)
		{
			return InventoryFilterMode.Crafting;
		}
		return InventoryFilterMode.Other;
	}
}
internal sealed class InventoryToolsPanel : MonoBehaviour
{
	private static readonly HashSet<InventoryToolsPanel> LivePanels = new HashSet<InventoryToolsPanel>();

	private Inventory _inventory;

	private InventoryScrollView _scrollView;

	private TMP_InputField _search;

	private TextMeshProUGUI _searchPlaceholder;

	private InventoryChoiceDropdown _language;

	private InventoryChoiceDropdown _filter;

	private InventoryChoiceDropdown _sortMode;

	private Button _directionButton;

	private TextMeshProUGUI _directionText;

	private Button _sortButton;

	private TextMeshProUGUI _sortButtonText;

	private TMP_FontAsset _font;

	private float _autoSortGuard;

	internal static InventoryToolsPanel Create(Inventory inventory, InventoryScrollView scrollView, RectTransform parent, Vector2 anchorMin, Vector2 anchorMax, Vector2 anchoredPosition, Vector2 size)
	{
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		//IL_006b: Unknown result type (might be due to invalid IL or missing references)
		//IL_007b: 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_0094: Unknown result type (might be due to invalid IL or missing references)
		//IL_009f: 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_00ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c8: 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_00ed: Unknown result type (might be due to invalid IL or missing references)
		//IL_0101: Unknown result type (might be due to invalid IL or missing references)
		//IL_0141: Unknown result type (might be due to invalid IL or missing references)
		InventoryToolsPanel componentInChildren = ((Component)parent).GetComponentInChildren<InventoryToolsPanel>(true);
		if ((Object)(object)componentInChildren != (Object)null)
		{
			componentInChildren._inventory = inventory;
			componentInChildren._scrollView = scrollView;
			componentInChildren.RefreshLocalization();
			return componentInChildren;
		}
		GameObject val = new GameObject("CBra_InventoryTools", new Type[4]
		{
			typeof(RectTransform),
			typeof(Image),
			typeof(Outline),
			typeof(InventoryToolsPanel)
		});
		RectTransform component = val.GetComponent<RectTransform>();
		((Transform)component).SetParent((Transform)(object)parent, false);
		component.anchorMin = anchorMin;
		component.anchorMax = anchorMax;
		component.pivot = new Vector2(0.5f, 0.5f);
		component.anchoredPosition = anchoredPosition;
		component.sizeDelta = size;
		((Graphic)val.GetComponent<Image>()).color = new Color(0.035f, 0.12f, 0.16f, 0.94f);
		Outline component2 = val.GetComponent<Outline>();
		((Shadow)component2).effectColor = new Color(0.35f, 0.75f, 0.78f, 0.9f);
		((Shadow)component2).effectDistance = new Vector2(1f, -1f);
		InventoryToolsPanel component3 = val.GetComponent<InventoryToolsPanel>();
		component3._inventory = inventory;
		component3._scrollView = scrollView;
		component3._font = (((Object)(object)inventory.GoldTXT != (Object)null) ? ((TMP_Text)inventory.GoldTXT).font : null);
		component3.Build(size);
		component3.RefreshLocalization();
		((Transform)component).SetAsLastSibling();
		component3.DisableExternalAutoSortButtons();
		return component3;
	}

	internal static void RefreshAllLocalization()
	{
		InventoryToolsPanel[] array = LivePanels.Where((InventoryToolsPanel panel) => (Object)(object)panel != (Object)null).ToArray();
		for (int num = 0; num < array.Length; num++)
		{
			array[num].RefreshLocalization();
		}
	}

	private void OnEnable()
	{
		LivePanels.Add(this);
	}

	private void OnDisable()
	{
		if ((Object)(object)_search != (Object)null && _search.isFocused)
		{
			GameData.PlayerTyping = false;
		}
	}

	private void OnDestroy()
	{
		LivePanels.Remove(this);
		if ((Object)(object)_search != (Object)null && _search.isFocused)
		{
			GameData.PlayerTyping = false;
		}
	}

	private void Update()
	{
		_autoSortGuard -= Time.unscaledDeltaTime;
		if (_autoSortGuard <= 0f)
		{
			_autoSortGuard = 2f;
			DisableExternalAutoSortButtons();
		}
	}

	private void Build(Vector2 size)
	{
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0087: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_0113: Unknown result type (might be due to invalid IL or missing references)
		//IL_011f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0155: Unknown result type (might be due to invalid IL or missing references)
		//IL_0161: 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_0192: Expected O, but got Unknown
		//IL_01af: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ec: Expected O, but got Unknown
		Transform transform = ((Component)this).transform;
		RectTransform parent = (RectTransform)(object)((transform is RectTransform) ? transform : null);
		float num = Math.Max(1f, size.x - 8f);
		_search = CreateSearch(parent, new Vector2(0f, 31f), new Vector2(num, 23f));
		float num2 = Math.Min(82f, num * 0.43f);
		float num3 = num - num2 - 4f;
		_language = InventoryChoiceDropdown.Create(parent, "CBra_LanguageDropdown", new Vector2((0f - (num - num2)) * 0.5f, 3f), new Vector2(num2, 23f), _font);
		_filter = InventoryChoiceDropdown.Create(parent, "CBra_FilterDropdown", new Vector2((num - num3) * 0.5f, 3f), new Vector2(num3, 23f), _font);
		float num4 = 28f;
		float num5 = 52f;
		float num6 = 6f;
		float num7 = num - num4 - num5 - num6;
		float num8 = (0f - num) * 0.5f;
		_sortMode = InventoryChoiceDropdown.Create(parent, "CBra_SortModeDropdown", new Vector2(num8 + num7 * 0.5f, -25f), new Vector2(num7, 23f), _font);
		_directionButton = CreateButton(parent, "CBra_SortDirection", new Vector2(num8 + num7 + 3f + num4 * 0.5f, -25f), new Vector2(num4, 23f), out _directionText);
		((UnityEvent)_directionButton.onClick).AddListener((UnityAction)delegate
		{
			InventoryToolsState.TogglePendingDirection();
			RefreshLocalization();
		});
		_sortButton = CreateButton(parent, "CBra_SortButton", new Vector2(num * 0.5f - num5 * 0.5f, -25f), new Vector2(num5, 23f), out _sortButtonText);
		((UnityEvent)_sortButton.onClick).AddListener(new UnityAction(SortNow));
		_language.SetSelectionHandler(delegate(int index)
		{
			ModLocalization.SetModeByIndex(index);
		});
		_filter.SetSelectionHandler(delegate(int index)
		{
			InventoryToolsState.SetFilter(index);
			_scrollView?.LayoutSlots(resetScroll: true);
			RefreshLocalization();
		});
		_sortMode.SetSelectionHandler(delegate(int index)
		{
			InventoryToolsState.SetPendingSortMode(index);
			RefreshLocalization();
		});
	}

	private TMP_InputField CreateSearch(RectTransform parent, Vector2 position, Vector2 size)
	{
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Expected O, but got Unknown
		//IL_0034: Unknown result type (might be due to invalid IL or missing references)
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_0057: 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_009a: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
		//IL_0109: Unknown result type (might be due to invalid IL or missing references)
		//IL_0114: Unknown result type (might be due to invalid IL or missing references)
		//IL_011e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0163: Unknown result type (might be due to invalid IL or missing references)
		//IL_0179: Unknown result type (might be due to invalid IL or missing references)
		//IL_0184: Unknown result type (might be due to invalid IL or missing references)
		//IL_018f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0199: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e9: 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_0213: Unknown result type (might be due to invalid IL or missing references)
		//IL_021d: Expected O, but got Unknown
		//IL_021e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0228: Expected O, but got Unknown
		//IL_0229: Unknown result type (might be due to invalid IL or missing references)
		//IL_0233: Expected O, but got Unknown
		GameObject val = new GameObject("CBra_InventorySearch", new Type[2]
		{
			typeof(RectTransform),
			typeof(Image)
		});
		RectTransform component = val.GetComponent<RectTransform>();
		ConfigureRect(component, parent, position, size);
		Image component2 = val.GetComponent<Image>();
		((Graphic)component2).color = new Color(0.015f, 0.035f, 0.045f, 0.96f);
		RectTransform component3 = new GameObject("Text Area", new Type[2]
		{
			typeof(RectTransform),
			typeof(RectMask2D)
		}).GetComponent<RectTransform>();
		((Transform)component3).SetParent((Transform)(object)component, false);
		component3.anchorMin = Vector2.zero;
		component3.anchorMax = Vector2.one;
		component3.offsetMin = new Vector2(7f, 1f);
		component3.offsetMax = new Vector2(-7f, -1f);
		TextMeshProUGUI val2 = CreateText(component3, "Text", string.Empty, 13f, (TextAlignmentOptions)4097);
		RectTransform rectTransform = ((TMP_Text)val2).rectTransform;
		rectTransform.anchorMin = Vector2.zero;
		rectTransform.anchorMax = Vector2.one;
		rectTransform.offsetMin = Vector2.zero;
		rectTransform.offsetMax = Vector2.zero;
		_searchPlaceholder = CreateText(component3, "Placeholder", string.Empty, 13f, (TextAlignmentOptions)4097);
		((Graphic)_searchPlaceholder).color = new Color(0.72f, 0.78f, 0.8f, 0.72f);
		RectTransform rectTransform2 = ((TMP_Text)_searchPlaceholder).rectTransform;
		rectTransform2.anchorMin = Vector2.zero;
		rectTransform2.anchorMax = Vector2.one;
		rectTransform2.offsetMin = Vector2.zero;
		rectTransform2.offsetMax = Vector2.zero;
		TMP_InputField obj = val.AddComponent<TMP_InputField>();
		obj.textViewport = component3;
		obj.textComponent = (TMP_Text)(object)val2;
		obj.placeholder = (Graphic)(object)_searchPlaceholder;
		((Selectable)obj).targetGraphic = (Graphic)(object)component2;
		obj.lineType = (LineType)0;
		obj.contentType = (ContentType)0;
		obj.characterLimit = 80;
		obj.customCaretColor = true;
		obj.caretColor = Color.white;
		obj.selectionColor = new Color(0.25f, 0.7f, 0.8f, 0.45f);
		obj.onValueChanged = new OnChangeEvent();
		obj.onSelect = new SelectionEvent();
		obj.onDeselect = new SelectionEvent();
		((UnityEvent<string>)(object)obj.onValueChanged).AddListener((UnityAction<string>)delegate(string value)
		{
			InventoryToolsState.SetSearch(value);
			_scrollView?.LayoutSlots(resetScroll: true);
		});
		((UnityEvent<string>)(object)obj.onSelect).AddListener((UnityAction<string>)delegate
		{
			GameData.PlayerTyping = true;
		});
		((UnityEvent<string>)(object)obj.onDeselect).AddListener((UnityAction<string>)delegate
		{
			GameData.PlayerTyping = false;
		});
		obj.SetTextWithoutNotify(InventoryToolsState.SearchText);
		return obj;
	}

	private Button CreateButton(RectTransform parent, string name, Vector2 position, Vector2 size, out TextMeshProUGUI label)
	{
		//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_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_003d: 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)
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0093: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00be: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
		//IL_0114: Unknown result type (might be due to invalid IL or missing references)
		//IL_0128: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = new GameObject(name, new Type[3]
		{
			typeof(RectTransform),
			typeof(Image),
			typeof(Button)
		});
		RectTransform component = val.GetComponent<RectTransform>();
		ConfigureRect(component, parent, position, size);
		Image component2 = val.GetComponent<Image>();
		((Graphic)component2).color = new Color(0.08f, 0.26f, 0.31f, 0.98f);
		Button component3 = val.GetComponent<Button>();
		((Selectable)component3).targetGraphic = (Graphic)(object)component2;
		ColorBlock colors = ((Selectable)component3).colors;
		((ColorBlock)(ref colors)).highlightedColor = new Color(0.16f, 0.46f, 0.5f, 1f);
		((ColorBlock)(ref colors)).pressedColor = new Color(0.72f, 0.55f, 0.22f, 1f);
		((Selectable)component3).colors = colors;
		label = CreateText(component, "Label", string.Empty, 12f, (TextAlignmentOptions)514);
		((Graphic)label).raycastTarget = false;
		RectTransform rectTransform = ((TMP_Text)label).rectTransform;
		rectTransform.anchorMin = Vector2.zero;
		rectTransform.anchorMax = Vector2.one;
		rectTransform.offsetMin = new Vector2(2f, 0f);
		rectTransform.offsetMax = new Vector2(-2f, 0f);
		return component3;
	}

	private TextMeshProUGUI CreateText(RectTransform parent, string name, string value, float size, TextAlignmentOptions alignment)
	{
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = new GameObject(name, new Type[2]
		{
			typeof(RectTransform),
			typeof(TextMeshProUGUI)
		});
		((Transform)val.GetComponent<RectTransform>()).SetParent((Transform)(object)parent, false);
		TextMeshProUGUI component = val.GetComponent<TextMeshProUGUI>();
		((TMP_Text)component).text = value;
		((TMP_Text)component).fontSize = size;
		((TMP_Text)component).alignment = alignment;
		((Graphic)component).color = Color.white;
		((TMP_Text)component).enableWordWrapping = false;
		if ((Object)(object)_font != (Object)null)
		{
			((TMP_Text)component).font = _font;
		}
		return component;
	}

	private static void ConfigureRect(RectTransform rect, RectTransform parent, Vector2 position, Vector2 size)
	{
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0048: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		((Transform)rect).SetParent((Transform)(object)parent, false);
		rect.anchorMin = new Vector2(0.5f, 0.5f);
		rect.anchorMax = new Vector2(0.5f, 0.5f);
		rect.pivot = new Vector2(0.5f, 0.5f);
		rect.anchoredPosition = position;
		rect.sizeDelta = size;
	}

	private void SortNow()
	{
		if (InventorySorter.TrySort(_inventory))
		{
			_scrollView?.LayoutSlots(resetScroll: true);
		}
		RefreshLocalization();
	}

	private void RefreshLocalization()
	{
		if ((Object)(object)_searchPlaceholder != (Object)null)
		{
			((TMP_Text)_searchPlaceholder).text = ModLocalization.Text("Suchen …", "Search …");
		}
		_language?.SetOptions(ModLocalization.Text("Spr.", "Lang."), ModLocalization.LanguageOptionLabels, ModLocalization.CurrentModeIndex);
		_filter?.SetOptions(ModLocalization.Text("Filter", "Filter"), InventoryToolsState.FilterLabels, (int)InventoryToolsState.Filter);
		_sortMode?.SetOptions(ModLocalization.Text("Sort.", "Sort"), InventoryToolsState.SortLabels, (int)InventoryToolsState.PendingSortMode);
		if ((Object)(object)_directionText != (Object)null)
		{
			((TMP_Text)_directionText).text = (InventoryToolsState.PendingDescending ? "↓" : "↑");
		}
		if ((Object)(object)_sortButtonText != (Object)null)
		{
			((TMP_Text)_sortButtonText).text = ModLocalization.Text("Sort.", "Sort");
		}
		_scrollView?.LayoutSlots();
	}

	private void DisableExternalAutoSortButtons()
	{
		Transform val = (((Object)(object)_inventory?.InvWindow != (Object)null) ? _inventory.InvWindow.transform : ((Component)this).transform.parent);
		if ((Object)(object)val == (Object)null)
		{
			return;
		}
		Transform[] componentsInChildren = ((Component)val).GetComponentsInChildren<Transform>(true);
		foreach (Transform val2 in componentsInChildren)
		{
			if ((Object)(object)val2 != (Object)null && (Object)(object)((Component)val2).gameObject != (Object)(object)((Component)this).gameObject && string.Equals(((Object)val2).name, "AutoSortButton", StringComparison.Ordinal))
			{
				((Component)val2).gameObject.SetActive(false);
			}
		}
	}
}
internal sealed class InventoryChoiceDropdown : MonoBehaviour
{
	private TextMeshProUGUI _caption;

	private RectTransform _optionsPanel;

	private TMP_FontAsset _font;

	private string _prefix = string.Empty;

	private IReadOnlyList<string> _options = Array.Empty<string>();

	private int _selected;

	private Action<int> _selectionHandler;

	internal static InventoryChoiceDropdown Create(RectTransform parent, string name, Vector2 position, Vector2 size, TMP_FontAsset font)
	{
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_005a: Unknown result type (might be due to invalid IL or missing references)
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0084: Unknown result type (might be due to invalid IL or missing references)
		//IL_008f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0096: Unknown result type (might be due to invalid IL or missing references)
		//IL_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_00c2: 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_0115: Unknown result type (might be due to invalid IL or missing references)
		//IL_0134: Unknown result type (might be due to invalid IL or missing references)
		//IL_0153: Unknown result type (might be due to invalid IL or missing references)
		//IL_016a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0174: Expected O, but got Unknown
		GameObject val = new GameObject(name, new Type[4]
		{
			typeof(RectTransform),
			typeof(Image),
			typeof(Button),
			typeof(InventoryChoiceDropdown)
		});
		RectTransform component = val.GetComponent<RectTransform>();
		((Transform)component).SetParent((Transform)(object)parent, false);
		component.anchorMin = new Vector2(0.5f, 0.5f);
		component.anchorMax = new Vector2(0.5f, 0.5f);
		component.pivot = new Vector2(0.5f, 0.5f);
		component.anchoredPosition = position;
		component.sizeDelta = size;
		Image component2 = val.GetComponent<Image>();
		((Graphic)component2).color = new Color(0.055f, 0.18f, 0.22f, 0.98f);
		Button component3 = val.GetComponent<Button>();
		((Selectable)component3).targetGraphic = (Graphic)(object)component2;
		InventoryChoiceDropdown component4 = val.GetComponent<InventoryChoiceDropdown>();
		component4._font = font;
		component4._caption = component4.CreateText(component, "Caption", 11.5f);
		((TMP_Text)component4._caption).rectTransform.anchorMin = Vector2.zero;
		((TMP_Text)component4._caption).rectTransform.anchorMax = Vector2.one;
		((TMP_Text)component4._caption).rectTransform.offsetMin = new Vector2(3f, 0f);
		((TMP_Text)component4._caption).rectTransform.offsetMax = new Vector2(-3f, 0f);
		((UnityEvent)component3.onClick).AddListener(new UnityAction(component4.Toggle));
		return component4;
	}

	internal void SetSelectionHandler(Action<int> handler)
	{
		_selectionHandler = handler;
	}

	internal void SetOptions(string prefix, IReadOnlyList<string> options, int selected)
	{
		_prefix = prefix ?? string.Empty;
		_options = options ?? Array.Empty<string>();
		_selected = Mathf.Clamp(selected, 0, Math.Max(0, _options.Count - 1));
		if ((Object)(object)_caption != (Object)null)
		{
			string text = ((_options.Count > 0) ? _options[_selected] : string.Empty);
			((TMP_Text)_caption).text = _prefix + ": " + text + " ▼";
		}
		if ((Object)(object)_optionsPanel != (Object)null && ((Component)_optionsPanel).gameObject.activeSelf)
		{
			BuildOptions();
		}
	}

	private void Toggle()
	{
		bool flag = (Object)(object)_optionsPanel == (Object)null || !((Component)_optionsPanel).gameObject.activeSelf;
		InventoryChoiceDropdown[] componentsInChildren = ((Component)((Component)this).transform.parent).GetComponentsInChildren<InventoryChoiceDropdown>(true);
		foreach (InventoryChoiceDropdown inventoryChoiceDropdown in componentsInChildren)
		{
			if ((Object)(object)inventoryChoiceDropdown != (Object)(object)this)
			{
				inventoryChoiceDropdown.Close();
			}
		}
		if (!flag)
		{
			Close();
			return;
		}
		BuildOptions();
		((Component)_optionsPanel).gameObject.SetActive(true);
		((Component)this).transform.SetAsLastSibling();
	}

	private void Close()
	{
		if ((Object)(object)_optionsPanel != (Object)null)
		{
			((Component)_optionsPanel).gameObject.SetActive(false);
		}
	}

	private void BuildOptions()
	{
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Expected O, but got Unknown
		//IL_006a: 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_009e: 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_00dc: 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_019a: Unknown result type (might be due to invalid IL or missing references)
		//IL_019f: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
		//IL_020c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0222: Unknown result type (might be due to invalid IL or missing references)
		//IL_022c: Unknown result type (might be due to invalid IL or missing references)
		//IL_026e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0253: Unknown result type (might be due to invalid IL or missing references)
		//IL_02af: Unknown result type (might be due to invalid IL or missing references)
		//IL_02bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d9: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f2: Unknown result type (might be due to invalid IL or missing references)
		//IL_0309: Unknown result type (might be due to invalid IL or missing references)
		//IL_0313: Expected O, but got Unknown
		if ((Object)(object)_optionsPanel == (Object)null)
		{
			GameObject val = new GameObject("Options", new Type[2]
			{
				typeof(RectTransform),
				typeof(Image)
			});
			_optionsPanel = val.GetComponent<RectTransform>();
			((Transform)_optionsPanel).SetParent(((Component)this).transform, false);
			_optionsPanel.anchorMin = new Vector2(0f, 0f);
			_optionsPanel.anchorMax = new Vector2(1f, 0f);
			_optionsPanel.pivot = new Vector2(0.5f, 1f);
			_optionsPanel.anchoredPosition = new Vector2(0f, -1f);
			((Graphic)val.GetComponent<Image>()).color = new Color(0.015f, 0.05f, 0.065f, 0.99f);
		}
		Transform[] array = ((IEnumerable)_optionsPanel).Cast<Transform>().ToArray();
		for (int i = 0; i < array.Length; i++)
		{
			Object.DestroyImmediate((Object)(object)((Component)array[i]).gameObject);
		}
		_optionsPanel.sizeDelta = new Vector2(0f, 22f * (float)_options.Count + 2f);
		for (int j = 0; j < _options.Count; j++)
		{
			int captured = j;
			GameObject val2 = new GameObject($"Option_{j}", new Type[3]
			{
				typeof(RectTransform),
				typeof(Image),
				typeof(Button)
			});
			RectTransform component = val2.GetComponent<RectTransform>();
			((Transform)component).SetParent((Transform)(object)_optionsPanel, false);
			component.anchorMin = new Vector2(0f, 1f);
			component.anchorMax = new Vector2(1f, 1f);
			component.pivot = new Vector2(0.5f, 1f);
			component.anchoredPosition = new Vector2(0f, -1f - (float)j * 22f);
			component.sizeDelta = new Vector2(0f, 22f);
			Image component2 = val2.GetComponent<Image>();
			((Graphic)component2).color = ((j == _selected) ? new Color(0.55f, 0.4f, 0.12f, 0.98f) : new Color(0.055f, 0.18f, 0.22f, 0.98f));
			Button component3 = val2.GetComponent<Button>();
			((Selectable)component3).targetGraphic = (Graphic)(object)component2;
			TextMeshProUGUI obj = CreateText(component, "Label", 11.5f);
			((TMP_Text)obj).text = _options[j];
			((TMP_Text)obj).rectTransform.anchorMin = Vector2.zero;
			((TMP_Text)obj).rectTransform.anchorMax = Vector2.one;
			((TMP_Text)obj).rectTransform.offsetMin = new Vector2(3f, 0f);
			((TMP_Text)obj).rectTransform.offsetMax = new Vector2(-3f, 0f);
			((UnityEvent)component3.onClick).AddListener((UnityAction)delegate
			{
				_selected = captured;
				Close();
				_selectionHandler?.Invoke(captured);
			});
		}
	}

	private TextMeshProUGUI CreateText(RectTransform parent, string name, float size)
	{
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = new GameObject(name, new Type[2]
		{
			typeof(RectTransform),
			typeof(TextMeshProUGUI)
		});
		((Transform)val.GetComponent<RectTransform>()).SetParent((Transform)(object)parent, false);
		TextMeshProUGUI component = val.GetComponent<TextMeshProUGUI>();
		((TMP_Text)component).alignment = (TextAlignmentOptions)514;
		((TMP_Text)component).fontSize = size;
		((Graphic)component).color = Color.white;
		((TMP_Text)component).enableWordWrapping = false;
		((Graphic)component).raycastTarget = false;
		if ((Object)(object)_font != (Object)null)
		{
			((TMP_Text)component).font = _font;
		}
		return component;
	}
}
internal static class InventorySorter
{
	private sealed class SlotSnapshot
	{
		internal readonly ItemIcon Slot;

		internal readonly Item Item;

		internal readonly int Quantity;

		internal readonly int OriginalIndex;

		internal SlotSnapshot(ItemIcon slot, Item item, int quantity, int originalIndex)
		{
			Slot = slot;
			Item = item;
			Quantity = quantity;
			OriginalIndex = originalIndex;
		}
	}

	private sealed class HotkeySnapshot
	{
		internal readonly Hotkeys Hotkey;

		internal readonly ItemIcon OriginalSlot;

		internal readonly int OriginalInvSlotIndex;

		internal HotkeySnapshot(Hotkeys hotkey, ItemIcon originalSlot, int originalInvSlotIndex)
		{
			Hotkey = hotkey;
			OriginalSlot = originalSlot;
			OriginalInvSlotIndex = originalInvSlotIndex;
		}
	}

	private sealed class SortEntry
	{
		internal readonly Item Item;

		internal readonly int Quantity;

		internal readonly int OriginalIndex;

		internal readonly List<Hotkeys> Hotkeys;

		internal SortEntry(Item item, int quantity, int originalIndex, List<Hotkeys> hotkeys)
		{
			Item = item;
			Quantity = quantity;
			OriginalIndex = originalIndex;
			Hotkeys = hotkeys;
		}
	}

	private sealed class StackGroup
	{
		internal readonly Item Item;

		internal readonly int OriginalIndex;

		internal readonly List<Hotkeys> Hotkeys = new List<Hotkeys>();

		internal long Total;

		internal StackGroup(Item item, int originalIndex)
		{
			Item = item;
			OriginalIndex = originalIndex;
		}
	}

	private readonly struct ComparisonSlot
	{
		internal readonly Item Item;

		internal readonly int Quantity;

		internal ComparisonSlot(Item item, int quantity)
		{
			Item = item;
			Quantity = quantity;
		}
	}

	private sealed class ReferenceComparer<T> : IEqualityComparer<T> where T : class
	{
		internal static readonly ReferenceComparer<T> Instance = new ReferenceComparer<T>();

		public bool Equals(T left, T right)
		{
			return left == right;
		}

		public int GetHashCode(T value)
		{
			return RuntimeHelpers.GetHashCode(value);
		}
	}

	internal static bool TrySort(Inventory inventory)
	{
		if (!CanSort(inventory, out var reason))
		{
			TaschenhaendlerPlugin.NotifyPlayer(reason);
			return false;
		}
		CharacterProgress current = ProgressStore.Current;
		List<ItemIcon> list = inventory.StoredSlots.Where((ItemIcon slot) => IsSortableSlot(inventory, slot)).ToList();
		List<SlotSnapshot> snapshot = list.Select((ItemIcon slot, int index) => new SlotSnapshot(slot, slot.MyItem, slot.Quantity, index)).ToList();
		List<HotkeySnapshot> hotkeys = CaptureHotkeys(list);
		bool flag = false;
		try
		{
			List<SortEntry> list2 = BuildEntries(snapshot, hotkeys, inventory.Empty, TaschenhaendlerPlugin.Instance.MergeStacksOnSort.Value);
			InventorySortMode mode = InventoryToolsState.PendingSortMode;
			bool descending = InventoryToolsState.PendingDescending;
			list2.Sort((SortEntry left, SortEntry right) => CompareEntries(left, right, mode, descending));
			foreach (ItemIcon item in list)
			{
				item.MyItem = inventory.Empty;
				item.Quantity = 0;
				item.assignedHotkey = null;
			}
			for (int num = 0; num < list2.Count; num++)
			{
				SortEntry sortEntry = list2[num];
				ItemIcon val = list[num];
				val.MyItem = sortEntry.Item;
				val.Quantity = sortEntry.Quantity;
				foreach (Hotkeys hotkey in sortEntry.Hotkeys)
				{
					if (!((Object)(object)hotkey == (Object)null))
					{
						hotkey.AssignItemFrominv(val);
						hotkey.UpdateImg();
						val.assignedHotkey = hotkey;
					}
				}
			}
			foreach (ItemIcon item2 in list)
			{
				item2.UpdateSlotImage();
			}
			inventory.UpdatePlayerInventory();
			InventoryExpansionController.CaptureOverflow(current);
			if (!ProgressStore.Save())
			{
				throw new InvalidOperationException("Sidecar save failed");
			}
			flag = true;
			if (!InventoryExpansionController.SaveNativeAfterSidecarCommit())
			{
				throw new InvalidOperationException("Native character save failed");
			}
			TaschenhaendlerPlugin.NotifyPlayer(ModLocalization.Text("Inventar sortiert; Hotkeys und Zusatzplätze wurden beibehalten.", "Inventory sorted; hotkeys and extra slots were preserved."));
			return true;
		}
		catch (Exception arg)
		{
			RestoreSnapshot(inventory, snapshot, hotkeys);
			InventoryExpansionController.CaptureOverflow(current);
			if (flag)
			{
				ProgressStore.Save();
			}
			TaschenhaendlerPlugin.ModLog.LogError((object)$"Inventarsortierung wurde zurückgerollt: {arg}");
			TaschenhaendlerPlugin.NotifyPlayer(ModLocalization.Text("Sortierung fehlgeschlagen; das Inventar wurde vollständig zurückgesetzt.", "Sorting failed; the inventory was fully restored."));
			return false;
		}
	}

	internal static int CompareItemSlots(ItemIcon left, ItemIcon right, InventorySortMode mode)
	{
		Item myItem = left.MyItem;
		Item myItem2 = right.MyItem;
		return mode switch
		{
			InventorySortMode.Name => CultureInfo.InvariantCulture.CompareInfo.Compare(myItem.ItemName ?? string.Empty, myItem2.ItemName ?? string.Empty, CompareOptions.IgnoreCase), 
			InventorySortMode.Type => CompareType(myItem, myItem2), 
			InventorySortMode.Level => myItem.ItemLevel.CompareTo(myItem2.ItemLevel), 
			InventorySortMode.Value => myItem.ItemValue.CompareTo(myItem2.ItemValue), 
			InventorySortMode.Quantity => GetQuantityKey(left).CompareTo(GetQuantityKey(right)), 
			_ => 0, 
		};
	}

	private static bool CanSort(Inventory inventory, out string reason)
	{
		if ((Object)(object)inventory == (Object)null || !inventory.isPlayer || (Object)(object)inventory.InvWindow == (Object)null || !inventory.InvWindow.activeSelf)
		{
			reason = ModLocalization.Text("Öffne zuerst dein Inventar.", "Open your inventory first.");
			return false;
		}
		if (ProgressStore.Current == null)
		{
			reason = ModLocalization.Text("Kein aktiver Charakterfortschritt gefunden.", "No active character progress was found.");
			return false;
		}
		if (GameData.InCharSelect || GameData.VendorWindowOpen || GameData.AuctionWindowOpen || GameData.Trading)
		{
			reason = ModLocalization.Text("Während eines anderen Inventarvorgangs kann nicht sortiert werden.", "Sorting is unavailable during another inventory transaction.");
			return false;
		}
		if ((Object)(object)GameData.ItemOnCursor != (Object)null && (Object)(object)GameData.ItemOnCursor != (Object)(object)inventory.Empty)
		{
			reason = ModLocalization.Text("Lege zuerst das Item am Mauszeiger ab.", "Place the item on your cursor first.");
			return false;
		}
		if (inventory.StoredSlots.Any((ItemIcon slot) => (Object)(object)slot != (Object)null && slot.dragging))
		{
			reason = ModLocalization.Text("Beende zuerst das Ziehen eines Items.", "Finish dragging the item first.");
			return false;
		}
		reason = string.Empty;
		return true;
	}

	private static bool IsSortableSlot(Inventory inventory, ItemIcon slot)
	{
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)slot != (Object)null && (int)slot.ThisSlotType == 0 && !slot.TrashSlot && !slot.VendorSlot && !slot.LootSlot && !slot.BankSlot && !slot.DisplayOnly && !slot.NotInInventory && !slot.CosmeticOnly && !slot.NoStackables && (inventory.CosmeticSlots == null || !inventory.CosmeticSlots.Contains(slot)))
		{
			return !InventoryExpansionController.IsReservedUnresolvedSlot(slot);
		}
		return false;
	}

	private static List<HotkeySnapshot> CaptureHotkeys(List<ItemIcon> targets)
	{
		List<HotkeySnapshot> list = new List<HotkeySnapshot>();
		if (GameData.GM?.HKManager?.AllHotkeys == null)
		{
			return list;
		}
		HashSet<ItemIcon> hashSet = new HashSet<ItemIcon>(targets);
		foreach (Hotkeys allHotkey in GameData.GM.HKManager.AllHotkeys)
		{
			if ((Object)(object)allHotkey?.AssignedItem != (Object)null && hashSet.Contains(allHotkey.AssignedItem))
			{
				list.Add(new HotkeySnapshot(allHotkey, allHotkey.AssignedItem, allHotkey.InvSlotIndex));
			}
		}
		return list;
	}

	private static List<SortEntry> BuildEntries(List<SlotSnapshot> snapshot, List<HotkeySnapshot> hotkeys, Item empty, bool mergeStacks)
	{
		List<SortEntry> list = new List<SortEntry>();
		Dictionary<ItemIcon, List<Hotkeys>> dictionary = (from binding in hotkeys
			group binding by binding.OriginalSlot).ToDictionary((IGrouping<ItemIcon, HotkeySnapshot> group) => group.Key, (IGrouping<ItemIcon, HotkeySnapshot> group) => group.Select((HotkeySnapshot binding) => binding.Hotkey).ToList());
		Dictionary<Item, StackGroup> dictionary2 = new Dictionary<Item, StackGroup>(ReferenceComparer<Item>.Instance);
		Dictionary<Item, int> dictionary3 = new Dictionary<Item, int>(ReferenceComparer<Item>.Instance);
		foreach (SlotSnapshot item in snapshot)
		{
			if (!((Object)(object)item.Item == (Object)null) && !((Object)(object)item.Item == (Object)(object)empty) && item.Item.Stackable)
			{
				List<Hotkeys> value;
				int num = (dictionary.TryGetValue(item.Slot, out value) ? value.Count : 0);
				dictionary3.TryGetValue(item.Item, out var value2);
				dictionary3[item.Item] = value2 + num;
			}
		}
		foreach (SlotSnapshot item2 in snapshot)
		{
			if ((Object)(object)item2.Item == (Object)null || (Object)(object)item2.Item == (Object)(object)empty)
			{
				continue;
			}
			List<Hotkeys> value3;
			List<Hotkeys> list2 = (dictionary.TryGetValue(item2.Slot, out value3) ? value3 : new List<Hotkeys>());
			int value4;
			bool flag = !dictionary3.TryGetValue(item2.Item, out value4) || value4 <= 1;
			if (mergeStacks && item2.Item.Stackable && flag)
			{
				if (!dictionary2.TryGetValue(item2.Item, out var value5))
				{
					value5 = new StackGroup(item2.Item, item2.OriginalIndex);
					dictionary2.Add(item2.Item, value5);
				}
				value5.Total += Math.Max(1L, item2.Quantity);
				value5.Hotkeys.AddRange(list2);
			}
			else
			{
				list.Add(new SortEntry(item2.Item, item2.Quantity, item2.OriginalIndex, list2));
			}
		}
		foreach (StackGroup value6 in dictionary2.Values)
		{
			long num2 = value6.Total;
			bool flag2 = true;
			while (num2 > 0)
			{
				int num3 = (int)((num2 > int.MaxValue) ? int.MaxValue : num2);
				list.Add(new SortEntry(value6.Item, num3, value6.OriginalIndex, flag2 ? value6.Hotkeys : new List<Hotkeys>()));
				num2 -= num3;
				flag2 = false;
			}
		}
		return list;
	}

	private static int CompareEntries(SortEntry left, SortEntry right, InventorySortMode mode, bool descending)
	{
		ComparisonSlot left2 = new ComparisonSlot(left.Item, left.Quantity);
		ComparisonSlot right2 = new ComparisonSlot(right.Item, right.Quantity);
		int num = CompareValues(left2, right2, mode);
		if (descending)
		{
			num = -num;
		}
		if (num != 0)
		{
			return num;
		}
		int num2 = CultureInfo.InvariantCulture.CompareInfo.Compare(left.Item.ItemName ?? string.Empty, right.Item.ItemName ?? string.Empty, CompareOptions.IgnoreCase);
		if (num2 != 0)
		{
			return num2;
		}
		int num3 = string.Compare(((BaseScriptableObject)left.Item).Id, ((BaseScriptableObject)right.Item).Id, StringComparison.Ordinal);
		if (num3 == 0)
		{
			return left.OriginalIndex.CompareTo(right.OriginalIndex);
		}
		return num3;
	}

	private static int CompareValues(ComparisonSlot left, ComparisonSlot right, InventorySortMode mode)
	{
		return mode switch
		{
			InventorySortMode.Name => CultureInfo.InvariantCulture.CompareInfo.Compare(left.Item.ItemName ?? string.Empty, right.Item.ItemName ?? string.Empty, CompareOptions.IgnoreCase), 
			InventorySortMode.Type => CompareType(left.Item, right.Item), 
			InventorySortMode.Level => left.Item.ItemLevel.CompareTo(right.Item.ItemLevel), 
			InventorySortMode.Value => left.Item.ItemValue.CompareTo(right.Item.ItemValue), 
			InventorySortMode.Quantity => ((!left.Item.Stackable) ? 1 : left.Quantity).CompareTo((!right.Item.Stackable) ? 1 : right.Quantity), 
			_ => 0, 
		};
	}

	private static int CompareType(Item left, Item right)
	{
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: Expected I4, but got Unknown
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_002c: Expected I4, but got Unknown
		int num = GetTypeRank(left).CompareTo(GetTypeRank(right));
		if (num == 0)
		{
			return ((int)left.RequiredSlot).CompareTo((int)right.RequiredSlot);
		}
		return num;
	}

	private static int GetTypeRank(Item item)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: Invalid comparison between Unknown and I4
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Invalid comparison between Unknown and I4
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: Invalid comparison between Unknown and I4
		//IL_0027: 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)
		if ((int)item.RequiredSlot == 13 || (int)item.RequiredSlot == 14 || (int)item.RequiredSlot == 15 || item.WeaponDmg != 0 || (int)item.ThisWeaponType != 0)
		{
			return 0;
		}
		if ((int)item.RequiredSlot != 0)
		{
			return 1;
		}
		if ((Object)(object)item.ItemEffectOnClick != (Object)null || (Object)(object)item.ItemSkillUse != (Object)null || item.Disposable)
		{
			return 2;
		}
		if (item.Template || item.FuelSource || item.Mining > 0 || item.FurnitureSet)
		{
			return 3;
		}
		if ((Object)(object)item.AssignQuestOnRead != (Object)null || (Object)(object)item.CompleteOnRead != (Object)null || !string.IsNullOrWhiteSpace(item.BookTitle))
		{
			return 4;
		}
		return 5;
	}

	private static int GetQuantityKey(ItemIcon slot)
	{
		if (!((Object)(object)slot.MyItem != (Object)null) || !slot.MyItem.Stackable)
		{
			return 1;
		}
		return slot.Quantity;
	}

	private static void RestoreSnapshot(Inventory inventory, List<SlotSnapshot> snapshot, List<HotkeySnapshot> hotkeys)
	{
		foreach (SlotSnapshot item in snapshot)
		{
			item.Slot.MyItem = item.Item;
			item.Slot.Quantity = item.Quantity;
			item.Slot.assignedHotkey = null;
			item.Slot.UpdateSlotImage();
		}
		foreach (HotkeySnapshot hotkey in hotkeys)
		{
			if (!((Object)(object)hotkey.Hotkey == (Object)null) && !((Object)(object)hotkey.OriginalSlot == (Object)null))
			{
				hotkey.Hotkey.AssignItemFrominv(hotkey.OriginalSlot);
				hotkey.Hotkey.InvSlotIndex = hotkey.OriginalInvSlotIndex;
				hotkey.Hotkey.UpdateImg();
				hotkey.OriginalSlot.assignedHotkey = hotkey.Hotkey;
			}
		}
		inventory.UpdatePlayerInventory();
	}
}
[HarmonyPatch(typeof(Inventory), "UpdatePlayerInventory")]
internal static class InventoryToolsRefreshPatch
{
	[HarmonyPostfix]
	[HarmonyPriority(0)]
	private static void Postfix(Inventory __instance)
	{
		if ((Object)(object)__instance != (Object)null && __instance.isPlayer)
		{
			InventoryExpansionController.RefreshLayout();
		}
	}
}
internal enum ModLanguageMode
{
	Auto,
	German,
	English
}
internal static class ModLocalization
{
	private static readonly string[] StableModes = new string[3] { "Auto", "German", "English" };

	internal static bool IsGerman
	{
		get
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Invalid comparison between Unknown and I4
			switch (CurrentMode)
			{
			case ModLanguageMode.Auto:
				if ((int)Application.systemLanguage != 15)
				{
					return string.Equals(CultureInfo.CurrentUICulture.TwoLetterISOLanguageName, "de", StringComparison.OrdinalIgnoreCase);
				}
				return true;
			default:
				return false;
			case ModLanguageMode.German:
				return true;
			}
		}
	}

	internal static ModLanguageMode CurrentMode
	{
		get
		{
			if (!TryParseMode(TaschenhaendlerPlugin.Instance?.LanguageMode?.Value, out var mode))
			{
				return ModLanguageMode.Auto;
			}
			return mode;
		}
	}

	internal static int CurrentModeIndex => (int)CurrentMode;

	internal static IReadOnlyList<string> LanguageOptionLabels
	{
		get
		{
			if (IsGerman)
			{
				return new string[3] { "Auto", "Deutsch", "Englisch" };
			}
			return new string[3] { "Auto", "German", "English" };
		}
	}

	internal static string MerchantRole => Text("Taschen-Händlerin", "Bag Merchant");

	internal static string MessagePrefix => Text("Taschen-Händlerin", "Bag Merchant");

	internal static string Text(string german, string english)
	{
		if (!IsGerman)
		{
			return english;
		}
		return german;
	}

	internal static string Format(string german, string english, params object[] arguments)
	{
		return string.Format(Text(german, english), arguments);
	}

	internal static void SetModeByIndex(int index)
	{
		index = Mathf.Clamp(index, 0, StableModes.Length - 1);
		if (TaschenhaendlerPlugin.Instance?.LanguageMode != null)
		{
			TaschenhaendlerPlugin.Instance.LanguageMode.Value = StableModes[index];
		}
	}

	internal static bool TryParseMode(string value, out ModLanguageMode mode)
	{
		if (string.Equals(value, "German", StringComparison.OrdinalIgnoreCase) || string.Equals(value, "Deutsch", StringComparison.OrdinalIgnoreCase) || string.Equals(value, "de", StringComparison.OrdinalIgnoreCase))
		{
			mode = ModLanguageMode.German;
			return true;
		}
		if (string.Equals(value, "English", StringComparison.OrdinalIgnoreCase) || string.Equals(value, "Englisch", StringComparison.OrdinalIgnoreCase) || string.Equals(value, "en", StringComparison.OrdinalIgnoreCase))
		{
			mode = ModLanguageMode.English;
			return true;
		}
		if (string.IsNullOrWhiteSpace(value) || string.Equals(value, "Auto", StringComparison.OrdinalIgnoreCase) || string.Equals(value, "Automatic", StringComparison.OrdinalIgnoreCase) || string.Equals(value, "Automatisch", StringComparison.OrdinalIgnoreCase))
		{
			mode = ModLanguageMode.Auto;
			return true;
		}
		mode = ModLanguageMode.Auto;
		return false;
	}
}
internal sealed class BagVendorMarker : MonoBehaviour
{
	internal string SceneName;

	internal Vector3 AnchorPosition;

	internal int SourceVendorInstanceId;

	internal int NameIndex;

	private IEnumerator Start()
	{
		yield return null;
		MerchantController.RefreshVendorPresentation(this);
	}
}
internal static class MerchantController
{
	private sealed class ProgressWriteException : Exception
	{
	}

	private static readonly string[] GermanVendorNames = new string[12]
	{
		"Mara – Taschen-Händlerin", "Borin – Gepäckmeister", "Lina – Hüterin der Taschen", "Taro – Taschen-Händler", "Selma – Reiseausstatterin", "Oren – Gepäckhändler", "Nira – Taschen-Händlerin", "Fenn – Meister der Beutel", "Kara – Reisehändlerin", "Jorin – Taschen-Händler",
		"Alva – Gepäckmeisterin", "Rumo – Reiseausstatter"
	};

	private static readonly string[] EnglishVendorNames = new string[12]
	{
		"Mara – Bag Merchant", "Borin – Luggage Master", "Lina – Keeper of Bags", "Taro – Bag Merchant", "Selma – Travel Outfitter", "Oren – Luggage Merchant", "Nira – Bag Merchant", "Fenn – Master of Pouches", "Kara – Travel Merchant", "Jorin – Bag Merchant",
		"Alva – Luggage Expert", "Rumo – Travel Outfitter"
	};

	private static TaschenhaendlerPlugin _plugin;

	private static readonly HashSet<int> PendingSources = new HashSet<int>();

	internal static Item ExpansionItem { get; private set; }

	internal static void Initialize(TaschenhaendlerPlugin plugin)
	{
		_plugin = plugin;
		PendingSources.Clear();
	}

	internal static void Shutdown()
	{
		PendingSources.Clear();
		_plugin = null;
	}

	internal static void OnSceneLoaded(Scene scene)
	{
		PendingSources.Clear();
	}

	internal static void OnSceneUnloaded(Scene scene)
	{
		PendingSources.Clear();
	}

	internal static void RegisterExpansionItem(ItemDatabase database)
	{
		//IL_0153: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)database == (Object)null)
		{
			return;
		}
		try
		{
			Dictionary<string, Item> dictionary = Traverse.Create((object)database).Field("itemDict").GetValue<Dictionary<string, Item>>();
			if (dictionary != null && dictionary.TryGetValue("CBRA_TASCHENHAENDLER_INVENTARERWEITERUNG_V1", out var value) && (Object)(object)value != (Object)null)
			{
				ExpansionItem = value;
				RefreshOffer();
				RebindExistingExpansionItems();
				InventoryExpansionController.RestorePendingExpansionItems();
				return;
			}
			Item value2 = null;
			dictionary?.TryGetValue("315843", out value2);
			value2 = value2 ?? database.ItemDB?.FirstOrDefault((Func<Item, bool>)((Item val2) => (Object)(object)val2 != (Object)null && (Object)(object)val2.ItemIcon != (Object)null));
			value2 = value2 ?? database.ItemDBList?.FirstOrDefault((Func<Item, bool>)((Item val2) => (Object)(object)val2 != (Object)null && (Object)(object)val2.ItemIcon != (Object)null));
			if ((Object)(object)value2 == (Object)null)
			{
				TaschenhaendlerPlugin.ModLog.LogError((object)"Inventarerweiterungsitem konnte nicht erzeugt werden: Keine Item-Vorlage gefunden.");
				return;
			}
			Item item = Object.Instantiate<Item>(value2);
			((Object)item).name = "CBra Inventory Expansion";
			((BaseScriptableObject)item).Id = "CBRA_TASCHENHAENDLER_INVENTARERWEITERUNG_V1";
			item.ItemName = ModLocalization.Text("Inventarerweiterung (+10 Plätze)", "Inventory Expansion (+10 slots)");
			item.ItemLevel = 1;
			item.RequiredSlot = (SlotType)0;
			item.Classes = new List<Class>
			{
				GameData.ClassDB?.Paladin,
				GameData.ClassDB?.Arcanist,
				GameData.ClassDB?.Duelist,
				GameData.ClassDB?.Druid,
				GameData.ClassDB?.Stormcaller,
				GameData.ClassDB?.Reaver
			}.Where((Class characterClass) => (Object)(object)characterClass != (Object)null).Distinct().ToList();
			item.Stackable = true;
			item.Disposable = true;
			item.Unique = false;
			item.PlayerCannotSe