Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of SortHeim v1.1.1
plugins/SortHeim.dll
Decompiled 2 weeks agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("SortHeim")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+3bc822943cc177320d298d9c607871813110b9f7")] [assembly: AssemblyProduct("SortHeim")] [assembly: AssemblyTitle("SortHeim")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace SortHeim { internal static class Icons { private static readonly Dictionary<string, Sprite> Embedded = new Dictionary<string, Sprite>(); public static Sprite Encumbered { get { ObjectDB instance = ObjectDB.instance; if ((Object)(object)instance == (Object)null) { return null; } StatusEffect statusEffect = instance.GetStatusEffect(SEMan.s_statusEffectEncumbered); if (!((Object)(object)statusEffect != (Object)null)) { return null; } return statusEffect.m_icon; } } public static Sprite Scale => LoadEmbedded("scale.png"); public static Sprite Type => LoadEmbedded("type.png"); public static Sprite Gem => LoadEmbedded("gem.png"); private static Sprite LoadEmbedded(string name) { //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected O, but got Unknown //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) if (Embedded.TryGetValue(name, out var value) && (Object)(object)value != (Object)null) { return value; } using Stream stream = typeof(Icons).Assembly.GetManifestResourceStream("SortHeim." + name); if (stream == null) { SortHeimPlugin.Log.LogWarning((object)("Missing embedded icon " + name)); return null; } byte[] array = new byte[stream.Length]; stream.Read(array, 0, array.Length); Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); ImageConversion.LoadImage(val, array); ((Texture)val).filterMode = (FilterMode)1; Sprite val2 = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f)); Embedded[name] = val2; return val2; } } [HarmonyPatch(typeof(InventoryGui))] internal static class InventoryGuiPatches { private const float Spacing = 6f; private static readonly SortMode[] Modes = new SortMode[4] { SortMode.Name, SortMode.Type, SortMode.Weight, SortMode.Rarity }; private static readonly List<Image> PendingWeightIcons = new List<Image>(); [HarmonyPostfix] [HarmonyPatch("Awake")] private static void Awake_Postfix(InventoryGui __instance) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown //IL_0037: 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_0072: 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_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_020e: Unknown result type (might be due to invalid IL or missing references) //IL_021a: Unknown result type (might be due to invalid IL or missing references) //IL_0229: Unknown result type (might be due to invalid IL or missing references) //IL_0233: Unknown result type (might be due to invalid IL or missing references) //IL_0249: Unknown result type (might be due to invalid IL or missing references) //IL_024e: Unknown result type (might be due to invalid IL or missing references) PendingWeightIcons.Clear(); Button takeAllButton = __instance.m_takeAllButton; if ((Object)(object)takeAllButton == (Object)null) { SortHeimPlugin.Log.LogWarning((object)"Could not find a button to copy; sort buttons disabled (hotkeys still work)."); return; } RectTransform val = (RectTransform)((Component)takeAllButton).transform; Rect rect = val.rect; float num = ((Rect)(ref rect)).height; if (num <= 1f) { num = 36f; } float num2; if (SortHeimPlugin.Style.Value != ButtonStyle.Text) { num2 = Mathf.Round(num * 1.5f); } else { rect = val.rect; num2 = ((Rect)(ref rect)).width; } float num3 = num2; if (num3 <= 1f) { num3 = 140f; } float num4 = ((SortHeimPlugin.ButtonWidth.Value > 0f) ? SortHeimPlugin.ButtonWidth.Value : num3); Vector2 size = default(Vector2); ((Vector2)(ref size))..ctor(num4, num); UITooltip tooltipSource = ((IEnumerable<UITooltip>)((Component)__instance).GetComponentsInChildren<UITooltip>(true)).FirstOrDefault((Func<UITooltip, bool>)((UITooltip t) => (Object)(object)t.m_tooltipPrefab != (Object)null)); if (SortHeimPlugin.ShowChestButtons.Value && (Object)(object)__instance.m_container != (Object)null) { for (int num5 = 0; num5 < Modes.Length; num5++) { SortMode mode = Modes[num5]; Place(CreateButton(takeAllButton, __instance.m_container, mode, "chest", delegate { InventorySorter.SortOpenContainer(mode); }, tooltipSource), size, new Vector2(0f, 0f), new Vector2(0f, 1f), SortHeimPlugin.ChestButtonOffset.Value + new Vector2((float)num5 * (num4 + 6f), 0f)); } } if (!SortHeimPlugin.ShowInventoryButtons.Value || !((Object)(object)__instance.m_player != (Object)null)) { return; } for (int num6 = 0; num6 < Modes.Length; num6++) { SortMode mode2 = Modes[num6]; Place(CreateButton(takeAllButton, __instance.m_player, mode2, "inventory", delegate { InventorySorter.SortPlayerInventory(mode2); }, tooltipSource), size, new Vector2(1f, 1f), new Vector2(0f, 1f), SortHeimPlugin.InventoryButtonOffset.Value + new Vector2(0f, (float)(-num6) * (num + 6f))); } } private static void Place(RectTransform rect, Vector2 size, Vector2 anchor, Vector2 pivot, Vector2 position) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) Vector2 anchorMin = (rect.anchorMax = anchor); rect.anchorMin = anchorMin; rect.pivot = pivot; rect.sizeDelta = size; rect.anchoredPosition = position; } private static RectTransform CreateButton(Button template, RectTransform parent, SortMode mode, string target, Action onClick, UITooltip tooltipSource) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Expected O, but got Unknown //IL_010b: 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_0110: 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_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_0243: Unknown result type (might be due to invalid IL or missing references) //IL_0249: Expected O, but got Unknown GameObject val = Object.Instantiate<GameObject>(((Component)template).gameObject, (Transform)(object)parent, false); ((Object)val).name = $"SortHeim_{target}_{mode}"; Button component = val.GetComponent<Button>(); component.onClick = new ButtonClickedEvent(); ((UnityEvent)component.onClick).AddListener((UnityAction)delegate { onClick(); }); ((Selectable)component).interactable = true; UIGamePad[] componentsInChildren = val.GetComponentsInChildren<UIGamePad>(true); foreach (UIGamePad val2 in componentsInChildren) { if ((Object)(object)val2.m_hint != (Object)null) { Object.Destroy((Object)(object)val2.m_hint); } Object.Destroy((Object)(object)val2); } Component[] componentsInChildren2 = val.GetComponentsInChildren<Component>(true); foreach (Component val3 in componentsInChildren2) { if ((Object)(object)val3 != (Object)null && ((object)val3).GetType().Name == "Localize") { Object.Destroy((Object)(object)val3); } } TMP_Text componentInChildren = val.GetComponentInChildren<TMP_Text>(true); Color tint = (((Object)(object)componentInChildren != (Object)null) ? ((Graphic)componentInChildren).color : Color.white); if (SortHeimPlugin.Style.Value == ButtonStyle.Text) { if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.text = "Sort: " + mode; } } else { switch (mode) { case SortMode.Name: if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.text = "A-Z"; } break; case SortMode.Type: if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.text = ""; } AddIcon(val, Icons.Type, tint); break; case SortMode.Weight: { if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.text = ""; } Image val4 = AddIcon(val, Icons.Scale, tint); if (!TryUseEncumberedIcon(val4)) { PendingWeightIcons.Add(val4); } break; } case SortMode.Rarity: if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.text = ""; } AddIcon(val, Icons.Gem, tint); break; } } string text = ((mode == SortMode.Rarity) ? "rarity (biome)" : mode.ToString().ToLowerInvariant()); AddTooltip(val, "Sort " + target + " by " + text, tooltipSource); val.SetActive(true); return (RectTransform)val.transform; } private static Image AddIcon(GameObject button, Sprite sprite, Color tint) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: 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_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0077: 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) GameObject val = new GameObject("Icon", new Type[2] { typeof(RectTransform), typeof(Image) }); RectTransform val2 = (RectTransform)val.transform; ((Transform)val2).SetParent(button.transform, false); val2.anchorMin = Vector2.zero; val2.anchorMax = Vector2.one; val2.offsetMin = new Vector2(4f, 4f); val2.offsetMax = new Vector2(-4f, -4f); Image component = val.GetComponent<Image>(); component.preserveAspect = true; ((Graphic)component).raycastTarget = false; component.sprite = sprite; ((Graphic)component).color = tint; return component; } private static bool TryUseEncumberedIcon(Image image) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) Sprite encumbered = Icons.Encumbered; if ((Object)(object)encumbered == (Object)null || (Object)(object)image == (Object)null) { return false; } image.sprite = encumbered; ((Graphic)image).color = Color.white; return true; } private static void AddTooltip(GameObject button, string text, UITooltip source) { UITooltip val = button.GetComponent<UITooltip>(); if ((Object)(object)val == (Object)null) { if ((Object)(object)source == (Object)null) { return; } val = button.AddComponent<UITooltip>(); val.m_tooltipPrefab = source.m_tooltipPrefab; } val.m_topic = ""; val.m_text = text; } [HarmonyPostfix] [HarmonyPatch("Update")] private static void Update_Postfix() { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) if (!InventoryGui.IsVisible()) { return; } if (PendingWeightIcons.Count > 0) { PendingWeightIcons.RemoveAll((Image i) => (Object)(object)i == (Object)null || TryUseEncumberedIcon(i)); } if (IsTyping()) { return; } KeyboardShortcut value = SortHeimPlugin.NameKey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { InventorySorter.SortActive(SortMode.Name); return; } value = SortHeimPlugin.TypeKey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { InventorySorter.SortActive(SortMode.Type); return; } value = SortHeimPlugin.WeightKey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { InventorySorter.SortActive(SortMode.Weight); return; } value = SortHeimPlugin.RarityKey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { InventorySorter.SortActive(SortMode.Rarity); } } private static bool IsTyping() { if ((!((Object)(object)Chat.instance != (Object)null) || !Chat.instance.HasFocus()) && !Console.IsVisible()) { return TextInput.IsVisible(); } return true; } } internal static class InventorySorter { public static bool SortActive(SortMode mode) { InventoryGui instance = InventoryGui.instance; if ((Object)(object)instance != (Object)null && (Object)(object)instance.m_currentContainer != (Object)null) { return SortOpenContainer(mode); } return SortPlayerInventory(mode); } public static bool SortOpenContainer(SortMode mode) { if (!CanSortNow(out var gui)) { return false; } Container currentContainer = gui.m_currentContainer; if ((Object)(object)currentContainer == (Object)null) { return false; } ZNetView nview = currentContainer.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid() || !nview.IsOwner()) { SortHeimPlugin.Log.LogWarning((object)"Not the owner of this container; skipping sort."); return false; } Inventory inventory = currentContainer.GetInventory(); if (inventory == null) { return false; } Sort(inventory, mode, (ItemData item) => false, 0); return true; } public static bool SortPlayerInventory(SortMode mode) { if (!CanSortNow(out var _)) { return false; } Player localPlayer = Player.m_localPlayer; Inventory val = (((Object)(object)localPlayer != (Object)null) ? ((Humanoid)localPlayer).GetInventory() : null); if (val == null) { return false; } bool keepHotbar = SortHeimPlugin.KeepHotbar.Value; bool keepEquipped = SortHeimPlugin.KeepEquipped.Value; Sort(val, mode, (ItemData item) => (keepHotbar && item.m_gridPos.y == 0) || (keepEquipped && item.m_equipped), keepHotbar ? 1 : 0); return true; } private static bool CanSortNow(out InventoryGui gui) { gui = InventoryGui.instance; if ((Object)(object)gui != (Object)null && InventoryGui.IsVisible()) { return gui.m_dragItem == null; } return false; } public static void Sort(Inventory inventory, SortMode mode, Func<ItemData, bool> isPinned, int firstRow) { //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) List<ItemData> inventory2 = inventory.m_inventory; if (inventory2.Count == 0) { return; } List<ItemData> list = inventory2.Where(isPinned).ToList(); List<ItemData> list2 = (from i in inventory2 where !isPinned(i) orderby i.m_gridPos.y, i.m_gridPos.x select i).ToList(); if (list2.Count == 0) { return; } if (SortHeimPlugin.MergeStacks.Value) { Merge(list2); } List<ItemData> list3 = Order(list2, mode).ToList(); HashSet<Vector2i> hashSet = new HashSet<Vector2i>(list.Select((ItemData i) => i.m_gridPos)); int width = inventory.GetWidth(); int height = inventory.GetHeight(); int num = 0; Vector2i val = default(Vector2i); for (int num2 = firstRow; num2 < height; num2++) { if (num >= list3.Count) { break; } for (int num3 = 0; num3 < width; num3++) { if (num >= list3.Count) { break; } ((Vector2i)(ref val))..ctor(num3, num2); if (!hashSet.Contains(val)) { list3[num++].m_gridPos = val; } } } if (num < list3.Count) { SortHeimPlugin.Log.LogError((object)"Ran out of slots while sorting; aborting."); return; } inventory2.Clear(); inventory2.AddRange(list); inventory2.AddRange(list3); inventory.Changed(false, false); } private static IEnumerable<ItemData> Order(List<ItemData> items, SortMode mode) { bool flag = SortHeimPlugin.DirectionFor(mode) == SortDirection.Descending; StringComparer currentCultureIgnoreCase = StringComparer.CurrentCultureIgnoreCase; IOrderedEnumerable<ItemData> source; switch (mode) { case SortMode.Weight: { bool totals = SortHeimPlugin.UseStackTotals.Value; source = (flag ? items.OrderByDescending((ItemData i) => WeightOf(i, totals)) : items.OrderBy((ItemData i) => WeightOf(i, totals))); source = source.ThenBy(ItemCategories.Get).ThenBy<ItemData, string>(DisplayName, currentCultureIgnoreCase); break; } case SortMode.Type: source = (flag ? items.OrderByDescending(ItemCategories.Get) : items.OrderBy(ItemCategories.Get)); source = source.ThenBy<ItemData, string>(DisplayName, currentCultureIgnoreCase); break; case SortMode.Rarity: { Dictionary<ItemData, int> tiers = items.ToDictionary((ItemData i) => i, RarityTiers.Get); source = items.OrderBy((ItemData i) => (tiers[i] == -1) ? 1 : 0); source = (flag ? source.ThenByDescending((ItemData i) => tiers[i]) : source.ThenBy((ItemData i) => tiers[i])); source = source.ThenBy(ItemCategories.Get).ThenBy<ItemData, string>(DisplayName, currentCultureIgnoreCase); break; } default: source = (flag ? items.OrderByDescending<ItemData, string>(DisplayName, currentCultureIgnoreCase) : items.OrderBy<ItemData, string>(DisplayName, currentCultureIgnoreCase)); break; } return source.ThenByDescending((ItemData i) => i.m_quality).ThenByDescending((ItemData i) => i.m_stack); } private static string DisplayName(ItemData item) { string name = item.m_shared.m_name; if (Localization.instance == null) { return name; } return Localization.instance.Localize(name); } private static float WeightOf(ItemData item, bool totals) { if (!totals) { return item.m_shared.m_weight; } return item.GetWeight(-1); } private static void Merge(List<ItemData> items) { for (int i = 0; i < items.Count; i++) { ItemData val = items[i]; int maxStackSize = val.m_shared.m_maxStackSize; if (maxStackSize <= 1) { continue; } for (int j = i + 1; j < items.Count; j++) { if (val.m_stack >= maxStackSize) { break; } ItemData val2 = items[j]; if (val2.m_stack > 0 && CanStack(val, val2)) { int num = Math.Min(maxStackSize - val.m_stack, val2.m_stack); val.m_stack += num; val2.m_stack -= num; } } } items.RemoveAll((ItemData val3) => val3.m_stack <= 0); } private static bool CanStack(ItemData a, ItemData b) { if (a.m_shared.m_name == b.m_shared.m_name && a.m_quality == b.m_quality && a.m_variant == b.m_variant) { return a.m_worldLevel == b.m_worldLevel; } return false; } } internal static class ItemCategories { public enum Category { Weapon, Ammo, Shield, Armor, Utility, Tool, Food, Potion, Wood, Stone, Ore, Metal, Hide, Seed, Material, Trophy, Fish, Misc } private static readonly Dictionary<string, Category> Materials = Build((Category.Wood, "Wood FineWood RoundLog ElderBark YggdrasilWood Blackwood"), (Category.Stone, "Stone Flint Obsidian Crystal BlackMarble Grausten"), (Category.Metal, "Copper Tin Bronze Iron Silver BlackMetal Flametal FlametalNew"), (Category.Seed, "Acorn FirCone PineCone")); public static Category Get(ItemData item) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Expected I4, but got Unknown SharedData shared = item.m_shared; ItemType itemType = shared.m_itemType; switch (itemType - 1) { case 2: case 3: case 13: case 19: case 21: return Category.Weapon; case 8: case 22: return Category.Ammo; case 4: return Category.Shield; case 5: case 6: case 10: case 11: case 16: return Category.Armor; case 17: case 23: return Category.Utility; case 14: case 18: return Category.Tool; case 1: if (!(shared.m_food > 0f) && !(shared.m_foodStamina > 0f) && !(shared.m_foodEitr > 0f)) { return Category.Potion; } return Category.Food; case 0: return MaterialCategory(((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : ""); case 12: return Category.Trophy; case 20: return Category.Fish; default: return Category.Misc; } } private static Category MaterialCategory(string prefab) { if (Materials.TryGetValue(prefab, out var value)) { return value; } if (EndsWith(prefab, "Hide") || EndsWith(prefab, "Pelt") || prefab == "LeatherScraps") { return Category.Hide; } if (EndsWith(prefab, "Seeds")) { return Category.Seed; } if (EndsWith(prefab, "Ore") || EndsWith(prefab, "OreNew") || EndsWith(prefab, "Scrap")) { return Category.Ore; } return Category.Material; } private static bool EndsWith(string s, string suffix) { return s.EndsWith(suffix, StringComparison.Ordinal); } private static Dictionary<string, Category> Build(params (Category category, string names)[] groups) { Dictionary<string, Category> dictionary = new Dictionary<string, Category>(); for (int i = 0; i < groups.Length; i++) { (Category category, string names) tuple = groups[i]; Category item = tuple.category; string[] array = tuple.names.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries); foreach (string key in array) { dictionary[key] = item; } } return dictionary; } } internal static class RarityTiers { private class Source { public readonly List<string> Ingredients = new List<string>(); public int StationTier; } public const int Unknown = -1; private static readonly string[] BaseTable = new string[7] { "Wood Stone Flint Resin LeatherScraps DeerHide DeerMeat RawMeat NeckTail Feathers Raspberry Mushroom Dandelion Honey QueenBee TrophyBoar TrophyDeer TrophyNeck TrophyEikthyr HardAntler FishRaw BeechSeeds Coins", "CopperOre TinOre RoundLog FineWood Blueberries Carrot CarrotSeeds MushroomYellow GreydwarfEye SurtlingCore AncientSeed TrophyGreydwarf TrophyGreydwarfBrute TrophyGreydwarfShaman TrophySkeleton TrophySkeletonHildir TrophyTheElder TrophyForestTroll TrollHide BoneFragments Thistle FirCone PineCone CryptKey Amber AmberPearl Ruby TrophyBjorn BjornHide BjornPaw BjornMeat", "IronScrap IronOre Ooze Bloodbag Guck Entrails Chain WitheredBone ElderBark Turnip TurnipSeeds TrophyDraugr TrophyDraugrElite TrophyDraugrFem TrophyBlob TrophyLeech TrophySkeletonPoison TrophyWraith TrophyBonemass TrophyAbomination TrophySurtling Wishbone SerpentMeat SerpentScale TrophySerpent Root SilverNecklace", "SilverOre WolfPelt WolfFang WolfMeat FreezeGland Obsidian Crystal TrophyWolf TrophyFenring TrophyCultist TrophyCultist_Hildir TrophyHatchling TrophySGolem TrophyUlv TrophyDragonQueen DragonEgg DragonTear Onion OnionSeeds WolfHairBundle", "BlackMetalScrap Barley Flax Needle LoxMeat LoxPelt Tar Cloudberry TrophyDeathsquito TrophyGoblin TrophyGoblinBrute TrophyGoblinShaman TrophyGoblinBruteBrosBrute TrophyGoblinBruteBrosShaman TrophyBjornUndead TrophyLox TrophyGrowth TrophyGoblinKing YagluthDrop GoblinTotem ChickenMeat ChickenEgg", "YggdrasilWood BlackMarble BlackCore Sap Softtissue Carapace Mandible BugMeat GiantBloodSack HareMeat ScaleHide Bilebag RoyalJelly MushroomJotunPuffs MushroomMagecap TrophyHare TrophySeeker TrophySeekerBrute TrophyTick TrophyGjall TrophyDvergr TrophySeekerQueen QueenDrop DvergrNeedle DvergrKeyFragment Wisp", "Grausten Blackwood FlametalOreNew CharredBone CelestialFeather MorgenSinew MorgenHeart AskHide AskBladder AsksvinMeat VoltureMeat VoltureEgg BoneMawSerpentMeat BonemawSerpentTooth ProustitePowder SulfurStone MoltenCore Fiddleheadfern MushroomSmokePuff Vineberry VineberrySeeds TrophyAsksvin TrophyCharredArcher TrophyCharredMelee TrophyCharredMage TrophyFallenValkyrie TrophyMorgen TrophyVolture TrophyBonemawSerpent Trophy_BoneMawSerpent TrophyFader FaderDrop" }; private static readonly Dictionary<string, int> StationTiers = new Dictionary<string, int> { { "$piece_workbench", 0 }, { "$piece_forge", 1 }, { "$piece_cauldron", 1 }, { "$piece_stonecutter", 1 }, { "$piece_artisanstation", 4 }, { "$piece_blackforge", 5 }, { "$piece_magetable", 5 } }; private static Dictionary<string, int> _base; private static Dictionary<string, List<Source>> _sources; private static readonly Dictionary<string, int> Cache = new Dictionary<string, int>(); private static ObjectDB _builtFor; public static void Invalidate() { _builtFor = null; Cache.Clear(); } public static int Get(ItemData item) { GameObject dropPrefab = item.m_dropPrefab; if ((Object)(object)dropPrefab == (Object)null) { return -1; } EnsureBuilt(); return Resolve(((Object)dropPrefab).name, new HashSet<string>()); } private static void EnsureBuilt() { ObjectDB instance = ObjectDB.instance; if ((Object)(object)instance != (Object)null && (Object)(object)instance == (Object)(object)_builtFor) { return; } Cache.Clear(); _base = new Dictionary<string, int>(); for (int i = 0; i < BaseTable.Length; i++) { string[] array = BaseTable[i].Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries); foreach (string key in array) { _base[key] = i; } } ApplyOverrides(_base); _sources = new Dictionary<string, List<Source>>(); if ((Object)(object)instance != (Object)null) { AddRecipes(instance); } if ((Object)(object)ZNetScene.instance != (Object)null) { AddConversions(ZNetScene.instance); } _builtFor = (((Object)(object)instance != (Object)null && (Object)(object)ZNetScene.instance != (Object)null) ? instance : null); } private static void ApplyOverrides(Dictionary<string, int> table) { string[] array = SortHeimPlugin.TierOverrides.Value.Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text in array) { string[] array2 = text.Split('='); if (array2.Length == 2 && int.TryParse(array2[1].Trim(), out var result)) { table[array2[0].Trim()] = result; } else { SortHeimPlugin.Log.LogWarning((object)("Ignoring malformed TierOverrides entry '" + text.Trim() + "'")); } } } private static void AddRecipes(ObjectDB db) { foreach (Recipe recipe in db.m_recipes) { if ((Object)(object)recipe == (Object)null || (Object)(object)recipe.m_item == (Object)null || recipe.m_resources == null) { continue; } Source source = new Source(); if ((Object)(object)recipe.m_craftingStation != (Object)null) { StationTiers.TryGetValue(recipe.m_craftingStation.m_name, out source.StationTier); } Requirement[] resources = recipe.m_resources; foreach (Requirement val in resources) { if (val != null && (Object)(object)val.m_resItem != (Object)null) { source.Ingredients.Add(((Object)val.m_resItem).name); } } AddSource(((Object)recipe.m_item).name, source); } } private static void AddConversions(ZNetScene scene) { foreach (GameObject prefab in scene.m_prefabs) { if ((Object)(object)prefab == (Object)null) { continue; } Smelter component = prefab.GetComponent<Smelter>(); if ((Object)(object)component != (Object)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) { 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)) { continue; } foreach (ItemConversion item3 in component3.m_conversion) { AddConversion(item3?.m_from, item3?.m_to); } } } private static void AddConversion(ItemDrop from, ItemDrop to) { if (!((Object)(object)from == (Object)null) && !((Object)(object)to == (Object)null)) { Source source = new Source(); source.Ingredients.Add(((Object)from).name); AddSource(((Object)to).name, source); } } private static void AddSource(string product, Source source) { if (!_sources.TryGetValue(product, out var value)) { value = (_sources[product] = new List<Source>()); } value.Add(source); } private static int Resolve(string name, HashSet<string> visiting) { if (_base.TryGetValue(name, out var value)) { return value; } if (Cache.TryGetValue(name, out value)) { return value; } if (!_sources.TryGetValue(name, out var value2)) { return -1; } if (!visiting.Add(name)) { return -1; } int num = int.MaxValue; foreach (Source item in value2) { int num2 = item.StationTier; foreach (string ingredient in item.Ingredients) { num2 = Math.Max(num2, Resolve(ingredient, visiting)); } num = Math.Min(num, num2); } visiting.Remove(name); value = ((num == int.MaxValue) ? (-1) : num); Cache[name] = value; return value; } } public enum SortMode { Name, Type, Weight, Rarity } public enum ButtonStyle { Icons, Text } public enum SortDirection { Ascending, Descending } [BepInPlugin("mbaya.SortHeim", "SortHeim", "1.1.1")] public class SortHeimPlugin : BaseUnityPlugin { public const string Guid = "mbaya.SortHeim"; public const string Name = "SortHeim"; public const string Version = "1.1.1"; internal static ManualLogSource Log; internal static ConfigEntry<bool> ShowChestButtons; internal static ConfigEntry<Vector2> ChestButtonOffset; internal static ConfigEntry<bool> ShowInventoryButtons; internal static ConfigEntry<Vector2> InventoryButtonOffset; internal static ConfigEntry<float> ButtonWidth; internal static ConfigEntry<ButtonStyle> Style; internal static ConfigEntry<bool> MergeStacks; internal static ConfigEntry<bool> UseStackTotals; internal static ConfigEntry<bool> KeepHotbar; internal static ConfigEntry<bool> KeepEquipped; internal static ConfigEntry<SortDirection> NameDirection; internal static ConfigEntry<SortDirection> TypeDirection; internal static ConfigEntry<SortDirection> WeightDirection; internal static ConfigEntry<SortDirection> RarityDirection; internal static ConfigEntry<string> TierOverrides; internal static ConfigEntry<KeyboardShortcut> NameKey; internal static ConfigEntry<KeyboardShortcut> TypeKey; internal static ConfigEntry<KeyboardShortcut> WeightKey; internal static ConfigEntry<KeyboardShortcut> RarityKey; private Harmony _harmony; private void Awake() { //IL_0045: 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_021f: Unknown result type (might be due to invalid IL or missing references) //IL_0243: Unknown result type (might be due to invalid IL or missing references) //IL_0267: Unknown result type (might be due to invalid IL or missing references) //IL_028b: Unknown result type (might be due to invalid IL or missing references) //IL_02ce: Unknown result type (might be due to invalid IL or missing references) //IL_02d8: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; ShowChestButtons = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "ShowChestButtons", true, "Show the sort buttons under an open chest."); ChestButtonOffset = ((BaseUnityPlugin)this).Config.Bind<Vector2>("UI", "ChestButtonOffset", new Vector2(0f, -6f), "Offset (x, y) of the chest button row from the bottom-left corner of the chest panel. Takes effect on next game start."); ShowInventoryButtons = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "ShowInventoryButtons", true, "Show the sort buttons next to your inventory."); InventoryButtonOffset = ((BaseUnityPlugin)this).Config.Bind<Vector2>("UI", "InventoryButtonOffset", new Vector2(6f, 0f), "Offset (x, y) of the inventory button column from the top-right corner of the inventory panel. Takes effect on next game start."); Style = ((BaseUnityPlugin)this).Config.Bind<ButtonStyle>("UI", "ButtonStyle", ButtonStyle.Icons, "Icons = compact symbol buttons (A-Z, type, weight, rarity icons). Text = wider buttons labelled 'Sort: Name' etc. Takes effect on next game start."); ButtonWidth = ((BaseUnityPlugin)this).Config.Bind<float>("UI", "ButtonWidth", 0f, "Width of each sort button in pixels. 0 = automatic (compact for Icons, Take All width for Text). Takes effect on next game start."); MergeStacks = ((BaseUnityPlugin)this).Config.Bind<bool>("Sorting", "MergeStacks", true, "Combine partial stacks of the same item before sorting."); UseStackTotals = ((BaseUnityPlugin)this).Config.Bind<bool>("Sorting", "UseStackTotals", true, "Sort by the whole stack's weight (true) or a single item's weight (false)."); KeepHotbar = ((BaseUnityPlugin)this).Config.Bind<bool>("Inventory", "KeepHotbar", true, "Never move items in your hotbar row (slots 1-8) when sorting your inventory."); KeepEquipped = ((BaseUnityPlugin)this).Config.Bind<bool>("Inventory", "KeepEquipped", true, "Never move equipped items when sorting your inventory."); NameDirection = ((BaseUnityPlugin)this).Config.Bind<SortDirection>("Sorting", "NameDirection", SortDirection.Ascending, "Order used when sorting by name."); TypeDirection = ((BaseUnityPlugin)this).Config.Bind<SortDirection>("Sorting", "TypeDirection", SortDirection.Ascending, "Order used when sorting by type. Ascending = weapons, ammo, shields, armor, utility, tools, food, potions, wood, stone, ore, metal, hides, seeds, other materials, trophies, fish, misc. Items within a type are sorted by name."); WeightDirection = ((BaseUnityPlugin)this).Config.Bind<SortDirection>("Sorting", "WeightDirection", SortDirection.Descending, "Order used when sorting by weight."); RarityDirection = ((BaseUnityPlugin)this).Config.Bind<SortDirection>("Sorting", "RarityDirection", SortDirection.Descending, "Order used when sorting by rarity. Descending = latest biome first. Items with no known tier always go last."); TierOverrides = ((BaseUnityPlugin)this).Config.Bind<string>("Sorting", "TierOverrides", "", "Set or fix an item's rarity tier, as comma-separated PrefabName=Tier pairs, e.g. 'TrophyMoose=7, Coins=0'. Tiers: 0 Meadows, 1 Black Forest, 2 Swamp, 3 Mountain, 4 Plains, 5 Mistlands, 6 Ashlands, 7 Deep North."); NameKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Hotkeys", "SortByName", KeyboardShortcut.Empty, "Sort by name (the open chest, or your inventory if no chest is open)."); TypeKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Hotkeys", "SortByType", KeyboardShortcut.Empty, "Sort by type (the open chest, or your inventory if no chest is open)."); WeightKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Hotkeys", "SortByWeight", KeyboardShortcut.Empty, "Sort by weight (the open chest, or your inventory if no chest is open)."); RarityKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Hotkeys", "SortByRarity", KeyboardShortcut.Empty, "Sort by rarity (the open chest, or your inventory if no chest is open)."); TierOverrides.SettingChanged += delegate { RarityTiers.Invalidate(); }; _harmony = new Harmony("mbaya.SortHeim"); _harmony.PatchAll(typeof(SortHeimPlugin).Assembly); Log.LogInfo((object)"SortHeim 1.1.1 loaded"); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } internal static SortDirection DirectionFor(SortMode mode) { return mode switch { SortMode.Type => TypeDirection.Value, SortMode.Weight => WeightDirection.Value, SortMode.Rarity => RarityDirection.Value, _ => NameDirection.Value, }; } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }