Some mods target the Mono version of the game, which is available by opting into the Steam beta branch "alternate-beta"
Decompiled source of ClockApp v1.0.1
SOClockApp.dll
Decompiled an hour 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")] [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("CLOCKFACE.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")); MelonCoroutines.Start(WaitForPhone()); harmony.PatchAll(); } 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) { } } }