Some mods target the Mono version of the game, which is available by opting into the Steam beta branch "alternate"
Decompiled source of ExpandRecipe v0.2.0
ExpandRecipe.dll
Decompiled 10 months agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using ExpandRecipe; using HarmonyLib; using Il2CppScheduleOne.ItemFramework; using Il2CppScheduleOne.Product; using Il2CppScheduleOne.StationFramework; using Il2CppScheduleOne.UI.Phone.ProductManagerApp; using Il2CppScheduleOne.UI.Tooltips; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using MelonLoader; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: MelonInfo(typeof(Main), "ExpandRecipe", "0.2.0", "Robb Manes", "https://github.com/robbmanes/Schedule1_ExpandRecipe")] [assembly: MelonGame("TVGS", "Schedule I")] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("ExpandRecipe")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+a5df1431d5ba73febb258438632c9750b62963c2")] [assembly: AssemblyProduct("ExpandRecipe")] [assembly: AssemblyTitle("ExpandRecipe")] [assembly: NeutralResourcesLanguage("en-US")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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 ExpandRecipe { public class Main : MelonMod { public string testedVersion = "0.3.4f4"; public static ProductManager productManager; public static ProductManagerApp productManagerApp; public override void OnInitializeMelon() { MelonLogger.Msg("Tested on Schedule I version \"" + testedVersion + "\""); } public override void OnSceneWasLoaded(int buildIndex, string sceneName) { if (sceneName == "Main") { try { productManager = Object.FindObjectsOfType<ProductManager>()[0]; } catch (Exception value) { MelonLogger.Error($"Failed to find base Product Manager: {value}"); } } ((MelonMod)this).OnSceneWasLoaded(buildIndex, sceneName); } public static List<List<IngredientQuantity>> GetExpandedRecipes(ProductEntry productEntry) { List<StationRecipe> recipes = productEntry.Definition.Recipes; List<List<IngredientQuantity>> list = new List<List<IngredientQuantity>>(); return GetExpandedRecipesInternal(recipes); } private static List<List<IngredientQuantity>> GetExpandedRecipesInternal(List<StationRecipe> baseRecipes) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Invalid comparison between Unknown and I4 //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Invalid comparison between Unknown and I4 //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Expected O, but got Unknown List<List<IngredientQuantity>> list = new List<List<IngredientQuantity>>(); if (baseRecipes.Count > 0) { Enumerator<StationRecipe> enumerator = baseRecipes.GetEnumerator(); while (enumerator.MoveNext()) { StationRecipe current = enumerator.Current; List<IngredientQuantity> list2 = new List<IngredientQuantity>(); Enumerator<IngredientQuantity> enumerator2 = current.Ingredients.GetEnumerator(); while (enumerator2.MoveNext()) { IngredientQuantity current2 = enumerator2.Current; list2 = (((int)current2.Item.Category != 0) ? list2.Prepend(current2).ToList() : list2.Append(current2).ToList()); } foreach (IngredientQuantity ingredient in list2) { bool flag = false; if ((int)ingredient.Item.Category == 0) { Func<ProductDefinition, bool> func = (ProductDefinition x) => ((ItemDefinition)x).ID == ingredient.Item.ID; ProductDefinition val = productManager.AllProducts.Find(Predicate<ProductDefinition>.op_Implicit(func)) ?? throw new Exception("Could not find base product for \"'" + ingredient.Item.Name + "'\""); if (val.Recipes.Count <= 0) { if (list.Count > 0 && list[list.Count - 1].Count > 0) { list[list.Count - 1] = list[list.Count - 1].Prepend(ingredient).ToList(); } flag = true; break; } for (int i = 0; i < val.Recipes.Count; i++) { StationRecipe val2 = val.Recipes[i]; Enumerator<IngredientQuantity> enumerator4 = val2.Ingredients.GetEnumerator(); while (enumerator4.MoveNext()) { IngredientQuantity current3 = enumerator4.Current; IngredientQuantity val3 = new IngredientQuantity(); if (val2.Ingredients.Contains(ingredient)) { val3 = current3; if (list.Count > 0 && list[list.Count - 1].Count > 0) { list[list.Count - 1] = list[list.Count - 1].Prepend(ingredient).ToList(); } flag = true; break; } } } if (flag) { break; } List<List<IngredientQuantity>> expandedRecipesInternal = GetExpandedRecipesInternal(val.Recipes); foreach (List<IngredientQuantity> item in expandedRecipesInternal) { item.AddRange(list2); item.Remove(ingredient); list.Add(item); } } else { List<IngredientQuantity> list3 = new List<IngredientQuantity>(); list3.Add(ingredient); list.Add(list3); } } } } foreach (List<IngredientQuantity> item2 in list.ToList()) { foreach (List<IngredientQuantity> item3 in list.ToList()) { if (item2 != item3 && !item3.Except(item2).Any() && item3.Except(item2).ToList().Count <= 0) { list.Remove(item3); } } } return list; } public static GameObject CreateExpandedRecipesTextUIGameObject(GameObject gameObjectToClone, GameObject parentGameObject) { Transform val = parentGameObject.transform.Find("ExpandedRecipesText"); GameObject gameObject; if ((Object)(object)val == (Object)null) { gameObject = Object.Instantiate<GameObject>(gameObjectToClone, parentGameObject.transform).gameObject; ((Object)gameObject).name = "ExpandedRecipesText"; gameObject.GetComponent<Text>().text = "Expanded Recipe(s)"; } else { gameObject = ((Component)val).gameObject; } gameObject.gameObject.SetActive(true); return gameObject; } public static GameObject GetOrCreateExpandedRecipesUIGameObject(GameObject parentGameObject) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown Transform val = parentGameObject.transform.Find("ExpandedRecipes"); GameObject gameObject; if ((Object)(object)val == (Object)null) { gameObject = Object.Instantiate<GameObject>(new GameObject(), parentGameObject.transform).gameObject; ((Object)gameObject).name = "ExpandedRecipes"; VerticalLayoutGroup val2 = gameObject.gameObject.AddComponent<VerticalLayoutGroup>(); ((HorizontalOrVerticalLayoutGroup)val2).spacing = 8f; ((HorizontalOrVerticalLayoutGroup)val2).childScaleHeight = false; ((HorizontalOrVerticalLayoutGroup)val2).childScaleWidth = false; ((HorizontalOrVerticalLayoutGroup)val2).childControlHeight = false; ((HorizontalOrVerticalLayoutGroup)val2).childControlWidth = false; ((HorizontalOrVerticalLayoutGroup)val2).childForceExpandHeight = false; ((HorizontalOrVerticalLayoutGroup)val2).childForceExpandWidth = false; } else { gameObject = ((Component)val).gameObject; gameObject.DestroyChildren(); } gameObject.gameObject.SetActive(true); return gameObject; } public static GameObject CreateExpandedRecipeUIGameObject(GameObject gameObjectToClone, GameObject parentGameObject) { GameObject gameObject = Object.Instantiate<GameObject>(gameObjectToClone, parentGameObject.transform).gameObject; gameObject.gameObject.SetActive(true); HorizontalLayoutGroup val = gameObject.gameObject.AddComponent<HorizontalLayoutGroup>(); ((LayoutGroup)val).childAlignment = (TextAnchor)4; ((HorizontalOrVerticalLayoutGroup)val).childScaleHeight = false; ((HorizontalOrVerticalLayoutGroup)val).childScaleWidth = false; ((HorizontalOrVerticalLayoutGroup)val).childControlHeight = false; ((HorizontalOrVerticalLayoutGroup)val).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)val).childForceExpandHeight = false; ((HorizontalOrVerticalLayoutGroup)val).childForceExpandWidth = false; gameObject.DestroyChildren(); return gameObject; } public static GameObject CreateBaseProductUIGameObject(GameObject gameObjectToClone, GameObject parentGameObject, List<IngredientQuantity> expandedRecipe) { IngredientQuantity val = expandedRecipe.First(); GameObject gameObject = Object.Instantiate<GameObject>(gameObjectToClone, parentGameObject.transform).gameObject; gameObject.GetComponent<Image>().sprite = val.Item.Icon; gameObject.GetComponent<Image>().preserveAspect = true; gameObject.GetComponent<Tooltip>().text = ((Object)val.Item).name; return gameObject; } public static GameObject CreatePlusUIGameObject(GameObject gameObjectToClone, GameObject parentGameObject) { GameObject gameObject = Object.Instantiate<GameObject>(gameObjectToClone, parentGameObject.transform).gameObject; gameObject.GetComponent<Image>().preserveAspect = true; return gameObject; } public static GameObject CreateMixUIGameObject(GameObject gameObjectToClone, GameObject parentGameObject, IngredientQuantity ingredient) { GameObject gameObject = Object.Instantiate<GameObject>(gameObjectToClone, parentGameObject.transform).gameObject; gameObject.GetComponent<Image>().sprite = ingredient.Item.Icon; gameObject.GetComponent<Image>().preserveAspect = true; gameObject.GetComponent<Tooltip>().text = ((Object)ingredient.Item).name; return gameObject; } public static GameObject CreateArrowUIGameObject(GameObject gameObjectToClone, GameObject parentGameObject) { return Object.Instantiate<GameObject>(gameObjectToClone, parentGameObject.transform).gameObject; } public static GameObject CreateOutputUIGameObject(GameObject gameObjectToClone, GameObject parentGameObject, ItemQuantity finalProduct) { GameObject gameObject = Object.Instantiate<GameObject>(gameObjectToClone, parentGameObject.transform).gameObject; gameObject.GetComponent<Image>().sprite = finalProduct.Item.Icon; gameObject.GetComponent<Image>().preserveAspect = true; gameObject.GetComponent<Tooltip>().text = ((Object)finalProduct.Item).name; return gameObject; } public static void BuildUIWithRecipe(ProductEntry productEntry, List<List<IngredientQuantity>> expandedRecipes, GameObject recipeTextUI, GameObject recipesContainerUI) { ItemQuantity product = productEntry.Definition.Recipes[0].Product; GameObject val = ((Component)recipesContainerUI.transform.Find("Recipe")).gameObject ?? throw new Exception("Unable to find recipeUI GameObject"); GameObject gameObjectToClone = ((Component)val.transform.Find("Product")).gameObject ?? throw new Exception("Unable to find productUI GameObject"); GameObject gameObjectToClone2 = ((Component)val.transform.Find("Plus")).gameObject ?? throw new Exception("Unable to find plusUI GameObject"); GameObject gameObjectToClone3 = ((Component)val.transform.Find("Mixer")).gameObject ?? throw new Exception("Unable to find mixerUI GameObject"); GameObject gameObjectToClone4 = ((Component)val.transform.Find("Arrow")).gameObject ?? throw new Exception("Unable to find arrowUI GameObject"); GameObject gameObjectToClone5 = ((Component)val.transform.Find("Output")).gameObject ?? throw new Exception("Unable to find outputUI GameObject"); CreateExpandedRecipesTextUIGameObject(recipeTextUI, recipesContainerUI); GameObject orCreateExpandedRecipesUIGameObject = GetOrCreateExpandedRecipesUIGameObject(recipesContainerUI); foreach (List<IngredientQuantity> expandedRecipe in expandedRecipes) { GameObject parentGameObject = CreateExpandedRecipeUIGameObject(val, orCreateExpandedRecipesUIGameObject); CreateBaseProductUIGameObject(gameObjectToClone, parentGameObject, expandedRecipe); expandedRecipe.Remove(expandedRecipe[0]); int num = expandedRecipe.Count; foreach (IngredientQuantity item in expandedRecipe) { if (num >= 0) { CreatePlusUIGameObject(gameObjectToClone2, parentGameObject); } CreateMixUIGameObject(gameObjectToClone3, parentGameObject, item); num--; } CreateArrowUIGameObject(gameObjectToClone4, parentGameObject); CreateOutputUIGameObject(gameObjectToClone5, parentGameObject, product); } } } public static class GameObjectExtensions { public static void DestroyChildren(this GameObject thisGameObject) { int childCount = thisGameObject.transform.GetChildCount(); for (int num = childCount - 1; num >= 0; num--) { Transform child = thisGameObject.transform.GetChild(num); Object.Destroy((Object)(object)((Component)child).gameObject); } } } [HarmonyPatch(typeof(ProductManagerApp), "SelectProduct")] public static class ProductManager_SelectProduct_Patch { public static void Prefix(ProductManagerApp __instance, ProductEntry entry) { Main.productManagerApp = __instance; Transform val; Transform val2; try { ProductAppDetailPanel detailPanel = __instance.DetailPanel; val = ((Component)detailPanel).transform.Find("Scroll View/Viewport/Content/RecipesContainer"); if ((Object)(object)val == (Object)null) { MelonLogger.Error("Can't find RecipesContainer object in current scene"); return; } val2 = ((Component)detailPanel).transform.Find("Scroll View/Viewport/Content/Recipes"); if ((Object)(object)val2 == (Object)null) { MelonLogger.Error("Can't find Recipes object in current scene"); return; } Transform val3 = val.Find("ExpandedRecipes"); if ((Object)(object)val3 != (Object)null) { ((Component)val3).gameObject.SetActive(false); } } catch (Exception value) { MelonLogger.Error($"Failed to find Phone UI components: {value}"); return; } List<List<IngredientQuantity>> expandedRecipes; try { expandedRecipes = Main.GetExpandedRecipes(entry); foreach (List<IngredientQuantity> item in expandedRecipes) { string name = ((ItemDefinition)entry.Definition).Name; string text = ""; foreach (IngredientQuantity item2 in item) { if (text.Length > 0) { text += " + "; } text += ((Object)item2.Item).name; } MelonLogger.Msg("Expanded Recipe for \"" + name + "\": " + text); } } catch (Exception value2) { MelonLogger.Error($"Failed to get expanded recipe list: {value2}"); return; } try { Main.BuildUIWithRecipe(entry, expandedRecipes, ((Component)val2).gameObject, ((Component)val).gameObject); } catch (Exception value3) { MelonLogger.Error($"Exception raised building UI component: {value3}"); } } } }