Decompiled source of How to QuickSell v1.0.0

KeyErrorFinn.QuickSell.dll

Decompiled 2 days ago
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 BepInEx.Configuration;
using BepInEx.Logging;
using FishNet.Object;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[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("KeyErrorFinn.QuickSell")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("How to QuickSell")]
[assembly: AssemblyTitle("KeyErrorFinn.QuickSell")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace KeyErrorFinn.QuickSell
{
	[BepInPlugin("com.keyerrorfinn.quicksell", "How to QuickSell", "1.0.0")]
	public sealed class QuickSellPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.keyerrorfinn.quicksell";

		public const string PluginName = "How to QuickSell";

		public const string PluginVersion = "1.0.0";

		private ConfigEntry<KeyboardShortcut> _sellAllHotkey;

		private ConfigEntry<bool> _sellFloorItems;

		private ConfigEntry<bool> _sellInventoryItems;

		private ConfigEntry<bool> _onlySellFish;

		private ConfigEntry<float> _floorSaleWaterBuffer;

		private QuickSellService _quickSell;

		private QuickSellSettingsUI _settingsUi;

		private void Awake()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			_sellAllHotkey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "Sell all hotkey", new KeyboardShortcut((KeyCode)289, Array.Empty<KeyCode>()), "Sell all enabled item groups. You must be the host/server.");
			_sellFloorItems = ((BaseUnityPlugin)this).Config.Bind<bool>("Sale options", "Sell all floor items", false, "Sell dead fish on the loaded island and in the nearby water. Shellfish and other non-fish creatures are excluded.");
			_sellInventoryItems = ((BaseUnityPlugin)this).Config.Bind<bool>("Sale options", "Sell all inventory items", true, "Sell eligible items in the local inventory, including a freshly picked-up item held outside the inventory bar.");
			_onlySellFish = ((BaseUnityPlugin)this).Config.Bind<bool>("Safety", "Only sell fish", true, "When enabled, only fish/creature items are sold. Turn this off to sell all non-quest items with value.");
			_floorSaleWaterBuffer = ((BaseUnityPlugin)this).Config.Bind<float>("Sale options", "Floor sale water buffer", 30f, "Additional distance beyond the island edge for dead fish floating in nearby water.");
			_quickSell = new QuickSellService(((BaseUnityPlugin)this).Logger, _sellFloorItems, _sellInventoryItems, _onlySellFish, _floorSaleWaterBuffer);
			_settingsUi = new QuickSellSettingsUI(((BaseUnityPlugin)this).Logger);
			((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} {1} loaded. Press {2} to sell enabled items.", "How to QuickSell", "1.0.0", _sellAllHotkey.Value));
			_settingsUi.Install(_sellFloorItems, _sellInventoryItems);
		}

		private void Update()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			KeyboardShortcut value = _sellAllHotkey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				_quickSell.TrySell();
			}
		}

		private void OnDestroy()
		{
			_settingsUi?.Dispose();
		}
	}
	internal sealed class QuickSellService
	{
		private sealed class FieldInfoCache
		{
			private readonly FieldInfo _itemSlots = AccessTools.Field(typeof(PlayerInventory), "_itemSlots");

			public List<InventorySlot> GetSlots(PlayerInventory inventory)
			{
				return _itemSlots?.GetValue(inventory) as List<InventorySlot>;
			}
		}

		private readonly ManualLogSource _log;

		private readonly ConfigEntry<bool> _sellFloorItems;

		private readonly ConfigEntry<bool> _sellInventoryItems;

		private readonly ConfigEntry<bool> _onlySellFish;

		private readonly ConfigEntry<float> _floorSaleWaterBuffer;

		private readonly FieldInfoCache _fields = new FieldInfoCache();

		private readonly HashSet<Item> _itemsBeingSold = new HashSet<Item>();

		public QuickSellService(ManualLogSource log, ConfigEntry<bool> sellFloorItems, ConfigEntry<bool> sellInventoryItems, ConfigEntry<bool> onlySellFish, ConfigEntry<float> floorSaleWaterBuffer)
		{
			_log = log;
			_sellFloorItems = sellFloorItems;
			_sellInventoryItems = sellInventoryItems;
			_onlySellFish = onlySellFish;
			_floorSaleWaterBuffer = floorSaleWaterBuffer;
		}

		public void TrySell()
		{
			_itemsBeingSold.RemoveWhere((Item item) => (Object)(object)item == (Object)null);
			if ((Object)(object)Player.LocalPlayer == (Object)null || (Object)(object)Player.LocalPlayer.Inventory == (Object)null)
			{
				_log.LogWarning((object)"QuickSell is only available after the local player has spawned.");
				return;
			}
			if ((Object)(object)Server.Instance == (Object)null || !((NetworkBehaviour)Server.Instance).IsServerInitialized)
			{
				_log.LogWarning((object)"QuickSell must be used by the lobby host. It cannot change a remote server's inventory or money.");
				return;
			}
			PlayerInventory inventory = Player.LocalPlayer.Inventory;
			NPC val = SaleRules.FindMoneyNpc();
			if ((Object)(object)val == (Object)null)
			{
				_log.LogWarning((object)"No money NPC is loaded. Move to the trader island before using QuickSell.");
				return;
			}
			List<InventorySlot> slots = _fields.GetSlots(inventory);
			if (slots == null)
			{
				_log.LogError((object)"Unable to read the game's inventory slots; the game may have changed.");
				return;
			}
			Item heldItem;
			bool heldItemIsOutsideInventory;
			List<Item> list = CollectItems(inventory, slots, out heldItem, out heldItemIsOutsideInventory);
			if (list.Count == 0)
			{
				_log.LogInfo((object)"No eligible inventory items to sell.");
				return;
			}
			foreach (Item item in list)
			{
				_itemsBeingSold.Add(item);
			}
			try
			{
				if (_sellInventoryItems.Value)
				{
					inventory.ApplySlot(-1);
				}
				if (heldItemIsOutsideInventory)
				{
					Player.LocalPlayer.Holding.DropItem(false, heldItem);
				}
				foreach (Item item2 in list)
				{
					if (inventory.HasItemInInventory(item2))
					{
						inventory.RemoveItem(item2);
					}
					MoneyManager.SellItem(item2);
					item2.DestroyItem((byte)2, val.ID);
				}
				_log.LogInfo((object)$"Sold {list.Count} item(s) through money NPC {val.ID}.");
			}
			catch (Exception arg)
			{
				_log.LogError((object)$"QuickSell stopped: {arg}");
			}
		}

		private List<Item> CollectItems(PlayerInventory inventory, List<InventorySlot> slots, out Item heldItem, out bool heldItemIsOutsideInventory)
		{
			List<Item> list = new List<Item>();
			if (_sellInventoryItems.Value)
			{
				list.AddRange(from slot in slots
					select (!((Object)(object)slot == (Object)null)) ? slot.Item : null into item
					where SaleRules.IsEligibleForSale(item, _onlySellFish.Value) && !_itemsBeingSold.Contains(item)
					select item);
			}
			heldItem = Player.LocalPlayer.Holding.HeldItem;
			heldItemIsOutsideInventory = _sellInventoryItems.Value && SaleRules.IsEligibleForSale(heldItem, _onlySellFish.Value) && !_itemsBeingSold.Contains(heldItem) && !inventory.HasItemInInventory(heldItem);
			if (heldItemIsOutsideInventory)
			{
				list.Add(heldItem);
			}
			if (_sellFloorItems.Value)
			{
				list.AddRange(from item in SaleRules.GetFloorFishNearIsland(_floorSaleWaterBuffer.Value, _log)
					where !_itemsBeingSold.Contains(item)
					select item);
			}
			return list.Distinct().ToList();
		}
	}
	internal sealed class QuickSellSettingsUI : IDisposable
	{
		private readonly ManualLogSource _log;

		private readonly List<GameObject> _nativeSettingsRows = new List<GameObject>();

		public QuickSellSettingsUI(ManualLogSource log)
		{
			_log = log;
		}

		public void Install(ConfigEntry<bool> sellFloorItems, ConfigEntry<bool> sellInventoryItems)
		{
			TextMeshProUGUI val = ((IEnumerable<TextMeshProUGUI>)Resources.FindObjectsOfTypeAll<TextMeshProUGUI>()).FirstOrDefault((Func<TextMeshProUGUI, bool>)((TextMeshProUGUI text) => (Object)(object)text != (Object)null && !string.IsNullOrEmpty(((TMP_Text)text).text) && ((TMP_Text)text).text.Trim() == "Blood"));
			if ((Object)(object)val == (Object)null || (Object)(object)((TMP_Text)val).transform.parent == (Object)null || (Object)(object)((TMP_Text)val).transform.parent.parent == (Object)null)
			{
				_log.LogWarning((object)"Gameplay options are not loaded yet; QuickSell settings will appear after the next ScriptEngine reload.");
				return;
			}
			Transform parent = ((TMP_Text)val).transform.parent;
			Transform parent2 = parent.parent;
			AddSpacer(parent2, "MassSellSettingsSpacer", 6f);
			AddToggle(parent2, parent, "QuickSell: Floor Fish", sellFloorItems);
			AddToggle(parent2, parent, "QuickSell: Inventory Fish/Creatures", sellInventoryItems);
			AddSpacer(parent2, "MassSellSettingsBottomPadding", 10f);
			MakeScrollable(parent2);
		}

		public void Dispose()
		{
			foreach (GameObject nativeSettingsRow in _nativeSettingsRows)
			{
				if ((Object)(object)nativeSettingsRow != (Object)null)
				{
					Object.Destroy((Object)(object)nativeSettingsRow);
				}
			}
			_nativeSettingsRows.Clear();
		}

		private void AddSpacer(Transform layout, string name, float height)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			GameObject val = new GameObject(name, new Type[2]
			{
				typeof(RectTransform),
				typeof(LayoutElement)
			});
			val.transform.SetParent(layout, false);
			LayoutElement component = val.GetComponent<LayoutElement>();
			component.minHeight = height;
			component.preferredHeight = height;
			component.flexibleHeight = 0f;
			_nativeSettingsRows.Add(val);
		}

		private void AddToggle(Transform layout, Transform templateRow, string labelText, ConfigEntry<bool> setting)
		{
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Expected O, but got Unknown
			GameObject val = Object.Instantiate<GameObject>(((Component)templateRow).gameObject, layout);
			((Object)val).name = "MassSell" + labelText.Replace(" ", string.Empty) + "Row";
			_nativeSettingsRows.Add(val);
			TextMeshProUGUI val2 = val.GetComponentsInChildren<TextMeshProUGUI>(true).FirstOrDefault();
			if ((Object)(object)val2 != (Object)null)
			{
				Behaviour[] components = ((Component)val2).GetComponents<Behaviour>();
				foreach (Behaviour val3 in components)
				{
					if (((object)val3).GetType().Name == "LocalizeStringEvent")
					{
						val3.enabled = false;
					}
				}
				((TMP_Text)val2).text = labelText;
			}
			Toggle componentInChildren = val.GetComponentInChildren<Toggle>(true);
			if ((Object)(object)componentInChildren == (Object)null)
			{
				_log.LogError((object)"Native settings toggle template was missing its Toggle component.");
				Object.Destroy((Object)(object)val);
				return;
			}
			componentInChildren.onValueChanged = new ToggleEvent();
			componentInChildren.isOn = setting.Value;
			((UnityEvent<bool>)(object)componentInChildren.onValueChanged).AddListener((UnityAction<bool>)delegate(bool value)
			{
				setting.Value = value;
			});
		}

		private static void MakeScrollable(Transform layout)
		{
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Expected O, but got Unknown
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Expected O, but got Unknown
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			GameObject val;
			Transform parent;
			if ((Object)(object)layout.parent != (Object)null && ((Object)layout.parent).name == "MassSellGameplayViewport")
			{
				val = ((Component)layout.parent).gameObject;
				parent = val.transform.parent;
			}
			else
			{
				parent = layout.parent;
				if ((Object)(object)parent == (Object)null)
				{
					return;
				}
				val = new GameObject("MassSellGameplayViewport", new Type[3]
				{
					typeof(RectTransform),
					typeof(RectMask2D),
					typeof(Image)
				});
				val.transform.SetParent(parent, false);
				layout.SetParent(val.transform, false);
			}
			if (!((Object)(object)parent == (Object)null))
			{
				RectTransform component = val.GetComponent<RectTransform>();
				component.anchorMin = Vector2.zero;
				component.anchorMax = Vector2.one;
				component.offsetMin = new Vector2(0f, 8f);
				component.offsetMax = new Vector2(-18f, -8f);
				Image obj = val.GetComponent<Image>() ?? val.AddComponent<Image>();
				((Graphic)obj).color = new Color(0f, 0f, 0f, 0f);
				((Graphic)obj).raycastTarget = true;
				RectTransform val2 = (RectTransform)layout;
				val2.anchorMin = new Vector2(0f, 1f);
				val2.anchorMax = new Vector2(1f, 1f);
				val2.pivot = new Vector2(0.5f, 1f);
				val2.anchoredPosition = Vector2.zero;
				val2.sizeDelta = new Vector2(0f, val2.sizeDelta.y);
				ContentSizeFitter obj2 = ((Component)layout).GetComponent<ContentSizeFitter>() ?? ((Component)layout).gameObject.AddComponent<ContentSizeFitter>();
				obj2.horizontalFit = (FitMode)0;
				obj2.verticalFit = (FitMode)2;
				ScrollRect component2 = ((Component)parent).GetComponent<ScrollRect>();
				if ((Object)(object)component2 != (Object)null)
				{
					Object.Destroy((Object)(object)component2);
				}
				ScrollRect obj3 = val.GetComponent<ScrollRect>() ?? val.AddComponent<ScrollRect>();
				obj3.content = val2;
				obj3.viewport = component;
				obj3.horizontal = false;
				obj3.vertical = true;
				obj3.movementType = (MovementType)2;
				obj3.scrollSensitivity = 35f;
				obj3.verticalScrollbar = CreateOrUpdateScrollbar(parent);
				obj3.verticalScrollbarVisibility = (ScrollbarVisibility)1;
				obj3.verticalScrollbarSpacing = 4f;
				Canvas.ForceUpdateCanvases();
				LayoutRebuilder.ForceRebuildLayoutImmediate(val2);
			}
		}

		private static Scrollbar CreateOrUpdateScrollbar(Transform display)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: 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_0229: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Expected O, but got Unknown
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0203: Unknown result type (might be due to invalid IL or missing references)
			Transform val = display.Find("MassSellGameplayScrollbar");
			GameObject val2 = (GameObject)(((Object)(object)val != (Object)null) ? ((object)((Component)val).gameObject) : ((object)new GameObject("MassSellGameplayScrollbar", new Type[3]
			{
				typeof(RectTransform),
				typeof(Image),
				typeof(Scrollbar)
			})));
			if ((Object)(object)val == (Object)null)
			{
				val2.transform.SetParent(display, false);
			}
			RectTransform component = val2.GetComponent<RectTransform>();
			component.anchorMin = new Vector2(1f, 0f);
			component.anchorMax = new Vector2(1f, 1f);
			component.pivot = new Vector2(1f, 0.5f);
			component.sizeDelta = new Vector2(10f, -16f);
			component.anchoredPosition = new Vector2(-5f, 0f);
			Image component2 = val2.GetComponent<Image>();
			((Graphic)component2).color = new Color(0f, 0f, 0f, 0.32f);
			((Graphic)component2).raycastTarget = true;
			Transform obj = val2.transform.Find("Sliding Area/Handle");
			RectTransform val3 = (RectTransform)(object)((obj is RectTransform) ? obj : null);
			if ((Object)(object)val3 == (Object)null)
			{
				GameObject val4 = new GameObject("Sliding Area", new Type[1] { typeof(RectTransform) });
				val4.transform.SetParent(val2.transform, false);
				RectTransform component3 = val4.GetComponent<RectTransform>();
				component3.anchorMin = Vector2.zero;
				component3.anchorMax = Vector2.one;
				component3.offsetMin = new Vector2(2f, 2f);
				component3.offsetMax = new Vector2(-2f, -2f);
				GameObject val5 = new GameObject("Handle", new Type[2]
				{
					typeof(RectTransform),
					typeof(Image)
				});
				val5.transform.SetParent(val4.transform, false);
				val3 = val5.GetComponent<RectTransform>();
				val3.anchorMin = Vector2.zero;
				val3.anchorMax = Vector2.one;
				val3.offsetMin = Vector2.zero;
				val3.offsetMax = Vector2.zero;
			}
			Image component4 = ((Component)val3).GetComponent<Image>();
			((Graphic)component4).color = new Color(1f, 1f, 1f, 0.82f);
			((Graphic)component4).raycastTarget = true;
			Scrollbar component5 = val2.GetComponent<Scrollbar>();
			component5.handleRect = val3;
			((Selectable)component5).targetGraphic = (Graphic)(object)component4;
			component5.direction = (Direction)2;
			return component5;
		}
	}
	internal static class SaleRules
	{
		public static bool IsEligibleForSale(Item item, bool onlySellFish)
		{
			if ((Object)(object)item == (Object)null || item.IsQuestItem || item.TotalWorth <= 0 || ((Object)(object)item.Creature != (Object)null && !item.Creature.IsDead))
			{
				return false;
			}
			if (onlySellFish && !((Object)(object)item.Fish != (Object)null))
			{
				return (Object)(object)item.Creature != (Object)null;
			}
			return true;
		}

		public static IEnumerable<Item> GetFloorFishNearIsland(float waterBuffer, ManualLogSource log)
		{
			if ((Object)(object)Island.CurIsland == (Object)null)
			{
				log.LogWarning((object)"No island is loaded, so floor-item selling was skipped.");
				return Enumerable.Empty<Item>();
			}
			float num = Mathf.Max(0f, Island.IslandSize + waterBuffer);
			float rangeSquared = num * num;
			return ItemManager.Items.Values.Where((Item item) => (Object)(object)item != (Object)null && (Object)(object)item.Fish != (Object)null && ((Creature)item.Fish).IsDead && !item.IsQuestItem && !item.IsInInventory && (Object)(object)item.Holder == (Object)null && (Object)(object)item.SyncedHolder == (Object)null && !item.IsDestroying && !((NetworkBehaviour)item).IsDeinitializing && item.TotalWorth > 0).Where(delegate(Item item)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0010: Unknown result type (might be due to invalid IL or missing references)
				//IL_0015: Unknown result type (might be due to invalid IL or missing references)
				Vector3 val = ((Component)item).transform.position - Island.IslandPos;
				return ((Vector3)(ref val)).sqrMagnitude <= rangeSquared;
			}).Distinct()
				.ToList();
		}

		public static NPC FindMoneyNpc()
		{
			return ((IEnumerable<NPC>)Object.FindObjectsByType<NPC>()).FirstOrDefault((Func<NPC, bool>)((NPC npc) => (Object)(object)npc != (Object)null && npc.Quests.Any((NPCQuest quest) => (Object)(object)quest != (Object)null && (int)quest.Type == 1)));
		}
	}
}