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 CraftingMenuSorter v1.0.0
CraftingMenuSorter.dll
Decompiled 2 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; 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_guiutils")] [assembly: IgnoresAccessChecksTo("assembly_utils")] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("CraftingMenuSorter")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+a59541c05bc4a4c108b884cdfa3e1722c866c52c")] [assembly: AssemblyProduct("CraftingMenuSorter")] [assembly: AssemblyTitle("CraftingMenuSorter")] [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 CraftingMenuSorter { internal enum Biome { Unknown, Meadows, BlackForest, Swamp, Mountains, Plains, Mistlands, Ashlands, DeepNorth } internal static class Biomes { private static readonly Dictionary<string, Biome> table; private static readonly Dictionary<string, Biome> overrides; private static string overridesFrom; private static readonly HashSet<string> reportedUnknown; static Biomes() { table = new Dictionary<string, Biome>(StringComparer.OrdinalIgnoreCase); overrides = new Dictionary<string, Biome>(StringComparer.OrdinalIgnoreCase); reportedUnknown = new HashSet<string>(StringComparer.OrdinalIgnoreCase); Add(Biome.Meadows, "Wood", "Stone", "Flint", "LeatherScraps", "DeerHide", "Resin", "Feathers", "HardAntler", "BoneFragments", "Honey", "Raspberry", "Mushroom", "Dandelion", "QueenBee", "Thistle", "Beech1", "BeechSeeds", "Carrot", "CarrotSeeds"); Add(Biome.BlackForest, "Copper", "CopperOre", "CopperScrap", "Tin", "TinOre", "Bronze", "BronzeNails", "BronzeScrap", "FineWood", "RoundLog", "TrollHide", "GreydwarfEye", "SurtlingCore", "Coal", "AncientSeed", "Chitin", "Amber", "AmberPearl", "Ruby", "Coins", "SerpentScale", "SharpeningStone", "ThunderStone"); Add(Biome.Swamp, "Iron", "IronScrap", "IronOre", "IronNails", "Guck", "ElderBark", "Entrails", "Bloodbag", "BloodBag", "Ooze", "Chain", "WitheredBone", "Root", "Turnip", "TurnipSeeds"); Add(Biome.Mountains, "Silver", "SilverOre", "Obsidian", "Crystal", "WolfPelt", "WolfFang", "WolfClaw", "WolfHairBundle", "FreezeGland", "DragonTear", "FenrisHair", "FenrisClaw", "Onion", "OnionSeeds"); Add(Biome.Plains, "BlackMetal", "BlackMetalScrap", "LoxPelt", "LinenThread", "Flax", "Needle", "Tar", "Barley", "GoblinTotem", "JuteRed", "Cloudberry"); Add(Biome.Mistlands, "BlackMarble", "BlackCore", "Carapace", "Eitr", "Mandible", "Sap", "YggdrasilSap", "YggdrasilWood", "Softtissue", "SoftTissue", "Wisp", "ScaleHide", "DvergrNeedle", "RoyalJelly", "BileBag", "JuteBlue", "MushroomJotunPuffs", "MushroomMagecap", "DvergrKey", "ChickenEgg", "YggdrasilRoot"); Add(Biome.Ashlands, "Flametal", "FlametalOre", "FlametalNew", "AskHide", "AskBladder", "Blackwood", "BlackWood", "Grausten", "Sulfur", "SulfurStone", "ProustitePowder", "CharredBone", "CharredBones", "CharredCogwheel", "MoltenCore", "MorgenHeart", "CelestialFeather", "BonemawSerpentScale", "BoneMawSerpentScale", "BonemawScale", "Vineberry", "Fiddleheadfern", "VoltureEgg", "CharredSpawnerFragments"); Add(Biome.DeepNorth, "BlackIce", "FrostCore", "FrostWisp", "HoarFrost", "BearHide", "BjornHide", "MooseHide", "SealHide", "NornThread", "AncientGemstoneBlack", "BloodGoldOre", "BloodGold", "GoldOre", "ElakingHair", "FirConeFrost", "FlowStone", "PowderedDragonEgg", "DarkWood"); } private static void Add(Biome biome, params string[] prefabs) { foreach (string key in prefabs) { table[key] = biome; } } public static Biome Of(Recipe recipe) { Biome biome = Biome.Unknown; if ((Object)(object)recipe == (Object)null || recipe.m_resources == null) { return biome; } Requirement[] resources = recipe.m_resources; for (int i = 0; i < resources.Length; i++) { Biome biome2 = OfMaterial(resources[i]); if (biome2 > biome) { biome = biome2; } } return biome; } public static string Describe(Recipe recipe) { if ((Object)(object)recipe == (Object)null || recipe.m_resources == null) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(); Requirement[] resources = recipe.m_resources; foreach (Requirement requirement in resources) { if (stringBuilder.Length > 0) { stringBuilder.Append(", "); } stringBuilder.Append(PrefabName(requirement)).Append('=').Append(OfMaterial(requirement)); } return stringBuilder.ToString(); } public static void Invalidate() { overridesFrom = null; } private static Biome OfMaterial(Requirement requirement) { string text = PrefabName(requirement); if (text.Length == 0) { return Biome.Unknown; } ReadOverrides(); if (overrides.TryGetValue(text, out var value) || table.TryGetValue(text, out value)) { return value; } if (Plugin.LogRows.Value && !text.StartsWith("Trophy", StringComparison.OrdinalIgnoreCase) && !text.EndsWith("Trophy", StringComparison.OrdinalIgnoreCase) && reportedUnknown.Add(text)) { Plugin.Log.LogInfo((object)("Material " + text + " is in no biome table; add it to Biomes.Overrides as " + text + "=<Biome>")); } return Biome.Unknown; } private static string PrefabName(Requirement requirement) { if (requirement == null || (Object)(object)requirement.m_resItem == (Object)null) { return string.Empty; } return ((Object)((Component)requirement.m_resItem).gameObject).name; } private static void ReadOverrides() { string text = Plugin.BiomeOverrides.Value ?? string.Empty; if (text == overridesFrom) { return; } overridesFrom = text; overrides.Clear(); string[] array = text.Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split(new char[1] { '=' }); if (array2.Length == 2) { string text2 = array2[0].Trim(); if (text2.Length > 0 && Enum.TryParse<Biome>(array2[1].Trim(), ignoreCase: true, out var result)) { overrides[text2] = result; } } } } } internal static class CookedResults { private const int MaxSteps = 4; private static readonly Dictionary<string, SharedData> results = new Dictionary<string, SharedData>(); private static ZNetScene builtFor; public static SharedData Resolve(SharedData shared) { if (shared == null) { return null; } Build(); SharedData val = shared; for (int i = 0; i < 4; i++) { if (string.IsNullOrEmpty(val.m_name)) { break; } if (!results.TryGetValue(val.m_name, out var value)) { break; } if (value == val) { break; } val = value; } return val; } private static void Build() { ZNetScene instance = ZNetScene.instance; if ((Object)(object)instance == (Object)null || (Object)(object)builtFor == (Object)(object)instance) { return; } builtFor = instance; results.Clear(); int num = 0; foreach (GameObject prefab in instance.m_prefabs) { if ((Object)(object)prefab == (Object)null) { continue; } CookingStation component = prefab.GetComponent<CookingStation>(); if ((Object)(object)component == (Object)null || component.m_conversion == null) { continue; } num++; foreach (ItemConversion item in component.m_conversion) { if (item != null && !((Object)(object)item.m_from == (Object)null) && !((Object)(object)item.m_to == (Object)null)) { SharedData val = ((item.m_from.m_itemData != null) ? item.m_from.m_itemData.m_shared : null); SharedData val2 = ((item.m_to.m_itemData != null) ? item.m_to.m_itemData.m_shared : null); if (val != null && val2 != null && !string.IsNullOrEmpty(val.m_name) && !results.ContainsKey(val.m_name)) { results[val.m_name] = val2; } } } } Plugin.Log.LogDebug((object)$"{results.Count} cooking conversions read from {num} cooking station prefabs"); } } internal enum FoodKind { Health, Stamina, Magic, Balanced, Other } internal static class FoodSections { private static readonly FoodKind[] DefaultOrder = new FoodKind[5] { FoodKind.Health, FoodKind.Stamina, FoodKind.Magic, FoodKind.Balanced, FoodKind.Other }; public static bool TryPlace(List<RecipeDataPair> rows, out List<SectionDef> sections, out Placement[] placements) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) sections = null; placements = null; FoodKind[] array = new FoodKind[rows.Count]; SharedData[] array2 = (SharedData[])(object)new SharedData[rows.Count]; bool flag = false; for (int i = 0; i < rows.Count; i++) { SharedData val = SectionLayout.SharedOf(rows[i]); if (IsIngredient(val)) { array2[i] = val; array[i] = FoodKind.Other; } else { array2[i] = CookedResults.Resolve(val); array[i] = Classify(array2[i]); flag |= array[i] != FoodKind.Other; } } if (!flag) { return false; } if (Plugin.LogRows.Value) { LogRows(rows, array2, array); } List<FoodKind> list = SectionLayout.ParseOrder(Plugin.FoodOrder.Value, DefaultOrder, Alias); sections = new List<SectionDef>(list.Count); foreach (FoodKind item in list) { FoodKind k = item; sections.Add(new SectionDef("food:" + k, () => Label(k), KindColor(k), k == FoodKind.Other, k != FoodKind.Other)); } placements = new Placement[rows.Count]; for (int num = 0; num < rows.Count; num++) { SharedData val2 = SectionLayout.SharedOf(rows[num]); placements[num] = new Placement { Section = list.IndexOf(array[num]), Value = ((array2[num] != null) ? SortValue(array2[num], array[num]) : 0f), Name = ((val2 != null) ? Localization.instance.Localize(val2.m_name) : string.Empty) }; } return true; } public static FoodKind Classify(SharedData shared) { if (shared == null) { return FoodKind.Other; } float food = shared.m_food; float foodStamina = shared.m_foodStamina; float foodEitr = shared.m_foodEitr; if (food <= 0f && foodStamina <= 0f && foodEitr <= 0f) { return FoodKind.Other; } if (food < foodEitr / 2f && foodStamina < foodEitr / 2f) { return FoodKind.Magic; } if (foodStamina < food / 2f) { return FoodKind.Health; } if (food < foodStamina / 2f) { return FoodKind.Stamina; } if (Plugin.BalancedSection.Value) { return FoodKind.Balanced; } if (!(food >= foodStamina)) { return FoodKind.Stamina; } return FoodKind.Health; } public static float SortValue(SharedData shared, FoodKind kind) { return kind switch { FoodKind.Health => shared.m_food, FoodKind.Stamina => shared.m_foodStamina, FoodKind.Magic => shared.m_foodEitr, FoodKind.Balanced => Mathf.Max(new float[3] { shared.m_food, shared.m_foodStamina, shared.m_foodEitr }), _ => 0f, }; } private static bool IsIngredient(SharedData own) { if (own == null) { return true; } if (own.m_food <= 0f && own.m_foodStamina <= 0f && own.m_foodEitr <= 0f) { return true; } string value = Plugin.Ingredients.Value; if (string.IsNullOrEmpty(value)) { return false; } string value2 = Localization.instance.Localize(own.m_name); string[] array = value.Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length != 0 && (text.Equals(own.m_name, StringComparison.OrdinalIgnoreCase) || text.Equals(value2, StringComparison.OrdinalIgnoreCase))) { return true; } } return false; } private static void LogRows(List<RecipeDataPair> rows, SharedData[] foods, FoodKind[] kinds) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: 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_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_0220: 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) Player localPlayer = Player.m_localPlayer; CraftingStation val = (((Object)(object)localPlayer != (Object)null) ? localPlayer.GetCurrentCraftingStation() : null); Plugin.Log.LogInfo((object)string.Format("Food list at {0}: {1} rows", ((Object)(object)val != (Object)null) ? val.m_name : "no station", rows.Count)); for (int i = 0; i < rows.Count; i++) { RecipeDataPair val2 = rows[i]; Recipe recipe = ((RecipeDataPair)(ref val2)).Recipe; ItemDrop val3 = (((Object)(object)recipe != (Object)null) ? recipe.m_item : null); SharedData val4 = SectionLayout.SharedOf(rows[i]); string text = (((Object)(object)val3 != (Object)null) ? ((Object)((Component)val3).gameObject).name : "null"); string text2 = SectionLayout.RowText(rows[i]); if (val4 == null) { Plugin.Log.LogInfo((object)string.Format(" {0}: recipe {1} prefab {2} has no item data; row text '{3}'", i, ((Object)(object)recipe != (Object)null) ? ((Object)recipe).name : "null", text, text2)); continue; } SharedData val5 = foods[i]; string text3 = ((val5 != null && val5 != val4) ? $" cooks into {Localization.instance.Localize(val5.m_name)} ({val5.m_name}) type {val5.m_itemType} health {val5.m_food} stamina {val5.m_foodStamina} eitr {val5.m_foodEitr}" : string.Empty); ManualLogSource log = Plugin.Log; string text4 = $" {i}: {Localization.instance.Localize(val4.m_name)} ({val4.m_name}) prefab {text} recipe {((Object)recipe).name} type {val4.m_itemType} "; object[] obj = new object[7] { val4.m_food, val4.m_foodStamina, val4.m_foodEitr, null, null, null, null }; val2 = rows[i]; obj[3] = ((RecipeDataPair)(ref val2)).CanCraft; obj[4] = text3; obj[5] = kinds[i]; obj[6] = text2; log.LogInfo((object)(text4 + string.Format("health {0} stamina {1} eitr {2} craftable {3}{4} -> {5}; row text '{6}'", obj))); } } private static string Alias(string name) { if (!name.Equals("Eitr", StringComparison.OrdinalIgnoreCase)) { return name; } return FoodKind.Magic.ToString(); } private static string Label(FoodKind kind) { return kind switch { FoodKind.Health => Plugin.HealthLabel.Value, FoodKind.Stamina => Plugin.StaminaLabel.Value, FoodKind.Magic => Plugin.MagicFoodLabel.Value, FoodKind.Balanced => Plugin.BalancedLabel.Value, _ => Plugin.OtherLabel.Value, }; } private static Color KindColor(FoodKind kind) { //IL_0027: 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_0051: 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_007b: Unknown result type (might be due to invalid IL or missing references) return (Color)(kind switch { FoodKind.Health => new Color(1f, 0.5f, 0.5f), FoodKind.Stamina => new Color(1f, 1f, 0.5f), FoodKind.Magic => new Color(0.565f, 0.565f, 1f), FoodKind.Balanced => new Color(1f, 0.8f, 0.5f), _ => new Color(0.8f, 0.8f, 0.8f), }); } } internal enum GearKind { OneHanded, TwoHanded, DualWielding, Bows, Magic, Shields, Armor, Capes, Trinkets, Tools, Ammunition, Bombs, Misc } internal static class GearSections { private static readonly GearKind[] DefaultOrder = new GearKind[13] { GearKind.OneHanded, GearKind.TwoHanded, GearKind.DualWielding, GearKind.Bows, GearKind.Magic, GearKind.Shields, GearKind.Armor, GearKind.Capes, GearKind.Trinkets, GearKind.Tools, GearKind.Ammunition, GearKind.Bombs, GearKind.Misc }; private static readonly Color HeadingColor = new Color(1f, 0.85f, 0.55f); public static bool TryPlace(List<RecipeDataPair> rows, out List<SectionDef> sections, out Placement[] placements) { //IL_001c: 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_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_013c: 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) sections = null; placements = null; GearKind[] array = new GearKind[rows.Count]; bool flag = false; for (int i = 0; i < rows.Count; i++) { array[i] = Classify(SectionLayout.SharedOf(rows[i])); flag |= array[i] != GearKind.Misc; } if (!flag) { return false; } List<GearKind> list = SectionLayout.ParseOrder(Plugin.GearOrder.Value, DefaultOrder, null); sections = new List<SectionDef>(list.Count); foreach (GearKind item in list) { GearKind k = item; sections.Add(new SectionDef("gear:" + k, () => Label(k), HeadingColor, k == GearKind.Misc, showValue: false)); } placements = new Placement[rows.Count]; for (int num = 0; num < rows.Count; num++) { SharedData val = SectionLayout.SharedOf(rows[num]); RecipeDataPair val2 = rows[num]; float num2 = (float)Biomes.Of(((RecipeDataPair)(ref val2)).Recipe); if (array[num] == GearKind.Ammunition) { float num3 = num2; val2 = rows[num]; num2 = num3 + AmmoGroup(val, ((RecipeDataPair)(ref val2)).Recipe); } string text = ((val != null) ? Localization.instance.Localize(val.m_name) : string.Empty); placements[num] = new Placement { Section = list.IndexOf(array[num]), Value = num2, Name = ((array[num] == GearKind.Armor) ? ArmorSortKey(val, text) : text) }; } if (Plugin.LogRows.Value) { LogRows(rows, array, placements); } return true; } public static GearKind Classify(SharedData shared) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Invalid comparison between Unknown and I4 //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Invalid comparison between Unknown and I4 //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Invalid comparison between Unknown and I4 //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Invalid comparison between Unknown and I4 //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Invalid comparison between Unknown and I4 //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Expected I4, but got Unknown //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Invalid comparison between Unknown and I4 //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Invalid comparison between Unknown and I4 //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Invalid comparison between Unknown and I4 //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Invalid comparison between Unknown and I4 //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Invalid comparison between Unknown and I4 //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Invalid comparison between Unknown and I4 //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Invalid comparison between Unknown and I4 //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Invalid comparison between Unknown and I4 //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Invalid comparison between Unknown and I4 //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Invalid comparison between Unknown and I4 //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Invalid comparison between Unknown and I4 //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Invalid comparison between Unknown and I4 //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Invalid comparison between Unknown and I4 //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Invalid comparison between Unknown and I4 //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Invalid comparison between Unknown and I4 //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Invalid comparison between Unknown and I4 //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Invalid comparison between Unknown and I4 //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Invalid comparison between Unknown and I4 //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Invalid comparison between Unknown and I4 if (shared == null) { return GearKind.Misc; } ItemType itemType = shared.m_itemType; SkillType skillType = shared.m_skillType; if ((int)skillType == 9 || (int)skillType == 10) { return GearKind.Magic; } if (shared.m_attack != null && shared.m_attack.m_consumeItem) { if (!((DamageTypes)(ref shared.m_damages)).HaveDamage()) { return GearKind.Misc; } return GearKind.Bombs; } if (shared.m_attackStatusEffect is SE_Harpooned) { return GearKind.Tools; } if (shared.m_tamedOnly || (int)shared.m_animationState == 17) { return GearKind.Tools; } if ((int)itemType <= 17) { switch (itemType - 4) { default: if ((int)itemType != 17) { goto IL_00d0; } return GearKind.Capes; case 2: case 3: case 7: return GearKind.Armor; case 1: return GearKind.Shields; case 5: break; case 0: goto IL_00bb; case 4: case 6: goto IL_00d0; } } else { if ((int)itemType == 19) { return GearKind.Tools; } if ((int)itemType != 23) { if ((int)itemType == 24) { return GearKind.Trinkets; } goto IL_00d0; } } return GearKind.Ammunition; IL_00bb: if (!((DamageTypes)(ref shared.m_damages)).HaveDamage()) { return GearKind.Misc; } return GearKind.Bows; IL_00d0: if (((int)itemType == 3 || (int)itemType == 14 || (int)itemType == 22 || (int)itemType == 15) && !((DamageTypes)(ref shared.m_damages)).HaveDamage()) { if ((int)itemType == 15 || shared.m_attack == null || !((Object)(object)shared.m_attack.m_attackProjectile != (Object)null)) { return GearKind.Misc; } return GearKind.Tools; } if ((int)skillType == 8 || (int)skillType == 14) { if (!((DamageTypes)(ref shared.m_damages)).HaveDamage()) { return GearKind.Misc; } return GearKind.Bows; } if ((int)skillType == 12) { return GearKind.Tools; } if ((int)itemType == 3 && (int)skillType == 7 && !HasElementalDamage(shared)) { return GearKind.Tools; } if ((int)itemType == 22 || (int)shared.m_animationState == 15 || (int)skillType == 11) { return GearKind.DualWielding; } if ((int)itemType == 3 || (int)itemType == 15) { return GearKind.OneHanded; } if ((int)itemType == 14) { return GearKind.TwoHanded; } return GearKind.Misc; } private static string ArmorSortKey(SharedData shared, string name) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Invalid comparison between Unknown and I4 //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Invalid comparison between Unknown and I4 //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Invalid comparison between Unknown and I4 if (shared == null) { return name; } string text = (string.IsNullOrEmpty(shared.m_setName) ? name : shared.m_setName); ItemType itemType = shared.m_itemType; int num = (((int)itemType != 6) ? (((int)itemType == 7) ? 1 : (((int)itemType != 11) ? 3 : 2)) : 0); return text + "|" + num + "|" + name; } private static float AmmoGroup(SharedData shared, Recipe recipe) { string text = shared?.m_ammoType; string text2 = ((!string.IsNullOrEmpty(text)) ? text : (((Object)(object)recipe != (Object)null && (Object)(object)recipe.m_item != (Object)null) ? ((Object)((Component)recipe.m_item).gameObject).name : string.Empty)); if (text2.IndexOf("arrow", StringComparison.OrdinalIgnoreCase) >= 0) { return 200f; } if (text2.IndexOf("bolt", StringComparison.OrdinalIgnoreCase) >= 0) { return 100f; } return 0f; } private static bool HasElementalDamage(SharedData shared) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0021: 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_003b: Unknown result type (might be due to invalid IL or missing references) DamageTypes damages = shared.m_damages; if (!(damages.m_fire > 0f) && !(damages.m_frost > 0f) && !(damages.m_lightning > 0f) && !(damages.m_poison > 0f)) { return damages.m_spirit > 0f; } return true; } private static void LogRows(List<RecipeDataPair> rows, GearKind[] kinds, Placement[] placements) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: 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_00a9: 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_0161: 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_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) Player localPlayer = Player.m_localPlayer; CraftingStation val = (((Object)(object)localPlayer != (Object)null) ? localPlayer.GetCurrentCraftingStation() : null); Plugin.Log.LogInfo((object)string.Format("Gear list at {0}: {1} rows", ((Object)(object)val != (Object)null) ? val.m_name : "no station", rows.Count)); for (int i = 0; i < rows.Count; i++) { RecipeDataPair val2 = rows[i]; Recipe recipe = ((RecipeDataPair)(ref val2)).Recipe; ItemDrop val3 = (((Object)(object)recipe != (Object)null) ? recipe.m_item : null); SharedData val4 = SectionLayout.SharedOf(rows[i]); string text = (((Object)(object)val3 != (Object)null) ? ((Object)((Component)val3).gameObject).name : "null"); string text2 = SectionLayout.RowText(rows[i]); if (val4 == null) { Plugin.Log.LogInfo((object)string.Format(" {0}: recipe {1} prefab {2} has no item data; row text '{3}'", i, ((Object)(object)recipe != (Object)null) ? ((Object)recipe).name : "null", text, text2)); continue; } StringBuilder stringBuilder = new StringBuilder(); object[] obj = new object[11] { i, Localization.instance.Localize(val4.m_name), val4.m_name, text, ((Object)recipe).name, val4.m_itemType, val4.m_skillType, val4.m_animationState, val4.m_movementModifier, val4.m_setName, null }; val2 = rows[i]; obj[10] = ((RecipeDataPair)(ref val2)).CanCraft; stringBuilder.Append(string.Format(" {0}: {1} ({2}) prefab {3} recipe {4} type {5} skill {6} anim {7} movement {8} set '{9}' craftable {10}", obj)); stringBuilder.Append($" ingredients [{Biomes.Describe(recipe)}] -> {(Biome)(placements[i].Value % 100f)} {kinds[i]}; row text '{text2}'"); Plugin.Log.LogInfo((object)stringBuilder.ToString()); } } private static string Label(GearKind kind) { return kind switch { GearKind.OneHanded => Plugin.OneHandedLabel.Value, GearKind.TwoHanded => Plugin.TwoHandedLabel.Value, GearKind.DualWielding => Plugin.DualWieldingLabel.Value, GearKind.Bows => Plugin.BowsLabel.Value, GearKind.Magic => Plugin.MagicGearLabel.Value, GearKind.Shields => Plugin.ShieldsLabel.Value, GearKind.Armor => Plugin.ArmorLabel.Value, GearKind.Capes => Plugin.CapesLabel.Value, GearKind.Trinkets => Plugin.TrinketsLabel.Value, GearKind.Tools => Plugin.ToolsLabel.Value, GearKind.Ammunition => Plugin.AmmunitionLabel.Value, GearKind.Bombs => Plugin.BombsLabel.Value, _ => Plugin.MiscLabel.Value, }; } } [BepInPlugin("rhenn.craftingmenusorter", "CraftingMenuSorter", "1.0.0")] public class Plugin : BaseUnityPlugin { public const string PluginGUID = "rhenn.craftingmenusorter"; public const string PluginName = "CraftingMenuSorter"; public const string PluginVersion = "1.0.0"; internal static ManualLogSource Log; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<bool> GreyedOutLast; internal static ConfigEntry<bool> ShowValues; internal static ConfigEntry<bool> RememberCollapsed; internal static ConfigEntry<string> FoodOrder; internal static ConfigEntry<bool> BalancedSection; internal static ConfigEntry<string> Ingredients; internal static ConfigEntry<string> HealthLabel; internal static ConfigEntry<string> StaminaLabel; internal static ConfigEntry<string> MagicFoodLabel; internal static ConfigEntry<string> BalancedLabel; internal static ConfigEntry<string> OtherLabel; internal static ConfigEntry<string> GearOrder; internal static ConfigEntry<string> ArmorLabel; internal static ConfigEntry<string> OneHandedLabel; internal static ConfigEntry<string> TwoHandedLabel; internal static ConfigEntry<string> DualWieldingLabel; internal static ConfigEntry<string> BowsLabel; internal static ConfigEntry<string> MagicGearLabel; internal static ConfigEntry<string> ShieldsLabel; internal static ConfigEntry<string> CapesLabel; internal static ConfigEntry<string> TrinketsLabel; internal static ConfigEntry<string> ToolsLabel; internal static ConfigEntry<string> AmmunitionLabel; internal static ConfigEntry<string> BombsLabel; internal static ConfigEntry<string> MiscLabel; internal static ConfigEntry<string> BiomeOverrides; internal static ConfigEntry<bool> LogRows; private readonly Harmony harmony = new Harmony("rhenn.craftingmenusorter"); private void Awake() { Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master switch, read when the game starts. false: the mod loads nothing and every crafting list stays as the game has it."); GreyedOutLast = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "GreyedOutLast", true, "true: inside each section the recipes you can craft right now come first and the greyed-out ones you lack ingredients for go to the bottom of the section, each part in the section's own order. false: one list per section in the section's order, whether you have the ingredients or not."); ShowValues = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowValues", true, "Food lists only: shows the number a section is sorted by at the right end of each of its rows (health, stamina, eitr). Gear rows never show a number."); RememberCollapsed = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "RememberCollapsed", true, "Saves which sections are collapsed with the character, so they stay collapsed after a restart. false: every section is open when you log in."); FoodOrder = ((BaseUnityPlugin)this).Config.Bind<string>("Food", "Order", "Health,Stamina,Magic,Balanced,Other", "The food sections from top to bottom, comma separated (Eitr is accepted for Magic). A section left out goes to the end. A section with no recipes at the current station is not shown."); BalancedSection = ((BaseUnityPlugin)this).Config.Bind<bool>("Food", "Balanced", true, "true: food with no clear main value (a white food icon in the inventory: neither health nor stamina is less than half the other) has its own Balanced section. false: such food is listed under whichever of its values is bigger."); Ingredients = ((BaseUnityPlugin)this).Config.Bind<string>("Food", "Ingredients", "$item_fishraw", "Items that are only ingredients for other recipes and belong under Other with no number, even though they can be cooked into food: comma separated, by the game's $ name or the name shown in the list (raw fish is $item_fishraw). An item with no health, stamina or eitr of its own is treated as an ingredient without being listed here."); HealthLabel = ((BaseUnityPlugin)this).Config.Bind<string>("FoodLabels", "Health", "Health", "Heading of the section for the red-icon foods. A $ token of the game's own, such as $item_food_health, is translated."); StaminaLabel = ((BaseUnityPlugin)this).Config.Bind<string>("FoodLabels", "Stamina", "Stamina", "Heading of the section for the yellow-icon foods. $item_food_stamina gives the game's word."); MagicFoodLabel = ((BaseUnityPlugin)this).Config.Bind<string>("FoodLabels", "Magic", "Magic", "Heading of the section for the blue-icon eitr foods. $item_food_eitr gives the game's word (Eitr)."); BalancedLabel = ((BaseUnityPlugin)this).Config.Bind<string>("FoodLabels", "Balanced", "Balanced", "Heading of the section for the white-icon foods, when Food.Balanced is on."); OtherLabel = ((BaseUnityPlugin)this).Config.Bind<string>("FoodLabels", "Other", "Other", "Heading of the section for everything at a food station that is not food, such as the mead bases at the cauldron and the raw fish at the preparation table."); GearOrder = ((BaseUnityPlugin)this).Config.Bind<string>("Gear", "Order", "OneHanded,TwoHanded,DualWielding,Bows,Magic,Shields,Armor,Capes,Trinkets,Tools,Ammunition,Bombs,Misc", "The gear sections from top to bottom at the workbench, forge, black forge, galdr table and any other station without food, comma separated. A section left out goes to the end. A section with no recipes at the current station is not shown."); OneHandedLabel = ((BaseUnityPlugin)this).Config.Bind<string>("GearLabels", "OneHanded", "One Handed", "Heading for one-handed weapons: swords, maces, knives, spears, torches. One-handed axes are under Tools."); TwoHandedLabel = ((BaseUnityPlugin)this).Config.Bind<string>("GearLabels", "TwoHanded", "Two Handed", "Heading for two-handed weapons: battleaxes, sledges, atgeirs, greatswords."); DualWieldingLabel = ((BaseUnityPlugin)this).Config.Bind<string>("GearLabels", "DualWielding", "Dual Wielding", "Heading for paired weapons and fist weapons: dual axes, dual knives, claws."); BowsLabel = ((BaseUnityPlugin)this).Config.Bind<string>("GearLabels", "Bows", "Bows", "Heading for bows and crossbows."); MagicGearLabel = ((BaseUnityPlugin)this).Config.Bind<string>("GearLabels", "Magic", "Magic", "Heading for staffs and wands: anything that uses the elemental or blood magic skill."); ShieldsLabel = ((BaseUnityPlugin)this).Config.Bind<string>("GearLabels", "Shields", "Shields", "Heading for shields."); ArmorLabel = ((BaseUnityPlugin)this).Config.Bind<string>("GearLabels", "Armor", "Armor", "Heading for helmets, chest and leg pieces. The pieces of one set stand together, helmet, chest, legs."); CapesLabel = ((BaseUnityPlugin)this).Config.Bind<string>("GearLabels", "Capes", "Capes", "Heading for capes."); TrinketsLabel = ((BaseUnityPlugin)this).Config.Bind<string>("GearLabels", "Trinkets", "Trinkets", "Heading for trinkets."); ToolsLabel = ((BaseUnityPlugin)this).Config.Bind<string>("GearLabels", "Tools", "Tools", "Heading for the hammer, hoe, cultivator, fishing rod and other tools, pickaxes, and one-handed axes."); AmmunitionLabel = ((BaseUnityPlugin)this).Config.Bind<string>("GearLabels", "Ammunition", "Ammunition", "Heading for arrows, bolts and any other ammunition."); BombsLabel = ((BaseUnityPlugin)this).Config.Bind<string>("GearLabels", "Bombs", "Bombs", "Heading for thrown items that are used up on the throw: ooze, bile, tar and frost bombs."); MiscLabel = ((BaseUnityPlugin)this).Config.Bind<string>("GearLabels", "Misc", "Misc", "Heading for everything else a gear station makes, in the game's order."); BiomeOverrides = ((BaseUnityPlugin)this).Config.Bind<string>("Biomes", "Overrides", "", "Gear is ordered by the biome of its ingredients, newest biome first, from a built-in table of the game's materials (by prefab name). Add or change entries here, comma separated, as Prefab=Biome, where Biome is one of Meadows, BlackForest, Swamp, Mountains, Plains, Mistlands, Ashlands, DeepNorth. Example: MyModOre=Swamp. With Debug.LogRecipeRows on, the log names every material the table does not know."); LogRows = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "LogRecipeRows", false, "Writes one line per recipe row to the BepInEx log each time a list is built: item, prefab, type, skill, food values or ingredients with their biomes, and the section it went to. For bug reports; off unless you are looking at a wrong row."); ((BaseUnityPlugin)this).Config.SettingChanged += OnSettingChanged; if (!Enabled.Value) { Log.LogInfo((object)"CraftingMenuSorter 1.0.0 is disabled in the config; nothing is patched."); return; } harmony.PatchAll(Assembly.GetExecutingAssembly()); Log.LogInfo((object)"CraftingMenuSorter 1.0.0 loaded."); } private static void OnSettingChanged(object sender, SettingChangedEventArgs e) { if ((object)e.ChangedSetting != Enabled && (object)e.ChangedSetting != LogRows) { Biomes.Invalidate(); SectionLayout.Refresh(); } } private void OnDestroy() { harmony.UnpatchSelf(); } } internal sealed class SectionDef { public readonly string Key; public readonly Func<string> Label; public readonly Color Color; public readonly bool KeepGameOrder; public readonly bool ShowValue; public SectionDef(string key, Func<string> label, Color color, bool keepGameOrder, bool showValue) { //IL_0015: 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) Key = key; Label = label; Color = color; KeepGameOrder = keepGameOrder; ShowValue = showValue; } } internal struct Placement { public int Section; public float Value; public string Name; } internal static class SectionLayout { private struct Entry { public RecipeDataPair Pair; public int GameIndex; public float Value; public string Name; } private const string CustomDataKey = "rhenn.craftingmenusorter.collapsed"; private const string HeadingName = "CraftingMenuSorter_Heading"; private const string NameObject = "name"; private const string IconObject = "icon"; private const string ValueObject = "QualityLevel"; private const string OpenMark = "[-] "; private const string CollapsedMark = "[+] "; private static readonly List<GameObject> headings = new List<GameObject>(); private static readonly HashSet<string> collapsed = new HashSet<string>(); private static Player collapsedOwner; private static bool warnedAboutPrefab; public static void Arrange(InventoryGui gui) { ClearHeadings(); if (!((Object)(object)gui == (Object)null) && gui.InCraftTab()) { List<RecipeDataPair> availableRecipes = gui.m_availableRecipes; if (availableRecipes != null && availableRecipes.Count != 0 && (FoodSections.TryPlace(availableRecipes, out var sections, out var placements) || GearSections.TryPlace(availableRecipes, out sections, out placements))) { Layout(gui, availableRecipes, sections, placements); } } } private static void Layout(InventoryGui gui, List<RecipeDataPair> rows, List<SectionDef> sections, Placement[] placements) { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0117: 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_017f: Unknown result type (might be due to invalid IL or missing references) LoadCollapsed(); float recipeListSpace = gui.m_recipeListSpace; float num = 0f; List<RecipeDataPair> list = new List<RecipeDataPair>(rows.Count); for (int i = 0; i < sections.Count; i++) { SectionDef sectionDef = sections[i]; List<Entry> list2 = new List<Entry>(); for (int j = 0; j < rows.Count; j++) { if (placements[j].Section == i) { list2.Add(new Entry { Pair = rows[j], GameIndex = j, Value = placements[j].Value, Name = (placements[j].Name ?? string.Empty) }); } } if (list2.Count == 0) { continue; } Sort(list2, sectionDef.KeepGameOrder); bool flag = !collapsed.Contains(sectionDef.Key); AddHeading(gui, sectionDef, list2.Count, flag, num); num -= recipeListSpace; foreach (Entry item in list2) { Entry current = item; list.Add(current.Pair); GameObject interfaceElement = ((RecipeDataPair)(ref current.Pair)).InterfaceElement; if (!((Object)(object)interfaceElement == (Object)null)) { Transform transform = interfaceElement.transform; ((RectTransform)((transform is RectTransform) ? transform : null)).anchoredPosition = new Vector2(0f, num); interfaceElement.SetActive(flag); if (flag) { num -= recipeListSpace; } if (sectionDef.ShowValue && Plugin.ShowValues.Value) { ShowValue(interfaceElement, sectionDef.Color, current.Value); } } } } if (list.Count == rows.Count) { rows.Clear(); rows.AddRange(list); } else { Plugin.Log.LogWarning((object)$"{list.Count} of {rows.Count} recipe rows were placed in sections; the game's order is kept."); } float num2 = Mathf.Max(gui.m_recipeListBaseSize, 0f - num); gui.m_recipeListRoot.SetSizeWithCurrentAnchors((Axis)1, num2); } public static void Refresh() { InventoryGui instance = InventoryGui.instance; if (!((Object)(object)instance == (Object)null) && InventoryGui.IsVisible()) { instance.UpdateCraftingPanel(false); } } public static SharedData SharedOf(RecipeDataPair pair) { if ((Object)(object)((RecipeDataPair)(ref pair)).Recipe == (Object)null || (Object)(object)((RecipeDataPair)(ref pair)).Recipe.m_item == (Object)null || ((RecipeDataPair)(ref pair)).Recipe.m_item.m_itemData == null) { return null; } return ((RecipeDataPair)(ref pair)).Recipe.m_item.m_itemData.m_shared; } public static string RowText(RecipeDataPair pair) { GameObject interfaceElement = ((RecipeDataPair)(ref pair)).InterfaceElement; if ((Object)(object)interfaceElement == (Object)null) { return "no row"; } Transform val = interfaceElement.transform.Find("name"); TMP_Text val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<TMP_Text>() : null); if (!((Object)(object)val2 != (Object)null)) { return "no name text"; } return val2.text; } public static List<T> ParseOrder<T>(string setting, T[] defaults, Func<string, string> alias) where T : struct { List<T> list = new List<T>(defaults.Length); string[] array = (setting ?? string.Empty).Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (alias != null) { text = alias(text); } if (Enum.TryParse<T>(text, ignoreCase: true, out var result) && !list.Contains(result)) { list.Add(result); } } foreach (T item in defaults) { if (!list.Contains(item)) { list.Add(item); } } return list; } private static void Sort(List<Entry> entries, bool keepGameOrder) { IOrderedEnumerable<Entry> source = (Plugin.GreyedOutLast.Value ? entries.OrderByDescending((Entry e) => ((RecipeDataPair)(ref e.Pair)).CanCraft) : entries.OrderBy((Entry e) => 0)); if (!keepGameOrder) { source = source.ThenByDescending((Entry e) => e.Value).ThenBy<Entry, string>((Entry e) => e.Name, StringComparer.CurrentCultureIgnoreCase); } List<Entry> collection = source.ThenBy((Entry e) => e.GameIndex).ToList(); entries.Clear(); entries.AddRange(collection); } private static void ShowValue(GameObject element, Color color, float value) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) Transform val = element.transform.Find("QualityLevel"); if (!((Object)(object)val == (Object)null)) { TMP_Text component = ((Component)val).GetComponent<TMP_Text>(); if (!((Object)(object)component == (Object)null)) { ((Component)val).gameObject.SetActive(true); component.text = value.ToString("0.#", CultureInfo.InvariantCulture); ((Graphic)component).color = color; } } } private static void AddHeading(InventoryGui gui, SectionDef section, int count, bool open, float y) { //IL_004d: 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_0074: Expected O, but got Unknown //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Expected O, but got Unknown //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) GameObject val = Object.Instantiate<GameObject>(gui.m_recipeElementPrefab, (Transform)(object)gui.m_recipeListRoot); ((Object)val).name = "CraftingMenuSorter_Heading"; val.SetActive(true); Transform transform = val.transform; ((RectTransform)((transform is RectTransform) ? transform : null)).anchoredPosition = new Vector2(0f, y); foreach (Transform item in val.transform) { Transform val2 = item; ((Component)val2).gameObject.SetActive(((Object)val2).name == "name"); } Transform val3 = val.transform.Find("name"); TMP_Text val4 = (((Object)(object)val3 != (Object)null) ? ((Component)val3).GetComponent<TMP_Text>() : null); if ((Object)(object)val4 != (Object)null) { string text = Localization.instance.Localize(section.Label()); val4.text = (open ? "[-] " : "[+] ") + text + " (" + count + ")"; ((Graphic)val4).color = section.Color; val4.fontStyle = (FontStyles)(val4.fontStyle | 1); val4.horizontalAlignment = (HorizontalAlignmentOptions)1; Transform transform2 = val.transform; AlignWithIcon((RectTransform)(object)((transform2 is RectTransform) ? transform2 : null), (RectTransform)(object)((val3 is RectTransform) ? val3 : null)); } else if (!warnedAboutPrefab) { warnedAboutPrefab = true; Plugin.Log.LogWarning((object)"The recipe row prefab has no 'name' text; the section headings are blank."); } Button component = val.GetComponent<Button>(); if ((Object)(object)component != (Object)null) { ((UnityEventBase)component.onClick).RemoveAllListeners(); string key = section.Key; ((UnityEvent)component.onClick).AddListener((UnityAction)delegate { Toggle(gui, key); }); } headings.Add(val); } private static void AlignWithIcon(RectTransform row, RectTransform name) { //IL_003d: 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_004d: 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_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_008e: 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_00a6: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)row == (Object)null || (Object)(object)name == (Object)null) { return; } Transform val = ((Transform)row).Find("icon"); RectTransform val2 = (RectTransform)(object)(((Object)(object)val != (Object)null) ? ((val is RectTransform) ? val : null) : null); if (!((Object)(object)val2 == (Object)null)) { Rect rect = row.rect; float width = ((Rect)(ref rect)).width; float num = val2.anchorMin.x * width + val2.offsetMin.x; float num2 = name.anchorMin.x * width + name.offsetMin.x - num; if (num2 > 0f) { name.offsetMin = new Vector2(name.offsetMin.x - num2, name.offsetMin.y); } } } private static void Toggle(InventoryGui gui, string key) { if (!collapsed.Remove(key)) { collapsed.Add(key); } SaveCollapsed(); if ((Object)(object)gui != (Object)null) { Arrange(gui); } } private static void ClearHeadings() { foreach (GameObject heading in headings) { if ((Object)(object)heading != (Object)null) { Object.Destroy((Object)(object)heading); } } headings.Clear(); } private static void LoadCollapsed() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || (Object)(object)collapsedOwner == (Object)(object)localPlayer) { return; } collapsedOwner = localPlayer; collapsed.Clear(); if (!Plugin.RememberCollapsed.Value || localPlayer.m_customData == null || !localPlayer.m_customData.TryGetValue("rhenn.craftingmenusorter.collapsed", out var value) || string.IsNullOrEmpty(value)) { return; } string[] array = value.Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length > 0) { collapsed.Add(text); } } } private static void SaveCollapsed() { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && Plugin.RememberCollapsed.Value && localPlayer.m_customData != null) { if (collapsed.Count == 0) { localPlayer.m_customData.Remove("rhenn.craftingmenusorter.collapsed"); } else { localPlayer.m_customData["rhenn.craftingmenusorter.collapsed"] = string.Join(",", collapsed.ToArray()); } } } } } namespace CraftingMenuSorter.Patches { [HarmonyPatch(typeof(InventoryGui), "UpdateRecipeList")] internal static class InventoryGui_UpdateRecipeList_Patch { [HarmonyPriority(200)] private static void Postfix(InventoryGui __instance) { try { SectionLayout.Arrange(__instance); } catch (Exception arg) { Plugin.Log.LogError((object)$"Arranging the crafting sections failed: {arg}"); } } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }