using 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 pendingSellItems = new List(); 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 , 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(); 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 list = (from x in Object.FindObjectsOfType() 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 list2 = new List(); 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 , 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(); 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 items, int target, int total, string mode) { pendingSellItems = new List(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 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 FindBestCombination(List items, int target, float multiplier, bool preferAtLeastTarget) { List result = new List(); int count = items.Count; if (count > 20) { items = items.OrderByDescending((GrabbableObject x) => x.scrapValue).Take(20).ToList(); count = items.Count; } if (preferAtLeastTarget) { List list = new List(); List result2 = new List(); int num = int.MaxValue; int num2 = -1; int num3 = 1 << count; for (int i = 1; i < num3; i++) { List list2 = new List(); 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 list3 = new List(); 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; } }