using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("NoAutoPickup")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.2.0")] [assembly: AssemblyInformationalVersion("0.1.2+7b3884cc30328564314e91bb160e4939887cae56")] [assembly: AssemblyProduct("NoAutoPickup")] [assembly: AssemblyTitle("NoAutoPickup")] [assembly: AssemblyVersion("0.1.2.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [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 NoAutoPickup { internal static class DrawerApi { private static MethodInfo? _allDrawers; internal static void Init() { Type type = Type.GetType("API.ClientSideV2, kg_ItemDrawers"); if (type == null) { Plugin.Log.LogInfo((object)"kg.ItemDrawers not found; drawer zones disabled until that mod is present."); return; } _allDrawers = type.GetMethod("AllDrawers", BindingFlags.Static | BindingFlags.Public); if (_allDrawers == null) { Plugin.Log.LogWarning((object)"kg.ItemDrawers loaded but API.ClientSideV2.AllDrawers was not found."); } else { Plugin.Log.LogInfo((object)"Using kg.ItemDrawers AllDrawers API for drawer exclusion zones."); } } internal static bool AnyInRange(Vector3 origin, float range) { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) if (_allDrawers == null) { return false; } float num = range * range; try { if (!(_allDrawers.Invoke(null, null) is IEnumerable enumerable)) { return false; } foreach (object item in enumerable) { ZNetView val = (ZNetView)((item is ZNetView) ? item : null); if (val != null && Object.op_Implicit((Object)(object)val) && val.IsValid() && Object.op_Implicit((Object)(object)((Component)val).transform)) { Vector3 val2 = ((Component)val).transform.position - origin; if (((Vector3)(ref val2)).sqrMagnitude <= num) { return true; } } } } catch (Exception ex) { if (ModConfig.DebugLogs.Value) { Plugin.Log.LogWarning((object)("ItemDrawers scan failed: " + ex.Message)); } } return false; } } internal static class ModConfig { internal static ConfigEntry Enabled = null; internal static ConfigEntry DebugLogs = null; internal static ConfigEntry RangePadding = null; internal static ConfigEntry VanillaFloor = null; internal static ConfigEntry FiresEnabled = null; internal static ConfigEntry OnlyIfBurning = null; internal static ConfigEntry FirePrefabs = null; internal static ConfigEntry TamesEnabled = null; internal static ConfigEntry DrawersEnabled = null; private static HashSet _firePrefabNames = new HashSet(StringComparer.OrdinalIgnoreCase); internal static void Bind(ConfigFile config) { Enabled = config.Bind("General", "Enabled", true, "If off, auto-pickup is never suppressed."); DebugLogs = config.Bind("General", "Debug Logs", false, "Log when auto-pickup is suppressed or restored, and the active exclusion range."); VanillaFloor = config.Bind("Range", "Vanilla Floor", 2f, "Valheim's default auto-pickup range. Used as the minimum in the max() of all pickup radii."); RangePadding = config.Bind("Range", "Range Padding", 0f, "Extra meters added after taking the largest pickup-related range."); FiresEnabled = config.Bind("Fires", "Enabled", true, "Suppress auto-pickup near campfires and hearths so tossed fuel/food is not vacuumed."); OnlyIfBurning = config.Bind("Fires", "Only If Burning", false, "If on, only lit fires count. Off = unlit campfires and hearths still suppress auto-pickup."); FirePrefabs = config.Bind("Fires", "Prefab Names", "fire_pit,hearth", "Comma-separated prefab names (Clone suffix ignored). Add bonfire here if you want it."); TamesEnabled = config.Bind("Tames", "Enabled", true, "Suppress auto-pickup near tamed animals so tossed food stays on the ground for them."); DrawersEnabled = config.Bind("Drawers", "Enabled", true, "Suppress auto-pickup near Item Drawers so tossed items can be vacuumed by drawers instead of players."); FirePrefabs.SettingChanged += delegate { RebuildFirePrefabs(); }; RebuildFirePrefabs(); } internal static bool IsWatchedFirePrefab(string prefabName) { return _firePrefabNames.Contains(prefabName); } private static void RebuildFirePrefabs() { _firePrefabNames = (from s in FirePrefabs.Value.Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries) select s.Trim() into s where s.Length > 0 select s).ToHashSet(StringComparer.OrdinalIgnoreCase); } } internal static class FireplaceRegistry { private static readonly HashSet Fires = new HashSet(); internal static IEnumerable All { get { Fires.RemoveWhere((Fireplace f) => !Object.op_Implicit((Object)(object)f)); return Fires; } } internal static void Add(Fireplace fire) { if (Object.op_Implicit((Object)(object)fire)) { Fires.Add(fire); } } internal static void Remove(Fireplace fire) { if (Object.op_Implicit((Object)(object)fire)) { Fires.Remove(fire); } } } [HarmonyPatch(typeof(Fireplace), "Awake")] internal static class FireplaceAwakePatch { private static void Postfix(Fireplace __instance) { FireplaceRegistry.Add(__instance); } } [HarmonyPatch(typeof(Player), "AutoPickup")] internal static class PlayerAutoPickupPatch { private static void Prefix(Player __instance) { try { PickupGuard.Tick(__instance); } catch (Exception arg) { Plugin.Log.LogError((object)$"NoAutoPickup tick failed: {arg}"); } } } [HarmonyPatch(typeof(Game), "Logout")] internal static class GameLogoutPatch { private static void Prefix() { PickupGuard.ForceRestore(); } } [HarmonyPatch(typeof(Player), "OnDestroy")] internal static class PlayerDestroyPatch { private static void Prefix(Player __instance) { if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer) { PickupGuard.ForceRestore(); } } } internal static class PlayerFields { private static readonly FieldRef EnableAutoPickup = AccessTools.StaticFieldRefAccess(AccessTools.DeclaredField(typeof(Player), "m_enableAutoPickup")); private static readonly FieldRef AutoPickupRange = AccessTools.FieldRefAccess(AccessTools.DeclaredField(typeof(Player), "m_autoPickupRange")); internal static bool GetEnabled() { return EnableAutoPickup.Invoke(); } internal static void SetEnabled(bool value, bool announce) { if (GetEnabled() != value) { EnableAutoPickup.Invoke() = value; if (announce) { Announce(); } } } internal static void Announce() { Player localPlayer = Player.m_localPlayer; if (Object.op_Implicit((Object)(object)localPlayer)) { string text = (GetEnabled() ? "$hud_on" : "$hud_off"); ((Character)localPlayer).Message((MessageType)1, "$hud_autopickup:" + text, 0, (Sprite)null, false); } } internal static float GetRange(Player player) { if (!((Object)(object)player == (Object)null)) { return AutoPickupRange.Invoke(player); } return 0f; } } internal static class PickupGuard { private static bool _inZone; private static bool _weSuppressed; private static bool _savedEnabled; private static string _lastReason = "none"; private static readonly List _tameScratch = new List(32); internal static void Tick(Player player) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer) { return; } if (!ModConfig.Enabled.Value) { ForceRestore(); return; } float range = RangeSources.CurrentExclusionRange(player); if (IsInExclusionZone(((Component)player).transform.position, range, out string reason)) { OnInsideZone(reason, range); } else { OnOutsideZone(); } } internal static void ForceRestore() { _inZone = false; RestoreIfNeeded(announce: false); } private static void OnInsideZone(string reason, float range) { if (!_inZone) { _inZone = true; _lastReason = reason; if (PlayerFields.GetEnabled()) { _savedEnabled = true; _weSuppressed = true; PlayerFields.SetEnabled(value: false, announce: true); if (ModConfig.DebugLogs.Value) { Plugin.Log.LogInfo((object)$"Auto-pickup off near {reason} (range {range:0.##})."); } } else if (ModConfig.DebugLogs.Value) { Plugin.Log.LogInfo((object)("Entered " + reason + " zone with auto-pickup already off.")); } return; } if (_weSuppressed && PlayerFields.GetEnabled()) { _weSuppressed = false; _savedEnabled = true; if (ModConfig.DebugLogs.Value) { Plugin.Log.LogInfo((object)"Auto-pickup hotkey turned it back on; leaving it on until you leave this zone."); } } if (ModConfig.DebugLogs.Value && reason != _lastReason) { Plugin.Log.LogInfo((object)("Zone is now " + reason + ".")); } _lastReason = reason; } private static void OnOutsideZone() { if (_inZone || _weSuppressed) { _inZone = false; RestoreIfNeeded(announce: true); } } private static void RestoreIfNeeded(bool announce) { if (_weSuppressed) { PlayerFields.SetEnabled(_savedEnabled, announce); _weSuppressed = false; if (ModConfig.DebugLogs.Value) { Plugin.Log.LogInfo((object)("Auto-pickup restored to " + (_savedEnabled ? "on" : "off") + ".")); } _lastReason = "none"; } } private static bool IsInExclusionZone(Vector3 origin, float range, out string reason) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) reason = "none"; if (range <= 0f) { return false; } if (ModConfig.FiresEnabled.Value && AnyFireInRange(origin, range)) { reason = "campfire/hearth"; return true; } if (ModConfig.TamesEnabled.Value && AnyTameInRange(origin, range)) { reason = "tamed animal"; return true; } if (ModConfig.DrawersEnabled.Value && DrawerApi.AnyInRange(origin, range)) { reason = "item drawer"; return true; } return false; } private static bool AnyFireInRange(Vector3 origin, float range) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) float num = range * range; foreach (Fireplace item in FireplaceRegistry.All) { if (Object.op_Implicit((Object)(object)item) && Object.op_Implicit((Object)(object)((Component)item).transform)) { Vector3 val = ((Component)item).transform.position - origin; if (!(((Vector3)(ref val)).sqrMagnitude > num) && (!ModConfig.OnlyIfBurning.Value || item.IsBurning()) && ModConfig.IsWatchedFirePrefab(PrefabName(((Component)item).gameObject))) { return true; } } } return false; } private static bool AnyTameInRange(Vector3 origin, float range) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) _tameScratch.Clear(); Character.GetCharactersInRange(origin, range, _tameScratch); foreach (Character item in _tameScratch) { if (Object.op_Implicit((Object)(object)item) && !item.IsPlayer() && item.IsTamed()) { return true; } } return false; } internal static string PrefabName(GameObject go) { if (!Object.op_Implicit((Object)(object)go)) { return string.Empty; } string name = ((Object)go).name; int num = name.IndexOf("(Clone)", StringComparison.Ordinal); if (num < 0) { return name; } return name.Substring(0, num).Trim(); } } [BepInPlugin("Ageous.NoAutoPickup", "NoAutoPickup", "0.1.2")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { public const string ModGUID = "Ageous.NoAutoPickup"; public const string ModName = "NoAutoPickup"; public const string ModVersion = "0.1.2"; internal static Plugin Instance; internal static ManualLogSource Log; internal static Harmony Harmony; private void Awake() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; ModConfig.Bind(((BaseUnityPlugin)this).Config); DrawerApi.Init(); Harmony = new Harmony("Ageous.NoAutoPickup"); Harmony.PatchAll(); Log.LogInfo((object)"NoAutoPickup 0.1.2 loaded (client auto-pickup guard)."); } private void OnDestroy() { PickupGuard.ForceRestore(); Harmony harmony = Harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } internal static class RangeSources { private static readonly Regex DrawerRange = new Regex("^\\s*DrawerPickupRange\\s*=\\s*([0-9]+(?:\\.[0-9]+)?)\\s*$", RegexOptions.Multiline | RegexOptions.CultureInvariant); private static readonly Regex BiggerRadius = new Regex("^\\s*Radius\\s*=\\s*([0-9]+(?:\\.[0-9]+)?)\\s*$", RegexOptions.Multiline | RegexOptions.CultureInvariant); internal static float CurrentExclusionRange(Player player) { float val = Math.Max(0f, ModConfig.VanillaFloor.Value); float range = PlayerFields.GetRange(player); if (range > 0f) { val = Math.Max(val, range); } val = Math.Max(val, ReadConfigFloat("net.mtnewton.biggerpickupradius.cfg", BiggerRadius)); val = Math.Max(val, ReadConfigFloat("kg.ItemDrawers.cfg", DrawerRange)); float num = Math.Max(0f, ModConfig.RangePadding.Value); return val + num; } private static float ReadConfigFloat(string fileName, Regex pattern) { try { string path = Path.Combine(Paths.ConfigPath, fileName); if (!File.Exists(path)) { return 0f; } Match match = pattern.Match(File.ReadAllText(path)); if (!match.Success) { return 0f; } if (float.TryParse(match.Groups[1].Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && result > 0f) { return result; } } catch (Exception ex) { if (ModConfig.DebugLogs.Value) { Plugin.Log.LogWarning((object)("Failed reading " + fileName + ": " + ex.Message)); } } return 0f; } } }