Decompiled source of SortingTweaks v1.1.0

SortingTweaks.dll

Decompiled 6 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.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("0.0.0.0")]
namespace SortingTweaks;

internal enum SortBy
{
	Default = 0,
	Id = 1,
	Name = 2,
	Weight = 4,
	Type = 5,
	Biome = 6
}
internal struct SortSetting
{
	public SortBy By;

	public bool Ascending;
}
internal static class ContainerSort
{
	private const string QssSortModule = "QuickStackStore.SortModule";

	private static ConfigEntry<float> buttonOffsetX;

	private static ConfigEntry<float> buttonOffsetY;

	private static readonly Dictionary<string, SortSetting> settings = new Dictionary<string, SortSetting>();

	private static string settingsPath;

	private static SortSetting? activeSetting;

	private static readonly SortBy[] sortCycle = new SortBy[6]
	{
		SortBy.Default,
		SortBy.Id,
		SortBy.Name,
		SortBy.Weight,
		SortBy.Type,
		SortBy.Biome
	};

	private static int[] typeToCategory;

	private static Action<Container> qssSortContainer;

	private static readonly FieldRef<InventoryGui, Container> currentContainerRef = AccessTools.FieldRefAccess<InventoryGui, Container>("m_currentContainer");

	private static Button byButton;

	private static Button directionButton;

	private static TMP_Text byText;

	private static TMP_Text directionText;

	private static Container shownContainer;

	internal static void Init(BaseUnityPlugin plugin, Harmony harmony)
	{
		//IL_0177: Unknown result type (might be due to invalid IL or missing references)
		//IL_018e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0199: Expected O, but got Unknown
		//IL_0199: Expected O, but got Unknown
		//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ba: Expected O, but got Unknown
		//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f0: Expected O, but got Unknown
		Type type = AccessTools.TypeByName("QuickStackStore.SortModule");
		if (type == null)
		{
			SortingTweaksPlugin.Log.LogError((object)"QuickStackStore.SortModule not found, per-container sort is off.");
			return;
		}
		MethodInfo methodInfo = AccessTools.Method(type, "SortInternal", (Type[])null, (Type[])null);
		MethodInfo methodInfo2 = AccessTools.Method(type, "SortCompare", (Type[])null, (Type[])null);
		MethodInfo methodInfo3 = AccessTools.Method(type, "SortContainer", (Type[])null, (Type[])null);
		if (methodInfo == null || methodInfo2 == null || methodInfo3 == null)
		{
			SortingTweaksPlugin.Log.LogError((object)"Quick Stack Store sorting methods not found (incompatible version?), per-container sort is off.");
			return;
		}
		FieldInfo fieldInfo = AccessTools.Field(type, "TypeToCategory");
		typeToCategory = ((fieldInfo != null) ? ((int[])fieldInfo.GetValue(null)) : null);
		qssSortContainer = AccessTools.MethodDelegate<Action<Container>>(methodInfo3, (object)null, true);
		buttonOffsetX = plugin.Config.Bind<float>("ContainerSort", "ButtonOffsetX", -12f, "Horizontal offset of the per-container sort buttons from the top right corner of the container panel. Negative moves them left.");
		buttonOffsetY = plugin.Config.Bind<float>("ContainerSort", "ButtonOffsetY", -10f, "Vertical offset of the per-container sort buttons from the top right corner of the container panel. Negative moves them down.");
		buttonOffsetX.SettingChanged += delegate
		{
			LayoutButtons();
		};
		buttonOffsetY.SettingChanged += delegate
		{
			LayoutButtons();
		};
		settingsPath = Path.Combine(Paths.ConfigPath, "sortingtweaks.valheim.containers.txt");
		LoadSettings();
		harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(ContainerSort), "SortInternalPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(ContainerSort), "SortInternalFinalizer", (Type[])null), (HarmonyMethod)null);
		harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(ContainerSort), "SortComparePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		harmony.Patch((MethodBase)AccessTools.Method(typeof(InventoryGui), "Update", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(ContainerSort), "InventoryGuiUpdatePostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
	}

	private static void SortInternalPrefix(Inventory inventory)
	{
		activeSetting = null;
		InventoryGui instance = InventoryGui.instance;
		Container val = (((Object)(object)instance != (Object)null) ? currentContainerRef.Invoke(instance) : null);
		if (!((Object)(object)val == (Object)null) && val.GetInventory() == inventory)
		{
			SortSetting setting = GetSetting(val);
			if (setting.By != SortBy.Default)
			{
				activeSetting = setting;
			}
		}
	}

	private static void SortInternalFinalizer()
	{
		activeSetting = null;
	}

	private static bool SortComparePrefix(ItemData a, ItemData b, ref int __result)
	{
		if (!activeSetting.HasValue)
		{
			return true;
		}
		SortSetting value = activeSetting.Value;
		int num = SortKey(a, value.By).CompareTo(SortKey(b, value.By));
		if (!value.Ascending)
		{
			num *= -1;
		}
		if (num == 0)
		{
			num = a.m_shared.m_name.CompareTo(b.m_shared.m_name);
		}
		if (num == 0)
		{
			num = -a.m_quality.CompareTo(b.m_quality);
		}
		if (num == 0)
		{
			num = -a.m_stack.CompareTo(b.m_stack);
		}
		__result = num;
		return false;
	}

	private static IComparable SortKey(ItemData item, SortBy by)
	{
		//IL_004b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: Expected I4, but got Unknown
		switch (by)
		{
		case SortBy.Name:
			return Localization.instance.Localize(item.m_shared.m_name);
		case SortBy.Weight:
			return item.m_shared.m_weight;
		case SortBy.Type:
		{
			int num = (int)item.m_shared.m_itemType;
			if (typeToCategory == null || num < 0 || num >= typeToCategory.Length)
			{
				return num;
			}
			return typeToCategory[num];
		}
		case SortBy.Biome:
			return ItemBiomes.SortKey(item);
		default:
			return item.m_shared.m_name;
		}
	}

	private static string KeyFor(Container container)
	{
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_005e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		string text = "world";
		try
		{
			if ((Object)(object)ZNet.instance != (Object)null)
			{
				text = ZNet.instance.GetWorldName();
			}
		}
		catch (Exception)
		{
		}
		Vector3 position = ((Component)container).transform.position;
		return string.Format(CultureInfo.InvariantCulture, "{0}|{1}|{2}|{3}", text, Mathf.RoundToInt(position.x * 10f), Mathf.RoundToInt(position.y * 10f), Mathf.RoundToInt(position.z * 10f));
	}

	private static SortSetting GetSetting(Container container)
	{
		if (settings.TryGetValue(KeyFor(container), out var value))
		{
			return value;
		}
		return new SortSetting
		{
			By = SortBy.Default,
			Ascending = true
		};
	}

	private static void SetSetting(Container container, SortSetting setting)
	{
		string key = KeyFor(container);
		if (setting.By == SortBy.Default)
		{
			settings.Remove(key);
		}
		else
		{
			settings[key] = setting;
		}
		SaveSettings();
	}

	private static void LoadSettings()
	{
		settings.Clear();
		try
		{
			if (!File.Exists(settingsPath))
			{
				return;
			}
			string[] array = File.ReadAllLines(settingsPath);
			for (int i = 0; i < array.Length; i++)
			{
				string[] array2 = array[i].Split(new char[1] { '\t' });
				if (array2.Length == 3 && int.TryParse(array2[1], out var result) && Enum.IsDefined(typeof(SortBy), result))
				{
					settings[array2[0]] = new SortSetting
					{
						By = (SortBy)result,
						Ascending = (array2[2] == "1")
					};
				}
			}
		}
		catch (Exception ex)
		{
			SortingTweaksPlugin.Log.LogWarning((object)("Could not read " + settingsPath + ": " + ex.Message));
		}
	}

	private static void SaveSettings()
	{
		try
		{
			List<string> list = new List<string>();
			foreach (KeyValuePair<string, SortSetting> setting in settings)
			{
				string[] obj = new string[5] { setting.Key, "\t", null, null, null };
				int num = (int)setting.Value.By;
				obj[2] = num.ToString();
				obj[3] = "\t";
				obj[4] = (setting.Value.Ascending ? "1" : "0");
				list.Add(string.Concat(obj));
			}
			File.WriteAllLines(settingsPath, list.ToArray());
		}
		catch (Exception ex)
		{
			SortingTweaksPlugin.Log.LogWarning((object)("Could not write " + settingsPath + ": " + ex.Message));
		}
	}

	private static void InventoryGuiUpdatePostfix(InventoryGui __instance)
	{
		if ((Object)(object)__instance != (Object)(object)InventoryGui.instance)
		{
			return;
		}
		Container val = currentContainerRef.Invoke(__instance);
		if ((Object)(object)val == (Object)null || (Object)(object)__instance.m_container == (Object)null)
		{
			shownContainer = null;
			return;
		}
		if ((Object)(object)byButton == (Object)null || (Object)(object)directionButton == (Object)null)
		{
			CreateButtons(__instance);
			shownContainer = null;
		}
		if ((Object)(object)val != (Object)(object)shownContainer)
		{
			shownContainer = val;
			RefreshLabels(val);
		}
	}

	private static void CreateButtons(InventoryGui gui)
	{
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: Expected O, but got Unknown
		//IL_0056: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: Expected O, but got Unknown
		byButton = CloneButton(gui, "sortingTweaksSortByButton", out byText);
		directionButton = CloneButton(gui, "sortingTweaksSortDirectionButton", out directionText);
		((UnityEvent)byButton.onClick).AddListener(new UnityAction(OnByClicked));
		((UnityEvent)directionButton.onClick).AddListener(new UnityAction(OnDirectionClicked));
		LayoutButtons();
	}

	private static Button CloneButton(InventoryGui gui, string name, out TMP_Text label)
	{
		//IL_0058: Unknown result type (might be due to invalid IL or missing references)
		//IL_005f: Expected O, but got Unknown
		Button val = Object.Instantiate<Button>(gui.m_takeAllButton, (Transform)(object)gui.m_container);
		((Object)val).name = name;
		((UnityEventBase)val.onClick).RemoveAllListeners();
		UIGamePad[] components = ((Component)val).GetComponents<UIGamePad>();
		for (int i = 0; i < components.Length; i++)
		{
			Object.Destroy((Object)(object)components[i]);
		}
		label = null;
		foreach (Transform item in ((Component)val).transform)
		{
			Transform val2 = item;
			TMP_Text component = ((Component)val2).GetComponent<TMP_Text>();
			if ((Object)(object)component != (Object)null && (Object)(object)label == (Object)null)
			{
				label = component;
				component.enableAutoSizing = true;
				component.fontSizeMin = 10f;
				component.fontSizeMax = 20f;
				component.textWrappingMode = (TextWrappingModes)0;
			}
			else
			{
				((Component)val2).gameObject.SetActive(false);
			}
		}
		((Component)val).gameObject.SetActive(true);
		return val;
	}

	private static void LayoutButtons()
	{
		if (!((Object)(object)byButton == (Object)null) && !((Object)(object)directionButton == (Object)null))
		{
			PlaceTopRight(directionButton, buttonOffsetX.Value, buttonOffsetY.Value, 62f, 30f);
			PlaceTopRight(byButton, buttonOffsetX.Value - 62f - 6f, buttonOffsetY.Value, 104f, 30f);
		}
	}

	private static void PlaceTopRight(Button button, float x, float y, float width, float height)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: 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_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_004a: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: 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)
		RectTransform val = (RectTransform)((Component)button).transform;
		val.anchorMin = new Vector2(1f, 1f);
		val.anchorMax = new Vector2(1f, 1f);
		val.pivot = new Vector2(1f, 1f);
		val.sizeDelta = new Vector2(width, height);
		val.anchoredPosition = new Vector2(x, y);
	}

	private static void RefreshLabels(Container container)
	{
		SortSetting setting = GetSetting(container);
		if ((Object)(object)byText != (Object)null)
		{
			byText.text = "Sort: " + Label(setting.By);
		}
		if ((Object)(object)directionText != (Object)null)
		{
			directionText.text = (setting.Ascending ? "Asc" : "Desc");
		}
		if ((Object)(object)directionButton != (Object)null)
		{
			((Selectable)directionButton).interactable = setting.By != SortBy.Default;
		}
	}

	private static string Label(SortBy by)
	{
		return by switch
		{
			SortBy.Id => "ID", 
			SortBy.Name => "Name", 
			SortBy.Weight => "Weight", 
			SortBy.Type => "Type", 
			SortBy.Biome => "Biome", 
			_ => "Default", 
		};
	}

	private static void OnByClicked()
	{
		Container val = shownContainer;
		if (!((Object)(object)val == (Object)null))
		{
			SortSetting setting = GetSetting(val);
			setting.By = sortCycle[(Array.IndexOf(sortCycle, setting.By) + 1) % sortCycle.Length];
			Apply(val, setting);
		}
	}

	private static void OnDirectionClicked()
	{
		Container val = shownContainer;
		if (!((Object)(object)val == (Object)null))
		{
			SortSetting setting = GetSetting(val);
			if (setting.By != SortBy.Default)
			{
				setting.Ascending = !setting.Ascending;
				Apply(val, setting);
			}
		}
	}

	private static void Apply(Container container, SortSetting setting)
	{
		SetSetting(container, setting);
		RefreshLabels(container);
		if (container.IsOwner())
		{
			qssSortContainer(container);
		}
	}
}
internal enum ComboScore
{
	Sum,
	Max
}
internal static class FoodTweaks
{
	private sealed class Row
	{
		public Recipe Recipe;

		public ItemData Item;

		public GameObject Element;

		public bool CanCraft;
	}

	private const string FoodCategoriesPlugin = "FoodCategories.Plugin";

	private const float EyeRowHeight = 25f;

	private static ConfigEntry<bool> showUncraftable;

	private static ConfigEntry<ComboScore> comboScore;

	private static IDictionary bars;

	private static MethodInfo apply;

	private static MethodInfo classify;

	private static Func<ItemData, bool> isFood;

	private static Func<ItemData, bool> isUnfilteredOnly;

	private static Func<Recipe, bool, bool> belongsToStation;

	private static Type filterEnum;

	private static FieldInfo barCurrent;

	private static FieldInfo barRoot;

	private static FieldInfo barPreparationStation;

	private static FieldInfo barButtons;

	private static FieldInfo barCraftTabTemplate;

	private static FieldInfo barUpgradeTabTemplate;

	private static FieldInfo availableRecipes;

	private static int filterAll;

	private static int filterHealth;

	private static int filterStamina;

	private static int filterEitr;

	private static int filterBalanced;

	private static int filterFeast;

	private static Type rowType;

	private static PropertyInfo rowRecipe;

	private static PropertyInfo rowItem;

	private static PropertyInfo rowElement;

	private static PropertyInfo rowCanCraft;

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

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

	private static Button eyeButton;

	private static Image eyeIcon;

	private static Sprite eyeOpen;

	private static Sprite eyeClosed;

	private static bool warned;

	internal static void Init(BaseUnityPlugin plugin, Harmony harmony)
	{
		//IL_02cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_02da: Expected O, but got Unknown
		//IL_0307: Unknown result type (might be due to invalid IL or missing references)
		//IL_0315: Expected O, but got Unknown
		Type type = AccessTools.TypeByName("FoodCategories.Plugin");
		if (type == null)
		{
			SortingTweaksPlugin.Log.LogError((object)"FoodCategories.Plugin not found, the food tweaks are off.");
			return;
		}
		filterEnum = type.GetNestedType("FoodFilter", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		Type nestedType = type.GetNestedType("FilterBar", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		FieldInfo fieldInfo = AccessTools.Field(type, "Bars");
		apply = AccessTools.Method(type, "Apply", (Type[])null, (Type[])null);
		classify = AccessTools.Method(type, "Classify", (Type[])null, (Type[])null);
		MethodInfo methodInfo = AccessTools.Method(type, "IsFood", (Type[])null, (Type[])null);
		MethodInfo methodInfo2 = AccessTools.Method(type, "IsUnfilteredOnly", (Type[])null, (Type[])null);
		MethodInfo methodInfo3 = AccessTools.Method(type, "BelongsToStation", (Type[])null, (Type[])null);
		availableRecipes = AccessTools.Field(typeof(InventoryGui), "m_availableRecipes");
		if (nestedType != null)
		{
			barCurrent = AccessTools.Field(nestedType, "Current");
			barRoot = AccessTools.Field(nestedType, "Root");
			barPreparationStation = AccessTools.Field(nestedType, "PreparationStation");
			barButtons = AccessTools.Field(nestedType, "Buttons");
			barCraftTabTemplate = AccessTools.Field(nestedType, "CraftTabTemplate");
			barUpgradeTabTemplate = AccessTools.Field(nestedType, "UpgradeTabTemplate");
		}
		if (new object[15]
		{
			filterEnum, nestedType, fieldInfo, apply, classify, methodInfo, methodInfo2, methodInfo3, availableRecipes, barCurrent,
			barRoot, barPreparationStation, barButtons, barCraftTabTemplate, barUpgradeTabTemplate
		}.Any((object x) => x == null))
		{
			SortingTweaksPlugin.Log.LogError((object)"Food Categories looks different than expected (incompatible version?), the food tweaks are off.");
			return;
		}
		bars = fieldInfo.GetValue(null) as IDictionary;
		if (bars == null)
		{
			SortingTweaksPlugin.Log.LogError((object)"Food Categories filter bars not found, the food tweaks are off.");
			return;
		}
		isFood = AccessTools.MethodDelegate<Func<ItemData, bool>>(methodInfo, (object)null, true);
		isUnfilteredOnly = AccessTools.MethodDelegate<Func<ItemData, bool>>(methodInfo2, (object)null, true);
		belongsToStation = AccessTools.MethodDelegate<Func<Recipe, bool, bool>>(methodInfo3, (object)null, true);
		filterAll = FilterValue("All");
		filterHealth = FilterValue("Health");
		filterStamina = FilterValue("Stamina");
		filterEitr = FilterValue("Eitr");
		filterBalanced = FilterValue("Balanced");
		filterFeast = FilterValue("Feast");
		showUncraftable = plugin.Config.Bind<bool>("Food", "ShowUncraftable", false, "Also list food recipes you know but cannot craft right now (the greyed out ones). Toggled by the eye button next to the food filters.");
		comboScore = plugin.Config.Bind<ComboScore>("Food", "ComboScore", ComboScore.Sum, "How an Eitr + Health or Eitr + Stamina combination is ranked. Sum adds both values, Max takes the higher one.");
		harmony.Patch((MethodBase)apply, (HarmonyMethod)null, new HarmonyMethod(typeof(FoodTweaks), "ApplyPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		MethodInfo methodInfo4 = AccessTools.Method(type, "IsFoodStation", (Type[])null, (Type[])null);
		if (methodInfo4 != null)
		{
			harmony.Patch((MethodBase)methodInfo4, new HarmonyMethod(typeof(FoodTweaks), "SkipMeadStation", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}
	}

	private static bool SkipMeadStation(ref bool __result, ref bool preparationStation)
	{
		CraftingStation val = (((Object)(object)Player.m_localPlayer != (Object)null) ? Player.m_localPlayer.GetCurrentCraftingStation() : null);
		if ((Object)(object)val == (Object)null || ((Object)((Component)val).gameObject).name.IndexOf("mead", StringComparison.OrdinalIgnoreCase) < 0)
		{
			return true;
		}
		preparationStation = false;
		__result = false;
		return false;
	}

	private static int FilterValue(string name)
	{
		return (int)Enum.Parse(filterEnum, name);
	}

	private static void ApplyPostfix(InventoryGui gui)
	{
		try
		{
			Refresh(gui);
		}
		catch (Exception ex)
		{
			if (!warned)
			{
				warned = true;
				SortingTweaksPlugin.Log.LogWarning((object)("Food tweaks failed: " + ex));
			}
		}
	}

	private static void Refresh(InventoryGui gui)
	{
		if ((Object)(object)gui == (Object)null || !gui.InCraftTab() || !bars.Contains(gui))
		{
			return;
		}
		object obj = bars[gui];
		object? value = barRoot.GetValue(obj);
		RectTransform val = (RectTransform)((value is RectTransform) ? value : null);
		if ((Object)(object)val != (Object)null && ((Component)val).gameObject.activeSelf && !InventoryGui.IsVisible())
		{
			((Component)val).gameObject.SetActive(false);
		}
		else if (!((Object)(object)val == (Object)null) && ((Component)val).gameObject.activeSelf)
		{
			IDictionary buttons = (IDictionary)barButtons.GetValue(obj);
			selected.RemoveWhere((int f) => !buttons.Contains(Enum.ToObject(filterEnum, f)));
			SyncCurrent(obj);
			HookButtons(gui, obj, buttons);
			EnsureEye(obj, val, buttons);
			StyleButtons(obj, buttons);
			Relayout(gui, (bool)barPreparationStation.GetValue(obj));
		}
	}

	private static bool IsCombo(out bool usesHealth)
	{
		usesHealth = selected.Contains(filterHealth);
		if (selected.Contains(filterEitr))
		{
			if (!usesHealth)
			{
				return selected.Contains(filterStamina);
			}
			return true;
		}
		return false;
	}

	private static void OnFilterClicked(InventoryGui gui, object bar, int filter)
	{
		if (selected.Contains(filter))
		{
			selected.Remove(filter);
		}
		else
		{
			if (filter == filterBalanced || filter == filterFeast)
			{
				selected.Clear();
			}
			else
			{
				selected.Remove(filterBalanced);
				selected.Remove(filterFeast);
				if (filter == filterHealth)
				{
					selected.Remove(filterStamina);
				}
				else if (filter == filterStamina)
				{
					selected.Remove(filterHealth);
				}
			}
			selected.Add(filter);
		}
		SyncCurrent(bar);
		Reapply(gui);
	}

	private static void SyncCurrent(object bar)
	{
		int value = filterAll;
		if (selected.Count > 0)
		{
			value = (selected.Contains(filterEitr) ? filterEitr : selected.First());
		}
		barCurrent.SetValue(bar, Enum.ToObject(filterEnum, value));
	}

	private static void Reapply(InventoryGui gui)
	{
		if ((Object)(object)gui != (Object)null)
		{
			apply.Invoke(null, new object[2] { gui, true });
		}
	}

	private static void Relayout(InventoryGui gui, bool preparationStation)
	{
		//IL_0172: Unknown result type (might be due to invalid IL or missing references)
		//IL_0183: Unknown result type (might be due to invalid IL or missing references)
		bool flag = selected.Count > 0;
		if (!flag && showUncraftable.Value)
		{
			return;
		}
		List<Row> list = ReadRows(gui);
		Func<Row, bool> predicate = (Row r) => showUncraftable.Value || r.CanCraft;
		List<Row> list2 = (flag ? (from r in (from r in list
				where isFood(r.Item) && belongsToStation(r.Recipe, preparationStation) && !isUnfilteredOnly(r.Item)
				where Matches(r.Item)
				select r).Where(predicate)
			orderby Score(r.Item) descending
			select r).ToList() : list.Where((Row r) => isFood(r.Item) && belongsToStation(r.Recipe, preparationStation)).Concat(list.Where((Row r) => !isFood(r.Item))).Where(predicate)
			.ToList());
		HashSet<Row> hashSet = new HashSet<Row>(list2);
		foreach (Row item in list)
		{
			item.Element.SetActive(hashSet.Contains(item));
		}
		float recipeListSpace = gui.m_recipeListSpace;
		for (int num = 0; num < list2.Count; num++)
		{
			RectTransform component = list2[num].Element.GetComponent<RectTransform>();
			component.anchoredPosition = new Vector2(component.anchoredPosition.x, (float)(-num) * recipeListSpace);
		}
		gui.m_recipeListRoot.SetSizeWithCurrentAnchors((Axis)1, Mathf.Max((float)list2.Count * recipeListSpace, 30f));
	}

	private static List<Row> ReadRows(InventoryGui gui)
	{
		List<Row> list = new List<Row>();
		if (!(availableRecipes.GetValue(gui) is IEnumerable enumerable))
		{
			return list;
		}
		foreach (object item in enumerable)
		{
			if (item != null)
			{
				Type type = item.GetType();
				if (type != rowType)
				{
					rowType = type;
					rowRecipe = AccessTools.Property(type, "Recipe");
					rowItem = AccessTools.Property(type, "ItemData");
					rowElement = AccessTools.Property(type, "InterfaceElement");
					rowCanCraft = AccessTools.Property(type, "CanCraft");
				}
				object? obj = rowRecipe?.GetValue(item, null);
				Recipe val = (Recipe)((obj is Recipe) ? obj : null);
				object? obj2 = rowItem?.GetValue(item, null);
				ItemData val2 = (ItemData)(((obj2 is ItemData) ? obj2 : null) ?? val?.m_item?.m_itemData);
				object? obj3 = rowElement?.GetValue(item, null);
				GameObject val3 = (GameObject)((obj3 is GameObject) ? obj3 : null);
				if (!((Object)(object)val == (Object)null) && val2 != null && !((Object)(object)val3 == (Object)null))
				{
					list.Add(new Row
					{
						Recipe = val,
						Item = val2,
						Element = val3,
						CanCraft = (rowCanCraft != null && (bool)rowCanCraft.GetValue(item, null))
					});
				}
			}
		}
		return list;
	}

	private static bool Matches(ItemData item)
	{
		if (IsCombo(out var usesHealth))
		{
			if (item.m_shared.m_foodEitr > 0f)
			{
				return PairedValue(item, usesHealth) > 0f;
			}
			return false;
		}
		int num = selected.First();
		return Convert.ToInt32(classify.Invoke(null, new object[1] { item })) == num;
	}

	private static float Score(ItemData item)
	{
		SharedData shared = item.m_shared;
		if (IsCombo(out var usesHealth))
		{
			float num = PairedValue(item, usesHealth);
			if (comboScore.Value != ComboScore.Max)
			{
				return shared.m_foodEitr + num;
			}
			return Mathf.Max(shared.m_foodEitr, num);
		}
		int num2 = selected.First();
		if (num2 == filterHealth)
		{
			return shared.m_food;
		}
		if (num2 == filterStamina)
		{
			return shared.m_foodStamina;
		}
		if (num2 == filterEitr)
		{
			return shared.m_foodEitr;
		}
		if (num2 == filterBalanced)
		{
			return shared.m_food + shared.m_foodStamina;
		}
		return 1f;
	}

	private static float PairedValue(ItemData item, bool usesHealth)
	{
		if (!usesHealth)
		{
			return item.m_shared.m_foodStamina;
		}
		return item.m_shared.m_food;
	}

	private static void HookButtons(InventoryGui gui, object bar, IDictionary buttons)
	{
		//IL_008d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0097: Expected O, but got Unknown
		foreach (DictionaryEntry button in buttons)
		{
			object? value = button.Value;
			Button val = (Button)((value is Button) ? value : null);
			if (!((Object)(object)val == (Object)null) && hookedButtons.Add(((Object)val).GetInstanceID()))
			{
				int filter = Convert.ToInt32(button.Key);
				((UnityEventBase)val.onClick).RemoveAllListeners();
				((UnityEvent)val.onClick).AddListener((UnityAction)delegate
				{
					OnFilterClicked(gui, bar, filter);
				});
			}
		}
	}

	private static void StyleButtons(object bar, IDictionary buttons)
	{
		object? value = barCraftTabTemplate.GetValue(bar);
		Image craftTemplate = (Image)((value is Image) ? value : null);
		object? value2 = barUpgradeTabTemplate.GetValue(bar);
		Image upgradeTemplate = (Image)((value2 is Image) ? value2 : null);
		foreach (DictionaryEntry button in buttons)
		{
			object? value3 = button.Value;
			Button val = (Button)((value3 is Button) ? value3 : null);
			if ((Object)(object)val != (Object)null)
			{
				StyleButton(val, selected.Contains(Convert.ToInt32(button.Key)), craftTemplate, upgradeTemplate);
			}
		}
		if ((Object)(object)eyeButton != (Object)null)
		{
			StyleButton(eyeButton, showUncraftable.Value, craftTemplate, upgradeTemplate);
			if ((Object)(object)eyeIcon != (Object)null)
			{
				eyeIcon.sprite = (showUncraftable.Value ? EyeSprite(slashed: false) : EyeSprite(slashed: true));
			}
		}
	}

	private static void StyleButton(Button button, bool isSelected, Image craftTemplate, Image upgradeTemplate)
	{
		//IL_009a: 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_002f: 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_0109: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
		Image component = ((Component)button).GetComponent<Image>();
		Image val = (isSelected ? craftTemplate : upgradeTemplate);
		if ((Object)(object)component != (Object)null && (Object)(object)val != (Object)null)
		{
			component.sprite = val.sprite;
			component.type = val.type;
			((Graphic)component).color = ((Graphic)val).color;
			((Graphic)component).material = ((Graphic)val).material;
			component.preserveAspect = val.preserveAspect;
			component.fillCenter = val.fillCenter;
		}
		Outline obj = ((Component)button).GetComponent<Outline>() ?? ((Component)button).gameObject.AddComponent<Outline>();
		((Behaviour)obj).enabled = isSelected;
		((Shadow)obj).effectColor = new Color(1f, 0.42f, 0.06f, 1f);
		((Shadow)obj).effectDistance = new Vector2(3f, -3f);
		Transform val2 = ((Component)button).transform.Find("Valheim Food Icon");
		Image val3 = (((Object)(object)val2 != (Object)null) ? ((Component)val2).GetComponent<Image>() : null);
		if ((Object)(object)val3 != (Object)null)
		{
			((Graphic)val3).rectTransform.sizeDelta = (isSelected ? new Vector2(21f, 21f) : new Vector2(17f, 17f));
		}
	}

	private static void EnsureEye(object bar, RectTransform root, IDictionary buttons)
	{
		//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c9: Expected O, but got Unknown
		//IL_0122: Unknown result type (might be due to invalid IL or missing references)
		//IL_0131: Unknown result type (might be due to invalid IL or missing references)
		//IL_0136: Unknown result type (might be due to invalid IL or missing references)
		//IL_0116: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)eyeButton != (Object)null && (Object)(object)((Component)eyeButton).transform.parent == (Object)(object)root)
		{
			return;
		}
		Button val = null;
		foreach (DictionaryEntry button in buttons)
		{
			object? value = button.Value;
			val = (Button)((value is Button) ? value : null);
			if ((Object)(object)val != (Object)null)
			{
				break;
			}
		}
		if (!((Object)(object)val == (Object)null))
		{
			GameObject obj = Object.Instantiate<GameObject>(((Component)val).gameObject, (Transform)(object)root, false);
			((Object)obj).name = "Sorting Tweaks Eye";
			eyeButton = obj.GetComponent<Button>();
			((UnityEventBase)eyeButton.onClick).RemoveAllListeners();
			((UnityEvent)eyeButton.onClick).AddListener(new UnityAction(OnEyeClicked));
			Transform val2 = obj.transform.Find("Valheim Food Icon");
			eyeIcon = (((Object)(object)val2 != (Object)null) ? ((Component)val2).GetComponent<Image>() : null);
			if ((Object)(object)eyeIcon != (Object)null)
			{
				((Graphic)eyeIcon).color = new Color(0.95f, 0.92f, 0.85f, 1f);
			}
			root.sizeDelta += new Vector2(0f, 25f);
		}
	}

	private static void OnEyeClicked()
	{
		showUncraftable.Value = !showUncraftable.Value;
		Reapply(InventoryGui.instance);
	}

	private static Sprite EyeSprite(bool slashed)
	{
		if (slashed)
		{
			return eyeClosed ?? (eyeClosed = BuildEye(slashed: true));
		}
		return eyeOpen ?? (eyeOpen = BuildEye(slashed: false));
	}

	private static Sprite BuildEye(bool slashed)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Expected O, but got Unknown
		//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_0173: Unknown result type (might be due to invalid IL or missing references)
		//IL_0158: Unknown result type (might be due to invalid IL or missing references)
		//IL_0178: Unknown result type (might be due to invalid IL or missing references)
		Texture2D val = new Texture2D(64, 64, (TextureFormat)4, false)
		{
			filterMode = (FilterMode)1,
			wrapMode = (TextureWrapMode)1
		};
		Color32[] array = (Color32[])(object)new Color32[4096];
		for (int i = 0; i < 64; i++)
		{
			for (int j = 0; j < 64; j++)
			{
				float num = ((float)j + 0.5f) / 64f * 2f - 1f;
				float num2 = ((float)i + 0.5f) / 64f * 2f - 1f;
				float num3 = 0.6f * (1f - num * num);
				bool flag = Mathf.Abs(num) <= 0.97f && Mathf.Abs(num2) <= num3;
				bool num4 = Mathf.Abs(num) <= 0.99f && Mathf.Abs(Mathf.Abs(num2) - num3) < 0.09f;
				bool flag2 = flag && num * num + num2 * num2 < 0.1f;
				bool flag3 = num4 || flag2;
				if (slashed)
				{
					float num5 = Mathf.Abs(num + num2);
					bool flag4 = num5 < 0.1f && Mathf.Abs(num) < 0.82f;
					bool flag5 = num5 < 0.22f && Mathf.Abs(num) < 0.9f;
					flag3 = flag4 || (flag3 && !flag5);
				}
				array[i * 64 + j] = (flag3 ? new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue) : new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, (byte)0));
			}
		}
		val.SetPixels32(array);
		val.Apply();
		return Sprite.Create(val, new Rect(0f, 0f, 64f, 64f), new Vector2(0.5f, 0.5f), 100f);
	}
}
internal static class ItemBiomes
{
	private sealed class Source
	{
		public string[] Inputs;

		public bool AnyInput;
	}

	internal const int Unknown = 99;

	private const int Meadows = 0;

	private const int BlackForest = 1;

	private const int Swamp = 2;

	private const int Mountain = 3;

	private const int Plains = 4;

	private const int Mistlands = 5;

	private const int Ashlands = 6;

	private const int DeepNorth = 7;

	private const int Ocean = 8;

	private const int Everywhere = 9;

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

	private static readonly Dictionary<string, int> raw = BuildRaw();

	private static readonly Dictionary<string, List<Source>> sources = new Dictionary<string, List<Source>>();

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

	private static bool built;

	private static Dictionary<string, int> BuildRaw()
	{
		Dictionary<string, int> map = new Dictionary<string, int>();
		Action<int, string[]> action = delegate(int biome, string[] prefabs)
		{
			foreach (string key in prefabs)
			{
				if (!map.ContainsKey(key))
				{
					map[key] = biome;
					order[key] = order.Count;
				}
			}
		};
		action(0, new string[15]
		{
			"Flint", "Raspberry", "Mushroom", "Honey", "Dandelion", "QueenBee", "LeatherScraps", "DeerHide", "DeerMeat", "RawMeat",
			"NeckTail", "HardAntler", "Acorn", "BeechSeeds", "BirchSeeds"
		});
		action(1, new string[19]
		{
			"Resin", "Blueberries", "Carrot", "CarrotSeeds", "Thistle", "CopperOre", "TinOre", "TrollHide", "BjornHide", "BjornPaw",
			"BjornMeat", "GreydwarfEye", "AncientSeed", "BoneFragments", "SurtlingCore", "MushroomYellow", "CryptKey", "FirCone", "PineCone"
		});
		action(2, new string[12]
		{
			"Ooze", "Bloodbag", "Entrails", "Chain", "IronScrap", "Guck", "WitheredBone", "Turnip", "TurnipSeeds", "ElderBark",
			"Root", "Wishbone"
		});
		action(3, new string[12]
		{
			"Obsidian", "FreezeGland", "WolfPelt", "WolfFang", "WolfMeat", "DragonTear", "DragonEgg", "SilverOre", "Crystal", "Onion",
			"OnionSeeds", "JuteRed"
		});
		action(4, new string[10] { "Cloudberry", "Barley", "Flax", "BlackMetalScrap", "LoxMeat", "LoxPelt", "Needle", "Tar", "GoblinTotem", "YagluthDrop" });
		action(5, new string[19]
		{
			"Carapace", "Mandible", "BugMeat", "Softtissue", "Sap", "YggdrasilWood", "BlackCore", "BlackMarble", "JuteBlue", "RoyalJelly",
			"MushroomJotunPuffs", "MushroomMagecap", "HareMeat", "ScaleHide", "Bilebag", "CopperScrap", "QueenDrop", "SeekerAspic", "DvergrKey"
		});
		action(6, new string[17]
		{
			"FlametalOre", "FlametalOreNew", "AskHide", "AskBladder", "AsksvinMeat", "VoltureMeat", "VoltureEgg", "MorgenHeart", "MorgenSinew", "BoneMawSerpentMeat",
			"CelestialFeather", "FaderDrop", "Grausten", "Fiddleheadfern", "MushroomSmokePuff", "Vineberry", "AsksvinEgg"
		});
		action(7, new string[18]
		{
			"MooseHide", "MooseMeat", "MooseSinew", "SealBlubber", "SealHide", "ElakingHairBundle", "FrostCore", "BarkaBranch", "MoleClaws", "NornThread",
			"MemorialCoal", "Ice", "KaleSeeds", "Kale", "OatSeeds", "PoteitrSeeds", "Poteitr", "Lingonberry"
		});
		action(0, new string[2] { "Fish1", "Fish2" });
		action(1, new string[1] { "Fish5" });
		action(2, new string[1] { "Fish6" });
		action(3, new string[1] { "Fish4_cave" });
		action(4, new string[1] { "Fish7" });
		action(5, new string[2] { "Fish9", "Fish12" });
		action(6, new string[1] { "Fish11" });
		action(7, new string[1] { "Fish10" });
		action(8, new string[5] { "SerpentMeat", "SerpentScale", "Chitin", "Fish3", "Fish8" });
		action(9, new string[19]
		{
			"Wood", "Stone", "StoneRock", "Coal", "Egg", "ChickenEgg", "ChickenMeat", "Feathers", "FishingBait", "Thunderstone",
			"YmirRemains", "BarrelRings", "Tankard", "Coins", "AncientCoin", "Amber", "AmberPearl", "Ruby", "SilverNecklace"
		});
		AddResearched(action);
		return map;
	}

	internal static int SortKey(ItemData item)
	{
		string text = (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : null);
		if (!TrophyBiomes.PositionOf(text, out var position))
		{
			position = ((text == null || !order.TryGetValue(text, out position)) ? 9999 : (position + 100));
		}
		return Of(item) * 10000 + position;
	}

	internal static int Of(ItemData item)
	{
		string text = (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : null);
		if (text == null)
		{
			return 9;
		}
		EnsureBuilt();
		if (!resolved.TryGetValue(text, out var value))
		{
			value = Resolve(text, new HashSet<string>());
		}
		if (value != 99)
		{
			return value;
		}
		return 9;
	}

	private static void EnsureBuilt()
	{
		if (built || (Object)(object)ObjectDB.instance == (Object)null || (Object)(object)ZNetScene.instance == (Object)null || ObjectDB.instance.m_items.Count == 0)
		{
			return;
		}
		built = true;
		foreach (Recipe recipe in ObjectDB.instance.m_recipes)
		{
			if (!((Object)(object)recipe == (Object)null) && recipe.m_enabled && !((Object)(object)recipe.m_item == (Object)null) && recipe.m_resources != null)
			{
				string[] inputs = (from r in recipe.m_resources
					where r != null && (Object)(object)r.m_resItem != (Object)null
					select ((Object)((Component)r.m_resItem).gameObject).name).ToArray();
				AddSource(((Object)((Component)recipe.m_item).gameObject).name, inputs, recipe.m_requireOnlyOneIngredient);
			}
		}
		foreach (GameObject prefab in ZNetScene.instance.m_prefabs)
		{
			if ((Object)(object)prefab == (Object)null)
			{
				continue;
			}
			Smelter component = prefab.GetComponent<Smelter>();
			if ((Object)(object)component != (Object)null && component.m_conversion != null)
			{
				foreach (ItemConversion item in component.m_conversion)
				{
					AddConversion(item.m_from, item.m_to);
				}
			}
			CookingStation component2 = prefab.GetComponent<CookingStation>();
			if ((Object)(object)component2 != (Object)null && component2.m_conversion != null)
			{
				foreach (ItemConversion item2 in component2.m_conversion)
				{
					AddConversion(item2.m_from, item2.m_to);
				}
			}
			Fermenter component3 = prefab.GetComponent<Fermenter>();
			if (!((Object)(object)component3 != (Object)null) || component3.m_conversion == null)
			{
				continue;
			}
			foreach (ItemConversion item3 in component3.m_conversion)
			{
				AddConversion(item3.m_from, item3.m_to);
			}
		}
		LogUnresolved();
	}

	private static void AddConversion(ItemDrop from, ItemDrop to)
	{
		if ((Object)(object)from != (Object)null && (Object)(object)to != (Object)null)
		{
			AddSource(((Object)((Component)to).gameObject).name, new string[1] { ((Object)((Component)from).gameObject).name }, anyInput: false);
		}
	}

	private static void AddSource(string item, string[] inputs, bool anyInput)
	{
		if (inputs.Length != 0)
		{
			if (!sources.TryGetValue(item, out var value))
			{
				value = new List<Source>();
				sources[item] = value;
			}
			value.Add(new Source
			{
				Inputs = inputs,
				AnyInput = anyInput
			});
		}
	}

	private static int Resolve(string name, HashSet<string> path)
	{
		if (resolved.TryGetValue(name, out var value))
		{
			return value;
		}
		int value2 = TrophyBiomes.Of(name);
		if (value2 == 99 && !raw.TryGetValue(name, out value2))
		{
			value2 = 99;
		}
		if (value2 != 99)
		{
			resolved[name] = value2;
			return value2;
		}
		if (!path.Add(name))
		{
			return 99;
		}
		int num = 99;
		if (sources.TryGetValue(name, out var value3))
		{
			foreach (Source item in value3)
			{
				List<int> list = (from input in item.Inputs
					select Resolve(input, path) into v
					where v != 99
					select v).ToList();
				if (list.Count == 0)
				{
					continue;
				}
				if (list.Any((int v) => v != 9))
				{
					list.RemoveAll((int v) => v == 9);
				}
				int val = (item.AnyInput ? list.Min() : list.Max());
				num = Math.Min(num, val);
			}
		}
		path.Remove(name);
		resolved[name] = num;
		return num;
	}

	private static void LogUnresolved()
	{
		List<string> list = new List<string>();
		foreach (GameObject item in ObjectDB.instance.m_items)
		{
			if ((Object)(object)item != (Object)null && Resolve(((Object)item).name, new HashSet<string>()) == 99)
			{
				list.Add(((Object)item).name);
			}
		}
		list.Sort(StringComparer.Ordinal);
		SortingTweaksPlugin.Log.LogInfo((object)("Items without a biome (" + list.Count + "): " + string.Join(", ", list.ToArray())));
	}

	private static void AddResearched(Action<int, string[]> add)
	{
		add(0, new string[3] { "ArrowFire", "ArrowFlint", "Torch" });
		add(1, new string[4] { "BronzeScrap", "Ectoplasm", "Pukeberries", "RoundLog" });
		add(2, new string[3] { "ArrowIron", "ArrowPoison", "WrithanRoots" });
		add(3, new string[8] { "ArrowFrost", "chest_hildir2", "Upgrader3Armor", "Upgrader3Weapon", "Upgrader4Armor", "Upgrader4Weapon", "WolfClaw", "WolfHairBundle" });
		add(4, new string[3] { "chest_hildir3", "UndeadBjornRibcage", "YagluthDrop" });
		add(5, new string[5] { "DvergrKeyFragment", "DvergrNeedle", "GiantBloodSack", "QueenDrop", "Wisp" });
		add(6, new string[22]
		{
			"AsksvinCarrionNeck", "AsksvinCarrionPelvic", "AsksvinCarrionRibcage", "AsksvinCarrionSkull", "BellFragment", "Blackwood", "BonemawSerpentTooth", "CharcoalResin", "CharredBone", "CharredCogwheel",
			"Charredskull", "DyrnwynBladeFragment", "DyrnwynHiltFragment", "DyrnwynTipFragment", "GemstoneBlue", "GemstoneGreen", "GemstoneRed", "MoltenCore", "Pot_Shard_Green", "ProustitePowder",
			"SulfurStone", "VineberrySeeds"
		});
		add(7, new string[31]
		{
			"BloodGoldKey", "CrownJewel", "FirConeFrost", "Frostwood", "FrozenKingDrop", "GlowWorm", "GoldOre", "Leatherstraps", "MoldArmorGoldChest", "MoldArmorGoldHelmet",
			"MoldArmorGoldLegs", "MoldArmorMageChest", "MoldArmorMageHelmet", "MoldArmorMageLegs", "MoldArmormediumChest", "MoldArmorMediumHelmet", "MoldArmorMediumLegs", "MoldAxe", "MoldBow", "MoldKeys",
			"MoldMace", "MoldSpear", "MoldSword", "OozeMork", "OrbFrostFire", "OrbThunderBlood", "Snowball", "Upgrader6Armor", "Upgrader6Weapon", "Upgrader7Armor",
			"Upgrader7Weapon"
		});
		add(9, new string[49]
		{
			"ArmorDress10", "ArmorDress2", "ArmorDress3", "ArmorDress5", "ArmorDress6", "ArmorDress8", "ArmorDress9", "ArmorHarvester1", "ArmorHarvester2", "ArmorTunic10",
			"ArmorTunic2", "ArmorTunic3", "ArmorTunic5", "ArmorTunic6", "ArmorTunic8", "ArmorTunic9", "BlobVial", "CandleWick", "CuredSquirrelHamstring", "FireworksRocket_White",
			"FragrantBundle", "FreshSeaweed", "HelmetHat1", "HelmetHat10", "HelmetHat2", "HelmetHat3", "HelmetHat4", "HelmetHat5", "HelmetHat6", "HelmetHat7",
			"HelmetHat8", "HelmetHat9", "HelmetRootCrown", "HelmetStrawHat", "HelmetSweatBand", "Ironpit", "MeadTrollPheromones", "MushroomBzerker", "PowderedDragonEgg", "PungentPebbles",
			"ScytheHandle", "SpiceAshlands", "SpiceDeepNorth", "SpiceForests", "SpiceMistlands", "SpiceMountains", "SpiceOceans", "SpicePlains", "VineGreenSeeds"
		});
	}
}
[BepInPlugin("sortingtweaks.valheim", "Sorting Tweaks", "1.1.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class SortingTweaksPlugin : BaseUnityPlugin
{
	public const string PluginGuid = "sortingtweaks.valheim";

	public const string PluginName = "Sorting Tweaks";

	public const string PluginVersion = "1.1.0";

	internal const string QuickStackStoreGuid = "goldenrevolver.quick_stack_store";

	internal const string FoodCategoriesGuid = "branchofyore.foodcategories";

	internal static ManualLogSource Log;

	private void Awake()
	{
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_0016: Expected O, but got Unknown
		Log = ((BaseUnityPlugin)this).Logger;
		Harmony harmony = new Harmony("sortingtweaks.valheim");
		TabSound.Init((BaseUnityPlugin)(object)this, harmony);
		if (Chainloader.PluginInfos.ContainsKey("goldenrevolver.quick_stack_store"))
		{
			ContainerSort.Init((BaseUnityPlugin)(object)this, harmony);
		}
		else
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Quick Stack Store is not installed, per-container sort is off.");
		}
		if (Chainloader.PluginInfos.ContainsKey("branchofyore.foodcategories"))
		{
			FoodTweaks.Init((BaseUnityPlugin)(object)this, harmony);
		}
		else
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Food Categories is not installed, the food tweaks are off.");
		}
	}
}
internal static class TabSound
{
	private static ConfigEntry<bool> quietTabSwitch;

	private static ConfigEntry<bool> quietMouseClick;

	private static bool inTabHandler;

	internal static void Init(BaseUnityPlugin plugin, Harmony harmony)
	{
		//IL_0072: Unknown result type (might be due to invalid IL or missing references)
		//IL_0080: Expected O, but got Unknown
		//IL_0120: Unknown result type (might be due to invalid IL or missing references)
		//IL_0127: Expected O, but got Unknown
		//IL_0137: Unknown result type (might be due to invalid IL or missing references)
		//IL_013e: Expected O, but got Unknown
		//IL_016e: Unknown result type (might be due to invalid IL or missing references)
		//IL_017c: Expected O, but got Unknown
		quietTabSwitch = plugin.Config.Bind<bool>("Crafting", "QuietTabSwitch", true, "Play only the click sound when switching between the Craft and Upgrade tab, not the extra group change sound right after it.");
		quietMouseClick = plugin.Config.Bind<bool>("Interface", "QuietMouseClick", true, "Clicking a button with the mouse plays the select sound on press and the click sound on release. At a high frame rate both are audible as a double click, so skip the select sound for mouse clicks.");
		MethodInfo methodInfo = AccessTools.Method(typeof(ButtonSfx), "OnSelect", (Type[])null, (Type[])null);
		if (methodInfo != null)
		{
			harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(TabSound), "SkipMouseSelect", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}
		MethodInfo methodInfo2 = AccessTools.Method(typeof(InventoryGui), "SetActiveGroup", new Type[2]
		{
			typeof(int),
			typeof(bool)
		}, (Type[])null);
		MethodInfo methodInfo3 = AccessTools.Method(typeof(InventoryGui), "OnTabCraftPressed", (Type[])null, (Type[])null);
		MethodInfo methodInfo4 = AccessTools.Method(typeof(InventoryGui), "OnTabUpgradePressed", (Type[])null, (Type[])null);
		if (methodInfo2 == null || methodInfo3 == null || methodInfo4 == null)
		{
			SortingTweaksPlugin.Log.LogWarning((object)"Tab switch methods not found (incompatible Valheim version?), the tab sound tweak is off.");
			return;
		}
		HarmonyMethod val = new HarmonyMethod(typeof(TabSound), "EnterTabHandler", (Type[])null);
		HarmonyMethod val2 = new HarmonyMethod(typeof(TabSound), "LeaveTabHandler", (Type[])null);
		harmony.Patch((MethodBase)methodInfo3, val, (HarmonyMethod)null, (HarmonyMethod)null, val2, (HarmonyMethod)null);
		harmony.Patch((MethodBase)methodInfo4, val, (HarmonyMethod)null, (HarmonyMethod)null, val2, (HarmonyMethod)null);
		harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(TabSound), "MuteGroupSound", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
	}

	private static bool SkipMouseSelect(BaseEventData eventData)
	{
		if (quietMouseClick.Value)
		{
			return !(eventData is PointerEventData);
		}
		return true;
	}

	private static void EnterTabHandler()
	{
		inTabHandler = true;
	}

	private static void LeaveTabHandler()
	{
		inTabHandler = false;
	}

	private static void MuteGroupSound(ref bool playSound)
	{
		if (inTabHandler && quietTabSwitch.Value)
		{
			playSound = false;
		}
	}
}
internal static class TrophyBiomes
{
	internal const int Unknown = 99;

	private static readonly string[][] biomes = new string[9][]
	{
		new string[4] { "TrophyBoar", "TrophyDeer", "TrophyNeck", "TrophyEikthyr" },
		new string[10] { "TrophyGreydwarf", "TrophyGreydwarfBrute", "TrophyGreydwarfShaman", "TrophySkeletonPoison", "TrophySkeleton", "TrophyGhost", "TrophyFrostTroll", "TrophyBjorn", "TrophySkeletonHildir", "TrophyTheElder" },
		new string[10] { "TrophyAbomination", "TrophyBlob", "TrophyDraugr", "TrophyDraugrElite", "TrophyLeech", "TrophySurtling", "TrophyWraith", "TrophyKvastur", "TrophyWrithan", "TrophyBonemass" },
		new string[9] { "TrophyCultist", "TrophyHatchling", "TrophyFenring", "TrophyBlob_Frost", "TrophySGolem", "TrophyUlv", "TrophyWolf", "TrophyCultist_Hildir", "TrophyDragonQueen" },
		new string[10] { "TrophyDeathsquito", "TrophyGoblin", "TrophyGoblinBrute", "TrophyGoblinShaman", "TrophyGrowth", "TrophyLox", "TrophyBjornUndead", "TrophyGoblinBruteBrosShaman", "TrophyGoblinBruteBrosBrute", "TrophyGoblinKing" },
		new string[7] { "TrophyDvergr", "TrophyHare", "TrophyGjall", "TrophySeeker", "TrophySeekerBrute", "TrophyTick", "TrophySeekerQueen" },
		new string[10] { "TrophyAsksvin", "TrophyBonemawSerpent", "TrophyFallenValkyrie", "TrophyCharredArcher", "TrophyMorgen", "TrophyVolture", "TrophyCharredMage", "TrophyCharredMelee", "TrophyBlob_Lava", "TrophyFader" },
		new string[8] { "TrophyBarka", "TrophyElaking", "TrophyMole", "TrophyJotunWitch", "TrophyJotunWarrior", "TrophyMoose", "TrophyBlob_Morkhalla", "TrophySeal" },
		new string[1] { "TrophySerpent" }
	};

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

	private static readonly Dictionary<string, int> byPrefab = Build();

	private static Dictionary<string, int> Build()
	{
		Dictionary<string, int> dictionary = new Dictionary<string, int>();
		for (int i = 0; i < biomes.Length; i++)
		{
			for (int j = 0; j < biomes[i].Length; j++)
			{
				dictionary[biomes[i][j]] = i;
				positions[biomes[i][j]] = j;
			}
		}
		return dictionary;
	}

	internal static bool PositionOf(string prefab, out int position)
	{
		position = 0;
		if (prefab != null)
		{
			return positions.TryGetValue(prefab, out position);
		}
		return false;
	}

	internal static int Of(string prefab)
	{
		if (prefab == null || !byPrefab.TryGetValue(prefab, out var value))
		{
			return 99;
		}
		return value;
	}
}