Decompiled source of AreaPickup v1.0.1

AreaPickup.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn;
using Jotunn.Configs;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_utils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("AreaPickup")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1")]
[assembly: AssemblyProduct("AreaPickup")]
[assembly: AssemblyTitle("AreaPickup")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.1.0")]
[module: UnverifiableCode]
[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 AreaPickup
{
	[BepInPlugin("dreamscapist.valheim.areapickup", "AreaPickup", "1.0.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	internal class AreaPickupPlugin : BaseUnityPlugin
	{
		private class SweepResult
		{
			public int Collected;

			public int Pending;

			public bool InventoryFull;
		}

		public const string PluginGUID = "dreamscapist.valheim.areapickup";

		public const string PluginName = "AreaPickup";

		public const string PluginVersion = "1.0.1";

		private const float DefaultRange = 10f;

		private const float MinRange = 5f;

		private const float MaxRange = 100f;

		private const int OwnershipRetries = 4;

		private const float OwnershipRetryDelay = 0.3f;

		private const float PickableDropDelay = 0.6f;

		internal static bool SuppressTopLeftMessages;

		private ConfigEntry<float> _range;

		private ConfigEntry<KeyboardShortcut> _hotkey;

		private ConfigEntry<bool> _pickupGroundItems;

		private ConfigEntry<bool> _harvestPickables;

		private ConfigEntry<bool> _includeFish;

		private ConfigEntry<bool> _ignorePlacedItems;

		private ConfigEntry<bool> _debugLogging;

		private ConfigEntry<string> _blacklistEntry;

		private ConfigEntry<bool> _showPerItemMessages;

		private ConfigEntry<bool> _showSummary;

		private readonly HashSet<string> _blacklist = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private ButtonConfig _pickupButton;

		private Harmony _harmony;

		private bool _busy;

		private void Awake()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Expected O, but got Unknown
			BindConfig();
			_pickupButton = new ButtonConfig
			{
				Name = "AreaPickup_Pickup",
				ShortcutConfig = _hotkey
			};
			InputManager.Instance.AddButton("dreamscapist.valheim.areapickup", _pickupButton);
			_harmony = new Harmony("dreamscapist.valheim.areapickup");
			_harmony.PatchAll(typeof(AreaPickupPlugin).Assembly);
			Logger.LogInfo((object)"AreaPickup v1.0.1 loaded");
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		private void BindConfig()
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			_range = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Range", 10f, new ConfigDescription("Pickup radius in meters.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 100f), Array.Empty<object>()));
			_hotkey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "Hotkey", new KeyboardShortcut((KeyCode)101, (KeyCode[])(object)new KeyCode[1] { (KeyCode)304 }), "Key combination that picks up everything in range.");
			_pickupGroundItems = ((BaseUnityPlugin)this).Config.Bind<bool>("Filters", "PickupGroundItems", true, "Pick up dropped items lying on the ground.");
			_harvestPickables = ((BaseUnityPlugin)this).Config.Bind<bool>("Filters", "HarvestPickables", false, "Harvest pickables in range (berries, mushrooms, stones, flint, branches, grown crops, ...).");
			_includeFish = ((BaseUnityPlugin)this).Config.Bind<bool>("Filters", "IncludeFish", false, "Also grab fish. Off by default so live fish can't be scooped out of the water.");
			_ignorePlacedItems = ((BaseUnityPlugin)this).Config.Bind<bool>("Filters", "IgnorePlacedItems", true, "Leave placed-down items alone (food set out on tables, decorative items). Only loose, dropped items get picked up.");
			_debugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "LogCandidates", false, "Log every item found in range and why it was picked up or skipped. Use this if something placed still gets grabbed, or a dropped item gets left behind.");
			_blacklistEntry = ((BaseUnityPlugin)this).Config.Bind<string>("Filters", "Blacklist", "", "Comma-separated prefab names to ignore, e.g. Pickable_Carrot,Wood,Resin");
			_blacklistEntry.SettingChanged += delegate
			{
				ParseBlacklist();
			};
			ParseBlacklist();
			_showPerItemMessages = ((BaseUnityPlugin)this).Config.Bind<bool>("Messages", "ShowPerItemMessages", false, "Show vanilla's top-left 'added X' message for every item picked up.");
			_showSummary = ((BaseUnityPlugin)this).Config.Bind<bool>("Messages", "ShowSummary", true, "Show one summary message after each sweep.");
		}

		private void ParseBlacklist()
		{
			_blacklist.Clear();
			string[] array = _blacklistEntry.Value.Split(',');
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length > 0)
				{
					_blacklist.Add(text);
				}
			}
		}

		private void Update()
		{
			if (!_busy && ZInput.instance != null && ZInput.GetButtonDown(_pickupButton.Name))
			{
				Player localPlayer = Player.m_localPlayer;
				if (!((Object)(object)localPlayer == (Object)null) && !((Character)localPlayer).IsDead() && !((Character)localPlayer).IsTeleporting() && !((Character)localPlayer).InCutscene() && ((Character)localPlayer).TakeInput())
				{
					((MonoBehaviour)this).StartCoroutine(PickupRoutine(localPlayer));
				}
			}
		}

		private IEnumerator PickupRoutine(Player player)
		{
			_busy = true;
			try
			{
				float range = Mathf.Clamp(_range.Value, 5f, 100f);
				int harvested = 0;
				int collected = 0;
				bool inventoryFull = false;
				if (_harvestPickables.Value)
				{
					harvested = HarvestPickables(player, range);
					if (harvested > 0 && _pickupGroundItems.Value)
					{
						yield return (object)new WaitForSeconds(0.6f);
					}
				}
				if (_pickupGroundItems.Value)
				{
					for (int attempt = 0; attempt <= 4; attempt++)
					{
						if (!((Object)(object)player == (Object)null) && !((Character)player).IsDead())
						{
							SweepResult sweepResult = SweepItemDrops(player, range);
							collected += sweepResult.Collected;
							inventoryFull |= sweepResult.InventoryFull;
							if (sweepResult.Pending == 0)
							{
								break;
							}
							yield return (object)new WaitForSeconds(0.3f);
							continue;
						}
						yield break;
					}
				}
				if (_showSummary.Value && (Object)(object)player != (Object)null)
				{
					((Character)player).Message((MessageType)1, BuildSummary(collected, harvested, inventoryFull), 0, (Sprite)null, false);
				}
			}
			finally
			{
				AreaPickupPlugin areaPickupPlugin = this;
				SuppressTopLeftMessages = false;
				areaPickupPlugin._busy = false;
			}
		}

		private int HarvestPickables(Player player, float range)
		{
			//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_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			Vector3 position = ((Component)player).transform.position;
			int num = 0;
			SuppressTopLeftMessages = !_showPerItemMessages.Value;
			try
			{
				foreach (Pickable item in FindInRange<Pickable>(position, range))
				{
					if (!item.m_picked && HasValidView((Component)(object)item) && !IsBlacklisted(((Component)item).gameObject) && item.Interact((Humanoid)(object)player, false, false))
					{
						num++;
					}
				}
				foreach (PickableItem item2 in FindInRange<PickableItem>(position, range))
				{
					if (HasValidView((Component)(object)item2) && !IsBlacklisted(((Component)item2).gameObject) && item2.Interact((Humanoid)(object)player, false, false))
					{
						num++;
					}
				}
				return num;
			}
			finally
			{
				SuppressTopLeftMessages = false;
			}
		}

		private SweepResult SweepItemDrops(Player player, float range)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			SweepResult sweepResult = new SweepResult();
			Inventory inventory = ((Humanoid)player).GetInventory();
			SuppressTopLeftMessages = !_showPerItemMessages.Value;
			try
			{
				foreach (ItemDrop item in FindInRange<ItemDrop>(((Component)player).transform.position, range))
				{
					if ((Object)(object)item == (Object)null)
					{
						continue;
					}
					ZNetView component = ((Component)item).GetComponent<ZNetView>();
					if ((Object)(object)component == (Object)null || !component.IsValid() || IsBlacklisted(((Component)item).gameObject) || (!_includeFish.Value && (Object)(object)((Component)item).GetComponent<Fish>() != (Object)null))
					{
						continue;
					}
					item.Load();
					if (item.m_itemData == null)
					{
						continue;
					}
					if (_ignorePlacedItems.Value && IsPlaced(item, out var reason))
					{
						if (_debugLogging.Value)
						{
							Logger.LogInfo((object)("[AreaPickup] skip placed " + PrefabName(((Component)item).gameObject) + " (" + reason + ")"));
						}
						continue;
					}
					if (_debugLogging.Value)
					{
						Logger.LogInfo((object)($"[AreaPickup] take {PrefabName(((Component)item).gameObject)} x{item.m_itemData.m_stack} " + $"(autoPickup={item.m_autoPickup}, kinematic={IsKinematic(item)}, " + $"food={item.m_itemData.m_shared.m_food})"));
					}
					if (!inventory.CanAddItem(item.m_itemData, -1))
					{
						sweepResult.InventoryFull = true;
						continue;
					}
					if (!component.IsOwner())
					{
						item.RequestOwn();
						sweepResult.Pending++;
						continue;
					}
					int stack = item.m_itemData.m_stack;
					if (((Humanoid)player).Pickup(((Component)item).gameObject, false, false))
					{
						sweepResult.Collected += stack;
					}
				}
				return sweepResult;
			}
			finally
			{
				SuppressTopLeftMessages = false;
			}
		}

		private static List<T> FindInRange<T>(Vector3 center, float range) where T : Component
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			HashSet<T> hashSet = new HashSet<T>();
			List<T> list = new List<T>();
			Collider[] array = Physics.OverlapSphere(center, range, -1, (QueryTriggerInteraction)2);
			for (int i = 0; i < array.Length; i++)
			{
				T componentInParent = ((Component)array[i]).GetComponentInParent<T>();
				if ((Object)(object)componentInParent != (Object)null && hashSet.Add(componentInParent))
				{
					list.Add(componentInParent);
				}
			}
			list.Sort(delegate(T a, T b)
			{
				//IL_000b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				//IL_001b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0031: Unknown result type (might be due to invalid IL or missing references)
				//IL_0037: Unknown result type (might be due to invalid IL or missing references)
				//IL_003c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0041: Unknown result type (might be due to invalid IL or missing references)
				Vector3 val = ((Component)a).transform.position - center;
				float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
				val = ((Component)b).transform.position - center;
				return sqrMagnitude.CompareTo(((Vector3)(ref val)).sqrMagnitude);
			});
			return list;
		}

		private static bool HasValidView(Component component)
		{
			ZNetView component2 = component.GetComponent<ZNetView>();
			if ((Object)(object)component2 != (Object)null)
			{
				return component2.IsValid();
			}
			return false;
		}

		private static bool IsPlaced(ItemDrop drop, out string reason)
		{
			if (!drop.m_autoPickup)
			{
				reason = "autoPickup=false";
				return true;
			}
			if ((Object)(object)((Component)drop).GetComponentInParent<Piece>() != (Object)null)
			{
				reason = "part of a piece";
				return true;
			}
			if (IsKinematic(drop))
			{
				reason = "no free-moving rigidbody";
				return true;
			}
			reason = null;
			return false;
		}

		private static bool IsKinematic(ItemDrop drop)
		{
			Rigidbody component = ((Component)drop).GetComponent<Rigidbody>();
			if (!((Object)(object)component == (Object)null))
			{
				return component.isKinematic;
			}
			return true;
		}

		private bool IsBlacklisted(GameObject go)
		{
			if (_blacklist.Count > 0)
			{
				return _blacklist.Contains(PrefabName(go));
			}
			return false;
		}

		private static string PrefabName(GameObject go)
		{
			string text = ((Object)go).name;
			int num = text.IndexOf("(Clone)", StringComparison.Ordinal);
			if (num >= 0)
			{
				text = text.Substring(0, num);
			}
			return text.Trim();
		}

		private static string BuildSummary(int collected, int harvested, bool inventoryFull)
		{
			string text = ((collected == 0 && harvested == 0) ? "Nothing to pick up" : ((harvested <= 0) ? $"Picked up {collected} item(s)" : $"Picked up {collected} item(s), harvested {harvested}"));
			if (inventoryFull)
			{
				text += " — inventory full, some left behind";
			}
			return text;
		}
	}
	[HarmonyPatch(typeof(Player), "Message")]
	internal static class PlayerMessagePatch
	{
		private static bool Prefix(MessageType __0)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Invalid comparison between Unknown and I4
			if (AreaPickupPlugin.SuppressTopLeftMessages)
			{
				return (int)__0 != 1;
			}
			return true;
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}