Some mods target the Mono version of the game, which is available by opting into the Steam beta branch "alternate"
Decompiled source of ClockApp v1.0.5
SOClockApp.dll
Decompiled 10 months agousing System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using HarmonyLib; using MelonLoader; using SOClockApp; using ScheduleOne.DevUtilities; using ScheduleOne.EntityFramework; using ScheduleOne.Misc; using ScheduleOne.ObjectScripts; using ScheduleOne.UI; using ScheduleOne.UI.Phone; using TMPro; 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: ComVisible(false)] [assembly: MelonInfo(typeof(Plugin), "ClockApp", "9.28.2005.0", "Coolpaca", null)] [assembly: MelonGame("TVGS", "Schedule I")] [assembly: Guid("fa9babe3-6941-45f0-8a61-7a373b4bfefc")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: AssemblyCompany("SOClockApp")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+e808381af3d36c94ec4b08faa608ec7cc37297fa")] [assembly: AssemblyProduct("SOClockApp")] [assembly: AssemblyTitle("SOClockApp")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] namespace SOClockApp { public class ClockApp : App<ClockApp> { public List<TimerEntry> entries = new List<TimerEntry>(); public GUIStyle style = new GUIStyle(); public GameObject Content; public static GameObject entryPrefab = Plugin.clockBundle.LoadAsset<GameObject>("Assets/ClockEntry.prefab"); public override void Awake() { //IL_003a: Unknown result type (might be due to invalid IL or missing references) base.AppName = "Clock"; base.IconLabel = "Clock"; base.AppIcon = Plugin.LoadImage("icon.png"); ((Object)base.AppIcon).name = "Clock"; base.Orientation = (EOrientation<ClockApp>)1; base.AvailableInTutorial = true; Content = ((Component)((Component)((Component)this).transform).GetComponentInChildren<HorizontalLayoutGroup>()).gameObject; ((PlayerSingleton<ClockApp>)(object)this).Awake(); } public override void Start() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) base.Start(); style.fontSize = 20; style.normal.textColor = Color.white; } public override void Update() { base.Update(); for (int i = 0; i < entries.Count; i++) { entries[i].RemoveIfNull(); } PopulateEntriesAndUpdate(); } public void PopulateEntriesAndUpdate() { foreach (TimerEntry entry in entries) { if ((Object)(object)entry.entryObject == (Object)null) { entry.entryObject = Object.Instantiate<GameObject>(Plugin.clockBundle.LoadAsset<GameObject>("Assets/ClockEntry.prefab"), Content.transform); entry.entryText = entry.entryObject.GetComponentInChildren<Text>(); } if (!((Object)(object)entry.entryText != (Object)null)) { continue; } if ((Object)(object)entry.Alarm != (Object)null) { entry.entryText.text = entry.Name + "\n" + entry.Station + "\n" + ((TMP_Text)entry.Alarm.ScreenText).text; } if ((Object)(object)entry.Oven != (Object)null) { int num = 0; int num2 = 0; if (entry.Oven.CurrentOperation != null) { num = entry.Oven.CurrentOperation.GetCookDuration() - entry.Oven.CurrentOperation.CookProgress; num = Mathf.Max(0, num); num2 = num / 60; num %= 60; } entry.entryText.text = $"{entry.Name}\n{entry.Station}\n{num2:D2}:{num:D2}"; } if ((Object)(object)entry.Mk2station != (Object)null) { if (((MixingStation)entry.Mk2station).CurrentMixOperation != null) { entry.entryText.text = $"{entry.Name}\n{entry.Station}\n00:{((MixingStation)entry.Mk2station).GetMixTimeForCurrentOperation() - ((MixingStation)entry.Mk2station).CurrentMixTime}"; } entry.entryText.text = entry.Name + "\n" + entry.Station + "\n00:00"; } } } } public class Plugin : MelonMod { public static Harmony harmony; public static bool appUpdate; public static AssetBundle clockBundle; public static Object test; public static GameObject mainCanvasPrefab; public override void OnInitializeMelon() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown ((MelonBase)this).OnInitializeMelon(); MelonLogger.Msg("Clock app loaded!"); harmony = new Harmony("com.coolpaca.clockapp"); string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); clockBundle = AssetBundle.LoadFromFile(Path.Combine(directoryName, "clockbundle")); harmony.PatchAll(); } public override void OnSceneWasLoaded(int buildIndex, string sceneName) { ((MelonMod)this).OnSceneWasLoaded(buildIndex, sceneName); if (sceneName == "Main") { MelonCoroutines.Start(WaitForPhone()); } } public static IEnumerator WaitForPhone() { while ((Object)(object)PlayerSingleton<AppsCanvas>.Instance == (Object)null) { yield return null; } AppsCanvas appcanvas = PlayerSingleton<AppsCanvas>.Instance; mainCanvasPrefab = Object.Instantiate<GameObject>(clockBundle.LoadAsset<GameObject>("Assets/Clock.prefab"), ((Component)appcanvas.canvas).transform); ClockApp app = mainCanvasPrefab.AddComponent<ClockApp>(); ((App<ClockApp>)app).appContainer = ((Component)mainCanvasPrefab.transform.GetChild(0)).GetComponent<RectTransform>(); } public static Sprite LoadImage(string Name) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown //IL_006f: 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_007e: Unknown result type (might be due to invalid IL or missing references) string path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\" + Name; byte[] array = File.ReadAllBytes(path); Texture2D val = new Texture2D(2, 2); if (!ImageConversion.LoadImage(val, array)) { MelonLogger.Error("Failed to load image. Make sure its named \"" + Name + "\" exactly."); return null; } return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), Vector2.one / 2f); } } public class TimerEntry { public string Name; public string Station; public int GUID; public GameObject StationObject; public GameObject entryObject; public Text entryText; public DigitalAlarm Alarm; public LabOven Oven; public MixingStationMk2 Mk2station; public TimerEntry(string name, string station, int guid, GameObject stationObject, DigitalAlarm alarm) { Name = name; Station = station; GUID = guid; StationObject = stationObject; Alarm = alarm; } public TimerEntry(string name, string station, int guid, GameObject stationObject, LabOven oven) { Name = name; Station = station; GUID = guid; StationObject = stationObject; Oven = oven; } public TimerEntry(string name, string station, int guid, GameObject stationObject, MixingStationMk2 mixing) { Name = name; Station = station; GUID = guid; StationObject = stationObject; Mk2station = mixing; } public void RemoveIfNull() { if ((Object)(object)Mk2station == (Object)null && (Object)(object)Alarm == (Object)null && (Object)(object)Oven == (Object)null) { PlayerSingleton<ClockApp>.Instance.entries.Remove(this); } } } } namespace SOClockApp.Patches { [HarmonyPatch(typeof(DigitalAlarm), "Start")] internal static class GridItemPatch { private static bool Add = true; public static void Postfix(DigitalAlarm __instance) { GridItem componentInParent = ((Component)__instance).GetComponentInParent<GridItem>(); if ((Object)(object)componentInParent != (Object)null && ((BuildableItem)componentInParent).ItemInstance != null && (Object)(object)((BuildableItem)componentInParent).ItemInstance.Definition != (Object)null) { TimerEntry timerEntry = new TimerEntry(((BuildableItem)componentInParent).GetProperty(((Component)componentInParent.OwnerGrid).transform).propertyName, ((BuildableItem)componentInParent).ItemInstance.Definition.ID, ((Object)componentInParent).GetInstanceID(), ((Component)componentInParent).gameObject, __instance); foreach (TimerEntry entry in PlayerSingleton<ClockApp>.Instance.entries) { if (timerEntry.GUID == entry.GUID) { Add = false; } } if (Add) { PlayerSingleton<ClockApp>.Instance.entries.Add(timerEntry); } } Add = true; } } [HarmonyPatch(typeof(LabOven), "InitializeGridItem")] internal class LabOvenPatch { private static bool Add = true; public static void Postfix(LabOven __instance) { TimerEntry timerEntry = new TimerEntry(((BuildableItem)__instance).GetProperty(((Component)((GridItem)__instance).OwnerGrid).transform).propertyName, "Lab Oven", ((Object)((Component)__instance).gameObject).GetInstanceID(), ((Component)__instance).gameObject, __instance); foreach (TimerEntry entry in PlayerSingleton<ClockApp>.Instance.entries) { if (timerEntry.GUID == entry.GUID) { Add = false; } } if (Add) { PlayerSingleton<ClockApp>.Instance.entries.Add(timerEntry); } Add = true; } } [HarmonyPatch(typeof(MixingStation), "InitializeGridItem")] internal class MixingStationMk2Patch { private static bool Add = true; public static void Postfix(MixingStationMk2 __instance) { MixingStationMk2 val = default(MixingStationMk2); if (!((Component)__instance).TryGetComponent<MixingStationMk2>(ref val)) { return; } TimerEntry timerEntry = new TimerEntry(((BuildableItem)val).GetProperty(((Component)((GridItem)val).OwnerGrid).transform).propertyName, "Mixing Station Mk2", ((Object)((Component)val).gameObject).GetInstanceID(), ((Component)val).gameObject, val); foreach (TimerEntry entry in PlayerSingleton<ClockApp>.Instance.entries) { if (timerEntry.GUID == entry.GUID) { Add = false; } } if (Add) { PlayerSingleton<ClockApp>.Instance.entries.Add(timerEntry); } Add = true; } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }