Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of SmartSeller v1.0.10
SmartSeller.dll
Decompiled 3 weeks agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using HarmonyLib; using TMPro; using Unity.Netcode; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("SmartSeller")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("SmartSeller")] [assembly: AssemblyTitle("SmartSeller")] [assembly: AssemblyVersion("1.0.0.0")] namespace SmartSeller; [BepInPlugin("lucas.smartseller", "Smart Seller", "1.0.10")] public class SmartSellerPlugin : BaseUnityPlugin { private Harmony harmony; private void Awake() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown harmony = new Harmony("lucas.smartseller"); harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Smart Seller 1.0.10 loaded."); } } [HarmonyPatch(typeof(Terminal), "QuitTerminal")] public class TerminalQuitPatch { private static void Postfix() { TerminalPatch.CancelPendingSellFromTerminalExit(); } } [HarmonyPatch(typeof(Terminal), "ParsePlayerSentence")] public class TerminalPatch { private static List<GrabbableObject> pendingSellItems = new List<GrabbableObject>(); private static int pendingSellTarget = 0; private static int pendingSellTotal = 0; private static string pendingSellMode = ""; private static bool Prefix(Terminal __instance, ref TerminalNode __result, int ___textAdded, TMP_InputField ___screenText) { string text; try { if (___screenText == null) { return true; } if (___textAdded <= 0) { return true; } int num = ___screenText.text.Length - ___textAdded; if (num < 0) { return true; } text = ___screenText.text.Substring(num).Trim().ToLower(); } catch { return true; } if (HasPendingSell()) { if (text == "c" || text == "confirm") { __result = CreateTerminalNode(ConfirmPendingSell()); return false; } if (text == "d" || text == "deny" || text == "cancel") { __result = CreateTerminalNode(DenyPendingSell()); return false; } __result = CreateTerminalNode("Pending sell active.\nPress C to confirm sell or D to deny sell.\n\n"); return false; } if (text == "sell" || text.StartsWith("sell ")) { string message = HandleSellCommand(text); __result = CreateTerminalNode(message); return false; } return true; } public static void CancelPendingSellFromTerminalExit() { if (HasPendingSell()) { ClearPendingSell(); } } private static string HandleSellCommand(string text) { if (!IsAtCompany()) { return "You must be at the Company Building.\n\n"; } string[] array = text.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if (array.Length < 2) { return "Usage: sell <amount>, sell quota, sell min, or sell max\n\n"; } string argument = array[1].ToLower(); return PrepareSell(argument); } private static TerminalNode CreateTerminalNode(string message) { TerminalNode val = ScriptableObject.CreateInstance<TerminalNode>(); val.displayText = message; val.clearPreviousText = true; return val; } private static bool IsAtCompany() { try { if ((Object)(object)StartOfRound.Instance == (Object)null) { return false; } if ((Object)(object)StartOfRound.Instance.currentLevel == (Object)null) { return false; } SelectableLevel currentLevel = StartOfRound.Instance.currentLevel; if (currentLevel.levelID == 3) { return true; } string planetName = currentLevel.PlanetName; if (!string.IsNullOrEmpty(planetName)) { string text = planetName.ToLower(); if (text.Contains("company")) { return true; } if (text.Contains("gordion")) { return true; } } return false; } catch { return false; } } private static string PrepareSell(string argument) { float currentSellMultiplier = GetCurrentSellMultiplier(); List<GrabbableObject> list = (from x in Object.FindObjectsOfType<GrabbableObject>() where (Object)(object)x != (Object)null && x.isInShipRoom && x.scrapValue > 0 && !x.isHeld select x).ToList(); if (list.Count == 0) { return "No scrap found.\n\n"; } int result = 0; List<GrabbableObject> list2 = new List<GrabbableObject>(); if (argument == "quota" || argument == "min") { result = GetQuotaRemaining(); if (result <= 0) { return "Quota is already fulfilled.\n\n"; } list2 = FindBestCombination(list, result, currentSellMultiplier, preferAtLeastTarget: true); } else if (argument == "max") { list2 = list; result = CalculateTotal(list2, currentSellMultiplier); } else { if (!int.TryParse(argument, out result)) { return "Invalid command. Use: sell <amount>, sell quota, sell min, or sell max\n\n"; } if (result <= 0) { return "Sell amount must be higher than 0.\n\n"; } list2 = FindBestCombination(list, result, currentSellMultiplier, preferAtLeastTarget: false); } if (list2.Count == 0) { return "Could not find any matching scrap.\n\n"; } int num = CalculateTotal(list2, currentSellMultiplier); SetPendingSell(list2, result, num, argument); string text = ""; foreach (GrabbableObject item in list2) { int num2 = Mathf.RoundToInt((float)item.scrapValue * currentSellMultiplier); string text2 = "Unknown item"; if ((Object)(object)item.itemProperties != (Object)null) { text2 = item.itemProperties.itemName; } text = text + "Selected: " + text2 + " ($" + num2 + ")\n"; } text = text + "\nTarget: $" + result + " | Selected total: $" + num + "\n"; text += BuildProfitPreview(num); return text + "\nC to confirm sell | D to deny sell\n\n"; } private static string ConfirmPendingSell() { if (!HasPendingSell()) { return "No pending sell to confirm.\n\n"; } if (!IsAtCompany()) { ClearPendingSell(); return "Sell cancelled. You are no longer at the Company Building.\n\n"; } if ((Object)(object)NetworkManager.Singleton == (Object)null) { return "Could not access NetworkManager. Sell was not confirmed.\n\n"; } if (!NetworkManager.Singleton.IsHost && !NetworkManager.Singleton.IsServer) { return "Only the host can confirm automatic selling in this version.\n\n"; } DepositItemsDesk val = Object.FindObjectOfType<DepositItemsDesk>(); if ((Object)(object)val == (Object)null) { return "Could not find the Company sell desk. Sell was not confirmed.\n\n"; } pendingSellItems.RemoveAll((GrabbableObject x) => (Object)(object)x == (Object)null || x.scrapValue <= 0 || x.isHeld); if (pendingSellItems.Count == 0) { ClearPendingSell(); return "Pending sell expired. No valid scrap left to sell.\n\n"; } try { if (val.itemsOnCounter == null) { return "Could not access the Company counter list. Sell was not confirmed.\n\n"; } val.itemsOnCounter.RemoveAll((GrabbableObject x) => (Object)(object)x == (Object)null); if (val.itemsOnCounter.Count > 0) { return "The Company counter already has items on it. Clear the counter first, then confirm again.\n\n"; } for (int i = 0; i < pendingSellItems.Count; i++) { GrabbableObject item = pendingSellItems[i]; PrepareItemForCompanyDesk(item, val, i); if (!val.itemsOnCounter.Contains(item)) { val.itemsOnCounter.Add(item); } } List<GrabbableObject> list = new List<GrabbableObject>(pendingSellItems); int sellValue = CalculateTotal(list, GetCurrentSellMultiplier()); int count = list.Count; string text = BuildProfitPreview(sellValue); val.SellItemsOnServer(); RemoveSoldItemsFromWorld(list); val.itemsOnCounter.RemoveAll((GrabbableObject x) => (Object)(object)x == (Object)null); ClearPendingSell(); return "Confirmed sell.\nSold " + count + " selected item(s).\nTotal value: $" + sellValue + "\n" + text + "\n"; } catch (Exception ex) { return "Sell failed before confirmation finished.\nError: " + ex.Message + "\n\n"; } } private static string DenyPendingSell() { ClearPendingSell(); return "Sell denied. Returning to terminal.\n\n"; } private static void PrepareItemForCompanyDesk(GrabbableObject item, DepositItemsDesk desk, int index) { if (!((Object)(object)item == (Object)null)) { item.isInShipRoom = false; item.isInElevator = false; item.hasHitGround = true; ((Component)item).gameObject.SetActive(true); } } private static void RemoveSoldItemsFromWorld(List<GrabbableObject> soldItems) { if (soldItems == null) { return; } for (int i = 0; i < soldItems.Count; i++) { GrabbableObject val = soldItems[i]; if ((Object)(object)val == (Object)null) { continue; } try { val.isInShipRoom = false; val.isInElevator = false; val.scrapValue = 0; val.SetScrapValue(0); } catch { } try { NetworkObject component = ((Component)val).GetComponent<NetworkObject>(); if ((Object)(object)component != (Object)null && component.IsSpawned) { component.Despawn(true); continue; } } catch { } try { ((Component)val).gameObject.SetActive(false); Object.Destroy((Object)(object)((Component)val).gameObject); } catch { } } } private static bool HasPendingSell() { return pendingSellItems != null && pendingSellItems.Count > 0; } private static void SetPendingSell(List<GrabbableObject> items, int target, int total, string mode) { pendingSellItems = new List<GrabbableObject>(items); pendingSellTarget = target; pendingSellTotal = total; pendingSellMode = mode; } private static void ClearPendingSell() { pendingSellItems.Clear(); pendingSellTarget = 0; pendingSellTotal = 0; pendingSellMode = ""; } private static int GetQuotaRemaining() { try { int num = TimeOfDay.Instance.profitQuota - TimeOfDay.Instance.quotaFulfilled; if (num < 0) { num = 0; } return num; } catch { return 0; } } private static int GetQuotaFulfilled() { try { return TimeOfDay.Instance.quotaFulfilled; } catch { return 0; } } private static int GetProfitQuota() { try { return TimeOfDay.Instance.profitQuota; } catch { return 0; } } private static int GetDaysUntilDeadline() { try { return TimeOfDay.Instance.daysUntilDeadline; } catch { return 0; } } private static string BuildProfitPreview(int sellValue) { try { int quotaFulfilled = GetQuotaFulfilled(); int profitQuota = GetProfitQuota(); int daysUntilDeadline = GetDaysUntilDeadline(); if (profitQuota <= 0) { return "Profit bonus preview unavailable.\n"; } int num = quotaFulfilled + sellValue; int num2 = CalculateProfitBonus(quotaFulfilled, profitQuota, daysUntilDeadline); int num3 = CalculateProfitBonus(num, profitQuota, daysUntilDeadline); int num4 = num3 - num2; if (num4 < 0) { num4 = 0; } int num5 = sellValue + num4; int num6 = num + num3; string text = ""; text += "\nProfit quota preview:\n"; text = text + "Quota after sell: $" + num + " / $" + profitQuota + "\n"; if (num < profitQuota) { text = text + "Still needed for quota: $" + (profitQuota - num) + "\n"; text += "Profit bonus after sell: $0\n"; return text + "Expected value from this sell: $" + sellValue + "\n"; } int num7 = num - profitQuota; int value = 15 * daysUntilDeadline; text = text + "Over quota by: $" + num7 + "\n"; text = text + "Day efficiency modifier: " + FormatSignedMoney(value) + "\n"; text = text + "Profit bonus after sell: $" + num3 + "\n"; text = text + "Bonus added by this sell: +" + num4 + "\n"; text = text + "Expected value from this sell: $" + sellValue + " + $" + num4 + " = $" + num5 + "\n"; return text + "Total quota profit after sell: $" + num6 + "\n"; } catch { return "Profit bonus preview unavailable.\n"; } } private static string FormatSignedMoney(int value) { if (value > 0) { return "+$" + value; } if (value < 0) { return "-$" + Math.Abs(value); } return "$0"; } private static int CalculateProfitBonus(int quotaFulfilledValue, int profitQuota, int daysUntilDeadline) { if (quotaFulfilledValue < profitQuota) { return 0; } int num = quotaFulfilledValue - profitQuota; float num2 = (float)num / 5f + 15f * (float)daysUntilDeadline; int num3 = Mathf.FloorToInt(num2); if (num3 < 0) { num3 = 0; } return num3; } private static float GetCurrentSellMultiplier() { try { if ((Object)(object)StartOfRound.Instance != (Object)null) { return StartOfRound.Instance.companyBuyingRate; } } catch { } try { return TimeOfDay.Instance.daysUntilDeadline switch { 0 => 1f, 1 => 0.77f, 2 => 0.55f, _ => 0.33f, }; } catch { return 1f; } } private static int CalculateTotal(List<GrabbableObject> items, float multiplier) { int num = 0; for (int i = 0; i < items.Count; i++) { if (!((Object)(object)items[i] == (Object)null)) { num += Mathf.RoundToInt((float)items[i].scrapValue * multiplier); } } return num; } private static List<GrabbableObject> FindBestCombination(List<GrabbableObject> items, int target, float multiplier, bool preferAtLeastTarget) { List<GrabbableObject> result = new List<GrabbableObject>(); int count = items.Count; if (count > 20) { items = items.OrderByDescending((GrabbableObject x) => x.scrapValue).Take(20).ToList(); count = items.Count; } if (preferAtLeastTarget) { List<GrabbableObject> list = new List<GrabbableObject>(); List<GrabbableObject> result2 = new List<GrabbableObject>(); int num = int.MaxValue; int num2 = -1; int num3 = 1 << count; for (int i = 1; i < num3; i++) { List<GrabbableObject> list2 = new List<GrabbableObject>(); int num4 = 0; for (int j = 0; j < count; j++) { if ((i & (1 << j)) != 0) { list2.Add(items[j]); num4 += Mathf.RoundToInt((float)items[j].scrapValue * multiplier); } } if (num4 >= target) { if (num4 < num) { num = num4; list = list2; } } else if (num4 > num2) { num2 = num4; result2 = list2; } } if (list.Count > 0) { return list; } return result2; } int num5 = int.MaxValue; int num6 = 1 << count; for (int k = 1; k < num6; k++) { List<GrabbableObject> list3 = new List<GrabbableObject>(); int num7 = 0; for (int l = 0; l < count; l++) { if ((k & (1 << l)) != 0) { list3.Add(items[l]); num7 += Mathf.RoundToInt((float)items[l].scrapValue * multiplier); } } int num8 = Math.Abs(target - num7); if (num8 < num5) { num5 = num8; result = list3; if (num8 == 0) { break; } } } return result; } }