Some mods target the Mono version of the game, which is available by opting into the Steam beta branch "alternate"
Decompiled source of MVP Tracker v2.0.0
MVP_Tracker.dll
Decompiled 2 weeks agousing System; using System.Collections; 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 MVP_Tracker; using MelonLoader; using Microsoft.CodeAnalysis; using ScheduleOne.DevUtilities; using ScheduleOne.Economy; using ScheduleOne.ItemFramework; using ScheduleOne.Map; using ScheduleOne.NPCs; using ScheduleOne.Persistence; using ScheduleOne.Persistence.Datas; using ScheduleOne.PlayerScripts; using ScheduleOne.Product; using ScheduleOne.Quests; using ScheduleOne.UI.Compass; using UnityEngine; using UnityEngine.Events; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(Core), "MVP Tracker", "2.0.0", "Stehlel", null)] [assembly: MelonGame("TVGS", "Schedule I")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("MVP_Tracker")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+d99c5979ca98e3809bb6c05bd855a2ca8d0fc37a")] [assembly: AssemblyProduct("MVP_Tracker")] [assembly: AssemblyTitle("MVP_Tracker")] [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 MVP_Tracker { public class Core : MelonMod { [CompilerGenerated] private sealed class <CooldownTicker>d__12 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public Core <>4__this; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <CooldownTicker>d__12(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown int num = <>1__state; Core core = <>4__this; if (num != 0) { if (num != 1) { return false; } <>1__state = -1; core.Update(); } else { <>1__state = -1; } <>2__current = (object)new WaitForSecondsRealtime(1f); <>1__state = 1; return true; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <InitializeNextFrame>d__11 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public Core <>4__this; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <InitializeNextFrame>d__11(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { int num = <>1__state; Core core = <>4__this; switch (num) { default: return false; case 0: <>1__state = -1; <>2__current = null; <>1__state = 1; return true; case 1: <>1__state = -1; core.quest = QuestManaging.CreateQuest("MVP Tracker"); core.quest.ClearEntries(); try { core.quest.InitializeQuest("MVP Tracker", "Helps you keep track of the richest customers", Array.Empty<QuestEntryData>(), QuestManaging.guid); SaveManager instance = Singleton<SaveManager>.Instance; instance.Saveables.Remove((ISaveable)(object)core.quest); Quest quest = core.quest; IBaseSaveable val = (IBaseSaveable)(object)((quest is IBaseSaveable) ? quest : null); if (val != null) { instance.BaseSaveables.Remove(val); } } catch (Exception ex) { MelonLogger.Error((object)ex); return false; } core.quest.Begin(true); Customer.onCustomerUnlocked = (Action<Customer>)Delegate.Combine(Customer.onCustomerUnlocked, new Action<Customer>(core.HandleCustomerUnlocked)); foreach (Customer unlockedCustomer in Customer.UnlockedCustomers) { core.HandleCustomerUnlocked(unlockedCustomer); } MelonCoroutines.Start(core.CooldownTicker()); return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static readonly HashSet<string> RelevantFirstNames = new HashSet<string> { "Fiona", "Herbert", "Jen", "Lily", "Michael", "Pearl", "Ray", "Tobias", "Walter", "Jessi", "Carl", "Dean", "George", "Geraldine", "Alison", "Dennis", "Hank", "Harold", "Jack", "Jackie", "Jeremy", "Karen", "Chris" }; private List<QuestEntry> questEntries = new List<QuestEntry>(); private HashSet<string> entries = new HashSet<string>(); private HashSet<string> onCd = new HashSet<string>(); private readonly Dictionary<string, NPCPoI> pois = new Dictionary<string, NPCPoI>(); private readonly Dictionary<string, Element> compassEls = new Dictionary<string, Element>(); public Quest quest; private const int CooldownSeconds = 360; public override void OnInitializeMelon() { MelonLogger.Msg("MVP Tracker loaded"); SceneManager.sceneLoaded += OnSceneLoaded; } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown SaveManager instance = Singleton<SaveManager>.Instance; instance.onSaveStart.AddListener(new UnityAction(OnBeforeSave)); instance.onSaveComplete.AddListener(new UnityAction(OnAfterSave)); Customer.onCustomerUnlocked = (Action<Customer>)Delegate.Remove(Customer.onCustomerUnlocked, new Action<Customer>(HandleCustomerUnlocked)); if (!((Scene)(ref scene)).name.Equals("Main", StringComparison.OrdinalIgnoreCase)) { MelonCoroutines.Stop((object)CooldownTicker()); quest.ClearEntries(); if ((Object)(object)quest != (Object)null) { Object.Destroy((Object)(object)((Component)quest).gameObject); } quest = null; Customer.onCustomerUnlocked = (Action<Customer>)Delegate.Remove(Customer.onCustomerUnlocked, new Action<Customer>(HandleCustomerUnlocked)); return; } entries.Clear(); onCd.Clear(); pois.Clear(); compassEls.Clear(); questEntries.Clear(); QuestManaging.guid = null; if ((Object)(object)Player.Local != (Object)null) { MelonCoroutines.Start(InitializeNextFrame()); return; } Player.onLocalPlayerSpawned = (Action)Delegate.Combine(Player.onLocalPlayerSpawned, (Action)delegate { MelonCoroutines.Start(InitializeNextFrame()); }); } private void OnBeforeSave() { Singleton<SaveManager>.Instance.Saveables.Remove((ISaveable)(object)quest); if (!((Object)(object)quest == (Object)null)) { Quest.Quests.Remove(quest); Debug.Log((object)"[MVP Tracker] Dynamic quest suppressed from save"); } } private void OnAfterSave() { if (!((Object)(object)quest == (Object)null)) { if (!Quest.Quests.Contains(quest)) { Quest.Quests.Add(quest); } Debug.Log((object)"[MVP Tracker] Dynamic quest restored after save"); } } [IteratorStateMachine(typeof(<InitializeNextFrame>d__11))] private IEnumerator InitializeNextFrame() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <InitializeNextFrame>d__11(0) { <>4__this = this }; } [IteratorStateMachine(typeof(<CooldownTicker>d__12))] private IEnumerator CooldownTicker() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <CooldownTicker>d__12(0) { <>4__this = this }; } private void HandleCustomerUnlocked(Customer cust) { if (!((Object)(object)((cust != null) ? cust.NPC : null) == (Object)null) && RelevantFirstNames.Contains(cust.NPC.FirstName)) { Create(cust); } } internal void Create(Customer cust) { string iD = cust.NPC.ID; if (!entries.Contains(iD)) { createPOICompass(cust); entries.Add(iD); QuestEntry item = quest.AddEntry(cust.NPC.FirstName); questEntries.Add(item); } } internal void Update() { //IL_0222: Unknown result type (might be due to invalid IL or missing references) foreach (string entry in entries) { string id = entry.ToString(); Customer val = Customer.UnlockedCustomers.Find((Customer c) => c.NPC.ID == id); if ((Object)(object)val == (Object)null) { continue; } bool flag = (Object)(object)val.AssignedDealer != (Object)null; float num = val.TimeSinceInstantDealOffered; bool flag2 = num < 360f; bool flag3 = onCd.Contains(id); if (!pois.ContainsKey(id)) { continue; } NPCPoI val2 = pois[id]; Element val3 = compassEls[id]; foreach (QuestEntry questEntry in questEntries) { if (!questEntry.Title.Contains(val.NPC.FirstName)) { continue; } if (!flag) { if (flag2) { int num2 = 360 - (int)num; questEntry.SetActive(true); questEntry.SetEntryTitle($"<color=orange>{val.NPC.FirstName}: {num2 / 60}h {num2 % 60}m</color>"); ((Component)val2).gameObject.SetActive(false); val3.Visible = false; if (!flag3) { onCd.Add(id); } continue; } int num3 = calculateChance(val); (ProductDefinition, float, float, int) optimalProductAndPrice = GetOptimalProductAndPrice(val); float num4 = calculateDailyBudget(val); questEntry.SetActive(true); questEntry.SetEntryTitle($"<color=green>{val.NPC.FirstName} ({num3}%): Budget: {num4}$, {optimalProductAndPrice.Item4}x{((ItemDefinition)optimalProductAndPrice.Item1).Name} ({optimalProductAndPrice.Item2}$ ,{optimalProductAndPrice.Item3}%)</color>"); ((Component)val2).transform.position = ((Component)val.NPC).transform.position; ((POI)val2).SetMainText(val.NPC.FirstName); ((Component)val2).gameObject.SetActive(true); val3.Visible = true; if (flag3) { onCd.Remove(id); } } else { questEntry.SetActive(true); questEntry.SetEntryTitle("<color=red>" + val.NPC.FirstName + " Warning: Dealer assigned! " + ((Object)val.AssignedDealer).name + "</color> "); ((Component)val2).gameObject.SetActive(false); val3.Visible = false; } } } } public (ProductDefinition product, float optimalPrice, float acceptanceProbability, int optimalQuantity) GetOptimalProductAndPrice(Customer cust, int priceStep = 5) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) int count = cust.CustomerData.GetOrderDays(cust.CurrentAddiction, cust.NPC.RelationData.RelationDelta / 5f).Count; count = Mathf.Max(count, 1); float num = cust.CustomerData.GetAdjustedWeeklySpend(cust.NPC.RelationData.RelationDelta / 5f) / (float)count; EQuality correspondingQuality = StandardsMethod.GetCorrespondingQuality(cust.CustomerData.Standards); ProductDefinition val = null; float num2 = 0f; float num3 = 0f; float num4 = 0f; List<ProductDefinition> allProducts = NetworkSingleton<ProductManager>.Instance.AllProducts; if (allProducts == null || allProducts.Count == 0) { return (null, 0f, 0f, 0); } foreach (ProductDefinition item2 in allProducts) { ItemInstance defaultInstance = ((ItemDefinition)item2).GetDefaultInstance(1); ProductItemInstance val2 = (ProductItemInstance)(object)((defaultInstance is ProductItemInstance) ? defaultInstance : null); if (val2 != null) { ((QualityItemInstance)val2).Quality = correspondingQuality; if (item2.ValidPackaging != null && item2.ValidPackaging.Length != 0) { val2.SetPackaging(item2.ValidPackaging[1]); } } List<ItemInstance> list = new List<ItemInstance> { defaultInstance }; float num5 = Mathf.Max(item2.Price, (float)priceStep); float num6 = Mathf.Floor(num * 3f / (float)priceStep) * (float)priceStep; num6 = Mathf.Max(num5, num6); for (float num7 = num5; num7 <= num6; num7 += (float)priceStep) { float offerSuccessChance = cust.GetOfferSuccessChance(list, num7); float num8 = num7 * offerSuccessChance; if (num8 > num3) { num3 = num8; num2 = num7; val = item2; num4 = offerSuccessChance; } } } int item = 1; if ((Object)(object)val != (Object)null && num2 > 0f) { float productEnjoyment = cust.GetProductEnjoyment(val, correspondingQuality); float num9 = Mathf.Lerp(0.66f, 1.5f, productEnjoyment); int num10 = Mathf.RoundToInt(num * num9 / val.Price); num10 = Mathf.Clamp(num10, 1, 1000); if (num10 >= 14) { num10 = Mathf.RoundToInt((float)num10 / 5f) * 5; } item = num10; _ = num2 / (float)num10; } num4 = Mathf.RoundToInt(num4 * 100f); num2 = Mathf.RoundToInt(num2); return (val, num2, num4, item); } internal int calculateChance(Customer cust) { double num = (double)Mathf.Clamp01((float)cust.TimeSinceLastDealCompleted / 1440f) * 0.5; float num2 = cust.NPC.RelationData.NormalizedRelationDelta * 0.3f; float num3 = cust.CurrentAddiction * 0.2f; return Mathf.RoundToInt(((float)(num + (double)num2) + num3) * 100f); } internal float calculateDailyBudget(Customer cust) { float adjustedWeeklySpend = cust.CustomerData.GetAdjustedWeeklySpend(cust.NPC.RelationData.RelationDelta / 5f); int count = cust.CustomerData.GetOrderDays(cust.CurrentAddiction, cust.NPC.RelationData.RelationDelta / 5f).Count; return MathF.Round(adjustedWeeklySpend / (float)count); } internal ProductDefinition bestProduct(Customer cust) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) EQuality q = StandardsMethod.GetCorrespondingQuality(cust.CustomerData.Standards); return (from def in cust.OrderableProducts select new { def = def, Appeal = cust.GetProductEnjoyment(def, q) + Mathf.Lerp(1f, -1f, def.Price / def.MarketValue / 2f) } into x orderby x.Appeal descending select x).First().def; } internal void createPOICompass(Customer cust) { NPCPoI val = Object.Instantiate<NPCPoI>(NetworkSingleton<NPCManager>.Instance.PotentialCustomerPoIPrefab, ((Component)cust.NPC).transform); val.SetNPC(cust.NPC); ((POI)val).SetMainText(cust.NPC.FirstName); pois[cust.NPC.ID] = val; Element value = Singleton<CompassManager>.Instance.AddElement(((Component)val).transform, ((Component)((POI)val).UI).GetComponent<RectTransform>(), true); compassEls[cust.NPC.ID] = value; } } public static class QuestManaging { public static string guid; public static Quest CreateQuest(string title) { if (string.IsNullOrEmpty(guid)) { guid = Guid.NewGuid().ToString(); } return Object.Instantiate<GameObject>(((Component)NetworkSingleton<QuestManager>.Instance.DefaultQuests[0]).gameObject, NetworkSingleton<QuestManager>.Instance.QuestContainer).GetComponent<Quest>(); } public static QuestEntry AddEntry(this Quest quest, string entryTitle) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) QuestEntryData data = new QuestEntryData(entryTitle, (EQuestState)0); GameObject val = new GameObject(entryTitle); val.transform.SetParent(((Component)quest).transform, false); QuestEntry val2 = val.AddComponent<QuestEntry>(); quest.Entries.Add(val2); val2.SetData(data); return val2; } public static void ClearEntries(this Quest quest) { QuestEntry[] array = quest.Entries.ToArray(); for (int i = 0; i < array.Length; i++) { Object.Destroy((Object)(object)((Component)array[i]).gameObject); } quest.Entries.Clear(); } } }