Some mods target the Mono version of the game, which is available by opting into the Steam beta branch "alternate"
Decompiled source of MoreStations v1.0.0
MoreStations\MoreStations.dll
Decompiled a week agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using HarmonyLib; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppScheduleOne.Management; using Il2CppScheduleOne.UI.Management; using Il2CppSystem.Collections.Generic; using MelonLoader; using MelonLoader.Preferences; using MelonLoader.Utils; using Microsoft.CodeAnalysis; using MoreStations; using MoreStations.Config; using MoreStations.Patches; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(MoreStationsMod), "More Stations", "1.0.0", "brobb", null)] [assembly: MelonGame("TVGS", "Schedule I")] [assembly: AssemblyMetadata("NexusModID", "0")] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("MoreStations")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("MoreStations")] [assembly: AssemblyTitle("MoreStations")] [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 MoreStations { public class MoreStationsMod : MelonMod { public override void OnInitializeMelon() { StationConfig.Initialize(); StationLimits.Initialize(); ((MelonBase)this).LoggerInstance.Msg("More Stations initialized."); } } } namespace MoreStations.Patches { public static class StationLimits { private class StationLimitPatches { [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyPostfix] private static void BotanistConfigurationCtor(BotanistConfiguration __instance) { ApplyMaxItems((__instance != null) ? __instance.Assigns : null, BotanistMax); } [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyPostfix] private static void ChemistConfigurationCtor(ChemistConfiguration __instance) { ApplyMaxItems((__instance != null) ? __instance.Stations : null, ChemistMax); } [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyPostfix] private static void CleanerConfigurationCtor(CleanerConfiguration __instance) { ApplyMaxItems((__instance != null) ? __instance.Bins : null, JanitorMax); } [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyPostfix] private static void PackagerConfigurationCtor(PackagerConfiguration __instance) { ApplyMaxItems((__instance != null) ? __instance.Stations : null, HandlerMax); } [HarmonyPatch(typeof(BotanistConfigPanel), "Bind")] [HarmonyPostfix] private static void BotanistPanelBind(BotanistConfigPanel __instance) { ApplyLimits((__instance != null) ? __instance.PotsUI : null, BotanistMax, 0f, 0f); } [HarmonyPatch(typeof(ChemistConfigPanel), "Bind")] [HarmonyPostfix] private static void ChemistPanelBind(ChemistConfigPanel __instance) { ApplyLimits((__instance != null) ? __instance.StationsUI : null, ChemistMax, 0f, 0f); } [HarmonyPatch(typeof(CleanerConfigPanel), "Bind")] [HarmonyPostfix] private static void CleanerPanelBind(CleanerConfigPanel __instance) { ApplyLimits((__instance != null) ? __instance.BinsUI : null, JanitorMax, 0f, 0f); } [HarmonyPatch(typeof(PackagerConfigPanel), "Bind")] [HarmonyPostfix] private static void PackagerPanelBind(PackagerConfigPanel __instance) { ApplyLimits((__instance != null) ? __instance.StationsUI : null, HandlerMax, HandlerListHeight, HandlerListOffset); } } private static bool _initialized; private static readonly Dictionary<int, float> ScrollOffsets = new Dictionary<int, float>(); private static int JanitorMax => ClampMax(StationConfig.JanitorMaxStations.Value); private static int BotanistMax => ClampMax(StationConfig.BotanistMaxStations.Value); private static int ChemistMax => ClampMax(StationConfig.ChemistMaxStations.Value); private static int HandlerMax => ClampMax(StationConfig.HandlerMaxStations.Value); private static float HandlerListHeight => 100f; private static float HandlerListOffset => 100f; public static void Initialize() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) if (!_initialized) { new Harmony("MoreStations.StationLimits").PatchAll(typeof(StationLimitPatches)); _initialized = true; } } private static int ClampMax(int value) { return Math.Max(1, value); } private static void ApplyMaxItems(ObjectListField field, int maxItems) { if (field != null) { field.MaxItems = maxItems; } } private static void ApplyMaxItems(List<ObjectListField> fields, int maxItems) { if (fields != null) { Enumerator<ObjectListField> enumerator = fields.GetEnumerator(); while (enumerator.MoveNext()) { ApplyMaxItems(enumerator.Current, maxItems); } } } private static Transform GetContents(ObjectListFieldUI listUi) { Transform transform = ((Component)listUi).transform; Transform val = transform.Find("Contents"); if ((Object)(object)val != (Object)null) { return val; } Transform val2 = transform.Find("ScrollArea"); if ((Object)(object)val2 == (Object)null) { return null; } return val2.Find("Contents"); } private static void EnsureScrollable(ObjectListFieldUI listUi, float heightOverride, float verticalOffset) { //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Expected O, but got Unknown //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_0134: 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) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0213: Unknown result type (might be due to invalid IL or missing references) //IL_0228: 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) if ((Object)(object)listUi == (Object)null) { return; } Transform transform = ((Component)listUi).transform; Transform val = transform.Find("ScrollArea"); Transform contents = GetContents(listUi); if ((Object)(object)contents == (Object)null) { return; } RectTransform component = ((Component)contents).GetComponent<RectTransform>(); if ((Object)(object)component == (Object)null) { return; } float viewportHeight = GetViewportHeight(listUi, component, heightOverride); int instanceID = ((Object)listUi).GetInstanceID(); if ((Object)(object)val != (Object)null) { RectTransform component2 = ((Component)val).GetComponent<RectTransform>(); ApplyVerticalOffset(component2, instanceID, verticalOffset); if ((Object)(object)component2 != (Object)null && viewportHeight > 0f) { component2.SetSizeWithCurrentAnchors((Axis)1, viewportHeight); } LayoutElement val2 = ((Component)val).GetComponent<LayoutElement>(); if ((Object)(object)val2 == (Object)null) { val2 = ((Component)val).gameObject.AddComponent<LayoutElement>(); } if (viewportHeight > 0f) { val2.preferredHeight = viewportHeight; val2.minHeight = viewportHeight; } ScrollRect component3 = ((Component)val).GetComponent<ScrollRect>(); if ((Object)(object)component3 != (Object)null) { component3.content = component; component3.viewport = component2; } return; } GameObject val3 = new GameObject("ScrollArea"); val3.AddComponent<RectTransform>(); val3.AddComponent<RectMask2D>(); ScrollRect obj = val3.AddComponent<ScrollRect>(); val3.transform.SetParent(transform, false); RectTransform component4 = val3.GetComponent<RectTransform>(); component4.anchorMin = component.anchorMin; component4.anchorMax = component.anchorMax; component4.pivot = component.pivot; component4.anchoredPosition = component.anchoredPosition; component4.sizeDelta = component.sizeDelta; ((Transform)component4).localScale = ((Transform)component).localScale; ((Transform)component4).localRotation = ((Transform)component).localRotation; ApplyVerticalOffset(component4, instanceID, verticalOffset); if (viewportHeight > 0f) { component4.SetSizeWithCurrentAnchors((Axis)1, viewportHeight); } LayoutElement val4 = val3.GetComponent<LayoutElement>(); if ((Object)(object)val4 == (Object)null) { val4 = val3.AddComponent<LayoutElement>(); } if (viewportHeight > 0f) { val4.preferredHeight = viewportHeight; val4.minHeight = viewportHeight; } val3.transform.SetSiblingIndex(contents.GetSiblingIndex()); contents.SetParent(val3.transform, false); component.anchorMin = new Vector2(0f, 1f); component.anchorMax = new Vector2(1f, 1f); component.pivot = new Vector2(0f, 1f); component.anchoredPosition = Vector2.zero; ContentSizeFitter val5 = ((Component)component).GetComponent<ContentSizeFitter>(); if ((Object)(object)val5 == (Object)null) { val5 = ((Component)component).gameObject.AddComponent<ContentSizeFitter>(); } val5.horizontalFit = (FitMode)0; val5.verticalFit = (FitMode)2; obj.content = component; obj.horizontal = false; obj.vertical = true; obj.scrollSensitivity = 5f; obj.viewport = component4; } private static float GetViewportHeight(ObjectListFieldUI listUi, RectTransform contentsRect, float heightOverride) { //IL_0053: 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_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)contentsRect == (Object)null) { return 0f; } if (heightOverride > 0f) { return heightOverride; } int valueOrDefault = ((listUi == null) ? null : ((Il2CppArrayBase<RectTransform>)(object)listUi.Entries)?.Length).GetValueOrDefault(); Rect rect = contentsRect.rect; float height = ((Rect)(ref rect)).height; float num = 0f; if (valueOrDefault > 0) { RectTransform val = ((Il2CppArrayBase<RectTransform>)(object)listUi.Entries)[0]; if ((Object)(object)val != (Object)null) { rect = val.rect; num = ((Rect)(ref rect)).height; } } float num2 = 0f; float num3 = 0f; VerticalLayoutGroup component = ((Component)contentsRect).GetComponent<VerticalLayoutGroup>(); if ((Object)(object)component != (Object)null) { num2 = ((HorizontalOrVerticalLayoutGroup)component).spacing; num3 = ((LayoutGroup)component).padding.top + ((LayoutGroup)component).padding.bottom; } float num4 = 0f; if (num > 0f && valueOrDefault > 0) { num4 = num * (float)valueOrDefault + num2 * (float)Math.Max(0, valueOrDefault - 1) + num3; } Transform val2 = ((Component)contentsRect).transform.Find("AddNew"); if ((Object)(object)val2 != (Object)null) { RectTransform component2 = ((Component)val2).GetComponent<RectTransform>(); if ((Object)(object)component2 != (Object)null) { float num5 = num4; rect = component2.rect; num4 = num5 + (((Rect)(ref rect)).height + ((valueOrDefault > 0) ? num2 : 0f)); } } if (num4 > 0f && height > 0f) { return Math.Min(height, num4); } if (num4 > 0f) { return num4; } return height; } private static void ApplyVerticalOffset(RectTransform scrollRect, int key, float verticalOffset) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0036: 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) if (!((Object)(object)scrollRect == (Object)null)) { float num = ClampOffset(verticalOffset); float value = 0f; ScrollOffsets.TryGetValue(key, out value); float num2 = num - value; scrollRect.anchoredPosition = new Vector2(scrollRect.anchoredPosition.x, scrollRect.anchoredPosition.y + num2); ScrollOffsets[key] = num; } } private static float ClampOffset(float value) { return Mathf.Clamp(value, -500f, 500f); } private static void EnsureEntryObjects(ObjectListFieldUI listUi, int maxItems) { if ((Object)(object)listUi == (Object)null) { return; } Transform contents = GetContents(listUi); if ((Object)(object)contents == (Object)null) { return; } Transform val = contents.Find("Entry"); if ((Object)(object)val == (Object)null) { return; } GameObject gameObject = ((Component)val).gameObject; for (int i = 1; i < maxItems; i++) { string text = $"Entry ({i})"; if (!((Object)(object)contents.Find(text) != (Object)null)) { ((Object)Object.Instantiate<GameObject>(gameObject, contents, false)).name = text; } } Transform val2 = contents.Find("AddNew"); if ((Object)(object)val2 != (Object)null) { val2.SetAsLastSibling(); } } private static void RefreshEntryArray(ObjectListFieldUI listUi, int maxItems) { Transform contents = GetContents(listUi); if ((Object)(object)contents == (Object)null) { return; } RectTransform[] array = (RectTransform[])(object)new RectTransform[maxItems]; for (int i = 0; i < maxItems; i++) { string text = ((i == 0) ? "Entry" : $"Entry ({i})"); Transform val = contents.Find(text); if (!((Object)(object)val == (Object)null)) { array[i] = ((Component)val).GetComponent<RectTransform>(); } } Il2CppReferenceArray<RectTransform> val2 = new Il2CppReferenceArray<RectTransform>((long)array.Length); for (int j = 0; j < array.Length; j++) { ((Il2CppArrayBase<RectTransform>)(object)val2)[j] = array[j]; } listUi.Entries = val2; } private static void ApplyLimits(ObjectListFieldUI listUi, int maxItems, float heightOverride, float verticalOffset) { if (!((Object)(object)listUi == (Object)null)) { if (maxItems < 1) { maxItems = 1; } EnsureScrollable(listUi, heightOverride, verticalOffset); EnsureEntryObjects(listUi, maxItems); RefreshEntryArray(listUi, maxItems); ApplyMaxItems(listUi.Fields, maxItems); RefreshListUi(listUi); } } private static void RefreshListUi(ObjectListFieldUI listUi) { if (!((Object)(object)listUi == (Object)null)) { List<ObjectListField> fields = listUi.Fields; if (fields != null) { listUi.Bind(fields); } } } } } namespace MoreStations.Config { public static class StationConfig { private static MelonPreferences_Category _category; private static bool _initialized; public static MelonPreferences_Entry<int> JanitorMaxStations; public static MelonPreferences_Entry<int> BotanistMaxStations; public static MelonPreferences_Entry<int> ChemistMaxStations; public static MelonPreferences_Entry<int> HandlerMaxStations; private static string ConfigPath => Path.Combine(MelonEnvironment.UserDataDirectory, "MoreStations.cfg"); public static void Initialize() { if (_initialized) { return; } _category = MelonPreferences.CreateCategory("MoreStations", "More Stations", false, true); _category.SetFilePath(ConfigPath, true, false); JanitorMaxStations = _category.CreateEntry<int>("Janitor Station Max", 60, "Janitor Station Max", "Maximum station assignments for janitors.", false, false, (ValueValidator)null, (string)null); BotanistMaxStations = _category.CreateEntry<int>("Botanist Station Max", 60, "Botanist Station Max", "Maximum station assignments for botanists.", false, false, (ValueValidator)null, (string)null); ChemistMaxStations = _category.CreateEntry<int>("Chemist Station Max", 60, "Chemist Station Max", "Maximum station assignments for chemists.", false, false, (ValueValidator)null, (string)null); HandlerMaxStations = _category.CreateEntry<int>("Handler Station Max", 60, "Handler Station Max", "Maximum station assignments for handlers.", false, false, (ValueValidator)null, (string)null); if (!File.Exists(ConfigPath)) { foreach (MelonPreferences_Entry entry in _category.Entries) { entry.ResetToDefault(); } _category.SaveToFile(false); } _initialized = true; } } }