Some mods target the Mono version of the game, which is available by opting into the Steam beta branch "alternate"
Decompiled source of BusinessEmployment v1.0.1
BusinessEmployment-IL2CPP.dll
Decompiled a day agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BusinessEmployment; using BusinessEmployment.Behaviours; using BusinessEmployment.BetterSafe; using BusinessEmployment.Helpers; using BusinessEmployment.Persistence; using HarmonyLib; using Il2CppFishNet; using Il2CppFishNet.Connection; using Il2CppFishNet.Object; using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.Injection; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppScheduleOne.DevUtilities; using Il2CppScheduleOne.Dialogue; using Il2CppScheduleOne.Employees; using Il2CppScheduleOne.EntityFramework; using Il2CppScheduleOne.ItemFramework; using Il2CppScheduleOne.Money; using Il2CppScheduleOne.NPCs; using Il2CppScheduleOne.NPCs.Behaviour; using Il2CppScheduleOne.ObjectScripts; using Il2CppScheduleOne.PlayerScripts; using Il2CppScheduleOne.Property; using Il2CppScheduleOne.Storage; using Il2CppScheduleOne.UI; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using MelonLoader; using MelonLoader.Preferences; using Microsoft.CodeAnalysis; using S1API.Building; using S1API.GameTime; using S1API.Internal.Abstraction; using S1API.Items; using S1API.Lifecycle; using S1API.Rendering; using S1API.Saveables; using S1API.Shops; using S1API.Storage; using S1API.Utils; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: MelonInfo(typeof(global::BusinessEmployment.BusinessEmployment), "BusinessEmployment", "1.0.1", "k073l", null)] [assembly: MelonColor(1, 255, 195, 86)] [assembly: MelonGame("TVGS", "Schedule I")] [assembly: MelonPlatformDomain(/*Could not decode attribute arguments.*/)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("BusinessEmployment-IL2CPP")] [assembly: AssemblyConfiguration("IL2CPP")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+16384da6cef38c83df79b860c11807c9b0d3fcb3")] [assembly: AssemblyProduct("BusinessEmployment-IL2CPP")] [assembly: AssemblyTitle("BusinessEmployment-IL2CPP")] [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.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; } } [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 BusinessEmployment { public static class BuildInfo { public const string Name = "BusinessEmployment"; public const string Description = "Adds employees to Businesses. Automates laundering."; public const string Author = "k073l"; public const string Version = "1.0.1"; } public class BusinessEmployment : MelonMod { [CompilerGenerated] private sealed class <AddDelayed>d__9 : 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 <AddDelayed>d__9(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(2f); <>1__state = 1; return true; case 1: <>1__state = -1; if (SafeCreator.SafeAdded) { return false; } _logger.Msg("Adding safe to the shop"); SafeCreator.AddToShop(); 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 Instance _logger; private static MelonPreferences_Category _category; internal static MelonPreferences_Entry<float> EmpCut; internal static MelonPreferences_Entry<float> SafeCost; internal static MelonPreferences_Entry<bool> EnableSafeAutoRestock; public override void OnInitializeMelon() { _logger = ((MelonBase)this).LoggerInstance; _logger.Msg("BusinessEmployment initialized"); _category = MelonPreferences.CreateCategory("BusinessEmployment", "Business Employment Settings"); EmpCut = _category.CreateEntry<float>("BusinessEmploymentEmployeeCut", 5f, "Employee Cut", "Additional payment for Business employees for restocking the Golden Safe. (% of the cash total)", false, false, (ValueValidator)(object)new ValueRange<float>(0f, 100f), (string)null); SafeCost = _category.CreateEntry<float>("BusinessEmploymentGoldSafeCost", 5000f, "Golden Safe price", "Price of the Golden Safe item in the Boutique", false, false, (ValueValidator)(object)new ValueRange<float>(0f, 1E+09f), (string)null); EnableSafeAutoRestock = _category.CreateEntry<bool>("BusinessEmploymentEnableSafeAutoRestock", true, "Enable Golden Safe Auto-Restock", "If enabled, businesses with employees will automatically restock their Golden Safes when you sleep.", false, false, (ValueValidator)null, (string)null); GameLifecycle.OnPreLoad += CreateSafe; } public override void OnSceneWasLoaded(int buildIndex, string sceneName) { if (!(sceneName == "Menu")) { if (sceneName == "Main") { MelonCoroutines.Start(AddDelayed()); TimeManager.OnSleepStart = (Action)Delegate.Combine(TimeManager.OnSleepStart, new Action(SafeMethods.RefillSafe)); } } else { SafeCreator.SafeCreated = false; SafeCreator.SafeAdded = false; } } public override void OnSceneWasUnloaded(int buildIndex, string sceneName) { if (sceneName != "Main") { return; } try { TimeManager.OnSleepStart = (Action)Delegate.Remove(TimeManager.OnSleepStart, new Action(SafeMethods.RefillSafe)); } catch (Exception) { } } private static void CreateSafe() { if (!SafeCreator.SafeCreated) { _logger.Msg("Creating safe"); SafeCreator.CreateSafe(); } } [IteratorStateMachine(typeof(<AddDelayed>d__9))] private static IEnumerator AddDelayed() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <AddDelayed>d__9(0); } } } namespace BusinessEmployment.Persistence { public class LaunderBehaviourSave : Saveable { [SaveableField("BusinessEmploymentLaunderBehaviourSave")] public List<LaunderBehaviourSaveData> SaveDatas = new List<LaunderBehaviourSaveData>(); public static LaunderBehaviourSave Instance { get; private set; } = new LaunderBehaviourSave(); public LaunderBehaviourSave() { Instance = this; } protected override void OnLoaded() { foreach (LaunderBehaviourSaveData saveData in SaveDatas) { } } } public record LaunderBehaviourSaveData { public string PropertyCode { get; set; } public float MoneyLeftToLaunder { get; set; } } } namespace BusinessEmployment.Patches { [HarmonyPatch(typeof(Business))] internal class BusinessIdlePoints { [HarmonyPatch("Awake")] [HarmonyPostfix] private static void AddIdlePointAndEmployeeCapacity(Business __instance) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Expected O, but got Unknown //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null) { Melon<BusinessEmployment>.Logger.Error("Business instance is null"); return; } Transform spawnPoint = ((Property)__instance).SpawnPoint; if ((Object)(object)spawnPoint == (Object)null) { Melon<BusinessEmployment>.Logger.Error("SpawnPoint for " + ((Property)__instance).PropertyName + " is null"); return; } GameObject val = new GameObject("EmployeeIdlePoints"); val.transform.SetParent(((Component)__instance).transform); Transform val2 = Object.Instantiate<Transform>(spawnPoint, spawnPoint.position, spawnPoint.rotation); ((Component)val2).transform.SetParent(val.transform); List<Transform> list = new List<Transform> { val2 }; ((Property)__instance).EmployeeIdlePoints = Il2CppReferenceArray<Transform>.op_Implicit(list.ToArray()); ((Property)__instance).EmployeeCapacity = 1; } } [HarmonyPatch(typeof(DialogueController_Fixer))] internal class FixerDialogue { [HarmonyPatch("ModifyChoiceList")] [HarmonyPrefix] private static bool IncludeBusinesses(DialogueController_Fixer __instance, string dialogueLabel, ref List<DialogueChoiceData> existingChoices) { //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Expected O, but got Unknown if ((Object)(object)__instance == (Object)null) { return true; } if (dialogueLabel != "SELECT_LOCATION") { return true; } List<DialogueChoiceData> list = new List<DialogueChoiceData>(); Enumerator<DialogueChoiceData> enumerator = existingChoices.GetEnumerator(); while (enumerator.MoveNext()) { DialogueChoiceData current = enumerator.Current; list.Add(current); } Enumerator<Property> enumerator2 = Property.OwnedProperties.GetEnumerator(); while (enumerator2.MoveNext()) { Property current2 = enumerator2.Current; if (current2.EmployeeCapacity > 0) { list.Add(new DialogueChoiceData { ChoiceText = current2.PropertyName, ChoiceLabel = current2.PropertyCode }); } } existingChoices.Clear(); foreach (DialogueChoiceData item in list) { existingChoices.Add(item); } MelonDebug.Msg("Sanity Check"); Enumerator<DialogueChoiceData> enumerator4 = existingChoices.GetEnumerator(); while (enumerator4.MoveNext()) { DialogueChoiceData current4 = enumerator4.Current; MelonDebug.Msg(current4.ChoiceText + ": " + current4.ChoiceLabel); } return false; } } [HarmonyPatch(typeof(DialogueCanvas))] public static class DialoguePagingPatches { private const int ChoicesPerPage = 7; private static DialogueHandler _handler; private static DialogueNodeData _node; private static string _text; private static List<DialogueChoiceData> _fullChoices; private static int _pageIndex; private static int _pageCount; private static bool PagingActive => _fullChoices != null && _pageCount > 1; private static int ClampInt(int value, int min, int max) { if (value < min) { return min; } if (value > max) { return max; } return value; } [HarmonyPatch("DisplayDialogueNode")] [HarmonyPrefix] private static void DisplayDialogueNodePrefix(DialogueHandler diag, DialogueNodeData node, string dialogueText, ref List<DialogueChoiceData> choices) { try { if (choices == null || choices.Count <= 8) { ClearPagingIfNewNode(diag, node); return; } if ((Object)(object)_handler != (Object)(object)diag || _node != node) { _handler = diag; _node = node; _text = dialogueText; _pageIndex = 0; _fullChoices = CopyList(choices); _pageCount = ComputePageCount(_fullChoices.Count); } else { _text = dialogueText; if (_fullChoices == null || _fullChoices.Count != choices.Count) { _fullChoices = CopyList(choices); _pageCount = ComputePageCount(_fullChoices.Count); _pageIndex = ClampInt(_pageIndex, 0, Math.Max(0, _pageCount - 1)); } } if (PagingActive) { choices = BuildPageChoices(_fullChoices, _pageIndex, _pageCount); } } catch (Exception value) { Melon<BusinessEmployment>.Logger.Error($"Dialogue paging DisplayDialogueNodePrefix failed: {value}"); } } [HarmonyPatch("DisplayDialogueNode")] [HarmonyPostfix] private static void DisplayDialogueNodePostfix(DialogueHandler diag) { try { if (PagingActive && !((Object)(object)_handler != (Object)(object)diag)) { diag.CurrentChoices = _fullChoices; } } catch { } } [HarmonyPatch("ChoiceSelected")] [HarmonyPrefix] private static bool ChoiceSelectedPrefix(DialogueCanvas __instance, ref int choiceIndex) { try { if (!PagingActive) { return true; } if ((Object)(object)__instance == (Object)null) { return true; } if (_fullChoices == null || (Object)(object)_handler == (Object)null || _node == null || _text == null) { return true; } int count = _fullChoices.Count; int num = _pageIndex * 7; int val = Math.Max(0, count - num); int num2 = Math.Min(7, val); if (choiceIndex == num2) { _pageIndex = (_pageIndex + 1) % _pageCount; __instance.SkipNextRollout = true; __instance.DisplayDialogueNode(_handler, _node, _text, _fullChoices); return false; } if (choiceIndex < 0 || choiceIndex >= num2) { return true; } choiceIndex = num + choiceIndex; return true; } catch (Exception value) { Melon<BusinessEmployment>.Logger.Error($"Dialogue paging ChoiceSelectedPrefix failed: {value}"); return true; } } [HarmonyPatch("EndDialogue")] [HarmonyPostfix] private static void EndDialoguePostfix() { ClearPaging(); } [HarmonyPatch("IsChoiceValid")] [HarmonyPrefix] private static bool IsChoiceValidPrefix(int choiceIndex, ref string reason, ref bool __result) { try { if (!PagingActive) { return true; } if ((Object)(object)_handler == (Object)null || _fullChoices == null) { return true; } int count = _fullChoices.Count; int num = _pageIndex * 7; int val = Math.Max(0, count - num); int num2 = Math.Min(7, val); if (choiceIndex <= num2) { if (IsMoreIndex(choiceIndex)) { reason = string.Empty; __result = true; return false; } if (choiceIndex < 0 || choiceIndex >= num2) { reason = string.Empty; __result = false; return false; } choiceIndex = num + choiceIndex; } List<DialogueChoiceData> currentChoices = _handler.CurrentChoices; if (currentChoices == null || choiceIndex < 0 || choiceIndex >= currentChoices.Count) { reason = string.Empty; __result = false; return false; } DialogueChoiceData val2 = currentChoices[choiceIndex]; string text = ((val2 != null) ? val2.ChoiceLabel : null) ?? string.Empty; if (string.IsNullOrWhiteSpace(text)) { reason = string.Empty; __result = false; return false; } string text2 = default(string); bool flag = _handler.CheckChoice(text, ref text2); reason = text2; __result = flag; return false; } catch { return true; } } private static bool IsMoreIndex(int choiceIndex) { if (!PagingActive) { return false; } if (_fullChoices == null) { return false; } int count = _fullChoices.Count; int num = _pageIndex * 7; int val = Math.Max(0, count - num); int num2 = Math.Min(7, val); return choiceIndex == num2; } private static void ClearPagingIfNewNode(DialogueHandler diag, DialogueNodeData node) { if ((Object)(object)_handler != (Object)(object)diag || _node != node) { ClearPaging(); } } private static void ClearPaging() { _handler = null; _node = null; _text = null; _fullChoices = null; _pageIndex = 0; _pageCount = 0; } private static int ComputePageCount(int totalChoices) { if (totalChoices <= 8) { return 0; } return (totalChoices + 7 - 1) / 7; } private static List<DialogueChoiceData> CopyList(List<DialogueChoiceData> src) { List<DialogueChoiceData> val = new List<DialogueChoiceData>(); if (src == null) { return val; } for (int i = 0; i < src.Count; i++) { DialogueChoiceData val2 = src[i]; if (val2 != null) { val.Add(val2); } } return val; } private static List<DialogueChoiceData> BuildPageChoices(List<DialogueChoiceData> full, int pageIndex, int pageCount) { //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Expected O, but got Unknown List<DialogueChoiceData> val = new List<DialogueChoiceData>(); int count = full.Count; int num = pageIndex * 7; int num2 = Math.Min(7, Math.Max(0, count - num)); for (int i = 0; i < num2; i++) { DialogueChoiceData val2 = full[num + i]; if (val2 != null) { val.Add(val2); } } string choiceLabel = string.Empty; try { int num3 = (pageIndex + 1) % pageCount; int num4 = num3 * 7; if (num4 >= 0 && num4 < full.Count) { DialogueChoiceData val3 = full[num4]; if (val3 != null && !string.IsNullOrWhiteSpace(val3.ChoiceLabel)) { choiceLabel = val3.ChoiceLabel; } } } catch { } val.Add(new DialogueChoiceData { ChoiceLabel = choiceLabel, ChoiceText = $"More ({pageIndex + 1}/{pageCount})" }); return val; } } [HarmonyPatch(typeof(Packager))] public class HandlerLaunderBehaviorInitialize { [HarmonyPatch("UpdateBehaviour")] [HarmonyPostfix] private static void AddLaunderBehaviour(Packager __instance) { if (!((Object)(object)__instance == (Object)null) && !((Object)(object)((Employee)__instance).AssignedProperty == (Object)null) && Utils.Is<Business>((object)((Employee)__instance).AssignedProperty, out Business _) && ((Employee)__instance).CanWork() && !((Employee)__instance).Fired && !((Behaviour)__instance.PackagingBehaviour).Active && !((Behaviour)((Employee)__instance).MoveItemBehaviour).Active) { ((Employee)__instance).MarkIsWorking(); LaunderBehaviour.Tick(__instance); } } } } namespace BusinessEmployment.Helpers { public static class MelonLoggerExtensions { public static void Debug(this Instance logger, string message, bool stacktrace = true) { MelonDebug.Msg(stacktrace ? ("[" + GetCallerInfo() + "] " + message) : message); } private static string GetCallerInfo() { StackTrace stackTrace = new StackTrace(); for (int i = 2; i < stackTrace.FrameCount; i++) { StackFrame frame = stackTrace.GetFrame(i); MethodBase method = frame.GetMethod(); if (!(method?.DeclaringType == null)) { return method.DeclaringType.FullName + "." + method.Name; } } return "unknown"; } } public static class Il2CppListExtensions { public static IEnumerable<T> AsEnumerable<T>(this List<T> list) { return list ?? new List<T>(); } public static object ToNativeList<T>(this List<T> source) { return source.ToIl2CppList(); } public static List<T> ToIl2CppList<T>(this IEnumerable<T> source) { List<T> val = new List<T>(); foreach (T item in source) { val.Add(item); } return val; } public static List<T> ConvertToList<T>(List<T> il2CppList) { List<T> list = new List<T>(); T[] collection = Il2CppArrayBase<T>.op_Implicit(il2CppList.ToArray()); list.AddRange(collection); return list; } public static IEnumerable<T> AsEnumerable<T>(this List<T> list) { IEnumerable<T> result; if (list != null) { result = ((IEnumerable<T>)list._items).Take(list._size); } else { IEnumerable<T> enumerable = Array.Empty<T>(); result = enumerable; } return result; } public static object ToNativeList<T>(this List<T> source) { return source; } } public static class Utils { [CompilerGenerated] private sealed class <WaitForCondition>d__6 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public Func<bool> condition; public float timeout; public Action onTimeout; public Action onFinish; private float <startTime>5__1; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <WaitForCondition>d__6(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; <startTime>5__1 = Time.time; break; case 1: <>1__state = -1; break; } if (!condition()) { if (!float.IsNaN(timeout) && Time.time - <startTime>5__1 > timeout) { onTimeout?.Invoke(); return false; } <>2__current = null; <>1__state = 1; return true; } onFinish?.Invoke(); 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 <WaitForNetwork>d__5 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public IEnumerator routine; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <WaitForNetwork>d__5(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; break; case 1: <>1__state = -1; break; } if (!InstanceFinder.IsServer && !InstanceFinder.IsClient) { <>2__current = null; <>1__state = 1; return true; } MelonCoroutines.Start(routine); 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 <WaitForPlayer>d__4 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public IEnumerator routine; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <WaitForPlayer>d__4(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; break; case 1: <>1__state = -1; break; } if ((Object)(object)Player.Local == (Object)null || (Object)(object)((Component)Player.Local).gameObject == (Object)null) { <>2__current = null; <>1__state = 1; return true; } MelonCoroutines.Start(routine); 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 Instance Logger = new Instance("BusinessEmployment-Utils"); public static T? FindObjectByName<T>(string objectName) where T : Object { try { foreach (T item in Resources.FindObjectsOfTypeAll<T>()) { if (((Object)item).name != objectName) { continue; } Logger.Debug($"Found {typeof(T).Name} '{objectName}' directly in loaded objects"); return item; } return default(T); } catch (Exception ex) { Logger.Error($"Error finding {typeof(T).Name} '{objectName}': {ex.Message}"); return default(T); } } public static List<T> GetAllComponentsInChildrenRecursive<T>(GameObject obj) where T : Component { List<T> list = new List<T>(); if ((Object)(object)obj == (Object)null) { return list; } T[] array = Il2CppArrayBase<T>.op_Implicit(obj.GetComponents<T>()); if (array.Length != 0) { list.AddRange(array); } for (int i = 0; i < obj.transform.childCount; i++) { Transform child = obj.transform.GetChild(i); list.AddRange(GetAllComponentsInChildrenRecursive<T>(((Component)child).gameObject)); } return list; } public static bool Is<T>(object obj, out T? result) where T : Object { Object val = (Object)((obj is Object) ? obj : null); if (val != null) { Type val2 = Il2CppType.Of<T>(); Type il2CppType = val.GetIl2CppType(); if (val2.IsAssignableFrom(il2CppType)) { result = ((Il2CppObjectBase)val).TryCast<T>(); return result != null; } } result = default(T); return false; } [IteratorStateMachine(typeof(<WaitForPlayer>d__4))] public static IEnumerator WaitForPlayer(IEnumerator routine) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <WaitForPlayer>d__4(0) { routine = routine }; } [IteratorStateMachine(typeof(<WaitForNetwork>d__5))] public static IEnumerator WaitForNetwork(IEnumerator routine) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <WaitForNetwork>d__5(0) { routine = routine }; } [IteratorStateMachine(typeof(<WaitForCondition>d__6))] public static IEnumerator WaitForCondition(Func<bool> condition, float timeout = float.NaN, Action? onTimeout = null, Action? onFinish = null) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <WaitForCondition>d__6(0) { condition = condition, timeout = timeout, onTimeout = onTimeout, onFinish = onFinish }; } public static string GetHierarchyPath(this Transform transform) { if ((Object)(object)transform == (Object)null) { return "null"; } string text = ((Object)transform).name; Transform parent = transform.parent; while ((Object)(object)parent != (Object)null) { text = ((Object)parent).name + "/" + text; parent = parent.parent; } return text; } public static T GetOrAddComponent<T>(this GameObject gameObject) where T : Component { T component = gameObject.GetComponent<T>(); if ((Object)(object)component != (Object)null) { return component; } component = gameObject.AddComponent<T>(); Logger.Debug("Added component " + typeof(T).Name + " to GameObject " + ((Object)gameObject).name); return component; } public static Material? DrawDebugVisuals(this GameObject gameObject, Color? color = null) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Expected O, but got Unknown //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) Renderer component = gameObject.GetComponent<Renderer>(); if ((Object)(object)component == (Object)null) { Logger.Error("GameObject " + ((Object)gameObject).name + " has no Renderer component"); return null; } Color valueOrDefault = color.GetValueOrDefault(); if (!color.HasValue) { ((Color)(ref valueOrDefault))..ctor(1f, 0f, 1f, 0.5f); color = valueOrDefault; } Shader val = Shader.Find("Universal Render Pipeline/Lit"); if ((Object)(object)val == (Object)null) { return null; } Material val2 = new Material(val); if (val2.HasProperty("_Surface")) { val2.SetFloat("_Surface", 1f); } Color value = color.Value; if (value.a <= 0f) { value.a = 0.2f; } if (val2.HasProperty("_BaseColor")) { val2.SetColor("_BaseColor", value); } if (val2.HasProperty("_EmissionColor")) { val2.EnableKeyword("_EMISSION"); val2.SetColor("_EmissionColor", new Color(value.r, value.g, value.b) * 1.5f); } val2.SetInt("_ZWrite", 0); val2.renderQueue = 3000; Material material = component.material; component.material = val2; return material; } } } namespace BusinessEmployment.BetterSafe { internal class FilterHelper { [CompilerGenerated] private sealed class <WaitSearchAdd>d__2 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public float timeout; private float <startTime>5__1; private IEnumerable<StorageEntity> <betterSafes>5__2; private IEnumerator<StorageEntity> <>s__3; private StorageEntity <betterSafe>5__4; private Enumerator<ItemSlot> <>s__5; private ItemSlot <slot>5__6; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <WaitSearchAdd>d__2(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <betterSafes>5__2 = null; <>s__3 = null; <betterSafe>5__4 = null; <>s__5 = null; <slot>5__6 = null; <>1__state = -2; } private bool MoveNext() { //IL_026c: Unknown result type (might be due to invalid IL or missing references) //IL_0276: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; if (filter_Cash == null) { filter_Cash = new ItemFilter_Cash(); } <startTime>5__1 = Time.time; break; case 1: <>1__state = -1; <betterSafes>5__2 = null; break; } if (Time.time - <startTime>5__1 <= timeout) { <betterSafes>5__2 = from bi in (from p in Property.Properties.AsEnumerable<Property>() select p.BuildableItems into items where items != null select items.AsEnumerable<BuildableItem>()).Aggregate((IEnumerable<BuildableItem> a, IEnumerable<BuildableItem> b) => a.Concat(b)) select Utils.Is<PlaceableStorageEntity>((object)bi, out PlaceableStorageEntity result) ? result : null into r where (Object)(object)r != (Object)null select r into pse where ((BuildableItem)pse).SaveFolderName.Contains("gold_safe") select pse.StorageEntity; <>s__3 = <betterSafes>5__2.GetEnumerator(); try { while (<>s__3.MoveNext()) { <betterSafe>5__4 = <>s__3.Current; <>s__5 = <betterSafe>5__4.ItemSlots.GetEnumerator(); while (<>s__5.MoveNext()) { <slot>5__6 = <>s__5.Current; if (!<slot>5__6.HardFilters.AsEnumerable<ItemFilter>().Any((ItemFilter itFi) => ((object)itFi).GetType() == ((object)filter_Cash).GetType())) { <slot>5__6.AddFilter((ItemFilter)(object)filter_Cash); <slot>5__6 = null; } } <>s__5 = null; <betterSafe>5__4 = null; } } finally { if (<>s__3 != null) { <>s__3.Dispose(); } } <>s__3 = null; <>2__current = (object)new WaitForSecondsRealtime(0.5f); <>1__state = 1; return true; } 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 ItemFilter_Cash filter_Cash; private static Instance _logger = Melon<BusinessEmployment>.Logger; [IteratorStateMachine(typeof(<WaitSearchAdd>d__2))] internal static IEnumerator WaitSearchAdd(float timeout) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <WaitSearchAdd>d__2(0) { timeout = timeout }; } } [RegisterTypeInIl2Cpp] public class ItemFilter_Cash : ItemFilter { public ItemFilter_Cash(IntPtr ptr) : base(ptr) { } public ItemFilter_Cash() : base(ClassInjector.DerivedConstructorPointer<ItemFilter_Cash>()) { ClassInjector.DerivedConstructorBody((Il2CppObjectBase)(object)this); } public override bool DoesItemMatchFilter(ItemInstance item) { CashInstance result; return Utils.Is<CashInstance>((object)item, out result); } } public class SafeCreator { internal const string SAFE_ID = "gold_safe"; private const string SAFE_NAME = "Golden Safe"; internal const int SLOT_COUNT = 20; public static bool SafeCreated; public static bool SafeAdded; private static readonly Color GoldMetalColor = new Color(1f, 0.766f, 0.336f, 1f); public static void CreateSafe() { BuildableItemDefinition val = BuildableItemCreator.CloneFrom("safe").WithBasicInfo("gold_safe", "Golden Safe", "An upgraded version of the safe. Can hold only cash.").WithPricing(BusinessEmployment.SafeCost.Value, 0.8f) .WithIcon(LoadIcon()) .Build(); BuildEvents.OnBuildableItemInitialized += ItemBuilt; BuildEvents.OnGridItemCreated += ItemBuilt; BuildEvents.OnSurfaceItemCreated += ItemBuilt; StorageEvents.OnStorageCreated += StorageCreated; StorageEvents.OnStorageLoading += StorageLoaded; StorageEvents.OnStorageOpening += StorageOpened; SafeCreated = true; } public static void AddToShop() { ItemDefinition itemDefinition = ItemManager.GetItemDefinition("gold_safe"); if (itemDefinition == (ItemDefinition)null) { Melon<BusinessEmployment>.Logger.Error("Error while adding safe to shop"); return; } Shop shopByName = ShopManager.GetShopByName("Bleuball's Boutique"); if (shopByName == null) { Melon<BusinessEmployment>.Logger.Error("Shop not found"); return; } shopByName.AddItem(itemDefinition, (float?)null); SafeAdded = true; } private static void EnsureCapacity(StorageEntity storage) { if (storage != null) { if (!storage.SetSlotCount(20)) { Melon<BusinessEmployment>.Logger.Error("Failed to add additional slots"); } else { MelonCoroutines.Start(FilterHelper.WaitSearchAdd(10f)); } } } private static void StorageCreated(StorageEventArgs args) { if (args != null && args.Storage != null && args.ItemId == "gold_safe") { args.Storage.Name = "Golden Safe"; EnsureCapacity(args.Storage); } } private static void StorageOpened(StorageEventArgs args) { if (args != null) { StorageEntity storage = args.Storage; if (storage != null) { storage.SyncCustomNameToDisplayName(); } } } private static void StorageLoaded(StorageLoadingEventArgs args) { if (args != null && ((StorageEventArgs)args).Storage != null && ((StorageEventArgs)args).ItemId == "gold_safe") { ((StorageEventArgs)args).Storage.Name = "Golden Safe"; EnsureCapacity(((StorageEventArgs)args).Storage); } } private static void ItemBuilt(BuildEventArgs args) { if (SafeCreated && args != null && args.ItemId == "gold_safe") { MaterialHelper.ReplaceMaterials(args.GameObject, (Func<Material, bool>)((Material mat) => ((Object)mat).name.ToLower().Contains("safe_body")), (Action<Material>)delegate(Material material) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) MaterialHelper.RemoveAllTextures(material); MaterialHelper.SetColor(material, "_BaseColor", GoldMetalColor); MaterialHelper.SetColor(material, "_Color", GoldMetalColor); MaterialHelper.SetFloat(material, "_Metallic", 0.85f); MaterialHelper.SetFloat(material, "_Smoothness", 0.65f); MaterialHelper.SetFloat(material, "_Glossiness", 0.65f); }); } } private static Sprite LoadIcon() { Assembly executingAssembly = Assembly.GetExecutingAssembly(); return ImageUtils.LoadImageFromResource(executingAssembly, "BusinessEmployment.assets.safe_icon.png", 100f, (FilterMode)1); } } public class SafeMethods { public static void RefillSafe() { if (!BusinessEmployment.EnableSafeAutoRestock.Value) { return; } PlaceableStorageEntity result3; IEnumerable<StorageEntity> enumerable = from bi in (from b in Business.OwnedBusinesses.AsEnumerable<Business>() where ((Property)b).Employees.Count > 0 select b into p select ((Property)p).BuildableItems into items where items != null select items.AsEnumerable<BuildableItem>()).Aggregate((IEnumerable<BuildableItem> a, IEnumerable<BuildableItem> b) => a.Concat(b)) select Utils.Is<PlaceableStorageEntity>((object)bi, out result3) ? result3 : null into r where (Object)(object)r != (Object)null select r into pse where ((BuildableItem)pse).SaveFolderName.Contains("gold_safe") select pse.StorageEntity; float value = BusinessEmployment.EmpCut.Value; float cashBalance = NetworkSingleton<MoneyManager>.Instance.cashBalance; int num = ((((ItemInstance)NetworkSingleton<MoneyManager>.Instance.cashInstance).StackLimit == 1) ? 1000 : ((ItemInstance)NetworkSingleton<MoneyManager>.Instance.cashInstance).StackLimit); float num2 = 0f; float num3 = 1f + value / 100f; foreach (StorageEntity item in enumerable) { Enumerator<ItemSlot> enumerator2 = item.ItemSlots.GetEnumerator(); while (enumerator2.MoveNext()) { ItemSlot current2 = enumerator2.Current; float num4 = 0f; if (Utils.Is<CashInstance>((object)current2.ItemInstance, out CashInstance result)) { if (result != null) { float num5 = (float)num - result.Balance; num4 = num5; } } else { num4 = num; } float num6 = num2 + num4; float num7 = num6 * num3; if (num7 > cashBalance) { float num8 = cashBalance / num3; num4 = num8 - num2; } if (num4 > 0f) { if (Utils.Is<CashInstance>((object)((ItemInstance)NetworkSingleton<MoneyManager>.Instance.cashInstance).GetCopy(-1), out CashInstance result2)) { if (result2 == null) { continue; } result2.Balance = num4; current2.InsertItem((ItemInstance)(object)result2); } num2 += num4; } if (num2 * num3 >= cashBalance) { break; } } if (num2 * num3 >= cashBalance) { break; } } float num9 = num2 * (value / 100f); float num10 = num2 + num9; NetworkSingleton<MoneyManager>.Instance.ChangeCashBalance(0f - num10, true, false); Melon<BusinessEmployment>.Logger.Msg($"Refilled safes! Inserted: {num2}, Employee cut: {num9}, Total cost: {num10}"); } } } namespace BusinessEmployment.Behaviours { public class LaunderBehaviour { private static readonly Dictionary<Packager, LaunderBehaviour> _instances = new Dictionary<Packager, LaunderBehaviour>(); private readonly Packager employee; private readonly Property property; private Business propertyAsBusiness; private ELaunderEmployeeState state = ELaunderEmployeeState.Idle; private PlaceableStorageEntity? currentSE; private LaunderingStation? station; public LaunderBehaviourSaveData SaveData; private LaunderBehaviour(Packager employee) { this.employee = employee; property = ((Employee)employee).AssignedProperty; if (Utils.Is<Business>((object)property, out Business result)) { if ((Object)(object)result == (Object)null) { Melon<BusinessEmployment>.Logger.Error("Property cannot be cast to business!"); } propertyAsBusiness = result; } else { Melon<BusinessEmployment>.Logger.Error("Property cannot be cast to business!"); } string propertyCode = property.PropertyCode; IEnumerable<LaunderBehaviourSaveData> source = LaunderBehaviourSave.Instance.SaveDatas.Where((LaunderBehaviourSaveData s) => s.PropertyCode == propertyCode); if (source.Any()) { SaveData = source.First(); return; } SaveData = new LaunderBehaviourSaveData { PropertyCode = propertyCode, MoneyLeftToLaunder = 0f }; LaunderBehaviourSave.Instance.SaveDatas.Add(SaveData); } private static LaunderBehaviour? GetOrCreate(Packager employee) { if ((Object)(object)employee == (Object)null) { return null; } CleanupNullEmployees(); if (_instances.TryGetValue(employee, out LaunderBehaviour value)) { return value; } value = new LaunderBehaviour(employee); _instances[employee] = value; return value; } public static void Tick(Packager employee) { GetOrCreate(employee)?.Tick(); } private void Tick() { if (!IsValidState()) { SetIdleAndWaitOutside(); return; } if (state == ELaunderEmployeeState.Idle && (!((Employee)employee).CanWork() || propertyAsBusiness.currentLaunderTotal >= propertyAsBusiness.LaunderCapacity)) { SetIdleAndWaitOutside(); return; } ProcessCurrentState(); if (Time.frameCount % 30 == 0) { MelonDebug.Msg($"Cash: {SaveData.MoneyLeftToLaunder}, state: {state}"); } } private bool IsValidState() { return (Object)(object)propertyAsBusiness != (Object)null && (Object)(object)property != (Object)null && (Object)(object)employee != (Object)null; } private void SetIdleAndWaitOutside() { state = ELaunderEmployeeState.Idle; ((Employee)employee).SubmitNoWorkReason("There's nothing for me to do right now.", "I need to have cash to launder or free laundering capacity.", 0); ((Employee)employee).SetIdle(true); Packager obj = employee; if (obj != null) { ((Employee)obj).SetWaitOutside(true); } } private void ProcessCurrentState() { switch (state) { case ELaunderEmployeeState.Idle: HandleIdleState(); break; case ELaunderEmployeeState.RetrieveCash: HandleRetrieveCashState(); break; case ELaunderEmployeeState.DepositCash: HandleDepositCashState(); break; default: HandleIdleState(); break; } } private void HandleIdleState() { //IL_00c9: Unknown result type (might be due to invalid IL or missing references) if (SaveData.MoneyLeftToLaunder > 0f && propertyAsBusiness.currentLaunderTotal < propertyAsBusiness.LaunderCapacity && EnsureLaunderingStationExists()) { ((Employee)employee).SetWaitOutside(false); MoveToLaunderingStation(); return; } List<PlaceableStorageEntity> list = GetAllStoragesWithCash().ToList(); if (!list.Any()) { MelonDebug.Msg("No reachable storage with cash found"); return; } foreach (PlaceableStorageEntity item in list) { Vector3? val = FindReachableAccessPoint(item); if (val.HasValue) { ((Employee)employee).SetWaitOutside(false); currentSE = item; ((Employee)employee).SetDestination(val.Value, true); state = ELaunderEmployeeState.RetrieveCash; break; } } } private Vector3? FindReachableAccessPoint(PlaceableStorageEntity storage) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) if (((storage != null) ? storage.AccessPoints : null) == null) { MelonDebug.Error("Storage accesspoints is null"); return null; } foreach (Transform item in (Il2CppArrayBase<Transform>)(object)storage.AccessPoints) { if (((NPC)employee).Movement.CanGetTo(item.position, 1f)) { return item.position; } } MelonDebug.Error("Wasn't able to find a good position to move to"); return null; } private void HandleRetrieveCashState() { if (((NPC)employee).Movement.IsMoving) { return; } if ((Object)(object)currentSE == (Object)null) { state = ELaunderEmployeeState.Idle; return; } CollectCashFromStorage(); if (!EnsureLaunderingStationExists()) { state = ELaunderEmployeeState.Idle; } else { MoveToLaunderingStation(); } } private void CollectCashFromStorage() { PlaceableStorageEntity? obj = currentSE; object obj2; if (obj == null) { obj2 = null; } else { StorageEntity storageEntity = obj.StorageEntity; obj2 = ((storageEntity != null) ? storageEntity.ItemSlots : null); } if (obj2 == null) { MelonDebug.Error("Storage itemslots is null"); return; } List<ItemSlot> itemSlots = currentSE.StorageEntity.ItemSlots; for (int i = 0; i < itemSlots.Count; i++) { if (Utils.Is<CashInstance>((object)itemSlots[i].ItemInstance, out CashInstance result)) { currentSE.StorageEntity.SetSlotLocked((NetworkConnection)null, i, true, ((NetworkBehaviour)employee).NetworkObject, "Taking out items"); if (result != null) { WithdrawForLaunder(propertyAsBusiness.LaunderCapacity, result); } } currentSE.StorageEntity.SetSlotLocked((NetworkConnection)null, i, false, ((NetworkBehaviour)employee).NetworkObject, "Taking out items"); if (SaveData.MoneyLeftToLaunder >= propertyAsBusiness.LaunderCapacity) { break; } } } private bool EnsureLaunderingStationExists() { if ((Object)(object)station != (Object)null) { return true; } station = (from bi in property.BuildableItems.AsEnumerable<BuildableItem>() select Utils.Is<LaunderingStation>((object)bi, out LaunderingStation result) ? result : null).FirstOrDefault((Func<LaunderingStation, bool>)((LaunderingStation r) => (Object)(object)r != (Object)null)); if ((Object)(object)station != (Object)null) { return true; } Melon<BusinessEmployment>.Logger.Error("Laundering station not found!"); return false; } private void MoveToLaunderingStation() { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)station == (Object)null)) { Vector3 val; if (!((Object)(object)((BuildableItem)station).BoundingCollider != (Object)null)) { val = ((Component)station).transform.position; } else { Bounds bounds = ((Collider)((BuildableItem)station).BoundingCollider).bounds; val = ((Bounds)(ref bounds)).ClosestPoint(((Component)employee).transform.position); } Vector3 val2 = val; Vector3 val3 = default(Vector3); ((NPC)employee).Movement.GetClosestReachablePoint(val2, ref val3); ((Employee)employee).SetDestination(val3, true); currentSE = null; state = ELaunderEmployeeState.DepositCash; } } private void HandleDepositCashState() { if (!((NPC)employee).Movement.IsMoving) { ((NPC)employee).SetAnimationTrigger_Networked((NetworkConnection)null, "GrabItem"); float num = propertyAsBusiness.LaunderCapacity - propertyAsBusiness.currentLaunderTotal; if (num > 0f) { float num2 = Math.Min(SaveData.MoneyLeftToLaunder, num); propertyAsBusiness.StartLaunderingOperation(num2, 0); SaveData.MoneyLeftToLaunder -= num2; } state = ELaunderEmployeeState.Idle; } } private void WithdrawForLaunder(float launderTarget, CashInstance cashInstance) { float num = launderTarget - SaveData.MoneyLeftToLaunder; if (!(num <= 0f) && !(cashInstance.Balance <= 0f)) { float num2 = Math.Min(num, cashInstance.Balance); ((NPC)employee).SetAnimationTrigger_Networked((NetworkConnection)null, "GrabItem"); cashInstance.ChangeBalance(0f - num2); SaveData.MoneyLeftToLaunder += num2; } } public IEnumerable<PlaceableStorageEntity?> GetAllStoragesWithCash() { PlaceableStorageEntity result2; CashInstance result; return from bi in property.BuildableItems.AsEnumerable<BuildableItem>() select Utils.Is<PlaceableStorageEntity>((object)bi, out result2) ? result2 : null into r where (Object)(object)r != (Object)null select r into pse where (Object)(object)pse.StorageEntity != (Object)(object)((Employee)employee).GetHome().Storage where pse.OutputSlots.AsEnumerable<ItemSlot>().Any((ItemSlot os) => Utils.Is<CashInstance>((object)os.ItemInstance, out result)) orderby ((BuildableItem)pse).SaveFolderName.Contains("gold_safe") select pse; } public static void Remove(Packager employee) { _instances.Remove(employee); } private static void CleanupNullEmployees() { List<Packager> list = _instances.Keys.Where((Packager e) => (Object)(object)e == (Object)null).ToList(); foreach (Packager item in list) { _instances.Remove(item); } } } public enum ELaunderEmployeeState { Idle, RetrieveCash, DepositCash } }
BusinessEmployment-Mono.dll
Decompiled a day agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BusinessEmployment; using BusinessEmployment.Behaviours; using BusinessEmployment.BetterSafe; using BusinessEmployment.Helpers; using BusinessEmployment.Persistence; using FishNet; using FishNet.Connection; using FishNet.Object; using HarmonyLib; using MelonLoader; using MelonLoader.Preferences; using Microsoft.CodeAnalysis; using S1API.Building; using S1API.GameTime; using S1API.Internal.Abstraction; using S1API.Items; using S1API.Lifecycle; using S1API.Rendering; using S1API.Saveables; using S1API.Shops; using S1API.Storage; using S1API.Utils; using ScheduleOne.DevUtilities; using ScheduleOne.Dialogue; using ScheduleOne.Employees; using ScheduleOne.EntityFramework; using ScheduleOne.ItemFramework; using ScheduleOne.Money; using ScheduleOne.NPCs; using ScheduleOne.NPCs.Behaviour; using ScheduleOne.ObjectScripts; using ScheduleOne.PlayerScripts; using ScheduleOne.Property; using ScheduleOne.Storage; using ScheduleOne.UI; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: MelonInfo(typeof(global::BusinessEmployment.BusinessEmployment), "BusinessEmployment", "1.0.1", "k073l", null)] [assembly: MelonColor(1, 255, 195, 86)] [assembly: MelonGame("TVGS", "Schedule I")] [assembly: MelonPlatformDomain(/*Could not decode attribute arguments.*/)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: AssemblyCompany("BusinessEmployment-Mono")] [assembly: AssemblyConfiguration("Mono")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+4a7a319981f82b5cd9ac41acb7a6c0eba51c6d19")] [assembly: AssemblyProduct("BusinessEmployment-Mono")] [assembly: AssemblyTitle("BusinessEmployment-Mono")] [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.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; } } [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 BusinessEmployment { public static class BuildInfo { public const string Name = "BusinessEmployment"; public const string Description = "Adds employees to Businesses. Automates laundering."; public const string Author = "k073l"; public const string Version = "1.0.1"; } public class BusinessEmployment : MelonMod { [CompilerGenerated] private sealed class <AddDelayed>d__9 : 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 <AddDelayed>d__9(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(2f); <>1__state = 1; return true; case 1: <>1__state = -1; if (SafeCreator.SafeAdded) { return false; } _logger.Msg("Adding safe to the shop"); SafeCreator.AddToShop(); 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 Instance _logger; private static MelonPreferences_Category _category; internal static MelonPreferences_Entry<float> EmpCut; internal static MelonPreferences_Entry<float> SafeCost; internal static MelonPreferences_Entry<bool> EnableSafeAutoRestock; public override void OnInitializeMelon() { _logger = ((MelonBase)this).LoggerInstance; _logger.Msg("BusinessEmployment initialized"); _category = MelonPreferences.CreateCategory("BusinessEmployment", "Business Employment Settings"); EmpCut = _category.CreateEntry<float>("BusinessEmploymentEmployeeCut", 5f, "Employee Cut", "Additional payment for Business employees for restocking the Golden Safe. (% of the cash total)", false, false, (ValueValidator)(object)new ValueRange<float>(0f, 100f), (string)null); SafeCost = _category.CreateEntry<float>("BusinessEmploymentGoldSafeCost", 5000f, "Golden Safe price", "Price of the Golden Safe item in the Boutique", false, false, (ValueValidator)(object)new ValueRange<float>(0f, 1E+09f), (string)null); EnableSafeAutoRestock = _category.CreateEntry<bool>("BusinessEmploymentEnableSafeAutoRestock", true, "Enable Golden Safe Auto-Restock", "If enabled, businesses with employees will automatically restock their Golden Safes when you sleep.", false, false, (ValueValidator)null, (string)null); GameLifecycle.OnPreLoad += CreateSafe; } public override void OnSceneWasLoaded(int buildIndex, string sceneName) { if (!(sceneName == "Menu")) { if (sceneName == "Main") { MelonCoroutines.Start(AddDelayed()); TimeManager.OnSleepStart = (Action)Delegate.Combine(TimeManager.OnSleepStart, new Action(SafeMethods.RefillSafe)); } } else { SafeCreator.SafeCreated = false; SafeCreator.SafeAdded = false; } } public override void OnSceneWasUnloaded(int buildIndex, string sceneName) { if (sceneName != "Main") { return; } try { TimeManager.OnSleepStart = (Action)Delegate.Remove(TimeManager.OnSleepStart, new Action(SafeMethods.RefillSafe)); } catch (Exception) { } } private static void CreateSafe() { if (!SafeCreator.SafeCreated) { _logger.Msg("Creating safe"); SafeCreator.CreateSafe(); } } [IteratorStateMachine(typeof(<AddDelayed>d__9))] private static IEnumerator AddDelayed() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <AddDelayed>d__9(0); } } } namespace BusinessEmployment.Persistence { public class LaunderBehaviourSave : Saveable { [SaveableField("BusinessEmploymentLaunderBehaviourSave")] public List<LaunderBehaviourSaveData> SaveDatas = new List<LaunderBehaviourSaveData>(); public static LaunderBehaviourSave Instance { get; private set; } = new LaunderBehaviourSave(); public LaunderBehaviourSave() { Instance = this; } protected override void OnLoaded() { foreach (LaunderBehaviourSaveData saveData in SaveDatas) { } } } public record LaunderBehaviourSaveData { public string PropertyCode { get; set; } public float MoneyLeftToLaunder { get; set; } } } namespace BusinessEmployment.Patches { [HarmonyPatch(typeof(Business))] internal class BusinessIdlePoints { [HarmonyPatch("Awake")] [HarmonyPostfix] private static void AddIdlePointAndEmployeeCapacity(Business __instance) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Expected O, but got Unknown //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null) { Melon<BusinessEmployment>.Logger.Error("Business instance is null"); return; } Transform spawnPoint = ((Property)__instance).SpawnPoint; if ((Object)(object)spawnPoint == (Object)null) { Melon<BusinessEmployment>.Logger.Error("SpawnPoint for " + ((Property)__instance).PropertyName + " is null"); return; } GameObject val = new GameObject("EmployeeIdlePoints"); val.transform.SetParent(((Component)__instance).transform); Transform val2 = Object.Instantiate<Transform>(spawnPoint, spawnPoint.position, spawnPoint.rotation); ((Component)val2).transform.SetParent(val.transform); List<Transform> list = new List<Transform> { val2 }; ((Property)__instance).EmployeeIdlePoints = list.ToArray(); ((Property)__instance).EmployeeCapacity = 1; } } [HarmonyPatch(typeof(DialogueController_Fixer))] internal class FixerDialogue { [HarmonyPatch("ModifyChoiceList")] [HarmonyPrefix] private static bool IncludeBusinesses(DialogueController_Fixer __instance, string dialogueLabel, ref List<DialogueChoiceData> existingChoices) { //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Expected O, but got Unknown if ((Object)(object)__instance == (Object)null) { return true; } if (dialogueLabel != "SELECT_LOCATION") { return true; } List<DialogueChoiceData> list = new List<DialogueChoiceData>(); foreach (DialogueChoiceData existingChoice in existingChoices) { list.Add(existingChoice); } foreach (Property ownedProperty in Property.OwnedProperties) { if (ownedProperty.EmployeeCapacity > 0) { list.Add(new DialogueChoiceData { ChoiceText = ownedProperty.PropertyName, ChoiceLabel = ownedProperty.PropertyCode }); } } existingChoices.Clear(); foreach (DialogueChoiceData item in list) { existingChoices.Add(item); } MelonDebug.Msg("Sanity Check"); foreach (DialogueChoiceData existingChoice2 in existingChoices) { MelonDebug.Msg(existingChoice2.ChoiceText + ": " + existingChoice2.ChoiceLabel); } return false; } } [HarmonyPatch(typeof(DialogueCanvas))] public static class DialoguePagingPatches { private const int ChoicesPerPage = 7; private static DialogueHandler _handler; private static DialogueNodeData _node; private static string _text; private static List<DialogueChoiceData> _fullChoices; private static int _pageIndex; private static int _pageCount; private static bool PagingActive => _fullChoices != null && _pageCount > 1; private static int ClampInt(int value, int min, int max) { if (value < min) { return min; } if (value > max) { return max; } return value; } [HarmonyPatch("DisplayDialogueNode")] [HarmonyPrefix] private static void DisplayDialogueNodePrefix(DialogueHandler diag, DialogueNodeData node, string dialogueText, ref List<DialogueChoiceData> choices) { try { if (choices == null || choices.Count <= 8) { ClearPagingIfNewNode(diag, node); return; } if ((Object)(object)_handler != (Object)(object)diag || _node != node) { _handler = diag; _node = node; _text = dialogueText; _pageIndex = 0; _fullChoices = new List<DialogueChoiceData>(choices); _pageCount = ComputePageCount(_fullChoices.Count); } else { _text = dialogueText; if (_fullChoices == null || _fullChoices.Count != choices.Count) { _fullChoices = new List<DialogueChoiceData>(choices); _pageCount = ComputePageCount(_fullChoices.Count); _pageIndex = ClampInt(_pageIndex, 0, Math.Max(0, _pageCount - 1)); } } if (PagingActive) { choices = BuildPageChoices(_fullChoices, _pageIndex, _pageCount); } } catch (Exception arg) { Melon<BusinessEmployment>.Logger.Error($"Dialogue paging DisplayDialogueNodePrefix failed: {arg}"); } } [HarmonyPatch("DisplayDialogueNode")] [HarmonyPostfix] private static void DisplayDialogueNodePostfix(DialogueHandler diag) { try { if (PagingActive && !((Object)(object)_handler != (Object)(object)diag)) { diag.CurrentChoices = _fullChoices; } } catch { } } [HarmonyPatch("ChoiceSelected")] [HarmonyPrefix] private static bool ChoiceSelectedPrefix(DialogueCanvas __instance, ref int choiceIndex) { try { if (!PagingActive) { return true; } if ((Object)(object)__instance == (Object)null) { return true; } if (_fullChoices == null || (Object)(object)_handler == (Object)null || _node == null || _text == null) { return true; } int count = _fullChoices.Count; int num = _pageIndex * 7; int val = Math.Max(0, count - num); int num2 = Math.Min(7, val); if (choiceIndex == num2) { _pageIndex = (_pageIndex + 1) % _pageCount; __instance.SkipNextRollout = true; __instance.DisplayDialogueNode(_handler, _node, _text, _fullChoices); return false; } if (choiceIndex < 0 || choiceIndex >= num2) { return true; } choiceIndex = num + choiceIndex; return true; } catch (Exception arg) { Melon<BusinessEmployment>.Logger.Error($"Dialogue paging ChoiceSelectedPrefix failed: {arg}"); return true; } } [HarmonyPatch("EndDialogue")] [HarmonyPostfix] private static void EndDialoguePostfix() { ClearPaging(); } [HarmonyPatch("IsChoiceValid")] [HarmonyPrefix] private static bool IsChoiceValidPrefix(int choiceIndex, ref string reason, ref bool __result) { try { if (!PagingActive) { return true; } if ((Object)(object)_handler == (Object)null || _fullChoices == null) { return true; } int count = _fullChoices.Count; int num = _pageIndex * 7; int val = Math.Max(0, count - num); int num2 = Math.Min(7, val); if (choiceIndex <= num2) { if (IsMoreIndex(choiceIndex)) { reason = string.Empty; __result = true; return false; } if (choiceIndex < 0 || choiceIndex >= num2) { reason = string.Empty; __result = false; return false; } choiceIndex = num + choiceIndex; } List<DialogueChoiceData> currentChoices = _handler.CurrentChoices; if (currentChoices == null || choiceIndex < 0 || choiceIndex >= currentChoices.Count) { reason = string.Empty; __result = false; return false; } string text = currentChoices[choiceIndex]?.ChoiceLabel ?? string.Empty; if (string.IsNullOrWhiteSpace(text)) { reason = string.Empty; __result = false; return false; } string text2 = default(string); bool flag = _handler.CheckChoice(text, ref text2); reason = text2; __result = flag; return false; } catch { return true; } } private static bool IsMoreIndex(int choiceIndex) { if (!PagingActive) { return false; } if (_fullChoices == null) { return false; } int count = _fullChoices.Count; int num = _pageIndex * 7; int val = Math.Max(0, count - num); int num2 = Math.Min(7, val); return choiceIndex == num2; } private static void ClearPagingIfNewNode(DialogueHandler diag, DialogueNodeData node) { if ((Object)(object)_handler != (Object)(object)diag || _node != node) { ClearPaging(); } } private static void ClearPaging() { _handler = null; _node = null; _text = null; _fullChoices = null; _pageIndex = 0; _pageCount = 0; } private static int ComputePageCount(int totalChoices) { if (totalChoices <= 8) { return 0; } return (totalChoices + 7 - 1) / 7; } private static List<DialogueChoiceData> BuildPageChoices(List<DialogueChoiceData> full, int pageIndex, int pageCount) { //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Expected O, but got Unknown List<DialogueChoiceData> list = new List<DialogueChoiceData>(); int count = full.Count; int num = pageIndex * 7; int num2 = Math.Min(7, Math.Max(0, count - num)); for (int i = 0; i < num2; i++) { DialogueChoiceData val = full[num + i]; if (val != null) { list.Add(val); } } string choiceLabel = string.Empty; try { int num3 = (pageIndex + 1) % pageCount; int num4 = num3 * 7; if (num4 >= 0 && num4 < full.Count) { DialogueChoiceData val2 = full[num4]; if (val2 != null && !string.IsNullOrWhiteSpace(val2.ChoiceLabel)) { choiceLabel = val2.ChoiceLabel; } } } catch { } list.Add(new DialogueChoiceData { ChoiceLabel = choiceLabel, ChoiceText = $"More ({pageIndex + 1}/{pageCount})" }); return list; } } [HarmonyPatch(typeof(Packager))] public class HandlerLaunderBehaviorInitialize { [HarmonyPatch("UpdateBehaviour")] [HarmonyPostfix] private static void AddLaunderBehaviour(Packager __instance) { if (!((Object)(object)__instance == (Object)null) && !((Object)(object)((Employee)__instance).AssignedProperty == (Object)null) && Utils.Is<Business>(((Employee)__instance).AssignedProperty, out Business _) && ((Employee)__instance).CanWork() && !((Employee)__instance).Fired && !((Behaviour)__instance.PackagingBehaviour).Active && !((Behaviour)((Employee)__instance).MoveItemBehaviour).Active) { ((Employee)__instance).MarkIsWorking(); LaunderBehaviour.Tick(__instance); } } } } namespace BusinessEmployment.Helpers { public static class MelonLoggerExtensions { public static void Debug(this Instance logger, string message, bool stacktrace = true) { MelonDebug.Msg(stacktrace ? ("[" + GetCallerInfo() + "] " + message) : message); } private static string GetCallerInfo() { StackTrace stackTrace = new StackTrace(); for (int i = 2; i < stackTrace.FrameCount; i++) { StackFrame frame = stackTrace.GetFrame(i); MethodBase method = frame.GetMethod(); if (!(method?.DeclaringType == null)) { return method.DeclaringType.FullName + "." + method.Name; } } return "unknown"; } } public static class Il2CppListExtensions { public static IEnumerable<T> AsEnumerable<T>(this List<T> list) { return list ?? new List<T>(); } public static object ToNativeList<T>(this List<T> source) { return source ?? new List<T>(); } } public static class Utils { [CompilerGenerated] private sealed class <WaitForCondition>d__6 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public Func<bool> condition; public float timeout; public Action onTimeout; public Action onFinish; private float <startTime>5__1; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <WaitForCondition>d__6(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; <startTime>5__1 = Time.time; break; case 1: <>1__state = -1; break; } if (!condition()) { if (!float.IsNaN(timeout) && Time.time - <startTime>5__1 > timeout) { onTimeout?.Invoke(); return false; } <>2__current = null; <>1__state = 1; return true; } onFinish?.Invoke(); 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 <WaitForNetwork>d__5 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public IEnumerator routine; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <WaitForNetwork>d__5(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; break; case 1: <>1__state = -1; break; } if (!InstanceFinder.IsServer && !InstanceFinder.IsClient) { <>2__current = null; <>1__state = 1; return true; } MelonCoroutines.Start(routine); 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 <WaitForPlayer>d__4 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public IEnumerator routine; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <WaitForPlayer>d__4(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; break; case 1: <>1__state = -1; break; } if ((Object)(object)Player.Local == (Object)null || (Object)(object)((Component)Player.Local).gameObject == (Object)null) { <>2__current = null; <>1__state = 1; return true; } MelonCoroutines.Start(routine); 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 Instance Logger = new Instance("BusinessEmployment-Utils"); public static T? FindObjectByName<T>(string objectName) where T : Object { try { T[] array = Resources.FindObjectsOfTypeAll<T>(); foreach (T val in array) { if (!(((Object)val).name != objectName)) { Logger.Debug("Found " + typeof(T).Name + " '" + objectName + "' directly in loaded objects"); return val; } } return default(T); } catch (Exception ex) { Logger.Error("Error finding " + typeof(T).Name + " '" + objectName + "': " + ex.Message); return default(T); } } public static List<T> GetAllComponentsInChildrenRecursive<T>(GameObject obj) where T : Component { List<T> list = new List<T>(); if ((Object)(object)obj == (Object)null) { return list; } T[] components = obj.GetComponents<T>(); if (components.Length != 0) { list.AddRange(components); } for (int i = 0; i < obj.transform.childCount; i++) { Transform child = obj.transform.GetChild(i); list.AddRange(GetAllComponentsInChildrenRecursive<T>(((Component)child).gameObject)); } return list; } public static bool Is<T>(object obj, out T? result) where T : class { if (obj is T val) { result = val; return true; } result = null; return false; } [IteratorStateMachine(typeof(<WaitForPlayer>d__4))] public static IEnumerator WaitForPlayer(IEnumerator routine) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <WaitForPlayer>d__4(0) { routine = routine }; } [IteratorStateMachine(typeof(<WaitForNetwork>d__5))] public static IEnumerator WaitForNetwork(IEnumerator routine) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <WaitForNetwork>d__5(0) { routine = routine }; } [IteratorStateMachine(typeof(<WaitForCondition>d__6))] public static IEnumerator WaitForCondition(Func<bool> condition, float timeout = float.NaN, Action? onTimeout = null, Action? onFinish = null) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <WaitForCondition>d__6(0) { condition = condition, timeout = timeout, onTimeout = onTimeout, onFinish = onFinish }; } public static string GetHierarchyPath(this Transform transform) { if ((Object)(object)transform == (Object)null) { return "null"; } string text = ((Object)transform).name; Transform parent = transform.parent; while ((Object)(object)parent != (Object)null) { text = ((Object)parent).name + "/" + text; parent = parent.parent; } return text; } public static T GetOrAddComponent<T>(this GameObject gameObject) where T : Component { T component = gameObject.GetComponent<T>(); if ((Object)(object)component != (Object)null) { return component; } component = gameObject.AddComponent<T>(); Logger.Debug("Added component " + typeof(T).Name + " to GameObject " + ((Object)gameObject).name); return component; } public static Material? DrawDebugVisuals(this GameObject gameObject, Color? color = null) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Expected O, but got Unknown //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) Renderer component = gameObject.GetComponent<Renderer>(); if ((Object)(object)component == (Object)null) { Logger.Error("GameObject " + ((Object)gameObject).name + " has no Renderer component"); return null; } Color valueOrDefault = color.GetValueOrDefault(); if (!color.HasValue) { ((Color)(ref valueOrDefault))..ctor(1f, 0f, 1f, 0.5f); color = valueOrDefault; } Shader val = Shader.Find("Universal Render Pipeline/Lit"); if ((Object)(object)val == (Object)null) { return null; } Material val2 = new Material(val); if (val2.HasProperty("_Surface")) { val2.SetFloat("_Surface", 1f); } Color value = color.Value; if (value.a <= 0f) { value.a = 0.2f; } if (val2.HasProperty("_BaseColor")) { val2.SetColor("_BaseColor", value); } if (val2.HasProperty("_EmissionColor")) { val2.EnableKeyword("_EMISSION"); val2.SetColor("_EmissionColor", new Color(value.r, value.g, value.b) * 1.5f); } val2.SetInt("_ZWrite", 0); val2.renderQueue = 3000; Material material = component.material; component.material = val2; return material; } } } namespace BusinessEmployment.BetterSafe { internal class FilterHelper { [CompilerGenerated] private sealed class <WaitSearchAdd>d__2 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public float timeout; private float <startTime>5__1; private IEnumerable<StorageEntity> <betterSafes>5__2; private IEnumerator<StorageEntity> <>s__3; private StorageEntity <betterSafe>5__4; private List<ItemSlot>.Enumerator <>s__5; private ItemSlot <slot>5__6; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <WaitSearchAdd>d__2(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <betterSafes>5__2 = null; <>s__3 = null; <betterSafe>5__4 = null; <>s__5 = default(List<ItemSlot>.Enumerator); <slot>5__6 = null; <>1__state = -2; } private bool MoveNext() { //IL_0286: Unknown result type (might be due to invalid IL or missing references) //IL_0290: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; if (filter_Cash == null) { filter_Cash = new ItemFilter_Cash(); } <startTime>5__1 = Time.time; break; case 1: <>1__state = -1; <betterSafes>5__2 = null; break; } if (Time.time - <startTime>5__1 <= timeout) { <betterSafes>5__2 = from bi in (from p in Property.Properties.AsEnumerable() select p.BuildableItems into items where items != null select items.AsEnumerable()).Aggregate((IEnumerable<BuildableItem> a, IEnumerable<BuildableItem> b) => a.Concat(b)) select Utils.Is<PlaceableStorageEntity>(bi, out PlaceableStorageEntity result) ? result : null into r where (Object)(object)r != (Object)null select r into pse where ((BuildableItem)pse).SaveFolderName.Contains("gold_safe") select pse.StorageEntity; <>s__3 = <betterSafes>5__2.GetEnumerator(); try { while (<>s__3.MoveNext()) { <betterSafe>5__4 = <>s__3.Current; <>s__5 = <betterSafe>5__4.ItemSlots.GetEnumerator(); try { while (<>s__5.MoveNext()) { <slot>5__6 = <>s__5.Current; if (!<slot>5__6.HardFilters.AsEnumerable().Any((ItemFilter itFi) => ((object)itFi).GetType() == ((object)filter_Cash).GetType())) { <slot>5__6.AddFilter((ItemFilter)(object)filter_Cash); <slot>5__6 = null; } } } finally { ((IDisposable)<>s__5).Dispose(); } <>s__5 = default(List<ItemSlot>.Enumerator); <betterSafe>5__4 = null; } } finally { if (<>s__3 != null) { <>s__3.Dispose(); } } <>s__3 = null; <>2__current = (object)new WaitForSecondsRealtime(0.5f); <>1__state = 1; return true; } 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 ItemFilter_Cash filter_Cash; private static Instance _logger = Melon<BusinessEmployment>.Logger; [IteratorStateMachine(typeof(<WaitSearchAdd>d__2))] internal static IEnumerator WaitSearchAdd(float timeout) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <WaitSearchAdd>d__2(0) { timeout = timeout }; } } [RegisterTypeInIl2Cpp] public class ItemFilter_Cash : ItemFilter { public override bool DoesItemMatchFilter(ItemInstance item) { CashInstance result; return Utils.Is<CashInstance>(item, out result); } } public class SafeCreator { internal const string SAFE_ID = "gold_safe"; private const string SAFE_NAME = "Golden Safe"; internal const int SLOT_COUNT = 20; public static bool SafeCreated; public static bool SafeAdded; private static readonly Color GoldMetalColor = new Color(1f, 0.766f, 0.336f, 1f); public static void CreateSafe() { BuildableItemDefinition val = BuildableItemCreator.CloneFrom("safe").WithBasicInfo("gold_safe", "Golden Safe", "An upgraded version of the safe. Can hold only cash.").WithPricing(BusinessEmployment.SafeCost.Value, 0.8f) .WithIcon(LoadIcon()) .Build(); BuildEvents.OnBuildableItemInitialized += ItemBuilt; BuildEvents.OnGridItemCreated += ItemBuilt; BuildEvents.OnSurfaceItemCreated += ItemBuilt; StorageEvents.OnStorageCreated += StorageCreated; StorageEvents.OnStorageLoading += StorageLoaded; StorageEvents.OnStorageOpening += StorageOpened; SafeCreated = true; } public static void AddToShop() { ItemDefinition itemDefinition = ItemManager.GetItemDefinition("gold_safe"); if (itemDefinition == (ItemDefinition)null) { Melon<BusinessEmployment>.Logger.Error("Error while adding safe to shop"); return; } Shop shopByName = ShopManager.GetShopByName("Bleuball's Boutique"); if (shopByName == null) { Melon<BusinessEmployment>.Logger.Error("Shop not found"); return; } shopByName.AddItem(itemDefinition, (float?)null); SafeAdded = true; } private static void EnsureCapacity(StorageEntity storage) { if (storage != null) { if (!storage.SetSlotCount(20)) { Melon<BusinessEmployment>.Logger.Error("Failed to add additional slots"); } else { MelonCoroutines.Start(FilterHelper.WaitSearchAdd(10f)); } } } private static void StorageCreated(StorageEventArgs args) { if (args != null && args.Storage != null && args.ItemId == "gold_safe") { args.Storage.Name = "Golden Safe"; EnsureCapacity(args.Storage); } } private static void StorageOpened(StorageEventArgs args) { if (args != null) { StorageEntity storage = args.Storage; if (storage != null) { storage.SyncCustomNameToDisplayName(); } } } private static void StorageLoaded(StorageLoadingEventArgs args) { if (args != null && ((StorageEventArgs)args).Storage != null && ((StorageEventArgs)args).ItemId == "gold_safe") { ((StorageEventArgs)args).Storage.Name = "Golden Safe"; EnsureCapacity(((StorageEventArgs)args).Storage); } } private static void ItemBuilt(BuildEventArgs args) { if (SafeCreated && args != null && args.ItemId == "gold_safe") { MaterialHelper.ReplaceMaterials(args.GameObject, (Func<Material, bool>)((Material mat) => ((Object)mat).name.ToLower().Contains("safe_body")), (Action<Material>)delegate(Material material) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) MaterialHelper.RemoveAllTextures(material); MaterialHelper.SetColor(material, "_BaseColor", GoldMetalColor); MaterialHelper.SetColor(material, "_Color", GoldMetalColor); MaterialHelper.SetFloat(material, "_Metallic", 0.85f); MaterialHelper.SetFloat(material, "_Smoothness", 0.65f); MaterialHelper.SetFloat(material, "_Glossiness", 0.65f); }); } } private static Sprite LoadIcon() { Assembly executingAssembly = Assembly.GetExecutingAssembly(); return ImageUtils.LoadImageFromResource(executingAssembly, "BusinessEmployment.assets.safe_icon.png", 100f, (FilterMode)1); } } public class SafeMethods { public static void RefillSafe() { if (!BusinessEmployment.EnableSafeAutoRestock.Value) { return; } PlaceableStorageEntity result3; IEnumerable<StorageEntity> enumerable = from bi in (from b in Business.OwnedBusinesses.AsEnumerable() where ((Property)b).Employees.Count > 0 select b into p select ((Property)p).BuildableItems into items where items != null select items.AsEnumerable()).Aggregate((IEnumerable<BuildableItem> a, IEnumerable<BuildableItem> b) => a.Concat(b)) select Utils.Is<PlaceableStorageEntity>(bi, out result3) ? result3 : null into r where (Object)(object)r != (Object)null select r into pse where ((BuildableItem)pse).SaveFolderName.Contains("gold_safe") select pse.StorageEntity; float value = BusinessEmployment.EmpCut.Value; float cashBalance = NetworkSingleton<MoneyManager>.Instance.cashBalance; int num = ((((ItemInstance)NetworkSingleton<MoneyManager>.Instance.cashInstance).StackLimit == 1) ? 1000 : ((ItemInstance)NetworkSingleton<MoneyManager>.Instance.cashInstance).StackLimit); float num2 = 0f; float num3 = 1f + value / 100f; foreach (StorageEntity item in enumerable) { foreach (ItemSlot itemSlot in item.ItemSlots) { float num4 = 0f; if (Utils.Is<CashInstance>(itemSlot.ItemInstance, out CashInstance result)) { if (result != null) { float num5 = (float)num - result.Balance; num4 = num5; } } else { num4 = num; } float num6 = num2 + num4; float num7 = num6 * num3; if (num7 > cashBalance) { float num8 = cashBalance / num3; num4 = num8 - num2; } if (num4 > 0f) { if (Utils.Is<CashInstance>(((ItemInstance)NetworkSingleton<MoneyManager>.Instance.cashInstance).GetCopy(-1), out CashInstance result2)) { if (result2 == null) { continue; } result2.Balance = num4; itemSlot.InsertItem((ItemInstance)(object)result2); } num2 += num4; } if (num2 * num3 >= cashBalance) { break; } } if (num2 * num3 >= cashBalance) { break; } } float num9 = num2 * (value / 100f); float num10 = num2 + num9; NetworkSingleton<MoneyManager>.Instance.ChangeCashBalance(0f - num10, true, false); Melon<BusinessEmployment>.Logger.Msg($"Refilled safes! Inserted: {num2}, Employee cut: {num9}, Total cost: {num10}"); } } } namespace BusinessEmployment.Behaviours { public class LaunderBehaviour { private static readonly Dictionary<Packager, LaunderBehaviour> _instances = new Dictionary<Packager, LaunderBehaviour>(); private readonly Packager employee; private readonly Property property; private Business propertyAsBusiness; private ELaunderEmployeeState state = ELaunderEmployeeState.Idle; private PlaceableStorageEntity? currentSE; private LaunderingStation? station; public LaunderBehaviourSaveData SaveData; private LaunderBehaviour(Packager employee) { this.employee = employee; property = ((Employee)employee).AssignedProperty; if (Utils.Is<Business>(property, out Business result)) { if ((Object)(object)result == (Object)null) { Melon<BusinessEmployment>.Logger.Error("Property cannot be cast to business!"); } propertyAsBusiness = result; } else { Melon<BusinessEmployment>.Logger.Error("Property cannot be cast to business!"); } string propertyCode = property.PropertyCode; IEnumerable<LaunderBehaviourSaveData> source = LaunderBehaviourSave.Instance.SaveDatas.Where((LaunderBehaviourSaveData s) => s.PropertyCode == propertyCode); if (source.Any()) { SaveData = source.First(); return; } SaveData = new LaunderBehaviourSaveData { PropertyCode = propertyCode, MoneyLeftToLaunder = 0f }; LaunderBehaviourSave.Instance.SaveDatas.Add(SaveData); } private static LaunderBehaviour? GetOrCreate(Packager employee) { if ((Object)(object)employee == (Object)null) { return null; } CleanupNullEmployees(); if (_instances.TryGetValue(employee, out LaunderBehaviour value)) { return value; } value = new LaunderBehaviour(employee); _instances[employee] = value; return value; } public static void Tick(Packager employee) { GetOrCreate(employee)?.Tick(); } private void Tick() { if (!IsValidState()) { SetIdleAndWaitOutside(); return; } if (state == ELaunderEmployeeState.Idle && (!((Employee)employee).CanWork() || propertyAsBusiness.currentLaunderTotal >= propertyAsBusiness.LaunderCapacity)) { SetIdleAndWaitOutside(); return; } ProcessCurrentState(); if (Time.frameCount % 30 == 0) { MelonDebug.Msg($"Cash: {SaveData.MoneyLeftToLaunder}, state: {state}"); } } private bool IsValidState() { return (Object)(object)propertyAsBusiness != (Object)null && (Object)(object)property != (Object)null && (Object)(object)employee != (Object)null; } private void SetIdleAndWaitOutside() { state = ELaunderEmployeeState.Idle; ((Employee)employee).SubmitNoWorkReason("There's nothing for me to do right now.", "I need to have cash to launder or free laundering capacity.", 0); ((Employee)employee).SetIdle(true); Packager obj = employee; if (obj != null) { ((Employee)obj).SetWaitOutside(true); } } private void ProcessCurrentState() { switch (state) { case ELaunderEmployeeState.Idle: HandleIdleState(); break; case ELaunderEmployeeState.RetrieveCash: HandleRetrieveCashState(); break; case ELaunderEmployeeState.DepositCash: HandleDepositCashState(); break; default: HandleIdleState(); break; } } private void HandleIdleState() { //IL_00c9: Unknown result type (might be due to invalid IL or missing references) if (SaveData.MoneyLeftToLaunder > 0f && propertyAsBusiness.currentLaunderTotal < propertyAsBusiness.LaunderCapacity && EnsureLaunderingStationExists()) { ((Employee)employee).SetWaitOutside(false); MoveToLaunderingStation(); return; } List<PlaceableStorageEntity> list = GetAllStoragesWithCash().ToList(); if (!list.Any()) { MelonDebug.Msg("No reachable storage with cash found"); return; } foreach (PlaceableStorageEntity item in list) { Vector3? val = FindReachableAccessPoint(item); if (val.HasValue) { ((Employee)employee).SetWaitOutside(false); currentSE = item; ((Employee)employee).SetDestination(val.Value, true); state = ELaunderEmployeeState.RetrieveCash; break; } } } private Vector3? FindReachableAccessPoint(PlaceableStorageEntity storage) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) if (((storage != null) ? storage.AccessPoints : null) == null) { MelonDebug.Error("Storage accesspoints is null"); return null; } Transform[] accessPoints = storage.AccessPoints; foreach (Transform val in accessPoints) { if (((NPC)employee).Movement.CanGetTo(val.position, 1f)) { return val.position; } } MelonDebug.Error("Wasn't able to find a good position to move to"); return null; } private void HandleRetrieveCashState() { if (((NPC)employee).Movement.IsMoving) { return; } if ((Object)(object)currentSE == (Object)null) { state = ELaunderEmployeeState.Idle; return; } CollectCashFromStorage(); if (!EnsureLaunderingStationExists()) { state = ELaunderEmployeeState.Idle; } else { MoveToLaunderingStation(); } } private void CollectCashFromStorage() { PlaceableStorageEntity? obj = currentSE; object obj2; if (obj == null) { obj2 = null; } else { StorageEntity storageEntity = obj.StorageEntity; obj2 = ((storageEntity != null) ? storageEntity.ItemSlots : null); } if (obj2 == null) { MelonDebug.Error("Storage itemslots is null"); return; } List<ItemSlot> itemSlots = currentSE.StorageEntity.ItemSlots; for (int i = 0; i < itemSlots.Count; i++) { if (Utils.Is<CashInstance>(itemSlots[i].ItemInstance, out CashInstance result)) { currentSE.StorageEntity.SetSlotLocked((NetworkConnection)null, i, true, ((NetworkBehaviour)employee).NetworkObject, "Taking out items"); if (result != null) { WithdrawForLaunder(propertyAsBusiness.LaunderCapacity, result); } } currentSE.StorageEntity.SetSlotLocked((NetworkConnection)null, i, false, ((NetworkBehaviour)employee).NetworkObject, "Taking out items"); if (SaveData.MoneyLeftToLaunder >= propertyAsBusiness.LaunderCapacity) { break; } } } private bool EnsureLaunderingStationExists() { if ((Object)(object)station != (Object)null) { return true; } station = (from bi in property.BuildableItems.AsEnumerable() select Utils.Is<LaunderingStation>(bi, out LaunderingStation result) ? result : null).FirstOrDefault((Func<LaunderingStation, bool>)((LaunderingStation r) => (Object)(object)r != (Object)null)); if ((Object)(object)station != (Object)null) { return true; } Melon<BusinessEmployment>.Logger.Error("Laundering station not found!"); return false; } private void MoveToLaunderingStation() { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)station == (Object)null)) { Vector3 val; if (!((Object)(object)((BuildableItem)station).BoundingCollider != (Object)null)) { val = ((Component)station).transform.position; } else { Bounds bounds = ((Collider)((BuildableItem)station).BoundingCollider).bounds; val = ((Bounds)(ref bounds)).ClosestPoint(((Component)employee).transform.position); } Vector3 val2 = val; Vector3 val3 = default(Vector3); ((NPC)employee).Movement.GetClosestReachablePoint(val2, ref val3); ((Employee)employee).SetDestination(val3, true); currentSE = null; state = ELaunderEmployeeState.DepositCash; } } private void HandleDepositCashState() { if (!((NPC)employee).Movement.IsMoving) { ((NPC)employee).SetAnimationTrigger_Networked((NetworkConnection)null, "GrabItem"); float num = propertyAsBusiness.LaunderCapacity - propertyAsBusiness.currentLaunderTotal; if (num > 0f) { float num2 = Math.Min(SaveData.MoneyLeftToLaunder, num); propertyAsBusiness.StartLaunderingOperation(num2, 0); SaveData.MoneyLeftToLaunder -= num2; } state = ELaunderEmployeeState.Idle; } } private void WithdrawForLaunder(float launderTarget, CashInstance cashInstance) { float num = launderTarget - SaveData.MoneyLeftToLaunder; if (!(num <= 0f) && !(cashInstance.Balance <= 0f)) { float num2 = Math.Min(num, cashInstance.Balance); ((NPC)employee).SetAnimationTrigger_Networked((NetworkConnection)null, "GrabItem"); cashInstance.ChangeBalance(0f - num2); SaveData.MoneyLeftToLaunder += num2; } } public IEnumerable<PlaceableStorageEntity?> GetAllStoragesWithCash() { PlaceableStorageEntity result2; CashInstance result; return from bi in property.BuildableItems.AsEnumerable() select Utils.Is<PlaceableStorageEntity>(bi, out result2) ? result2 : null into r where (Object)(object)r != (Object)null select r into pse where (Object)(object)pse.StorageEntity != (Object)(object)((Employee)employee).GetHome().Storage where pse.OutputSlots.AsEnumerable().Any((ItemSlot os) => Utils.Is<CashInstance>(os.ItemInstance, out result)) orderby ((BuildableItem)pse).SaveFolderName.Contains("gold_safe") select pse; } public static void Remove(Packager employee) { _instances.Remove(employee); } private static void CleanupNullEmployees() { List<Packager> list = _instances.Keys.Where((Packager e) => (Object)(object)e == (Object)null).ToList(); foreach (Packager item in list) { _instances.Remove(item); } } } public enum ELaunderEmployeeState { Idle, RetrieveCash, DepositCash } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { internal IgnoresAccessChecksToAttribute(string assemblyName) { } } }