using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using FishNet.Object; using HarmonyLib; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("BulkSellMod")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("BulkSellMod")] [assembly: AssemblyTitle("BulkSellMod")] [assembly: AssemblyVersion("1.0.0.0")] namespace BulkSellMod; [BepInPlugin("com.bulksell.htf", "BulkSellMod", "1.2.1")] [BepInProcess("How to Fish")] public class BulkSellMod : BaseUnityPlugin { private struct BulkSellJob { public NPC Npc; public NPCQuest Quest; public byte QuestIndex; public byte Slot; public PlayerInventory Inv; public int Remaining; public int ItemId; } [HarmonyPatch(typeof(NPC), "EatItem")] private static class EatPatch { [HarmonyPostfix] private static void Postfix(NPC __instance, NPCQuest quest, byte questIndex, Item item) { try { if (_isBulkFeeding || !SellWholeStack.Value || !_bulkArmed) { return; } _bulkArmed = false; if (!Object.op_Implicit((Object)(object)Server.Instance) || !((NetworkBehaviour)Server.Instance).IsServerInitialized || !Object.op_Implicit((Object)(object)item)) { return; } Player lastHolder = item.LastHolder; if (!Object.op_Implicit((Object)(object)lastHolder)) { return; } PlayerInventory inventory = lastHolder.Inventory; if (!Object.op_Implicit((Object)(object)inventory)) { return; } int num = 0; foreach (KeyValuePair item2 in inventory._items) { Item value = item2.Value; if (Object.op_Implicit((Object)(object)value) && value.ID == item.ID) { int num2 = StackCount(inventory, item2.Key) + 1; _bulkQueue.Enqueue(new BulkSellJob { Npc = __instance, Quest = quest, QuestIndex = questIndex, Slot = item2.Key, Inv = inventory, Remaining = num2, ItemId = item.ID }); num += num2; } } if (num > 0) { _logger.LogInfo((object)string.Format("{0}: Sold {1} of item {2} in one go.", "BulkSellMod", num, item.ID)); } } catch (Exception arg) { _logger.LogWarning((object)string.Format("{0}: bulk sell error: {1}", "BulkSellMod", arg)); } } } public const string PluginGuid = "com.bulksell.htf"; public const string PluginName = "BulkSellMod"; public const string PluginVersion = "1.2.1"; public static ConfigEntry SellWholeStack; private static bool _bulkArmed; private const int MaxFeedsPerFrame = 6; private static readonly Queue _bulkQueue = new Queue(); private static bool _isBulkFeeding; private static MethodInfo _popServerStack; private static MethodInfo _getStackCount; private static MethodInfo _eatItem; private static ManualLogSource _logger; private void Awake() { _logger = ((BaseUnityPlugin)this).Logger; SellWholeStack = ((BaseUnityPlugin)this).Config.Bind("General", "SellWholeStack", true, "Master switch for bulk sell. Hold LeftShift and press Q to arm ONE whole-stack sale, then feed a fish to the NPC."); Type type = AccessTools.TypeByName("StackableItems.StackManager"); if (type != null) { _popServerStack = type.GetMethod("PopServerStack", BindingFlags.Static | BindingFlags.Public); _getStackCount = type.GetMethod("GetStackCount", BindingFlags.Static | BindingFlags.Public); if (_popServerStack != null && _getStackCount != null) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"BulkSellMod: StackableItems found and hooked."); } } Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), "com.bulksell.htf"); ((BaseUnityPlugin)this).Logger.LogInfo((object)"BulkSellMod v1.2.1 loaded."); } private void Update() { Keyboard current = Keyboard.current; if (current != null && ((ButtonControl)current.leftShiftKey).isPressed && ((ButtonControl)current.qKey).wasPressedThisFrame && !_bulkArmed) { _bulkArmed = true; _logger.LogInfo((object)"BulkSellMod: Bulk armed for next fish feed. Throw a fish!"); } if (_bulkQueue.Count == 0) { return; } int num = 0; while (num < 6 && _bulkQueue.Count > 0) { BulkSellJob item = _bulkQueue.Dequeue(); num++; if (!Object.op_Implicit((Object)(object)item.Npc) || !Object.op_Implicit((Object)(object)item.Inv)) { continue; } Item val = PopStack(item.Inv, item.Slot); if ((Object)(object)val == (Object)null) { val = SlotItem(item.Inv, item.Slot); } if (Object.op_Implicit((Object)(object)val) && val.ID == item.ItemId && !val.IsDestroying && !((NetworkBehaviour)val).IsDeinitializing) { _isBulkFeeding = true; try { EatItemDirect(item.Npc, item.Quest, item.QuestIndex, val); } finally { _isBulkFeeding = false; } } if (item.Remaining > 1) { item.Remaining--; _bulkQueue.Enqueue(item); } } } private static Item PopStack(PlayerInventory inv, byte slot) { if (_popServerStack == null) { return null; } try { object? obj = _popServerStack.Invoke(null, new object[2] { inv, slot }); return (Item)((obj is Item) ? obj : null); } catch { return null; } } private static Item SlotItem(PlayerInventory inv, byte slot) { try { if (inv._items == null) { return null; } return inv._items[slot]; } catch { return null; } } private static int StackCount(PlayerInventory inv, byte slot) { if (_getStackCount == null) { return 0; } try { return (int)_getStackCount.Invoke(null, new object[2] { inv, slot }); } catch { return 0; } } private static void EatItemDirect(NPC npc, NPCQuest quest, byte questIndex, Item item) { if (_eatItem == null) { _eatItem = AccessTools.Method(typeof(NPC), "EatItem", (Type[])null, (Type[])null); } _eatItem.Invoke(npc, new object[3] { quest, questIndex, item }); } }