Some mods target the Mono version of the game, which is available by opting into the Steam beta branch "alternate"
Decompiled source of Alex Vance Bulk Order Mod v1.0.0
Mods/BulkBuyerMod.dll
Decompiled 2 weeks agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using MelonLoader; using S1API.DeadDrops; using S1API.Entities; using S1API.GameTime; using S1API.Items; using S1API.Leveling; using S1API.Messaging; using S1API.Money; using S1API.Products; using S1API.Quests; using S1API.Quests.Constants; using S1API.Saveables; 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(BulkBuyerMod), "Alex Vance Bulk Order Mod", "1.0.0", "AlexumYT", null)] [assembly: MelonGame("TVGS", "Schedule I")] [assembly: MelonColor(255, 191, 255, 0)] [assembly: AssemblyTitle("BulkBuyerMod")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("BulkBuyerMod")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("229bf8fb-1ecb-4270-9bcb-b33cc7231d4b")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.0.0")] public class BulkBuyerMod : MelonMod { public override void OnInitializeMelon() { MelonLogger.Msg("[BulkBuyer] Mod loaded"); } } public class AlexVance : NPC { private const int ProductPerLevel = 8; private const int ProductVariation = 10; private const float ProductPriceMultiplier = 1.2f; private const int MinDaysAllowed = 1; private const int MaxDaysAllowed = 3; [SaveableField("Order")] private OrderData _orderData; [SaveableField("Intro")] private readonly IntroData _introData = new IntroData(); private static readonly (string message, string response)[] IntroTexts = new(string, string)[3] { ("Yo, your uncle said you might could help me with supply. I'm running low.", "Where you wanna meet?"), ("I can't meet up. I'm over in Belland, like 300 miles out dawg.", "What about a dead drop?"), ("That works. I'll text you when I need a drop. Peace.", "") }; private static readonly string[] RandomRequestTexts = new string[6] { "Yo, I need {amount} {product} by {day}. I've got {price} with your name on it. Can you make that happen?", "My people are fienin. Got {amount} {product}?? Need it by {day} if you can. I'll drop {price} for it.", "Ayo, can you hit me with {amount} {product} by {day} for me? You know I need it, and I'll pay {price} for that.", "{amount} {product} by {day} - you got me?? I got you {price} waiting.", "I'm runnin' low on {product}. Can you push {amount} to me by {day}? I'll pay {price} for it.", "Bro, I'm out here scrambling. Can you get {amount} {product} by {day}? I'm desperate, I'll toss {price} your way." }; private static readonly string[] RandomAcceptResponses = new string[3] { "No doubt, send me a pin.", "Say no more, I gotchu.", "Aight, let's run it." }; private static readonly string[] RandomDenyResponses = new string[3] { "Nah, ain't got it right now.", "Can't do it at the moment.", "Nah, I can't make this week." }; private static readonly string[] RandomNegativeTexts = new string[3] { "Damn, aight... guess that's it then.", "Cool, I guess... catch you later.", "Shit, no no. Ok, i'll hit you up later tho." }; private static readonly string[] RandomPositiveTexts = new string[3] { "Hell yeah bro, thanks.", "Solid, I'll drop you the cash when I pull up.", "You a real one, fam. Appreciate you." }; private bool IntroCompleted => _introData != null && _introData.Progress >= IntroTexts.Length - 1; protected override void ConfigurePrefab(NPCPrefabBuilder builder) { builder.WithIdentity("alex_vance", "Alex", "Vance"); } protected override void OnCreated() { TimeManager.OnDayPass = (Action)Delegate.Combine(TimeManager.OnDayPass, new Action(OnDayPass)); } protected override void OnResponseLoaded(Response response) { MelonLogger.Msg(response.Label); switch (response.Label) { case "ACCEPT": response.OnTriggered = AcceptOrder; break; case "DENY": response.OnTriggered = DenyOrder; break; case "PROGRESS_INTRO": response.OnTriggered = ProgressIntro; break; } } private void OnDayPass() { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Invalid comparison between Unknown and I4 //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Invalid comparison between Unknown and I4 if (!IntroCompleted && (int)LevelManager.Rank >= 3) { ProgressIntro(); } else if (_orderData != null) { DenyOrder(); } else if ((int)TimeManager.CurrentDay == 1) { SendRandomText(); } } private void SendRandomText() { //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Expected I4, but got Unknown //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Expected I4, but got Unknown //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_0247: Unknown result type (might be due to invalid IL or missing references) //IL_024c: Unknown result type (might be due to invalid IL or missing references) //IL_0258: Unknown result type (might be due to invalid IL or missing references) //IL_0261: Unknown result type (might be due to invalid IL or missing references) //IL_0275: Expected O, but got Unknown //IL_0277: Unknown result type (might be due to invalid IL or missing references) //IL_027c: Unknown result type (might be due to invalid IL or missing references) //IL_0288: Unknown result type (might be due to invalid IL or missing references) //IL_0291: Unknown result type (might be due to invalid IL or missing references) //IL_02a5: Expected O, but got Unknown if (ProductManager.DiscoveredProducts.Length != 0) { List<ProductDefinition> list = ProductManager.FavouritedProducts.Where((ProductDefinition p) => (ItemDefinition)(object)p != (ItemDefinition)null).ToList(); List<ProductDefinition> list2 = new List<ProductDefinition>(ProductManager.DiscoveredProducts); MelonLogger.Msg($"[AlexVance] Favorites found: {list.Count}"); ProductDefinition val = null; if (list.Count >= 3) { val = list[Random.Range(0, list.Count)]; MelonLogger.Msg("[AlexVance] Using FAVORITE product pool."); } else { val = list2[Random.Range(0, list2.Count)]; MelonLogger.Msg("[AlexVance] Not enough favorites, using fallback pool."); } if ((ItemDefinition)(object)val == (ItemDefinition)null) { MelonLogger.Warning("[AlexVance] No product selected."); return; } _orderData = new OrderData { Product = val }; int num = LevelManager.Rank * 8; int num2 = Random.Range(-10, 11); _orderData.Amount = Mathf.Max(1, num + num2); _orderData.Price = Mathf.RoundToInt(val.Price * (float)_orderData.Amount * 1.2f); int num3 = Random.Range(1, 4); int num4 = (TimeManager.CurrentDay + num3) % Enum.GetValues(typeof(Day)).Length; Day val2 = (Day)num4; string text = RandomRequestTexts[Random.Range(0, RandomRequestTexts.Length)]; string text2 = text.Replace("{product}", "<color=#3399FF>" + ((ItemDefinition)_orderData.Product).Name + "</color>").Replace("{amount}", $"{_orderData.Amount}x").Replace("{day}", $"<color=#FF6600>{val2}</color>") .Replace("{price}", $"<color=#00CC00>${_orderData.Price:N0}</color>"); string text3 = RandomDenyResponses[Random.Range(0, RandomDenyResponses.Length)]; string text4 = RandomAcceptResponses[Random.Range(0, RandomAcceptResponses.Length)]; ((NPC)this).SendTextMessage(text2, (Response[])(object)new Response[2] { new Response { Label = "ACCEPT", Text = text4, OnTriggered = AcceptOrder }, new Response { Label = "DENY", Text = text3, OnTriggered = DenyOrder } }, 1.5f, true); } } private void DenyOrder() { string text = RandomNegativeTexts[Random.Range(0, RandomNegativeTexts.Length)]; ((NPC)this).SendTextMessage(text, (Response[])null, 1.5f, true); ClearPendingOrder(); } private void AcceptOrder() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown if (_orderData == null || (ItemDefinition)_orderData.Product == (ItemDefinition)null) { throw new Exception("No pending product assigned in BulkBuyer!"); } string text = RandomPositiveTexts[Random.Range(0, RandomPositiveTexts.Length)]; ((NPC)this).SendTextMessage(text, (Response[])null, 1.5f, true); QuestAlexOrder questAlexOrder = (QuestAlexOrder)(object)QuestManager.CreateQuest<QuestAlexOrder>((string)null); questAlexOrder.SetupAsNew(_orderData); ((Quest)questAlexOrder).Begin(); ClearPendingOrder(); } private void ClearPendingOrder() { _orderData = null; } private void ProgressIntro() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Expected O, but got Unknown if (_introData == null) { throw new Exception("No intro data assigned in BulkBuyer!"); } List<Response> list = new List<Response>(); if (!IntroCompleted) { list.Add(new Response { Label = "PROGRESS_INTRO", Text = IntroTexts[_introData.Progress].response, OnTriggered = ProgressIntro }); } ((NPC)this).SendTextMessage(IntroTexts[_introData.Progress].message, list.ToArray(), 1.5f, true); _introData.Progress++; } } internal class QuestAlexOrder : Quest { [SaveableField("Order")] private OrderData _orderData = new OrderData(); [SaveableField("DeadDrop")] private readonly DeadDropData _deadDropData = new DeadDropData(); private bool _rewardSpawned = false; private QuestEntry _deliveryQuestEntry; private QuestEntry _collectionQuestEntry; protected override string Title => "Alex Needs Product!"; protected override string Description => $"Deliver Alex his product and he'll drop you <color=#00CC00>${_orderData.Price:N0}</color> off."; protected override void OnCreated() { MelonLogger.Msg("[AlexQuest] Quest created."); } protected override void OnLoaded() { MelonLogger.Msg("[AlexQuest] Quest loaded from save."); if (_deadDropData?.DeliveryDeadDrop != null) { _deadDropData.DeliveryDeadDrop.Storage.OnClosed -= TryForDelivery; _deadDropData.DeliveryDeadDrop.Storage.OnClosed += TryForDelivery; MelonLogger.Msg("[AlexQuest] Re-hooked delivery drop."); } else { MelonLogger.Warning("[AlexQuest] Delivery drop missing on load."); } if (_deadDropData?.CollectionDeadDrop != null) { _deadDropData.CollectionDeadDrop.Storage.OnOpened -= TryForCollection; _deadDropData.CollectionDeadDrop.Storage.OnOpened += TryForCollection; MelonLogger.Msg("[AlexQuest] Re-hooked collection drop."); } else { MelonLogger.Warning("[AlexQuest] Collection drop missing on load."); } } public void SetupAsNew(OrderData pendingOrderData) { //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) MelonLogger.Msg("[AlexQuest] Setting up new order..."); _orderData = pendingOrderData; _deadDropData.DeliveryDeadDrop = DeadDropManager.All[Random.Range(0, DeadDropManager.All.Length)]; _deadDropData.CollectionDeadDrop = DeadDropManager.All[Random.Range(0, DeadDropManager.All.Length)]; DeadDropInstance deliveryDeadDrop = _deadDropData.DeliveryDeadDrop; MelonLogger.Msg("[AlexQuest] Delivery Drop: " + ((deliveryDeadDrop != null) ? deliveryDeadDrop.Name : null)); DeadDropInstance collectionDeadDrop = _deadDropData.CollectionDeadDrop; MelonLogger.Msg("[AlexQuest] Collection Drop: " + ((collectionDeadDrop != null) ? collectionDeadDrop.Name : null)); ProductDefinition product = _orderData.Product; string arg = ((product != null) ? ((ItemDefinition)product).Name : null) ?? "NULL_PRODUCT"; _deliveryQuestEntry = ((Quest)this).AddEntry($"Drop off {_orderData.Amount}x <color=#3399FF>{arg}</color> at the dead drop.", (Vector3?)_deadDropData.DeliveryDeadDrop.Position); _collectionQuestEntry = ((Quest)this).AddEntry($"Pick up <color=#00CC00>${_orderData.Price:N0}</color> at the dead drop.", (Vector3?)_deadDropData.CollectionDeadDrop.Position); _deadDropData.DeliveryDeadDrop.Storage.OnClosed += TryForDelivery; _deadDropData.CollectionDeadDrop.Storage.OnOpened += TryForCollection; MelonLogger.Msg("[AlexQuest] Event hooks attached."); } private void TryForDelivery() { //IL_01e8: Unknown result type (might be due to invalid IL or missing references) MelonLogger.Msg("[AlexQuest] Checking delivery..."); DeadDropInstance deliveryDeadDrop = _deadDropData.DeliveryDeadDrop; if (deliveryDeadDrop == null) { MelonLogger.Error("[AlexQuest] Delivery dead drop is NULL."); return; } ItemSlotInstance[] slots = deliveryDeadDrop.Storage.Slots; ItemSlotInstance[] array = slots; foreach (ItemSlotInstance val in array) { if (val.ItemInstance == null) { MelonLogger.Msg("[AlexQuest] Empty slot."); continue; } MelonLogger.Msg($"[AlexQuest] Slot Item: {val.ItemInstance.Definition.Name} | Qty: {val.Quantity}"); ItemInstance itemInstance = val.ItemInstance; ProductInstance val2 = (ProductInstance)(object)((itemInstance is ProductInstance) ? itemInstance : null); if (val2 != null) { MelonLogger.Msg($" -> IsPackaged: {val2.IsPackaged}"); MelonLogger.Msg($" -> Matches Order Product: {(ItemDefinition)(object)val2.Definition == (ItemDefinition)(object)_orderData.Product}"); } } List<ItemSlotInstance> list = slots.Where(delegate(ItemSlotInstance slot) { ItemInstance itemInstance2 = slot.ItemInstance; ProductInstance val3 = (ProductInstance)(object)((itemInstance2 is ProductInstance) ? itemInstance2 : null); if (val3 == null || !val3.IsPackaged) { return false; } ProductDefinition definition = val3.Definition; string text = ((definition != null) ? ((ItemDefinition)definition).Name : null); ProductDefinition product = _orderData.Product; string text2 = ((product != null) ? ((ItemDefinition)product).Name : null); bool flag = text == text2; MelonLogger.Msg($" -> Comparing '{text}' to '{text2}' = {flag}"); return flag; }).ToList(); if (list.Count == 0) { MelonLogger.Warning("[AlexQuest] No valid items found."); return; } int num = list.Sum((ItemSlotInstance slot) => slot.Quantity * ((ProductInstance)slot.ItemInstance).AppliedPackaging.Quantity); MelonLogger.Msg($"[AlexQuest] Total: {num} / Required: {_orderData.Amount}"); if (num < _orderData.Amount) { MelonLogger.Warning("[AlexQuest] Not enough product."); return; } int num2 = _orderData.Amount; foreach (ItemSlotInstance item in list) { int quantity = ((ProductInstance)item.ItemInstance).AppliedPackaging.Quantity; int num3 = Mathf.Min(item.Quantity * quantity, num2); item.AddQuantity(-(num3 / quantity)); num2 -= num3; MelonLogger.Msg($"[AlexQuest] Removed {num3}, Remaining: {num2}"); if (num2 <= 0) { break; } } QuestEntry deliveryQuestEntry = _deliveryQuestEntry; if (deliveryQuestEntry != null) { deliveryQuestEntry.Complete(); } QuestEntry collectionQuestEntry = _collectionQuestEntry; if (collectionQuestEntry != null) { collectionQuestEntry.SetState((QuestState)1); } deliveryDeadDrop.Storage.OnClosed -= TryForDelivery; MelonLogger.Msg("[AlexQuest] Delivery COMPLETE."); } private void TryForCollection() { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Expected O, but got Unknown //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Expected O, but got Unknown //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Expected O, but got Unknown //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Expected O, but got Unknown MelonLogger.Msg("[AlexQuest] Attempting payout..."); if (_rewardSpawned) { MelonLogger.Msg("[AlexQuest] Reward already spawned. Skipping."); return; } if (_orderData == null || _deadDropData.CollectionDeadDrop == null) { MelonLogger.Error("[AlexQuest] Missing data for payout."); return; } CashDefinition val = (CashDefinition)ItemManager.GetItemDefinition("cash"); CashInstance val2 = (CashInstance)((ItemDefinition)val).CreateInstance(1); val2.SetQuantity((float)_orderData.Price); if (_deadDropData.CollectionDeadDrop.Storage.CanItemFit((ItemInstance)val2, 1)) { _deadDropData.CollectionDeadDrop.Storage.AddItem((ItemInstance)val2); _rewardSpawned = true; MelonLogger.Msg($"[AlexQuest] Paid ${_orderData.Price}"); QuestEntry collectionQuestEntry = _collectionQuestEntry; if (collectionQuestEntry != null) { collectionQuestEntry.Complete(); } _deadDropData.CollectionDeadDrop.Storage.OnOpened -= TryForCollection; } else { MelonLogger.Warning("[AlexQuest] Not enough space for reward."); } } } public class DeadDropData { public DeadDropInstance DeliveryDeadDrop; public DeadDropInstance CollectionDeadDrop; } public class IntroData { public int Progress; public IntroData(int progress = 0) { Progress = progress; } } public class OrderData { public ProductDefinition Product; public int Amount; public int Price; }