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)));
}
}
}