Some mods target the Mono version of the game, which is available by opting into the Steam beta branch "alternate"
Decompiled source of EasyDeals v0.0.2
easy_deals.dll
Decompiled 3 months 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.InteropServices; using System.Runtime.Versioning; using System.Threading.Tasks; using HarmonyLib; using Il2CppScheduleOne.DevUtilities; using Il2CppScheduleOne.Economy; using Il2CppScheduleOne.GameTime; using Il2CppScheduleOne.ItemFramework; using Il2CppScheduleOne.Messaging; using Il2CppScheduleOne.Product; using Il2CppScheduleOne.Quests; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using MelonLoader; using TestingClass; using UnityEngine; using easy_deals; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: MelonInfo(typeof(MainHandler), "EasyDeals", "0.0.1", "Omar Ahmed", null)] [assembly: MelonGame("TVGS", "Schedule I")] [assembly: AssemblyTitle("easy_deals")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("easy_deals")] [assembly: AssemblyCopyright("Copyright © 2025")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("3a228b8d-b72a-44dc-96c4-530d3e296a3b")] [assembly: AssemblyFileVersion("0.1")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("0.1.0.0")] namespace TestingClass { internal static class TestingClass { private static ProductDefinition cachedProduct; public static void GenerateRandomQuests(int numberOfQuests) { //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Expected O, but got Unknown //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Expected O, but got Unknown //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Expected O, but got Unknown //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Expected O, but got Unknown List<Customer> val = new List<Customer>(); Enumerator<Customer> enumerator = Customer.UnlockedCustomers.GetEnumerator(); while (enumerator.MoveNext()) { Customer current = enumerator.Current; val.Add(current); } MelonLogger.Msg("we have : " + val.Count); if (val == null || val.Count == 0) { return; } Random random = new Random(); while (numberOfQuests > 0 && val.Count != 0) { int num = random.Next(0, val.Count); Customer val2 = val[num]; if ((Object)(object)val2 == (Object)null || val2.offeredContractInfo != null || (Object)(object)val2.DefaultDeliveryLocation == (Object)null) { val.RemoveAt(num); continue; } List<ProductDefinition> orderableProducts = val2.OrderableProducts; if ((Object)(object)cachedProduct == (Object)null && orderableProducts.Count > 0) { cachedProduct = orderableProducts[0]; } ProductDefinition val3 = null; if (orderableProducts.Count > 0) { val3 = orderableProducts[random.Next(0, orderableProducts.Count)]; } else if ((Object)(object)cachedProduct != (Object)null) { val3 = cachedProduct; } if ((Object)(object)val3 == (Object)null) { val.RemoveAt(num); continue; } ProductList val4 = new ProductList(); val4.entries = new List<Entry>(); Entry val5 = new Entry { ProductID = ((ItemDefinition)val3).ID, Quantity = 2, Quality = (EQuality)2 }; val4.entries.Add(val5); QuestWindowConfig val6 = new QuestWindowConfig { WindowStartTime = 0, WindowEndTime = 600 }; Guid gUID = val2.DefaultDeliveryLocation.GUID; string text = ((object)(Guid)(ref gUID)).ToString(); ContractInfo val7 = new ContractInfo(100f, val4, text, val6, true, 900, numberOfQuests, false); val2.OfferContract(val7); val.RemoveAt(num); numberOfQuests--; } } } } namespace easy_deals { public static class KeyBindings { public static KeyCode OpenMenuKey = (KeyCode)280; public static KeyCode AcceptContractKey = (KeyCode)279; public static KeyCode NextPeriodKey = (KeyCode)273; public static KeyCode PreviousPeriodKey = (KeyCode)274; public static KeyCode NextProductKey = (KeyCode)275; public static KeyCode PreviousProductKey = (KeyCode)276; public static KeyCode IncreasePrice = (KeyCode)43; public static KeyCode DecreasePrice = (KeyCode)45; } public class ContractManager { private static readonly object _lock = new object(); private static ContractManager _instance; private readonly List<PendingContract> _pendingContracts = new List<PendingContract>(); private readonly Queue<PendingContract> _contractPool = new Queue<PendingContract>(); private bool _isProcessing = false; private readonly Dictionary<string, float> _potentialGains = new Dictionary<string, float>(); private readonly Dictionary<string, int> _totalQuantities = new Dictionary<string, int>(); private readonly List<string> _offeredProducts = new List<string> { "All" }; private bool _productsChanged = false; public static ContractManager Instance { get { lock (_lock) { return _instance ?? (_instance = new ContractManager()); } } } public float OverallPriceModifier { get; set; } = 10f; private ContractManager() { _potentialGains["All"] = 0f; _totalQuantities["All"] = 0; MelonLogger.Msg("ContractManager initialized with default 'All' values"); } public void AddContract(ProductDefinition product, int quantity, float payment, Customer customer) { if ((Object)(object)product == (Object)null || string.IsNullOrEmpty(((ItemDefinition)product).Name)) { MelonLogger.Warning("AddContract failed: Product is null or has no name"); return; } PendingContract contract = GetContract(product, quantity, payment, customer); _pendingContracts.Add(contract); _potentialGains.TryGetValue("All", out var value); _potentialGains["All"] = value + payment + OverallPriceModifier; _totalQuantities.TryGetValue("All", out var value2); _totalQuantities["All"] = value2 + quantity; _potentialGains.TryGetValue(((ItemDefinition)product).Name, out var value3); _potentialGains[((ItemDefinition)product).Name] = value3 + payment + OverallPriceModifier; _totalQuantities.TryGetValue(((ItemDefinition)product).Name, out var value4); _totalQuantities[((ItemDefinition)product).Name] = value4 + quantity; if (!_offeredProducts.Contains(((ItemDefinition)product).Name)) { _offeredProducts.Add(((ItemDefinition)product).Name); _offeredProducts.Sort(); } MelonLogger.Msg($"Contract added: Product={((ItemDefinition)product).Name}, Quantity={quantity}, Payment={payment}, TotalContracts={_pendingContracts.Count}"); _productsChanged = true; } public void ProcessContracts(EDealWindow dealWindow, string productName = "All") { //IL_0041: Unknown result type (might be due to invalid IL or missing references) if (_pendingContracts.Count == 0) { MelonLogger.Msg("No contracts to process"); return; } if (_isProcessing) { MelonLogger.Msg("Already processing contracts"); return; } _isProcessing = true; MelonCoroutines.Start(ExecuteContracts(dealWindow, productName)); } private IEnumerator ExecuteContracts(EDealWindow dealWindow, string productName) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) List<PendingContract> contractsToProcess = ((productName == "All") ? _pendingContracts.ToList() : _pendingContracts.Where((PendingContract c) => c.ProductName == productName).ToList()); int processedCount = 0; _pendingContracts.RemoveAll((PendingContract c) => contractsToProcess.Contains(c)); RemoveContracts(contractsToProcess); List<IGrouping<string, PendingContract>> groupedContracts = (from c in contractsToProcess group c by c.CustomerName).ToList(); foreach (IGrouping<string, PendingContract> group in groupedContracts) { List<PendingContract> contracts = group.ToList(); for (int i = 0; i < contracts.Count; i += 3) { List<PendingContract> batch = contracts.Skip(i).Take(3).ToList(); yield return MelonCoroutines.Start(ProcessContractBatch(dealWindow, batch, 300)); processedCount += batch.Count; MelonLogger.Msg($"Processed batch of {batch.Count} contracts, total processed: {processedCount}/{contractsToProcess.Count}"); yield return (object)new WaitForSeconds(3f); } } foreach (PendingContract contract in contractsToProcess) { ReturnContract(contract); } _isProcessing = false; MelonLogger.Msg($"Completed processing {processedCount} contracts for {productName}"); _productsChanged = true; } private IEnumerator ProcessContractBatch(EDealWindow dealWindow, List<PendingContract> contracts, int delayBetweenContracts) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) foreach (PendingContract contract in contracts) { bool taskCompleted = false; float timeout = Time.time + 10f; MelonLogger.Msg("Processing contract for " + contract.ProductName); contract.Execute(dealWindow).ContinueWith(delegate(Task t) { taskCompleted = true; if (t.IsFaulted) { MelonLogger.Error("Contract execution failed: " + t.Exception?.InnerException?.Message); } }); while (!taskCompleted && Time.time < timeout) { yield return null; } yield return (object)new WaitForSeconds((float)delayBetweenContracts / 1000f); } } private void RemoveContracts(List<PendingContract> contracts) { foreach (PendingContract contract in contracts) { _potentialGains.TryGetValue("All", out var value); _potentialGains["All"] = value - (contract.Payment + OverallPriceModifier); _totalQuantities.TryGetValue("All", out var value2); _totalQuantities["All"] = value2 - contract.Quantity; _potentialGains.TryGetValue(contract.ProductName, out var value3); _potentialGains[contract.ProductName] = value3 - (contract.Payment + OverallPriceModifier); _totalQuantities.TryGetValue(contract.ProductName, out var value4); _totalQuantities[contract.ProductName] = value4 - contract.Quantity; if (!_pendingContracts.Any((PendingContract c) => c.ProductName == contract.ProductName)) { _potentialGains.Remove(contract.ProductName); _totalQuantities.Remove(contract.ProductName); _offeredProducts.Remove(contract.ProductName); } } } public void UpdateGainsForModifierChange() { _potentialGains.Clear(); _potentialGains["All"] = _pendingContracts.Sum((PendingContract c) => c.Payment + OverallPriceModifier); foreach (string productName in _offeredProducts.Where((string n) => n != "All")) { _potentialGains[productName] = _pendingContracts.Where((PendingContract c) => c.ProductName == productName).Sum((PendingContract c) => c.Payment + OverallPriceModifier); } } public List<string> GetOfferedProductNames() { return _offeredProducts; } public bool HasProductsChanged() { return _productsChanged; } public void ResetProductsChanged() { _productsChanged = false; } public float GetPotentialGain(string productName = "All") { _potentialGains.TryGetValue(productName, out var value); return value; } public int GetTotalQuantity(string productName = "All") { _totalQuantities.TryGetValue(productName, out var value); return value; } public List<PendingContract> GetPendingContracts() { return _pendingContracts; } private PendingContract GetContract(ProductDefinition product, int quantity, float payment, Customer customer) { PendingContract pendingContract; if (_contractPool.Count > 0) { pendingContract = _contractPool.Dequeue(); pendingContract.Reset(product, quantity, payment, customer); } else { pendingContract = new PendingContract(product, quantity, payment, customer); } return pendingContract; } private void ReturnContract(PendingContract contract) { _contractPool.Enqueue(contract); } } public class PendingContract { private ProductDefinition _product; private int _quantity; private float _payment; private Customer _customer; public string ProductName => ((Object)(object)_product != (Object)null) ? ((ItemDefinition)_product).Name : "Null Product"; public float Payment => _payment; public int Quantity => _quantity; public string CustomerName => ((Object)(object)_customer != (Object)null) ? ((Object)_customer).name : "Null Customer"; public ProductDefinition Product => _product; public PendingContract(ProductDefinition product, int quantity, float payment, Customer customer) { Reset(product, quantity, payment, customer); } public void Reset(ProductDefinition product, int quantity, float payment, Customer customer) { _product = product; _quantity = quantity; _payment = payment; _customer = customer; } public async Task Execute(EDealWindow dealWindow) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_customer == (Object)null || (Object)(object)_product == (Object)null) { MelonLogger.Warning("Skipping contract execution: Invalid customer or product"); return; } float finalPrice = _payment + ContractManager.Instance.OverallPriceModifier; int maxRetries = 2; int currentRetry = 0; while (currentRetry <= maxRetries) { try { MelonLogger.Msg($"Step 1: EvaluateCounteroffer for {ProductName} (attempt {currentRetry + 1})"); Task<bool> task1 = RunOnMainThread(delegate { _customer.EvaluateCounteroffer(_product, _quantity, finalPrice); return true; }); if (await Task.WhenAny(new Task[2] { task1, Task.Delay(5000) }) == task1) { await task1; await Task.Delay(200); MelonLogger.Msg("Step 2: SendCounteroffer for " + ProductName); Task<bool> task2 = RunOnMainThread(delegate { _customer.SendCounteroffer(_product, _quantity, finalPrice); return true; }); if (await Task.WhenAny(new Task[2] { task2, Task.Delay(5000) }) == task2) { await task2; await Task.Delay(1500); MelonLogger.Msg("Step 3: PlayerAcceptedContract for " + ProductName); Task<bool> task3 = RunOnMainThread(delegate { //IL_000d: Unknown result type (might be due to invalid IL or missing references) _customer.PlayerAcceptedContract(dealWindow); _customer.NPC.MSGConversation.ClearResponses(true); return true; }); if (await Task.WhenAny(new Task[2] { task3, Task.Delay(5000) }) != task3) { throw new TimeoutException("PlayerAcceptedContract timed out"); } MelonLogger.Msg("Contract successfully executed for " + ProductName); break; } throw new TimeoutException("SendCounteroffer timed out"); } throw new TimeoutException("EvaluateCounteroffer timed out"); } catch (Exception ex) { currentRetry++; MelonLogger.Error($"Contract execution attempt {currentRetry} failed for {ProductName}: {ex.Message}"); if (currentRetry > maxRetries) { MelonLogger.Error($"Failed to execute contract after {maxRetries + 1} attempts"); break; } await Task.Delay(1000); } } } private Task<T> RunOnMainThread<T>(Func<T> action) { TaskCompletionSource<T> taskCompletionSource = new TaskCompletionSource<T>(); MelonCoroutines.Start(ExecuteOnMainThread(action, taskCompletionSource)); return taskCompletionSource.Task; } private IEnumerator ExecuteOnMainThread<T>(Func<T> action, TaskCompletionSource<T> tcs) { yield return null; try { T result = action(); tcs.SetResult(result); } catch (Exception ex) { tcs.SetException(ex); } } } public class MainHandler : MelonMod { private MessagingManager _messagingManager; private ContractManager _contractManager = ContractManager.Instance; private TimeManager _timeManager; private float _sliderTextTimer = 0f; private bool _showSliderText = false; private bool _isMenuOpen = false; private int _selectedPeriodIndex = 0; private int _selectedProductIndex = 0; private readonly EDealWindow[] _dealPeriods; private List<string> _offeredProducts; public override void OnSceneWasInitialized(int buildIndex, string sceneName) { if (sceneName == "Main") { _messagingManager = NetworkSingleton<MessagingManager>.instance; _timeManager = NetworkSingleton<TimeManager>.instance; MelonLogger.Msg("Mod initialized in Main scene"); } } public EDealWindow GetTimePeriod(int time) { //IL_0021: 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_003c: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) int num = time % 2400; if (num >= 600 && num < 1200) { return (EDealWindow)0; } if (num >= 1200 && num < 1800) { return (EDealWindow)1; } if (num >= 1800 && num < 2400) { return (EDealWindow)2; } return (EDealWindow)3; } private (int start, int end) GetPeriodTimes(EDealWindow period) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected I4, but got Unknown return (int)period switch { 0 => (600, 1200), 1 => (1200, 1800), 2 => (1800, 2400), 3 => (0, 600), _ => (0, 600), }; } private string FormatGameTime(int ticks) { float num = (float)ticks / 100f; return (num >= 1f) ? $"{num:F1} hours" : $"{ticks:F0} minutes"; } private string FormatGameTimeForCurrent(int ticks) { float num = (float)(ticks % 2400) / 100f; int num2 = (int)num; int num3 = (int)((num - (float)num2) * 100f); return $"{num2:D2}:{num3:D2}"; } private string GetSliderDisplayText() { //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_timeManager == (Object)null) { return "Time not available"; } if (_offeredProducts == null || _offeredProducts.Count == 0) { return "No products available"; } if (_selectedProductIndex < 0 || _selectedProductIndex >= _offeredProducts.Count) { return "Invalid product selection"; } if (_selectedPeriodIndex < 0 || _selectedPeriodIndex >= _dealPeriods.Length) { return "Invalid period selection"; } int time = _timeManager.GetDateTime().time; int num = time % 2400; EDealWindow val = _dealPeriods[_selectedPeriodIndex]; string text = ((val == GetTimePeriod(time)) ? $"{val} (current)" : ((object)(EDealWindow)(ref val)).ToString()); (int start, int end) periodTimes = GetPeriodTimes(val); int item = periodTimes.start; int item2 = periodTimes.end; string text2 = _offeredProducts[_selectedProductIndex]; int totalQuantity = _contractManager.GetTotalQuantity(text2); float potentialGain = _contractManager.GetPotentialGain(text2); int ticks = ((item2 > num) ? (item2 - num) : (2400 - num + item2)); int ticks2 = ((item > num) ? (item - num) : (2400 - num + item)); return "Period: <color=#00FFFF>" + text + "</color>\n" + $"Product: <color=#FFFF00>{text2} [{totalQuantity} units]</color>\n" + $"Potential Gain: <color=#800080>${potentialGain:F2}</color>\n" + $"Extra Price: <color=#00FF00>${_contractManager.OverallPriceModifier:F2}</color>\n" + "Current Time: <color=#FFFFFF>" + FormatGameTimeForCurrent(time) + "</color>\nStarts in: <color=#FFA500>" + FormatGameTime(ticks2) + "</color>\nEnds in: <color=#FF0000>" + FormatGameTime(ticks) + "</color>"; } public override void OnUpdate() { //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0239: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_029c: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_0301: Unknown result type (might be due to invalid IL or missing references) //IL_0371: Unknown result type (might be due to invalid IL or missing references) if (_contractManager.HasProductsChanged()) { _offeredProducts = _contractManager.GetOfferedProductNames(); if (_offeredProducts.Count == 0) { _offeredProducts.Add("All"); _selectedProductIndex = 0; } else if (_selectedProductIndex >= _offeredProducts.Count) { _selectedProductIndex = _offeredProducts.Count - 1; } _contractManager.ResetProductsChanged(); } if (Input.GetKeyDown(KeyBindings.OpenMenuKey)) { _isMenuOpen = !_isMenuOpen; _showSliderText = _isMenuOpen; if (!_isMenuOpen) { _sliderTextTimer = 0f; } } if (Input.GetKeyDown(KeyBindings.PreviousPeriodKey)) { _selectedPeriodIndex = (_selectedPeriodIndex - 1 + _dealPeriods.Length) % _dealPeriods.Length; if (!_isMenuOpen) { _showSliderText = true; _sliderTextTimer = 2.5f; } } else if (Input.GetKeyDown(KeyBindings.NextPeriodKey)) { _selectedPeriodIndex = (_selectedPeriodIndex + 1) % _dealPeriods.Length; if (!_isMenuOpen) { _showSliderText = true; _sliderTextTimer = 2.5f; } } if (_offeredProducts.Count > 1) { if (Input.GetKeyDown(KeyBindings.PreviousProductKey)) { _selectedProductIndex = (_selectedProductIndex - 1 + _offeredProducts.Count) % _offeredProducts.Count; if (!_isMenuOpen) { _showSliderText = true; _sliderTextTimer = 2.5f; } } else if (Input.GetKeyDown(KeyBindings.NextProductKey)) { _selectedProductIndex = (_selectedProductIndex + 1) % _offeredProducts.Count; if (!_isMenuOpen) { _showSliderText = true; _sliderTextTimer = 2.5f; } } } if (Input.GetKeyDown(KeyBindings.AcceptContractKey)) { string productName = _offeredProducts[_selectedProductIndex]; _contractManager.ProcessContracts(_dealPeriods[_selectedPeriodIndex], productName); if (!_isMenuOpen) { _showSliderText = true; _sliderTextTimer = 2.5f; } } if (Input.GetKeyDown(KeyBindings.IncreasePrice) || Input.GetKeyDown((KeyCode)61)) { _contractManager.OverallPriceModifier += 5f; _contractManager.UpdateGainsForModifierChange(); if (!_isMenuOpen) { _showSliderText = true; _sliderTextTimer = 2.5f; } } if (Input.GetKeyDown(KeyBindings.DecreasePrice)) { _contractManager.OverallPriceModifier -= 5f; _contractManager.UpdateGainsForModifierChange(); if (!_isMenuOpen) { _showSliderText = true; _sliderTextTimer = 2.5f; } } if (Input.GetKeyDown((KeyCode)258)) { int time = _timeManager.GetDateTime().time; _timeManager.SetTime(time + 100, false); } if (Input.GetKeyDown((KeyCode)259)) { try { global::TestingClass.TestingClass.GenerateRandomQuests(10); } catch (Exception ex) { MelonLogger.Error((object)ex); } } if (_showSliderText && !_isMenuOpen) { _sliderTextTimer -= Time.deltaTime; if (_sliderTextTimer <= 0f) { _showSliderText = false; } } } public override void OnGUI() { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_005c: 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_00ce: Unknown result type (might be due to invalid IL or missing references) if (_showSliderText || _isMenuOpen) { GUI.color = new Color(0f, 0f, 0f, 0.7f); GUI.Box(new Rect((float)(Screen.width / 2 - 160), (float)(Screen.height / 2 - 120), 320f, 240f), ""); GUI.color = Color.white; GUI.skin.label.alignment = (TextAnchor)4; GUI.skin.label.fontSize = 22; GUI.skin.label.richText = true; GUI.Label(new Rect((float)(Screen.width / 2 - 150), (float)(Screen.height / 2 - 110), 300f, 220f), GetSliderDisplayText()); } } public MainHandler() { EDealWindow[] array = new EDealWindow[4]; RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/); _dealPeriods = (EDealWindow[])(object)array; _offeredProducts = new List<string> { "All" }; ((MelonMod)this)..ctor(); } } public static class DealsTracker { private static readonly Dictionary<string, ProductDefinition> _productCache = new Dictionary<string, ProductDefinition>(); public static void AddDealToList(Customer customer) { ContractInfo offeredContractInfo = customer.offeredContractInfo; object obj; if (offeredContractInfo == null) { obj = null; } else { ProductList products = offeredContractInfo.Products; obj = ((products != null) ? products.entries : null); } if (obj == null || offeredContractInfo.Products.entries.Count == 0) { MelonLogger.Warning("AddDealToList failed for " + ((Object)customer).name + ": No valid contract info"); return; } Entry val = offeredContractInfo.Products.entries[0]; string productId = val.ProductID; string customerName = ((Object)customer).name; if (ContractManager.Instance.GetPendingContracts().Any((PendingContract c) => c.CustomerName == customerName && ((ItemDefinition)c.Product).ID == productId)) { MelonLogger.Msg("Contract already exists for " + customerName + ", ProductID=" + productId); return; } if (!_productCache.TryGetValue(productId, out var value)) { Enumerator<ProductDefinition> enumerator = customer.OrderableProducts.GetEnumerator(); while (enumerator.MoveNext()) { ProductDefinition current = enumerator.Current; if (((ItemDefinition)current).ID == productId) { value = current; _productCache[productId] = value; break; } } if ((Object)(object)value == (Object)null) { MelonLogger.Warning("No matching product found for ProductID=" + productId + " in " + customerName + "'s OrderableProducts"); return; } } MelonLogger.Msg($"Adding contract: Product={((ItemDefinition)value).Name}, Quantity={val.Quantity}, Payment={offeredContractInfo.Payment}, Customer={customerName}"); ContractManager.Instance.AddContract(value, val.Quantity, offeredContractInfo.Payment, customer); } } [HarmonyPatch(typeof(Customer), "OfferContract")] public static class CustomerPatch { private static void Postfix(Customer __instance) { MelonLogger.Msg("Customer.OfferContract patch triggered"); DealsTracker.AddDealToList(__instance); } } [HarmonyPatch(typeof(Customer), "RpcReader___Observers_SetOfferedContract_4277245194")] public static class CustomerClientPatch { private static void Postfix(Customer __instance) { MelonLogger.Msg("Customer.RpcReader_SetOfferedContract patch triggered"); DealsTracker.AddDealToList(__instance); } } [HarmonyPatch(typeof(Customer), "Load")] public static class CustomerStartPatch { private static void Postfix(Customer __instance) { try { if ((Object)(object)__instance != (Object)null && __instance.offeredContractInfo != null) { MelonLogger.Msg("CustomerStartPatch: Found active contract for " + ((Object)__instance).name); DealsTracker.AddDealToList(__instance); } } catch (Exception ex) { MelonLogger.Error("Error in CustomerStartPatch for " + (((Object)(object)__instance != (Object)null) ? ((Object)__instance).name : "unknown") + ": " + ex.Message); } } } }