Some mods target the Mono version of the game, which is available by opting into the Steam beta branch "alternate"
Decompiled source of AutoRestockMono v1.1.0
Mods\AutoRestockMono.dll
Decompiled 14 hours agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Threading; using AutoRestock; using FishNet; using FishNet.Object; using HarmonyLib; using MelonLoader; using MelonLoader.Preferences; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using ScheduleOne; using ScheduleOne.DevUtilities; using ScheduleOne.EntityFramework; using ScheduleOne.GameTime; using ScheduleOne.ItemFramework; using ScheduleOne.Management; using ScheduleOne.Messaging; using ScheduleOne.Money; using ScheduleOne.NPCs; using ScheduleOne.NPCs.Behaviour; using ScheduleOne.ObjectScripts; using ScheduleOne.Persistence; using ScheduleOne.PlayerTasks; using ScheduleOne.Property; using ScheduleOne.StationFramework; using ScheduleOne.Storage; using ScheduleOne.UI; using ScheduleOne.UI.Items; using UnityEngine; using UnityEngine.Events; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: MelonInfo(typeof(AutoRestockMod), "AutoRestock", "1.1.0", "lasersquid", null)] [assembly: MelonGame("TVGS", "Schedule I")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("AutoRestockMono")] [assembly: AssemblyConfiguration("Mono")] [assembly: AssemblyFileVersion("1.1.0.0")] [assembly: AssemblyInformationalVersion("1.1.0+45ed0abd6ee8d22e276765008deb99ac2a061276")] [assembly: AssemblyProduct("AutoRestockMono")] [assembly: AssemblyTitle("AutoRestockMono")] [assembly: NeutralResourcesLanguage("en-US")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.1.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace AutoRestock { public class AutoRestockMod : MelonMod { public MelonPreferences_Category melonPrefs; public Harmony harmony = new Harmony("com.lasersquid.autorestock"); public override void OnInitializeMelon() { CreateMelonPreferences(); Utils.Initialize(this); ((MelonBase)this).LoggerInstance.Msg("Mod initialized."); } private void CreateMelonPreferences() { melonPrefs = MelonPreferences.CreateCategory("AutoRestock"); melonPrefs.SetFilePath("UserData/AutoRestock.cfg", true, false); melonPrefs.CreateEntry<float>("itemDiscount", 0f, "Restock discount", "Discount applied to restock price (0.2 = 20% off)", false, false, (ValueValidator)null, (string)null); melonPrefs.CreateEntry<bool>("payWithCash", true, "Pay for restock with cash", "True to pay with cash, false to pay with bank account", false, false, (ValueValidator)null, (string)null); melonPrefs.CreateEntry<int>("restockAmount", 0, "Amount to restock", "Amount to restock when a restock is triggered (enter 0 for item stacklimit)", false, false, (ValueValidator)null, (string)null); melonPrefs.CreateEntry<bool>("enableCauldrons", true, "Enable auto-restock on cauldrons", "Enable auto-restock on cauldrons", false, false, (ValueValidator)null, (string)null); melonPrefs.CreateEntry<bool>("enableMixingStations", true, "Enable auto-restock on mixing stations", "Enable auto-restock on mixing stations", false, false, (ValueValidator)null, (string)null); melonPrefs.CreateEntry<bool>("enableChemistryStations", true, "Enable auto-restock on chemistry stations", "Enable auto-restock on chemistry stations", false, false, (ValueValidator)null, (string)null); melonPrefs.CreateEntry<bool>("enablePackagingStations", true, "Enable auto-restock on packaging stations", "Enable auto-restock on packaging stations", false, false, (ValueValidator)null, (string)null); melonPrefs.CreateEntry<bool>("enableSpawnStations", true, "Enable auto-restock on mushroom spawn stations", "Enable auto-restock on mushroom spawn stations", false, false, (ValueValidator)null, (string)null); melonPrefs.CreateEntry<bool>("enableStorage", true, "Enable auto-restock on storage (shelves and safes)", "Enable auto-restock on storage (shelves and safes)", false, false, (ValueValidator)null, (string)null); melonPrefs.CreateEntry<bool>("playerRestockStations", true, "Enable auto-restock on stations after player-initiated actions (start cauldron, etc)", "Enable auto-restock on stations after player-initiated actions (start cauldron, etc)", false, false, (ValueValidator)null, (string)null); melonPrefs.CreateEntry<bool>("verboseLogs", false, "Print to the log for each auto-restock transaction", "Print to the log for each auto-restock transaction", false, false, (ValueValidator)null, (string)null); melonPrefs.CreateEntry<bool>("debugLogs", false, "Print debug logs", "Print debug logs", false, false, (ValueValidator)null, (string)null); melonPrefs.SaveToFile(false); } } public static class Utils { public class UnityObjectComparer : IEqualityComparer<Object> { public bool Equals(Object a, Object b) { return a.GetInstanceID() == b.GetInstanceID(); } public int GetHashCode(Object item) { return item.GetInstanceID(); } } private static AutoRestockMod Mod; private static Assembly S1Assembly; public static void Initialize(AutoRestockMod mod) { Mod = mod; } public static void PrintException(Exception e) { Warn("Exception: " + e.GetType().Name + " - " + e.Message); Warn("Source: " + e.Source); Warn(e.StackTrace ?? ""); if (e.InnerException != null) { Warn("Inner exception: " + e.InnerException.GetType().Name + " - " + e.InnerException.Message); Warn("Source: " + e.InnerException.Source); Warn(e.InnerException.StackTrace ?? ""); if (e.InnerException.InnerException != null) { Warn("Inner inner exception: " + e.InnerException.InnerException.GetType().Name + " - " + e.InnerException.InnerException.Message); Warn("Source: " + e.InnerException.InnerException.Source); Warn(e.InnerException.InnerException.StackTrace ?? ""); } } } public static void Log(string message) { ((MelonBase)Mod).LoggerInstance.Msg(message); } public static void Warn(string message) { ((MelonBase)Mod).LoggerInstance.Warning(message); } public static void Debug(string message) { if (Manager.isInitialized && Manager.melonPrefs.GetEntry<bool>("debugLogs").Value) { ((MelonBase)Mod).LoggerInstance.Msg("DEBUG: " + message); } } public static void VerboseLog(string message) { if (Manager.isInitialized && Manager.melonPrefs.GetEntry<bool>("verboseLogs").Value) { ((MelonBase)Mod).LoggerInstance.Msg(message); } } public static Treturn GetField<Ttarget, Treturn>(string fieldName, object target) where Treturn : class { return (Treturn)GetField<Ttarget>(fieldName, target); } public static object GetField<Ttarget>(string fieldName, object target) { return AccessTools.Field(typeof(Ttarget), fieldName).GetValue(target); } public static void SetField<Ttarget>(string fieldName, object target, object value) { AccessTools.Field(typeof(Ttarget), fieldName).SetValue(target, value); } public static Treturn GetProperty<Ttarget, Treturn>(string fieldName, object target) where Treturn : class { return (Treturn)GetProperty<Ttarget>(fieldName, target); } public static object GetProperty<Ttarget>(string fieldName, object target) { return AccessTools.Property(typeof(Ttarget), fieldName).GetValue(target); } public static void SetProperty<Ttarget>(string fieldName, object target, object value) { AccessTools.Property(typeof(Ttarget), fieldName).SetValue(target, value); } public static Treturn CallMethod<Ttarget, Treturn>(string methodName, object target) where Treturn : class { return (Treturn)CallMethod<Ttarget>(methodName, target, Array.Empty<object>()); } public static Treturn CallMethod<Ttarget, Treturn>(string methodName, object target, object[] args) where Treturn : class { return (Treturn)CallMethod<Ttarget>(methodName, target, args); } public static Treturn CallMethod<Ttarget, Treturn>(string methodName, Type[] argTypes, object target, object[] args) where Treturn : class { return (Treturn)CallMethod<Ttarget>(methodName, argTypes, target, args); } public static object CallMethod<Ttarget>(string methodName, object target) { return AccessTools.Method(typeof(Ttarget), methodName, (Type[])null, (Type[])null).Invoke(target, Array.Empty<object>()); } public static object CallMethod<Ttarget>(string methodName, object target, object[] args) { return AccessTools.Method(typeof(Ttarget), methodName, (Type[])null, (Type[])null).Invoke(target, args); } public static object CallMethod<Ttarget>(string methodName, Type[] argTypes, object target, object[] args) { return AccessTools.Method(typeof(Ttarget), methodName, argTypes, (Type[])null).Invoke(target, args); } public static T CastTo<T>(object o) { if (!(o is T result)) { return default(T); } return result; } public static bool Is<T>(object o) { return o is T; } public static T ToInterface<T>(object o) { return (T)o; } public static Type GetType(object o) { return o?.GetType(); } public static UnityAction ToUnityAction(Action action) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Expected O, but got Unknown return new UnityAction(action.Invoke); } public static UnityAction<T> ToUnityAction<T>(Action<T> action) { return action.Invoke; } public static Predicate<T> ToPredicate<T>(Func<T, bool> func) { return func.Invoke; } public static List<ItemDefinition> GetItemDefsContaining(List<string> terms) { List<ItemDefinition> allItems = Singleton<Registry>.Instance.GetAllItems(); return allItems.FindAll(ToPredicate((ItemDefinition def) => terms.Any((string term) => def.ID.Contains(term)))); } public static bool IsQualityIngredient(string itemID) { List<string> source = new List<string>(1) { "pseudo" }; return source.Any((string id) => itemID.Contains(id)); } public static StorableItemInstance GetItemInstance(string itemID, EQuality quality = 2) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) if (IsQualityIngredient(itemID)) { return (StorableItemInstance)(object)GetQualityItemInstance(itemID, quality); } return CastTo<StorableItemInstance>(Registry.GetItem(itemID).GetDefaultInstance(1)); } public static QualityItemInstance GetQualityItemInstance(string itemID, EQuality quality) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) Dictionary<EQuality, string> dictionary = new Dictionary<EQuality, string> { { (EQuality)4, "heavenly" }, { (EQuality)3, "highquality" }, { (EQuality)2, "" }, { (EQuality)1, "lowquality" }, { (EQuality)0, "trash" } }; if (IsQualityIngredient(itemID)) { string text = dictionary[quality] + itemID; QualityItemInstance val = CastTo<QualityItemInstance>(Registry.GetItem(text).GetDefaultInstance(1)); val.Quality = quality; return val; } Warn("itemid " + itemID + " is not a quality ingredient?"); return null; } public static bool IsStorageRack(ITransitEntity transitEntity) { if (transitEntity != null && Is<PlaceableStorageEntity>(transitEntity)) { PlaceableStorageEntity placeable = CastTo<PlaceableStorageEntity>(transitEntity); return IsStorageRack(placeable); } return false; } public static bool IsStorageRack(IItemSlotOwner slotOwner) { if (slotOwner != null && Is<StorageEntity>(slotOwner)) { StorageEntity entity = CastTo<StorageEntity>(slotOwner); return IsStorageRack(entity); } return false; } public static bool IsStorageRack(StorageEntity entity) { if ((Object)(object)entity != (Object)null) { PlaceableStorageEntity component = ((Component)entity).GetComponent<PlaceableStorageEntity>(); if ((Object)(object)component != (Object)null) { return IsStorageRack(component); } Warn("StorageEntity " + ((Object)entity).name + " did not have PlaceableStorageEntity component"); return false; } return false; } public static bool IsStorageRack(PlaceableStorageEntity placeable) { List<string> source = new List<string>(5) { "safe", "smallstoragerack", "mediumstoragerack", "largestoragerack", "wallmountedshelf" }; if ((Object)(object)placeable != (Object)null) { string placeableID = ((BuildableItem)placeable).ItemInstance.ID; return source.Any((string id) => placeableID.Contains(id)); } return false; } public static bool IsStation(ITransitEntity transitEntity) { if (transitEntity != null && Is<GridItem>(transitEntity)) { GridItem gridItem = CastTo<GridItem>(transitEntity); return IsStation(gridItem); } return false; } public static bool IsStation(IItemSlotOwner slotOwner) { if (slotOwner != null && Is<GridItem>(slotOwner)) { GridItem gridItem = CastTo<GridItem>(slotOwner); return IsStation(gridItem); } return false; } public static bool IsStation(GridItem gridItem) { List<Type> list = new List<Type>(7); list.Add(typeof(PackagingStation)); list.Add(typeof(Cauldron)); list.Add(typeof(ChemistryStation)); list.Add(typeof(MixingStation)); list.Add(typeof(MushroomSpawnStation)); list.Add(typeof(LabOven)); list.Add(typeof(DryingRack)); List<Type> source = list; if ((Object)(object)gridItem != (Object)null) { Type ownerType = GetType(gridItem); return source.Any((Type t) => t.IsAssignableFrom(ownerType)); } return false; } } public static class Manager { public class Transaction { public string itemID; public int quantity; public float discount; public float unitPrice; public float totalCost; public string property; public bool useCash; public SlotIdentifier slotID; public Transaction(string itemID, int quantity, float discount, float unitPrice, float totalCost, bool useCash, SlotIdentifier slotID) { this.itemID = itemID; this.quantity = quantity; this.discount = discount; this.unitPrice = unitPrice; this.totalCost = totalCost; this.useCash = useCash; this.slotID = slotID; } } public class SlotIdentifier { public List<float> gridLocation; public string type; public int slotIndex; public string property; public SlotIdentifier(string property, Vector2 gridLocation, int slotIndex, string type) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) this.property = property; this.gridLocation = new List<float>(new <>z__ReadOnlyArray<float>(new float[2] { gridLocation.x, gridLocation.y })); this.slotIndex = slotIndex; this.type = type; } [JsonConstructor] public SlotIdentifier(string property, List<float> gridLocation, int slotIndex, string type) { this.property = property; this.gridLocation = gridLocation; this.slotIndex = slotIndex; this.type = type; } } [CompilerGenerated] private sealed class <RestockCoroutine>d__26 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public ItemSlot slot; public StorableItemInstance item; public Transaction transaction; private bool <didPay>5__1; private bool <payWithCash>5__2; private float <balance>5__3; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <RestockCoroutine>d__26(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForEndOfFrame(); <>1__state = 1; return true; case 1: <>1__state = -1; slot.ClearStoredInstance(false); slot.ApplyLock(((NetworkBehaviour)oscar).NetworkObject, "Restocking item", false); slot.SetIsAddLocked(true); <>2__current = (object)new WaitForSeconds(1f); <>1__state = 2; return true; case 2: <>1__state = -1; Utils.VerboseLog($"Restocking {((ItemInstance)item).Name} (${transaction.unitPrice}) x{transaction.quantity} at {transaction.slotID.property}. Total: ${transaction.totalCost}."); <didPay>5__1 = false; if (transaction.totalCost <= 0f) { Utils.VerboseLog("Total cost of transaction is $0. Get a freebie!"); <didPay>5__1 = true; } else { <payWithCash>5__2 = melonPrefs.GetEntry<bool>("payWithCash").Value; <balance>5__3 = (<payWithCash>5__2 ? moneyManager.cashBalance : moneyManager.onlineBalance); if (<balance>5__3 < transaction.totalCost) { Utils.Log($"Insufficient balance to restock {((ItemInstance)item).Name} (${transaction.unitPrice}) x{transaction.quantity} at {transaction.slotID.property}, total ${transaction.totalCost}; aborting."); } else { if (<payWithCash>5__2) { moneyManager.ChangeCashBalance(0f - transaction.totalCost, true, false); } else { moneyManager.CreateOnlineTransaction("Restock", 0f - transaction.totalCost, 1f, ((ItemInstance)item).Definition.Name ?? ""); } <didPay>5__1 = true; } } slot.SetIsAddLocked(false); slot.RemoveLock(false); if (<didPay>5__1 && transaction.quantity > 0) { ((ItemInstance)item).SetQuantity(transaction.quantity - slot.Quantity); slot.AddItem((ItemInstance)(object)item, false); } AcquireMutex(); coroutines.Remove(transaction); ReleaseMutex(); 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(); } } public static ItemSlot playerClickedSlot; public static List<ItemSlot> playerStationOperationSlots; public static MelonPreferences_Category melonPrefs; private static TimeManager timeManager; private static MoneyManager moneyManager; private static SaveManager saveManager; private static NPC oscar; private static string ledgerString; private static string transactionString; private static List<Transaction> ledger; private static Dictionary<Transaction, object> coroutines; private static EDay ledgerDay; private static Mutex exclusiveLock; public static bool isInitialized; public static SlotIdentifier SerializeSlot(ItemSlot slot) { //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) GridItem val; string type; if (Utils.IsStation(slot.SlotOwner)) { val = Utils.CastTo<GridItem>(slot.SlotOwner); type = ((Object)val).name; } else { if (!Utils.IsStorageRack(slot.SlotOwner)) { Utils.Warn($"Couldn't serialize itemslot--not station or storage rack? ({Utils.GetType(slot.SlotOwner)})"); return null; } StorageEntity val2 = Utils.CastTo<StorageEntity>(slot.SlotOwner); val = ((Component)val2).gameObject.GetComponent<GridItem>(); type = val2.StorageEntityName; } string name = ((Object)((BuildableItem)val).ParentProperty).name; Vector2 gridLocation = (Vector2)Utils.GetField<GridItem>("_originCoordinate", val); return new SlotIdentifier(name, gridLocation, (int)Utils.GetProperty<ItemSlot>("SlotIndex", slot), type); } public static ItemSlot DeserializeSlot(SlotIdentifier identifier) { //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) try { List<Property> source = Object.FindObjectsOfType<Property>().ToList(); List<GridItem> list = Object.FindObjectsOfType<GridItem>().ToList(); Property property = ((IEnumerable<Property>)source).FirstOrDefault((Func<Property, bool>)((Property p) => ((Object)p).name == identifier.property)); List<GridItem> source2 = list.FindAll((GridItem g) => (Object)(object)((BuildableItem)g).ParentProperty == (Object)(object)property); Vector2 targetCoord = new Vector2(identifier.gridLocation[0], identifier.gridLocation[1]); GridItem val = ((IEnumerable<GridItem>)source2).FirstOrDefault((Func<GridItem, bool>)((GridItem g) => (Vector2)Utils.GetField<GridItem>("_originCoordinate", g) == targetCoord)); if ((Object)(object)val == (Object)null) { Utils.Warn("Couldn't deserialize slot--coordinates did not map to a griditem"); return null; } IItemSlotOwner val2; if (Utils.IsStation(val)) { val2 = Utils.ToInterface<IItemSlotOwner>(val); } else { if (!Utils.Is<PlaceableStorageEntity>(val)) { Utils.Warn($"couldn't deserialize slot--obj was not a station or placeablestorageentity ({Utils.GetType(val)})"); return null; } StorageEntity storageEntity = Utils.CastTo<PlaceableStorageEntity>(val).StorageEntity; val2 = Utils.ToInterface<IItemSlotOwner>(storageEntity); } return val2.ItemSlots[identifier.slotIndex]; } catch (Exception e) { Utils.PrintException(e); } return null; } public static void CompleteTransactions(List<Transaction> transactions) { try { foreach (Transaction transaction in transactions) { ItemSlot val = DeserializeSlot(transaction.slotID); if (val == null) { Utils.Warn("Couldn't deserialize slot!"); continue; } StorableItemInstance itemInstance = Utils.GetItemInstance(transaction.itemID, (EQuality)2); TryRestocking(val, itemInstance, transaction.quantity); } } catch (Exception e) { Utils.PrintException(e); } } public static void Initialize() { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) try { melonPrefs = MelonPreferences.GetCategory("AutoRestock"); timeManager = NetworkSingleton<TimeManager>.Instance; moneyManager = NetworkSingleton<MoneyManager>.Instance; saveManager = Singleton<SaveManager>.Instance; ledgerDay = timeManager.CurrentDay; oscar = ((IEnumerable<NPC>)Object.FindObjectsOfType<NPC>(true)).FirstOrDefault((Func<NPC, bool>)((NPC npc) => npc.ID == "oscar_holland")); playerClickedSlot = null; playerStationOperationSlots = new List<ItemSlot>(); coroutines = new Dictionary<Transaction, object>(); exclusiveLock = new Mutex(); TimeManager obj = timeManager; obj.onDayPass = (Action)Delegate.Combine(obj.onDayPass, new Action(OnDayPass)); saveManager.onSaveStart.AddListener(Utils.ToUnityAction(OnSaveStart)); ledgerString = GetSaveString() + "_ledger"; transactionString = GetSaveString() + "_inprogress"; if (!melonPrefs.HasEntry(ledgerString)) { melonPrefs.CreateEntry<string>(ledgerString, "[]", "", true); } if (!melonPrefs.HasEntry(transactionString)) { melonPrefs.CreateEntry<string>(transactionString, "[]", "", true); } melonPrefs.LoadFromFile(false); ledger = JsonConvert.DeserializeObject<List<Transaction>>(melonPrefs.GetEntry<string>(ledgerString).Value); isInitialized = true; Utils.Log("AutoRestock manager initialized."); } catch (Exception e) { Utils.PrintException(e); } try { List<Transaction> list = JsonConvert.DeserializeObject<List<Transaction>>(melonPrefs.GetEntry<string>(transactionString).Value); if (list.Count > 0) { Utils.Log(string.Format("Completing {0} pending transaction{1}.", list.Count, (list.Count > 1) ? "s" : "")); CompleteTransactions(list); } } catch (Exception e2) { Utils.PrintException(e2); } } public static void Stop() { if (isInitialized) { isInitialized = false; ledger.Clear(); StopCoroutines(); exclusiveLock.Dispose(); } } public static void StopCoroutines() { if (!isInitialized) { return; } foreach (KeyValuePair<Transaction, object> coroutine in coroutines) { MelonCoroutines.Stop(coroutine.Value); } coroutines.Clear(); } public static void AcquireMutex() { if (isInitialized) { exclusiveLock.WaitOne(); } } public static void ReleaseMutex() { if (isInitialized) { exclusiveLock.ReleaseMutex(); } } public static bool ItemIsRestockable(string itemID) { if (isInitialized) { List<string> list = new List<string>(4) { "Agriculture", "Consumable", "Ingredient", "Packaging" }; List<string> list2 = new List<string>(1) { "speedgrow" }; List<string> list3 = new List<string>(4) { "cocaleaf", "cocainebase", "liquidmeth", "shroomspawn" }; List<string> list4 = new List<string>(6) { "cocaseed", "granddaddypurpleseed", "greencrackseed", "ogkushseed", "sourdieselseed", "sporesyringe" }; ItemDefinition item = Registry.GetItem(itemID); return (list.Contains(((object)(EItemCategory)(ref item.Category)).ToString()) || list2.Contains(item.ID)) && !list3.Contains(item.ID); } return false; } public static void TryRestocking(ItemSlot slot, StorableItemInstance item, int quantity) { if (isInitialized && InstanceFinder.IsServer) { string iD = ((ItemInstance)item).ID; int num = Mathf.Max(melonPrefs.GetEntry<int>("restockAmount").Value, 0); int num2 = ((num == 0) ? quantity : num); float num3 = Mathf.Clamp01(melonPrefs.GetEntry<float>("itemDiscount").Value); float num4 = ((ItemInstance)item).GetMonetaryValue() * 2f / (float)((ItemInstance)item).Quantity; float num5 = num4 * (float)num2 * (1f - num3); bool value = melonPrefs.GetEntry<bool>("payWithCash").Value; SlotIdentifier slotIdentifier = SerializeSlot(slot); try { if (((ItemInstance)item).StackLimit == 0) { Utils.Debug($"Stacklimit ({((ItemInstance)item).StackLimit}) == 0. Not restocking."); ((ItemInstance)item).RequestClearSlot(); } else if (ItemIsRestockable(((ItemInstance)item).ID)) { float num6 = (value ? moneyManager.cashBalance : moneyManager.onlineBalance); if (num6 < num5) { Utils.Log($"Can't afford to restock {num2}x {iD} (${num5})."); } else if (num6 >= num5) { AcquireMutex(); Transaction transaction = new Transaction(iD, num2, num3, num4, num5, value, slotIdentifier); ledger.Add(transaction); Utils.Debug($"Starting restock coroutine ({iD} x{num2} at {slotIdentifier.property})."); coroutines[transaction] = MelonCoroutines.Start(RestockCoroutine(slot, item, transaction)); ReleaseMutex(); } } return; } catch (Exception e) { Utils.PrintException(e); ReleaseMutex(); ((ItemInstance)item).RequestClearSlot(); return; } } Utils.Log("Tried to restock item, but Manager was not initialized!"); } [IteratorStateMachine(typeof(<RestockCoroutine>d__26))] private static IEnumerator RestockCoroutine(ItemSlot slot, StorableItemInstance item, Transaction transaction) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <RestockCoroutine>d__26(0) { slot = slot, item = item, transaction = transaction }; } private static void OnDayPass() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) if (isInitialized && InstanceFinder.IsServer) { NetworkSingleton<MessagingManager>.Instance.SendMessage(new Message(GetReceipt(), (ESenderType)1, true, -1), true, oscar.ID); ledger.Clear(); ledgerDay = timeManager.CurrentDay; } } private static string GetSaveString() { string[] array = Singleton<LoadManager>.Instance.LoadedGameFolderPath.Split('\\'); return array[^2] + "_" + array[^1]; } public static void OnSaveStart() { if (isInitialized && InstanceFinder.IsServer) { string text = GetSaveString() + "_ledger"; if (melonPrefs.HasEntry(text)) { melonPrefs.GetEntry<string>(text).EditedValue = LedgerToJson(); } else { MelonPreferences_Entry val = melonPrefs.CreateEntry<string>(text, "", "", true); val.BoxedEditedValue = LedgerToJson(); } string text2 = GetSaveString() + "_inprogress"; if (melonPrefs.HasEntry(text2)) { melonPrefs.GetEntry<string>(text2).EditedValue = PendingTransactionsToJson(); } else { MelonPreferences_Entry val2 = melonPrefs.CreateEntry<string>(text2, "", "", true); val2.BoxedEditedValue = PendingTransactionsToJson(); } melonPrefs.SaveToFile(false); } } public static List<Transaction> GetPendingTransactions() { return coroutines.Keys.ToList(); } public static string PendingTransactionsToJson() { return JsonConvert.SerializeObject((object)GetPendingTransactions()); } public static float LedgerTotal() { return ledger.Aggregate(0f, (float accum, Transaction transaction) => accum + transaction.totalCost); } public static string LedgerToJson() { return JsonConvert.SerializeObject((object)ledger); } private static string GetReceipt() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) if (isInitialized) { string text = $"Restock receipt for {ledgerDay}:\n\n"; float num = 0f; if (ledger.Count > 0) { Dictionary<string, Dictionary<string, int>> dictionary = new Dictionary<string, Dictionary<string, int>>(); Dictionary<string, float> dictionary2 = new Dictionary<string, float>(); foreach (Transaction item in ledger) { if (!dictionary.ContainsKey(item.slotID.property)) { dictionary[item.slotID.property] = new Dictionary<string, int>(); } if (!dictionary[item.slotID.property].ContainsKey(item.itemID)) { dictionary[item.slotID.property][item.itemID] = 0; } dictionary[item.slotID.property][item.itemID] += item.quantity; dictionary2[item.itemID] = item.unitPrice; } foreach (string key in dictionary.Keys) { float num2 = 0f; text = text + key + ": \n"; foreach (KeyValuePair<string, int> item2 in dictionary[key]) { string name = Registry.GetItem(item2.Key).Name; text += $" {name} x{item2.Value} = ${dictionary2[item2.Key] * (float)item2.Value}\n"; num2 += dictionary2[item2.Key] * (float)item2.Value; } text += "=====================\n"; text += $" Property total: ${num2}\n\n"; num += num2; } } text += "=====================\n"; text += $" Grand total: ${num}\n\n"; return text + "Oscar says thank you for your business! :)"; } return "AutoRestock not initialized!"; } } [HarmonyPatch] public class PersistencePatches { [HarmonyPatch(typeof(LoadingScreen), "Close")] [HarmonyPostfix] public static void ClosePostfix(LoadingScreen __instance) { if (InstanceFinder.IsServer && !Manager.isInitialized) { Manager.Initialize(); } } [HarmonyPatch(typeof(LoadManager), "ExitToMenu")] [HarmonyPrefix] public static void ExitToMenuPrefix(LoadManager __instance) { if (InstanceFinder.IsServer && Manager.isInitialized) { Manager.Stop(); } } } [HarmonyPatch] public class CauldronPatches { [HarmonyPatch(typeof(Cauldron), "RemoveIngredients")] [HarmonyPrefix] public static void RemoveIngredientsPrefix(Cauldron __instance) { if (!InstanceFinder.IsServer || !Manager.isInitialized) { return; } try { if (Manager.melonPrefs.GetEntry<bool>("enableCauldrons").Value && __instance.LiquidSlot.ItemInstance.Quantity <= 1 && (Manager.melonPrefs.GetEntry<bool>("playerRestockStations").Value || (Object)(object)__instance.PlayerUserObject == (Object)null)) { StorableItemInstance val = Utils.CastTo<StorableItemInstance>(__instance.LiquidSlot.ItemInstance.GetCopy(-1)); Manager.TryRestocking(__instance.LiquidSlot, val, ((ItemInstance)val).StackLimit); } } catch (Exception e) { Utils.Warn(MethodBase.GetCurrentMethod().DeclaringType.Name + ":"); Utils.PrintException(e); } } } [HarmonyPatch] public class MixingStationPatches { [HarmonyPatch(typeof(MixingStation), "SendMixingOperation")] [HarmonyPrefix] public static void SendMixingOperationPrefix(MixingStation __instance, MixOperation operation) { if (!InstanceFinder.IsServer || !Manager.isInitialized) { return; } try { if (Manager.melonPrefs.GetEntry<bool>("enableMixingStations").Value) { float value = Utils.GetProperty<MixingStation, MixingStationConfiguration>("stationConfiguration", __instance).StartThrehold.GetData().Value; if (!((float)(__instance.MixerSlot.Quantity - operation.Quantity) >= value) && (Manager.melonPrefs.GetEntry<bool>("playerRestockStations").Value || (Object)(object)__instance.PlayerUserObject == (Object)null)) { StorableItemInstance itemInstance = Utils.GetItemInstance(operation.IngredientID, (EQuality)2); Manager.TryRestocking(__instance.MixerSlot, itemInstance, ((ItemInstance)itemInstance).StackLimit); } } } catch (Exception e) { Utils.Warn(MethodBase.GetCurrentMethod().DeclaringType.Name + ":"); Utils.PrintException(e); } } } [HarmonyPatch] public class PackagingStationPatches { [HarmonyPatch(typeof(PackagingStation), "PackSingleInstance")] [HarmonyPrefix] public static void PackSingleInstancePrefix(PackagingStation __instance) { if (!InstanceFinder.IsServer || !Manager.isInitialized) { return; } try { if (Manager.melonPrefs.GetEntry<bool>("enablePackagingStations").Value && __instance.PackagingSlot.ItemInstance != null && __instance.PackagingSlot.ItemInstance.Quantity <= 1 && (Manager.melonPrefs.GetEntry<bool>("playerRestockStations").Value || (Object)(object)__instance.PlayerUserObject == (Object)null)) { StorableItemInstance val = Utils.CastTo<StorableItemInstance>(__instance.PackagingSlot.ItemInstance.GetCopy(1)); Manager.TryRestocking(__instance.PackagingSlot, val, ((ItemInstance)val).StackLimit); } } catch (Exception e) { Utils.Warn(MethodBase.GetCurrentMethod().DeclaringType.Name + ":"); Utils.PrintException(e); } } } [HarmonyPatch] public class ChemistryStationPatches { private static List<List<T>> Permute<T>(List<T> nums) { List<List<T>> list = new List<List<T>>(); return DoPermute(nums, 0, nums.Count - 1, list); } private static List<List<T>> DoPermute<T>(List<T> nums, int start, int end, List<List<T>> list) { if (start == end) { list.Add(new List<T>(nums)); } else { for (int i = start; i <= end; i++) { Swap(nums, start, i); DoPermute(nums, start + 1, end, list); Swap(nums, start, i); } } return list; } private static void Swap<T>(List<T> list, int index1, int index2) { T value = list[index1]; list[index1] = list[index2]; list[index2] = value; } [HarmonyPatch(typeof(ChemistryStation), "SendCookOperation")] [HarmonyPrefix] public static bool SendCookOperationPrefix(ChemistryStation __instance, ChemistryCookOperation op) { //IL_0200: Unknown result type (might be due to invalid IL or missing references) //IL_0205: Unknown result type (might be due to invalid IL or missing references) //IL_0222: Unknown result type (might be due to invalid IL or missing references) //IL_0231: Unknown result type (might be due to invalid IL or missing references) //IL_02a8: Unknown result type (might be due to invalid IL or missing references) if (!InstanceFinder.IsServer || !Manager.isInitialized) { return true; } try { if (!Manager.melonPrefs.GetEntry<bool>("enableChemistryStations").Value) { return true; } if (Manager.melonPrefs.GetEntry<bool>("playerRestockStations").Value || (Object)(object)__instance.PlayerUserObject == (Object)null) { List<ItemDefinition> list2 = new List<ItemDefinition>(); foreach (IngredientQuantity i in op.Recipe.Ingredients) { if (!__instance.IngredientSlots.Any((ItemSlot slot) => slot.ItemInstance != null && slot.ItemInstance.Definition.Name.Contains(i.Item.Name))) { list2.Add(i.Item); } } List<List<ItemDefinition>> list3 = new List<List<ItemDefinition>>(); List<List<ItemDefinition>> list4 = new List<List<ItemDefinition>>(); if (list2.Count > 0) { List<ItemSlot> emptySlots = new List<ItemSlot>(); ItemSlot[] ingredientSlots = __instance.IngredientSlots; foreach (ItemSlot val in ingredientSlots) { if (val.Quantity == 0) { emptySlots.Add(val); } } list3 = Permute(list2); list4 = list3.Select(delegate(List<ItemDefinition> mapping) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) bool flag = true; for (int l = 0; l < mapping.Count; l++) { ItemInstance itemInstance2 = (ItemInstance)(object)Utils.GetItemInstance(mapping[l].ID, op.ProductQuality); if (!emptySlots[l].DoesItemMatchPlayerFilters(itemInstance2)) { flag = false; break; } } return flag ? mapping : null; }).ToList(); list4.RemoveAll((List<ItemDefinition> list) => list == null); if (list4.Count == 0) { string name = ((Object)((BuildableItem)__instance).ParentProperty).name; Vector2 val2 = (Vector2)Utils.GetField<GridItem>("_originCoordinate", __instance); Utils.Log($"Couldn't restock {((BuildableItem)__instance).GetManagementName()} at {name}({val2.x}, {val2.y}) because items do not agree with filters."); return true; } } int num = 0; ItemSlot[] ingredientSlots2 = __instance.IngredientSlots; foreach (ItemSlot val3 in ingredientSlots2) { if (val3.ItemInstance == null && list2.Count > 0) { ItemDefinition val4 = list4[0][num]; StorableItemInstance itemInstance = Utils.GetItemInstance(val4.ID, op.ProductQuality); Manager.TryRestocking(val3, itemInstance, ((ItemInstance)itemInstance).StackLimit); num++; continue; } int num2 = 0; foreach (IngredientQuantity ingredient in op.Recipe.Ingredients) { if (val3.ItemInstance.Definition.Name.Contains(ingredient.Item.Name)) { num2 = ingredient.Quantity; break; } } if (num2 > 0 && val3.ItemInstance.Quantity < num2) { StorableItemInstance val5 = Utils.CastTo<StorableItemInstance>(val3.ItemInstance.GetCopy(1)); Manager.TryRestocking(val3, val5, ((ItemInstance)val5).StackLimit); } } } } catch (Exception e) { Utils.Warn(MethodBase.GetCurrentMethod().DeclaringType.Name + ":"); Utils.PrintException(e); } return true; } } [HarmonyPatch] public class StorageEntityPatches { [HarmonyPatch(typeof(ItemUIManager), "Update")] [HarmonyPrefix] public static void UpdatePrefix(ItemUIManager __instance) { if (__instance.DraggingEnabled) { ItemSlotUI val = Utils.CallMethod<ItemUIManager, ItemSlotUI>("GetHoveredItemSlot", __instance); ItemSlotUI field = Utils.GetField<ItemUIManager, ItemSlotUI>("draggedSlot", __instance); if ((Object)(object)field == (Object)null && (Object)(object)val != (Object)null && (GameInput.GetButtonDown((ButtonCode)0) || GameInput.GetButtonDown((ButtonCode)1) || GameInput.GetButtonDown((ButtonCode)2)) && Manager.playerClickedSlot == null) { Manager.playerClickedSlot = val.assignedSlot; } } } [HarmonyPatch(typeof(ItemUIManager), "Update")] [HarmonyPostfix] public static void UpdatePostfix(ItemUIManager __instance) { if (__instance.DraggingEnabled) { ItemSlotUI field = Utils.GetField<ItemUIManager, ItemSlotUI>("draggedSlot", __instance); if ((Object)(object)field == (Object)null && Manager.playerClickedSlot != null) { Manager.playerClickedSlot = null; } } } [HarmonyPatch(typeof(ItemSlot), "ChangeQuantity")] [HarmonyPrefix] public static void ChangeQuantityPrefix(ItemSlot __instance, ref int change) { if (!InstanceFinder.IsServer || !Manager.isInitialized) { return; } try { if (__instance.ItemInstance != null && __instance.Quantity + change <= 0 && Manager.melonPrefs.GetEntry<bool>("enableStorage").Value && Manager.playerClickedSlot != __instance && Utils.IsStorageRack(__instance.SlotOwner)) { StorableItemInstance val = Utils.CastTo<StorableItemInstance>(__instance.ItemInstance.GetCopy(1)); Manager.TryRestocking(__instance, val, ((ItemInstance)val).StackLimit); } } catch (Exception e) { Utils.Warn(MethodBase.GetCurrentMethod().DeclaringType.Name + ":"); Utils.PrintException(e); } } } [HarmonyPatch] public class SpawnStationPatches { [HarmonyPatch(typeof(InocculateGrainBagTask), "Success")] [HarmonyPostfix] public static void SuccessPostfix(InocculateGrainBagTask __instance) { MushroomSpawnStation field = Utils.GetField<InocculateGrainBagTask, MushroomSpawnStation>("_station", __instance); if (field.GrainBagSlot.Quantity == 0 && Manager.melonPrefs.GetEntry<bool>("enableSpawnStations").Value && Manager.melonPrefs.GetEntry<bool>("playerRestockStations").Value) { StorableItemInstance itemInstance = Utils.GetItemInstance("grainbag", (EQuality)2); Manager.TryRestocking(field.GrainBagSlot, itemInstance, ((ItemInstance)itemInstance).StackLimit); } if (field.SyringeSlot.Quantity == 0 && Manager.melonPrefs.GetEntry<bool>("enableSpawnStations").Value && Manager.melonPrefs.GetEntry<bool>("playerRestockStations").Value) { StorableItemInstance itemInstance2 = Utils.GetItemInstance("sporesyringe", (EQuality)2); Manager.TryRestocking(field.SyringeSlot, itemInstance2, ((ItemInstance)itemInstance2).StackLimit); } } [HarmonyPatch(typeof(UseSpawnStationBehaviour), "StopWork")] [HarmonyPrefix] public static void StopWorkPrefix(UseSpawnStationBehaviour __instance) { if (__instance.Station.GrainBagSlot.Quantity == 0 && Manager.melonPrefs.GetEntry<bool>("enableSpawnStations").Value) { StorableItemInstance itemInstance = Utils.GetItemInstance("grainbag", (EQuality)2); Manager.TryRestocking(__instance.Station.GrainBagSlot, itemInstance, ((ItemInstance)itemInstance).StackLimit); } if (__instance.Station.SyringeSlot.Quantity == 0 && Manager.melonPrefs.GetEntry<bool>("enableSpawnStations").Value) { StorableItemInstance itemInstance2 = Utils.GetItemInstance("sporesyringe", (EQuality)2); Manager.TryRestocking(__instance.Station.SyringeSlot, itemInstance2, ((ItemInstance)itemInstance2).StackLimit); } } } } [CompilerGenerated] internal sealed class <>z__ReadOnlyArray<T> : IEnumerable, ICollection, IList, IEnumerable<T>, IReadOnlyCollection<T>, IReadOnlyList<T>, ICollection<T>, IList<T> { int ICollection.Count => _items.Length; bool ICollection.IsSynchronized => false; object ICollection.SyncRoot => this; object IList.this[int index] { get { return _items[index]; } set { throw new NotSupportedException(); } } bool IList.IsFixedSize => true; bool IList.IsReadOnly => true; int IReadOnlyCollection<T>.Count => _items.Length; T IReadOnlyList<T>.this[int index] => _items[index]; int ICollection<T>.Count => _items.Length; bool ICollection<T>.IsReadOnly => true; T IList<T>.this[int index] { get { return _items[index]; } set { throw new NotSupportedException(); } } public <>z__ReadOnlyArray(T[] items) { _items = items; } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)_items).GetEnumerator(); } void ICollection.CopyTo(Array array, int index) { ((ICollection)_items).CopyTo(array, index); } int IList.Add(object value) { throw new NotSupportedException(); } void IList.Clear() { throw new NotSupportedException(); } bool IList.Contains(object value) { return ((IList)_items).Contains(value); } int IList.IndexOf(object value) { return ((IList)_items).IndexOf(value); } void IList.Insert(int index, object value) { throw new NotSupportedException(); } void IList.Remove(object value) { throw new NotSupportedException(); } void IList.RemoveAt(int index) { throw new NotSupportedException(); } IEnumerator<T> IEnumerable<T>.GetEnumerator() { return ((IEnumerable<T>)_items).GetEnumerator(); } void ICollection<T>.Add(T item) { throw new NotSupportedException(); } void ICollection<T>.Clear() { throw new NotSupportedException(); } bool ICollection<T>.Contains(T item) { return ((ICollection<T>)_items).Contains(item); } void ICollection<T>.CopyTo(T[] array, int arrayIndex) { ((ICollection<T>)_items).CopyTo(array, arrayIndex); } bool ICollection<T>.Remove(T item) { throw new NotSupportedException(); } int IList<T>.IndexOf(T item) { return ((IList<T>)_items).IndexOf(item); } void IList<T>.Insert(int index, T item) { throw new NotSupportedException(); } void IList<T>.RemoveAt(int index) { throw new NotSupportedException(); } }