Some mods target the Mono version of the game, which is available by opting into the Steam beta branch "alternate"
Decompiled source of EmployeeManager v1.6.1
EmployeeManager.dll
Decompiled 10 months agousing System; using System.Collections; 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 Il2CppFishNet.Object; using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppScheduleOne; using Il2CppScheduleOne.DevUtilities; using Il2CppScheduleOne.Employees; using Il2CppScheduleOne.GameTime; using Il2CppScheduleOne.Messaging; using Il2CppScheduleOne.Money; using Il2CppScheduleOne.NPCs; using Il2CppScheduleOne.ObjectScripts; using Il2CppScheduleOne.Property; using Il2CppScheduleOne.Storage; using Il2CppScheduleOne.UI; using Il2CppScheduleOne.UI.Phone.ContactsApp; using Il2CppScheduleOne.UI.Phone.Messages; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using MelonLoader; using MelonLoader.Utils; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using SharedModUtils; using TestBot; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(Mod), "Employee Manager", "1.6.1", "Akermi", null)] [assembly: MelonGame("TVGS", "Schedule I")] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("EmployeeManager")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+afdf815b69ddb766048b12ca1750fe5aa7473dc4")] [assembly: AssemblyProduct("EmployeeManager")] [assembly: AssemblyTitle("EmployeeManager")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } public static class BotManagerStaticUtils { public static Transform[] GetIdleTransformsForProperty(string propertyName, int count) { //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Expected O, but got Unknown //IL_00aa: Unknown result type (might be due to invalid IL or missing references) Transform[] array = (Transform[])(object)new Transform[count]; if (propertyName.Contains("Barn")) { float num = 176.6752f; float num2 = -18f; float num3 = 0.5f; float num4 = 1.2f; int num5 = 5; for (int i = 0; i < count; i++) { int num6 = i / num5; int num7 = i % num5; float num8 = num + (float)num7 * num4; float num9 = num2 + (float)num6 * num4; GameObject val = new GameObject($"IdlePoint_{i}_{propertyName}"); val.transform.position = new Vector3(num8, num3, num9); array[i] = val.transform; } } return array; } } namespace SharedModUtils { public static class WageUtils { public static bool TryPayAllUnpaidEmployees(EmployeeManager employeeManager, out float totalCost, out string error) { totalCost = 0f; error = null; if ((Object)(object)employeeManager == (Object)null) { error = "Employee manager is not available."; return false; } for (int i = 0; i < employeeManager.AllEmployees._size; i++) { Employee val = employeeManager.AllEmployees[i]; if (!val.PaidForToday) { totalCost += val.DailyWage; } } float cashBalance = NetworkSingleton<MoneyManager>.instance.cashBalance; if (totalCost > cashBalance) { error = $"Wages (${totalCost}) exceed bank balance (${cashBalance})."; return false; } for (int j = 0; j < employeeManager.AllEmployees._size; j++) { employeeManager.AllEmployees[j].PaidForToday = true; } NetworkSingleton<MoneyManager>.Instance.ChangeCashBalance(0f - totalCost, true, false); return true; } public static float GetTotalUnpaidWages(EmployeeManager employeeMgr) { if ((Object)(object)employeeMgr == (Object)null || employeeMgr.AllEmployees == null) { return 0f; } float num = 0f; for (int i = 0; i < employeeMgr.AllEmployees._size; i++) { Employee val = employeeMgr.AllEmployees[i]; if ((Object)(object)val != (Object)null && !val.PaidForToday) { num += val.DailyWage; } } return num; } public static float GetTotalUnpaidWagesForProperty(Property prop) { float num = 0f; Enumerator<Employee> enumerator = prop.Employees.GetEnumerator(); while (enumerator.MoveNext()) { Employee current = enumerator.Current; if (!current.PaidForToday) { num += current.DailyWage; } } return num; } public static bool TryPayUnpaidEmployeesForProperty(Property prop, out float cost, out string error) { cost = GetTotalUnpaidWagesForProperty(prop); error = ""; if (cost <= 0f) { return true; } if (NetworkSingleton<MoneyManager>.Instance.cashBalance < cost) { error = "Not enough cash."; return false; } NetworkSingleton<MoneyManager>.Instance.ChangeCashBalance(0f - cost, true, false); Enumerator<Employee> enumerator = prop.Employees.GetEnumerator(); while (enumerator.MoveNext()) { Employee current = enumerator.Current; if (!current.PaidForToday) { current.SetIsPaid(); } } return true; } public static bool TryPayAllUnpaidEmployees(List<Employee> employees, out float totalCost, out string error) { totalCost = 0f; error = null; Enumerator<Employee> enumerator = employees.GetEnumerator(); while (enumerator.MoveNext()) { Employee current = enumerator.Current; if (!current.PaidForToday) { totalCost += current.DailyWage; } } float cashBalance = NetworkSingleton<MoneyManager>.instance.cashBalance; if (totalCost > cashBalance) { error = $"Insufficient funds: need ${totalCost}, have ${(int)cashBalance}"; return false; } enumerator = employees.GetEnumerator(); while (enumerator.MoveNext()) { enumerator.Current.PaidForToday = true; } NetworkSingleton<MoneyManager>.Instance.ChangeCashBalance(0f - totalCost, true, false); return true; } } } namespace Testbot { public class Property { public static IntPtr NativeFieldInfoPtr_EmployeeCapacity; static Property() { NativeFieldInfoPtr_EmployeeCapacity = IL2CPP.GetIl2CppField(Il2CppClassPointerStore<Property>.NativeClassPtr, "EmployeeCapacity"); } } } namespace TestBot { public class BotManager { [CompilerGenerated] private sealed class <WaitForSystems>d__5 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <WaitForSystems>d__5(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(3f); <>1__state = 1; return true; case 1: <>1__state = -1; 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 TestDialogue _dialogue; private GameObject _npcObject; private NPC _npc; private Mod _mod; private const int EmployeesPerPage = 6; public TestDialogue Dialogue => _dialogue; [IteratorStateMachine(typeof(<WaitForSystems>d__5))] public static IEnumerator WaitForSystems() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <WaitForSystems>d__5(0); } public static BotManager Create(Mod mod) { BotManager botManager = new BotManager(); botManager.Init(); botManager._mod = mod; return botManager; } private void Init() { NPC val = null; List<NPC> nPCRegistry = NPCManager.NPCRegistry; for (int i = 0; i < nPCRegistry.Count; i++) { NPC val2 = nPCRegistry[i]; if ((Object)(object)val2 != (Object)null && val2.FirstName == "Beth") { val = val2; break; } } if (!((Object)(object)val == (Object)null)) { _npcObject = Object.Instantiate<GameObject>(((Component)val).gameObject); _npc = _npcObject.GetComponent<NPC>(); _npc.FirstName = "Employee"; _npc.LastName = "Manager"; _npc.BakedGUID = Guid.NewGuid().ToString(); _npc.IsImportant = true; _npc.ConversationCanBeHidden = false; _npc.ShowRelationshipInfo = false; _npc.MSGConversation = null; _npc.ConversationCategories = new List<EConversationCategory>(); _npc.dialogueHandler = null; _npc.MugshotSprite = ((App<ContactsApp>)(object)PlayerSingleton<ContactsApp>.Instance).AppIcon; _npcObject.SetActive(true); ((Component)_npc).gameObject.SetActive(true); _npc.Awake(); ((NetworkBehaviour)_npc).NetworkInitializeIfDisabled(); NPCManager.NPCRegistry.Add(_npc); _npc.InitializeSaveable(); _dialogue = new TestDialogue(_npc); Run(); } } private void Run() { _dialogue.SendNPCMessage("Hello!"); _dialogue.SendNPCMessage("I'm here to help you manage your employees"); _dialogue.ShowResponses(new DialogueResponse[2] { new DialogueResponse("Manage properties", "Manage properties", ShowOwnedPropertiesMenu), new DialogueResponse("Pay employees", "Pay employees", ShowPayOptions) }, 0.5f); } private Transform[] GetIdleTransformsForProperty(string propertyName, int count) { //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Expected O, but got Unknown //IL_0139: Unknown result type (might be due to invalid IL or missing references) Transform[] array = (Transform[])(object)new Transform[count]; string text = propertyName.Replace(" ", "").ToLowerInvariant(); Property val = null; Enumerator<Property> enumerator = Property.Properties.GetEnumerator(); while (enumerator.MoveNext()) { Property current = enumerator.Current; if (current.PropertyName != null && current.PropertyName.Replace(" ", "").ToLowerInvariant() == text) { val = current; break; } } if ((Object)(object)val == (Object)null || (Object)(object)val.SpawnPoint == (Object)null) { MelonLogger.Warning("[EmployeeManager] Couldn't find spawn point for property: " + propertyName); return array; } Vector3 position = val.SpawnPoint.position; float num = 1.2f; int num2 = 5; for (int i = 0; i < count; i++) { int num3 = i / num2; int num4 = i % num2; float num5 = position.x + (float)num4 * num; float num6 = position.z + (float)num3 * num; float y = position.y; GameObject val2 = new GameObject($"IdlePoint_{i}_{propertyName}"); val2.transform.position = new Vector3(num5, y, num6); array[i] = val2.transform; } return array; } private void ShowOwnedPropertiesMenu() { List<DialogueResponse> list = new List<DialogueResponse>(); Enumerator<Property> enumerator = Property.Properties.GetEnumerator(); while (enumerator.MoveNext()) { Property prop = enumerator.Current; if (prop.IsOwned && prop.PropertyName != null) { int capacity = EmployeeConfigManager.GetCapacity(prop.PropertyName); if (prop.EmployeeCapacity < capacity) { prop.EmployeeCapacity = capacity; } prop.EmployeeIdlePoints = Il2CppReferenceArray<Transform>.op_Implicit(GetIdleTransformsForProperty(prop.PropertyName, prop.EmployeeCapacity)); list.Add(new DialogueResponse($"{prop.PropertyName} (Cap: {prop.EmployeeCapacity})", $"{prop.PropertyName} (Cap: {prop.EmployeeCapacity})", delegate { ShowPropertyActions(prop); })); } } if (list.Count == 0) { _dialogue.SendNPCMessage("You don't own any properties yet."); return; } list.Add(new DialogueResponse("BACK", "Back", Run)); _dialogue.ShowResponses(list.ToArray(), 0.5f); } private void ShowPropertyEmployeesWithBeds(Property prop, int page = 0) { //IL_00fc: Unknown result type (might be due to invalid IL or missing references) Property prop2 = prop; List<Employee> employees = prop2.Employees; if (employees == null || employees.Count == 0) { _dialogue.SendNPCMessage("No employees found in " + prop2.PropertyName + "."); return; } List<DialogueResponse> list = new List<DialogueResponse>(); int count = employees.Count; int num = Mathf.CeilToInt((float)count / 6f); int num2 = page * 6; int num3 = Mathf.Min(num2 + 6, count); for (int i = num2; i < num3; i++) { Employee emp = employees[i]; if (!((Object)(object)emp == (Object)null)) { BedItem bed = emp.GetBed(); string value = (((Object)(object)bed != (Object)null) ? $"→ Bed at {((Component)bed).transform.position}" : "→ No bed assigned"); list.Add(new DialogueResponse($"{((NPC)emp).FirstName} {((NPC)emp).LastName} ({((Object)emp).name})", $"{((NPC)emp).FirstName} {((NPC)emp).LastName} {value}", delegate { OpenEmployeeBed(emp); })); } } if (page > 0) { list.Add(new DialogueResponse("← Previous", "Previous Page", delegate { ShowPropertyEmployeesWithBeds(prop2, page - 1); })); } if (page < num - 1) { list.Add(new DialogueResponse("→ Next", "Next Page", delegate { ShowPropertyEmployeesWithBeds(prop2, page + 1); })); } list.Add(new DialogueResponse("↩ Back", "Back", delegate { ShowPropertyActions(prop2); })); _dialogue.SendNPCMessage($"Showing employees in {prop2.PropertyName} (Page {page + 1} of {num})"); _dialogue.ShowResponses(list.ToArray(), 0.5f); } private void OpenEmployeeBed(Employee employee) { BedItem bed = employee.GetBed(); if ((Object)(object)bed == (Object)null) { _dialogue.SendNPCMessage(((NPC)employee).FirstName + " has no assigned bed."); return; } StorageEntity storageEntity = ((PlaceableStorageEntity)bed).StorageEntity; if ((Object)(object)storageEntity == (Object)null) { _dialogue.SendNPCMessage("Bed found, but not a valid storage."); return; } GameplayMenu val = Object.FindObjectOfType<GameplayMenu>(); if ((Object)(object)val != (Object)null) { val.SetIsOpen(false); } _dialogue.SendPlayerMessage("Opened bed storage for " + ((NPC)employee).FirstName); Singleton<StorageMenu>.Instance.Open(storageEntity); } private void ShowPayOptions() { List<DialogueResponse> list = new List<DialogueResponse> { new DialogueResponse("Pay all employees", "Pay all", delegate { EmployeeManager employeeManager = Object.FindObjectOfType<EmployeeManager>(); float totalUnpaidWages = WageUtils.GetTotalUnpaidWages(employeeManager); if (totalUnpaidWages <= 0f) { _dialogue.SendNPCMessage("All employees are already paid for today."); } else { _dialogue.SendNPCMessage($"Pay total wages for ${totalUnpaidWages}?"); _dialogue.ShowResponses(new DialogueResponse[2] { new DialogueResponse("Yes", "Confirm", delegate { if (WageUtils.TryPayAllUnpaidEmployees(employeeManager, out float totalCost, out string error)) { _dialogue.SendPlayerMessage($"Paid ${totalCost} in wages."); _dialogue.SendNPCMessage("All employees are now paid."); } else { _dialogue.SendNPCMessage("Payment failed: " + error); } ShowPayOptions(); }), new DialogueResponse("Back", "Back", ShowPayOptions) }, 0.3f); } }), new DialogueResponse("Pay by property", "Per property", ShowPropertyPayList), new DialogueResponse("Back", "Back", Run) }; _dialogue.ShowResponses(list.ToArray(), 0.4f); } private void ShowPropertyPayList() { List<DialogueResponse> list = new List<DialogueResponse>(); Enumerator<Property> enumerator = Property.OwnedProperties.GetEnumerator(); while (enumerator.MoveNext()) { Property prop = enumerator.Current; if (prop.PropertyName == null || prop.Employees == null || prop.Employees.Count == 0) { continue; } float propWages = WageUtils.GetTotalUnpaidWagesForProperty(prop); string text = $"{prop.PropertyName} (${propWages})"; list.Add(new DialogueResponse(text, text, delegate { if (propWages <= 0f) { _dialogue.SendNPCMessage("All employees at " + prop.PropertyName + " are paid."); ShowPropertyPayList(); } else { _dialogue.SendNPCMessage($"Pay ${propWages} for {prop.PropertyName}?"); _dialogue.ShowResponses(new DialogueResponse[2] { new DialogueResponse("Yes", "Confirm", delegate { if (WageUtils.TryPayUnpaidEmployeesForProperty(prop, out float cost, out string error)) { _dialogue.SendPlayerMessage($"Paid ${cost} for {prop.PropertyName}."); _dialogue.SendNPCMessage("Done."); } else { _dialogue.SendNPCMessage("Failed: " + error); } ShowPropertyPayList(); }), new DialogueResponse("Back", "Back", ShowPropertyPayList) }, 0.3f); } })); } list.Add(new DialogueResponse("Back", "Back", ShowPayOptions)); _dialogue.ShowResponses(list.ToArray(), 0.4f); } private void ShowPropertyActions(Property prop) { Property prop2 = prop; List<DialogueResponse> list = new List<DialogueResponse>(); list.Add(new DialogueResponse("Increase capacity", "Increase capacity", delegate { Property obj = prop2; obj.EmployeeCapacity += 1; EmployeeConfigManager.SetCapacity(prop2.PropertyName, prop2.EmployeeCapacity); prop2.EmployeeIdlePoints = Il2CppReferenceArray<Transform>.op_Implicit(GetIdleTransformsForProperty(prop2.PropertyName, prop2.EmployeeCapacity)); _dialogue.SendPlayerMessage($"Increased capacity of {prop2.PropertyName} to {prop2.EmployeeCapacity}"); _dialogue.SendNPCMessage($"Capacity is now {prop2.EmployeeCapacity}"); EmployeeConfigManager.SaveConfig(); ShowPropertyActions(prop2); })); list.Add(new DialogueResponse("List beds", "List beds", delegate { ShowPropertyEmployeesWithBeds(prop2); })); AddHireOption("Hire Cleaner ($1,500)", 1500, "cleaner", prop2, list); AddHireOption("Hire Botanist ($1,500)", 1500, "botanist", prop2, list); AddHireOption("Hire Handler ($1,500)", 1500, "handler", prop2, list); AddHireOption("Hire Chemist ($2,000)", 2000, "chemist", prop2, list); EmployeeConfigManager.Normalize(prop2.PropertyName); string text = (EmployeeConfigManager.IsAutoPaymentEnabled(prop2.PropertyName) ? "ON" : "OFF"); list.Add(new DialogueResponse("Toggle Auto-Payment (Currently " + text + ")", "Toggle Auto-Payment (Currently " + text + ")", delegate { float totalUnpaidWagesForProperty = WageUtils.GetTotalUnpaidWagesForProperty(prop2); _dialogue.SendNPCMessage($"There are ${totalUnpaidWagesForProperty} in unpaid wages."); EmployeeConfigManager.ToggleAutoPayment(prop2.PropertyName); if (EmployeeConfigManager.IsAutoPaymentEnabled(prop2.PropertyName) && totalUnpaidWagesForProperty > 0f) { if (WageUtils.TryPayAllUnpaidEmployees(prop2.Employees, out float totalCost, out string error)) { _dialogue.SendNPCMessage($"Paid ${totalCost} in wages for {prop2.PropertyName}."); } else { _dialogue.SendNPCMessage("Payment failed: " + error); } } ShowPropertyActions(prop2); })); list.Add(new DialogueResponse("Back", "Back", ShowOwnedPropertiesMenu)); _dialogue.ShowResponses(list.ToArray(), 0.5f); } private void AddHireOption(string label, int fee, string employeeType, Property prop, List<DialogueResponse> responses) { string employeeType2 = employeeType; Property prop2 = prop; responses.Add(new DialogueResponse(label, label, delegate { _dialogue.SendNPCMessage($"Hiring a {employeeType2} at {prop2.PropertyName} will cost you ${fee}. Do you want to proceed?"); _dialogue.ShowResponses(new DialogueResponse[2] { new DialogueResponse("Yes, hire", "Confirm hire", delegate { if (NetworkSingleton<MoneyManager>.Instance.cashBalance < (float)fee) { _dialogue.SendNPCMessage($"You don't have enough money. Required: ${fee}"); } else { NetworkSingleton<MoneyManager>.Instance.ChangeCashBalance((float)(-fee), true, false); string text = prop2.PropertyName.Replace(" ", "").ToLowerInvariant(); Console.SubmitCommand("addemployee " + employeeType2 + " " + text); _dialogue.SendPlayerMessage($"Hired new {employeeType2} at {prop2.PropertyName} for ${fee}"); _dialogue.SendNPCMessage("The employee is on their way."); ShowPropertyActions(prop2); } }), new DialogueResponse("No", "Cancel", delegate { _dialogue.SendPlayerMessage("Cancelled hiring."); ShowPropertyActions(prop2); }) }, 0.3f); })); } public void Cleanup() { if ((Object)(object)_npcObject != (Object)null) { Object.Destroy((Object)(object)_npcObject); } if ((Object)(object)_npc != (Object)null && NPCManager.NPCRegistry.Contains(_npc)) { NPCManager.NPCRegistry.Remove(_npc); } } } public class DialogueResponse { public string Id; public string Text; public Action Action; public DialogueResponse[] NextResponses; public bool DisableDefaultBehavior; public DialogueResponse(string id, string text, Action action, bool disableDefaultBehavior = true, DialogueResponse[] next = null) { Id = id; Text = text; Action = action; DisableDefaultBehavior = disableDefaultBehavior; NextResponses = next; } } public static class EmployeeConfigManager { public static Dictionary<string, int> PropertyCapacities = new Dictionary<string, int>(); public static Dictionary<string, bool> AutoPaymentToggles = new Dictionary<string, bool>(); private static readonly string ConfigPath = Path.Combine(MelonEnvironment.UserDataDirectory, "Mods", "Configs", "employee_config.json"); private static string NormalizeName(string name) { return name?.Replace(" ", "").Trim().ToLowerInvariant(); } public static string Normalize(string name) { return NormalizeName(name); } public static void ToggleAutoPayment(string propertyName) { string key = NormalizeName(propertyName); bool flag = AutoPaymentToggles.ContainsKey(key) && AutoPaymentToggles[key]; AutoPaymentToggles[key] = !flag; SaveConfig(); } public static bool IsAutoPaymentEnabled(string propertyName) { string key = NormalizeName(propertyName); if (AutoPaymentToggles.ContainsKey(key)) { return AutoPaymentToggles[key]; } return false; } public static void LoadConfig() { try { if (!File.Exists(ConfigPath)) { Directory.CreateDirectory(Path.GetDirectoryName(ConfigPath)); return; } string text = File.ReadAllText(ConfigPath); Dictionary<string, object> dictionary = JsonConvert.DeserializeObject<Dictionary<string, object>>(text); if (dictionary != null && dictionary.ContainsKey("capacities") && dictionary.ContainsKey("autoPay")) { PropertyCapacities = JsonConvert.DeserializeObject<Dictionary<string, int>>(dictionary["capacities"].ToString()); AutoPaymentToggles = JsonConvert.DeserializeObject<Dictionary<string, bool>>(dictionary["autoPay"].ToString()); return; } Dictionary<string, int> obj = JsonConvert.DeserializeObject<Dictionary<string, int>>(text) ?? new Dictionary<string, int>(); PropertyCapacities = new Dictionary<string, int>(); foreach (KeyValuePair<string, int> item in obj) { string key = NormalizeName(item.Key); if (!PropertyCapacities.ContainsKey(key)) { PropertyCapacities[key] = item.Value; } } AutoPaymentToggles = new Dictionary<string, bool>(); SaveConfig(); } catch (Exception) { PropertyCapacities = new Dictionary<string, int>(); AutoPaymentToggles = new Dictionary<string, bool>(); } } public static void SaveConfig() { try { Directory.CreateDirectory(Path.GetDirectoryName(ConfigPath)); string contents = JsonConvert.SerializeObject((object)new Dictionary<string, object> { { "capacities", PropertyCapacities }, { "autoPay", AutoPaymentToggles } }, (Formatting)1); File.WriteAllText(ConfigPath, contents); } catch (Exception ex) { MelonLogger.Error("[EmployeeManager] Failed to save config: " + ex.Message); } } public static int GetCapacity(string propertyName, int fallback = 10) { string key = NormalizeName(propertyName); if (!PropertyCapacities.TryGetValue(key, out var value)) { return fallback; } return value; } public static void SetCapacity(string propertyName, int value) { string text = NormalizeName(propertyName); if (!string.IsNullOrWhiteSpace(text)) { PropertyCapacities[text] = value; } } public static bool HasCapacity(string propertyName) { string text = NormalizeName(propertyName); if (!string.IsNullOrWhiteSpace(text)) { return PropertyCapacities.ContainsKey(text); } return false; } } public class Mod : MelonMod { [CompilerGenerated] private sealed class <Initialize>d__5 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public Mod <>4__this; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <Initialize>d__5(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { int num = <>1__state; Mod mod = <>4__this; switch (num) { default: return false; case 0: <>1__state = -1; <>2__current = BotManager.WaitForSystems(); <>1__state = 1; return true; case 1: <>1__state = -1; ((MelonBase)mod).LoggerInstance.Msg("[Employee_Manager] BotManager..."); mod._botManager = BotManager.Create(mod); 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(); } } [CompilerGenerated] private sealed class <InitializeDay>d__6 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public Mod <>4__this; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <InitializeDay>d__6(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { int num = <>1__state; Mod mod = <>4__this; switch (num) { default: return false; case 0: <>1__state = -1; <>2__current = BotManager.WaitForSystems(); <>1__state = 1; return true; case 1: <>1__state = -1; mod._elapsedDays = mod._timeManager.ElapsedDays; 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(); } } [CompilerGenerated] private sealed class <WaitForPropertyRestoreThenPatch>d__7 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; private bool <hasTargetProps>5__2; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <WaitForPropertyRestoreThenPatch>d__7(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; goto IL_003b; case 1: <>1__state = -1; goto IL_003b; case 2: { <>1__state = -1; break; } IL_003b: if (Property.Properties == null || Property.Properties.Count == 0) { <>2__current = null; <>1__state = 1; return true; } <hasTargetProps>5__2 = false; break; } Enumerator<Property> enumerator; while (!<hasTargetProps>5__2) { enumerator = Property.Properties.GetEnumerator(); while (enumerator.MoveNext()) { Property current = enumerator.Current; if ((current.IsOwned || EmployeeConfigManager.HasCapacity(current.PropertyName)) && current.PropertyName != null) { <hasTargetProps>5__2 = true; break; } } if (!<hasTargetProps>5__2) { <>2__current = null; <>1__state = 2; return true; } } enumerator = Property.Properties.GetEnumerator(); while (enumerator.MoveNext()) { Property current2 = enumerator.Current; if (!string.IsNullOrWhiteSpace(current2.PropertyName) && (current2.IsOwned || EmployeeConfigManager.HasCapacity(current2.PropertyName))) { int capacity = EmployeeConfigManager.GetCapacity(current2.PropertyName); current2.EmployeeCapacity = capacity; current2.EmployeeIdlePoints = Il2CppReferenceArray<Transform>.op_Implicit(BotManagerStaticUtils.GetIdleTransformsForProperty(current2.PropertyName, current2.EmployeeCapacity)); } } 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 BotManager _botManager; private TimeManager _timeManager; private int _elapsedDays = -1; public override void OnInitializeMelon() { EmployeeConfigManager.LoadConfig(); } public override void OnSceneWasInitialized(int buildIndex, string sceneName) { ((MelonBase)this).LoggerInstance.Msg("Scene loaded: " + sceneName); if (sceneName == "Main") { _timeManager = Object.FindObjectOfType<TimeManager>(); if ((Object)(object)_timeManager != (Object)null) { MelonCoroutines.Start(InitializeDay()); } MelonCoroutines.Start(Initialize()); MelonCoroutines.Start(WaitForPropertyRestoreThenPatch()); } } [IteratorStateMachine(typeof(<Initialize>d__5))] private IEnumerator Initialize() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <Initialize>d__5(0) { <>4__this = this }; } [IteratorStateMachine(typeof(<InitializeDay>d__6))] private IEnumerator InitializeDay() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <InitializeDay>d__6(0) { <>4__this = this }; } [IteratorStateMachine(typeof(<WaitForPropertyRestoreThenPatch>d__7))] private IEnumerator WaitForPropertyRestoreThenPatch() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <WaitForPropertyRestoreThenPatch>d__7(0); } public override void OnUpdate() { if ((Object)(object)_timeManager == (Object)null || _elapsedDays == -1) { return; } int elapsedDays = _timeManager.ElapsedDays; if (elapsedDays == _elapsedDays || _timeManager.CurrentTime <= 700) { return; } _elapsedDays = elapsedDays; Enumerator<Property> enumerator = Property.Properties.GetEnumerator(); while (enumerator.MoveNext()) { Property current = enumerator.Current; if (EmployeeConfigManager.IsAutoPaymentEnabled(current.PropertyName) && WageUtils.GetTotalUnpaidWagesForProperty(current) > 0f && current.Employees != null) { if (WageUtils.TryPayAllUnpaidEmployees(current.Employees, out float totalCost, out string error)) { _botManager.Dialogue.SendNPCMessage($"[AutoPay] Paid ${totalCost} in {current.PropertyName}"); } else { _botManager.Dialogue.SendNPCMessage("[AutoPay] " + error + " for " + current.PropertyName); } } } } public override void OnSceneWasUnloaded(int buildIndex, string sceneName) { if (!(sceneName == "Main")) { return; } Enumerator<Property> enumerator = Property.Properties.GetEnumerator(); while (enumerator.MoveNext()) { Property current = enumerator.Current; if (current.IsOwned && current.PropertyName != null) { EmployeeConfigManager.SetCapacity(current.PropertyName, current.EmployeeCapacity); } } EmployeeConfigManager.SaveConfig(); _botManager?.Cleanup(); _botManager = null; } } public class TestDialogue { [CompilerGenerated] private sealed class <DelayedShowResponses>d__0 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public float delay; public TestDialogue <>4__this; public DialogueResponse[] next; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <DelayedShowResponses>d__0(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown int num = <>1__state; TestDialogue testDialogue = <>4__this; switch (num) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(delay); <>1__state = 1; return true; case 1: <>1__state = -1; testDialogue.ShowResponses(next, 0.1f); 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 readonly NPC _npc; private readonly MSGConversation _conversation; [IteratorStateMachine(typeof(<DelayedShowResponses>d__0))] private IEnumerator DelayedShowResponses(float delay, DialogueResponse[] next) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <DelayedShowResponses>d__0(0) { <>4__this = this, delay = delay, next = next }; } public TestDialogue(NPC npc) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown _npc = npc; _conversation = new MSGConversation(npc, npc.fullName); _npc.MSGConversation = _conversation; _conversation.messageHistory = new List<Message>(); _conversation.messageChainHistory = new List<MessageChain>(); _conversation.bubbles = new List<MessageBubble>(); _conversation.EntryVisible = true; if (_npc.ConversationCategories == null) { _npc.ConversationCategories = new List<EConversationCategory>(); _npc.ConversationCategories.Add((EConversationCategory)0); } _conversation.SetCategories(_npc.ConversationCategories); } public void SendNPCMessage(string text) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Expected O, but got Unknown Message val = new Message(text, (ESenderType)1, true, -1); _conversation.SendMessage(val, true, false); } public void SendPlayerMessage(string text) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Expected O, but got Unknown Message val = new Message(text, (ESenderType)0, true, -1); _conversation.SendMessage(val, true, false); } public void ShowResponses(DialogueResponse[] responses, float v) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Expected O, but got Unknown List<Response> val = new List<Response>(); foreach (DialogueResponse response in responses) { Action action = response.Action; Action action2 = delegate { action?.Invoke(); if (response.NextResponses != null && response.NextResponses.Length != 0) { MelonCoroutines.Start(DelayedShowResponses(0.5f, response.NextResponses)); } }; val.Add(new Response(response.Id, response.Text, Action.op_Implicit(action2), response.DisableDefaultBehavior)); } _conversation.ClearResponses(false); _conversation.ShowResponses(val, 0f, false); } } }