using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Logging; using HarmonyLib; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("ExtendedCraftingCategories")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+cf109efb2feabf1b08edb3810e40e13258f8fe8c")] [assembly: AssemblyProduct("ExtendedCraftingCategories")] [assembly: AssemblyTitle("ExtendedCraftingCategories")] [assembly: AssemblyVersion("1.0.0.0")] namespace ExtendedCraftingCategoriesMod; [BepInPlugin("com.iancula.extendedcraftingcategories", "Extended crafting categories", "1.0.0")] public class ExtendedCraftingCategoriesPlugin : BaseUnityPlugin { public const string GUID = "com.iancula.extendedcraftingcategories"; public const string NAME = "Extended crafting categories"; public const string VERSION = "1.0.0"; internal static ManualLogSource Log; private void Awake() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; if (!CraftingMenu_RefreshAutoRecipe_Patch.ValidateReflection()) { Log.LogError((object)"Reflection validation failed – patch will NOT be applied. Check the errors above."); return; } new Harmony("com.iancula.extendedcraftingcategories").PatchAll(); Log.LogInfo((object)"Extended crafting categories loaded successfully."); } } [HarmonyPatch(typeof(CraftingMenu), "RefreshAutoRecipe")] public static class CraftingMenu_RefreshAutoRecipe_Patch { private struct SectionTitles { public RectTransform T1; public RectTransform T2; public RectTransform T3; } private static FieldInfo _f_refreshComplexRequired; private static FieldInfo _f_simpleMode; private static FieldInfo _f_allRecipes; private static FieldInfo _f_complexRecipes; private static FieldInfo _f_displayTemplate; private static FieldInfo _f_recipeDisplays; private static FieldInfo _f_recipeSeparator; private static FieldInfo _f_lastCraftedRecipeIndex; private static FieldInfo _f_lastPouchVersion; private static FieldInfo _f_lastBagVersion; private static FieldInfo _f_lblStationName; private static PropertyInfo _p_localCharacter; private static MethodInfo _m_refreshAvailableIngredients; private static MethodInfo _m_refreshAutoIngredients; private static readonly Dictionary _titleCache = new Dictionary(); public static bool ValidateReflection() { return true & ResolveField(typeof(CraftingMenu), "m_refreshComplexeRecipeRequired", out _f_refreshComplexRequired) & ResolveField(typeof(CraftingMenu), "m_simpleMode", out _f_simpleMode) & ResolveField(typeof(CraftingMenu), "m_allRecipes", out _f_allRecipes) & ResolveField(typeof(CraftingMenu), "m_complexeRecipes", out _f_complexRecipes) & ResolveField(typeof(CraftingMenu), "m_recipeDisplayTemplate", out _f_displayTemplate) & ResolveField(typeof(CraftingMenu), "m_recipeDisplays", out _f_recipeDisplays) & ResolveField(typeof(CraftingMenu), "m_recipeSeparator", out _f_recipeSeparator) & ResolveField(typeof(CraftingMenu), "m_lastCraftedRecipeIndex", out _f_lastCraftedRecipeIndex) & ResolveField(typeof(CraftingMenu), "m_lastPouchVersion", out _f_lastPouchVersion) & ResolveField(typeof(CraftingMenu), "m_lastBagVersion", out _f_lastBagVersion) & ResolveField(typeof(CraftingMenu), "m_lblStationName", out _f_lblStationName) & ResolvePropertyInHierarchy(typeof(CraftingMenu), "LocalCharacter", out _p_localCharacter) & ResolveMethod(typeof(CraftingMenu), "RefreshAvailableIngredients", out _m_refreshAvailableIngredients) & ResolveMethod(typeof(CraftingMenu), "RefreshAutoIngredients", out _m_refreshAutoIngredients); } private static bool Prefix(CraftingMenu __instance) { try { return RunReplacement(__instance); } catch (Exception ex) { ExtendedCraftingCategoriesPlugin.Log.LogError((object)ex); return true; } } private static bool RunReplacement(CraftingMenu instance) { //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Expected O, but got Unknown //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Expected O, but got Unknown //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Expected O, but got Unknown //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_043c: Unknown result type (might be due to invalid IL or missing references) //IL_0446: Expected O, but got Unknown bool num = (bool)_f_refreshComplexRequired.GetValue(instance); bool flag = (bool)_f_simpleMode.GetValue(instance); List list = (List)_f_allRecipes.GetValue(instance); List> list2 = (List>)_f_complexRecipes.GetValue(instance); RecipeDisplay val = (RecipeDisplay)_f_displayTemplate.GetValue(instance); List recipeDisplays = (List)_f_recipeDisplays.GetValue(instance); RectTransform val2 = (RectTransform)_f_recipeSeparator.GetValue(instance); int lastCraftedIdx = (int)_f_lastCraftedRecipeIndex.GetValue(instance); Character val3 = (Character)_p_localCharacter.GetValue(instance, null); if ((Object)(object)val2 != (Object)null) { ((Component)val2).gameObject.SetActive(false); } _m_refreshAvailableIngredients.Invoke(instance, null); if (num) { list2.Clear(); if (!flag) { for (int i = 0; i < list.Count; i++) { Recipe val4 = list[i]; if (val4.IngredientCount > 1 && val3.Inventory.RecipeKnowledge.IsRecipeLearned(UID.op_Implicit(val4.UID))) { list2.Add(new KeyValuePair(i, val4)); } } } _f_refreshComplexRequired.SetValue(instance, false); } list2.Sort((Comparison>)Recipe.SortByName); if ((Object)(object)val == (Object)null) { Debug.LogError((object)"No RecipeDisplay template."); return false; } _f_lastPouchVersion.SetValue(instance, val3.Inventory.Pouch.Version); _f_lastBagVersion.SetValue(instance, val3.Inventory.HasABag ? ((ItemContainer)val3.Inventory.EquippedBag.Container).Version : (-1)); Transform parent = ((Component)val).transform.parent; EnsureTitles(instance, parent); SectionTitles sectionTitles = _titleCache[instance]; foreach (RecipeDisplay item in recipeDisplays) { ((Component)item).transform.SetSiblingIndex(int.MaxValue); } ((Transform)sectionTitles.T1).SetSiblingIndex(int.MaxValue); ((Transform)sectionTitles.T2).SetSiblingIndex(int.MaxValue); ((Transform)sectionTitles.T3).SetSiblingIndex(int.MaxValue); List list3 = new List(); List list4 = new List(); List list5 = new List(); int num2 = -1; for (int j = 0; j < list2.Count; j++) { if (j >= recipeDisplays.Count) { RecipeDisplay val5 = Object.Instantiate(val); ((Component)val5).transform.SetParent(parent); UnityEngineExtensions.ResetLocal(((Component)val5).transform, true); ((UIElement)val5).Show(); recipeDisplays.Add(val5); } object[] array = new object[3] { list2[j].Value, null, null }; bool flag2 = (bool)_m_refreshAutoIngredients.Invoke(instance, array); IList[] array2 = (IList[])array[1]; IList list6 = (IList)array[2]; int num3 = Categorize(flag2, array2); switch (num3) { case 0: list3.Add(j); break; case 1: list4.Add(j); break; default: list5.Add(j); break; } ((UIElement)recipeDisplays[j]).Show(); recipeDisplays[j].SetReferencedRecipe(list2[j].Value, flag2, array2, list6); int captured = j; ((UnityEventBase)recipeDisplays[j].onClick).RemoveAllListeners(); ((UnityEvent)recipeDisplays[j].onClick).AddListener((UnityAction)delegate { instance.OnRecipeSelected(captured, false); }); if (j == lastCraftedIdx) { num2 = num3; } } if (lastCraftedIdx != -1 && num2 != 0) { list4.Remove(lastCraftedIdx); list5.Remove(lastCraftedIdx); list3.Add(lastCraftedIdx); } List list7 = new List(); bool flag3 = list5.Count > 0; bool flag4 = list4.Count > 0; bool flag5 = list3.Count > 0 && (flag4 || flag3); if (flag5) { list7.Add(sectionTitles.T1); } foreach (int item2 in list3) { list7.Add(recipeDisplays[item2]); } if (flag4) { list7.Add(sectionTitles.T2); } foreach (int item3 in list4) { list7.Add(recipeDisplays[item3]); } if (flag3) { list7.Add(sectionTitles.T3); } foreach (int item4 in list5) { list7.Add(recipeDisplays[item4]); } int num4 = 1; foreach (object item5 in list7) { RectTransform val6 = (RectTransform)((item5 is RectTransform) ? item5 : null); if (val6 != null) { ((Component)val6).gameObject.SetActive(true); ((Transform)val6).SetSiblingIndex(num4++); continue; } RecipeDisplay val7 = (RecipeDisplay)((item5 is RecipeDisplay) ? item5 : null); if (val7 != null) { ((Component)val7).transform.SetSiblingIndex(num4++); val7.RealSiblingIndex = -1; } } ((Component)sectionTitles.T1).gameObject.SetActive(flag5); ((Component)sectionTitles.T2).gameObject.SetActive(flag4); ((Component)sectionTitles.T3).gameObject.SetActive(flag3); if (lastCraftedIdx != -1) { int num5 = list7.FindIndex(delegate(object o) { RecipeDisplay val8 = (RecipeDisplay)((o is RecipeDisplay) ? o : null); return val8 != null && (Object)(object)val8 == (Object)(object)recipeDisplays[lastCraftedIdx]; }); if (num5 != -1) { recipeDisplays[lastCraftedIdx].RealSiblingIndex = num5 + 1; } } for (int k = list2.Count; k < recipeDisplays.Count; k++) { ((UIElement)recipeDisplays[k]).Hide(); ((Component)recipeDisplays[k]).transform.SetSiblingIndex(int.MaxValue); } LayoutRebuilder.ForceRebuildLayoutImmediate((RectTransform)(object)((parent is RectTransform) ? parent : null)); return false; } private static int Categorize(bool canCraftFully, IList[] allMatches) { if (canCraftFully) { return 0; } if (allMatches != null) { foreach (IList list in allMatches) { if (list != null && list.Count > 0) { return 1; } } } return 2; } private static void EnsureTitles(CraftingMenu instance, Transform parent) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Expected O, but got Unknown //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) Font font = Resources.GetBuiltinResource("Arial.ttf"); if (_f_lblStationName != null) { object? value = _f_lblStationName.GetValue(instance); Text val = (Text)((value is Text) ? value : null); if ((Object)(object)val != (Object)null && (Object)(object)val.font != (Object)null) { font = val.font; } } foreach (Transform item in parent) { Transform val2 = item; if (((Object)val2).name.StartsWith("_SectionTitle_")) { Object.Destroy((Object)(object)((Component)val2).gameObject); } } SectionTitles sectionTitles = default(SectionTitles); sectionTitles.T1 = CreateTitle("- Craftable -", parent, new Color(0.4f, 0.9f, 0.4f), font); sectionTitles.T2 = CreateTitle("- Partially missing -", parent, new Color(1f, 0.85f, 0.3f), font); sectionTitles.T3 = CreateTitle("- Fully missing -", parent, new Color(0.9f, 0.3f, 0.3f), font); SectionTitles value2 = sectionTitles; _titleCache[instance] = value2; } private static RectTransform CreateTitle(string label, Transform parent, Color color, Font font) { //IL_000b: 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_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("_SectionTitle_" + label); val.transform.SetParent(parent, false); RectTransform result = val.AddComponent(); val.AddComponent().preferredHeight = 40f; Text obj = val.AddComponent(); ((Graphic)obj).color = color; obj.text = label; obj.font = font; obj.alignment = (TextAnchor)4; obj.fontStyle = (FontStyle)2; obj.fontSize = 22; obj.fontStyle = (FontStyle)3; obj.alignment = (TextAnchor)4; ((Graphic)obj).raycastTarget = false; return result; } private static bool ResolveField(Type type, string name, out FieldInfo result) { result = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (result != null) { return true; } ExtendedCraftingCategoriesPlugin.Log.LogError((object)("Missing field: " + name)); return false; } private static bool ResolveMethod(Type type, string name, out MethodInfo result) { result = type.GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (result != null) { return true; } ExtendedCraftingCategoriesPlugin.Log.LogError((object)("Missing method: " + name)); return false; } private static bool ResolvePropertyInHierarchy(Type type, string name, out PropertyInfo result) { while (type != null) { result = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); if (result != null) { return true; } type = type.BaseType; } result = null; return false; } }