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.6
SmartSeller.dll
Decompiled 8 hours 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.6")] 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.6 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 += BuildOvertimePreview(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); } } int sellValue = CalculateTotal(pendingSellItems, GetCurrentSellMultiplier()); int count = pendingSellItems.Count; string text = BuildOvertimePreview(sellValue); val.SellItemsOnServer(); 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 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 BuildOvertimePreview(int sellValue) { try { int quotaFulfilled = GetQuotaFulfilled(); int profitQuota = GetProfitQuota(); int daysUntilDeadline = GetDaysUntilDeadline(); int num = quotaFulfilled + sellValue; int num2 = num - profitQuota; if (profitQuota <= 0) { return "Overtime preview unavailable.\n"; } if (num2 <= 0) { int num3 = profitQuota - num; if (num3 < 0) { num3 = 0; } return "Overtime bonus preview: $0\nQuota after sell: $" + num + " / $" + profitQuota + "\nStill needed for quota: $" + num3 + "\n"; } int num4 = CalculateOvertimeBonus(num, profitQuota, daysUntilDeadline); return "Overtime bonus preview: $" + num4 + "\nQuota after sell: $" + num + " / $" + profitQuota + "\nOver quota by: $" + num2 + "\nDays left bonus used: " + daysUntilDeadline + " day(s)\n"; } catch { return "Overtime preview unavailable.\n"; } } private static int CalculateOvertimeBonus(int quotaFulfilledAfterSell, int profitQuota, int daysUntilDeadline) { int num = quotaFulfilledAfterSell - profitQuota; if (num <= 0) { return 0; } 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; } }