Decompiled source of ProximityInteraction v1.0.4

plugins/ProximityInteraction.dll

Decompiled a month ago
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 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("nussbacker")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Made by nussbacker")]
[assembly: AssemblyDescription("Proximity-based interaction targeting for Valheim.")]
[assembly: AssemblyFileVersion("1.0.4.0")]
[assembly: AssemblyInformationalVersion("1.0.4")]
[assembly: AssemblyProduct("ProximityInteraction")]
[assembly: AssemblyTitle("ProximityInteraction")]
[assembly: AssemblyVersion("1.0.4.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 ProximityInteraction
{
	[BepInPlugin("nussbacker.valheim.proximityinteraction", "Proximity Interaction", "1.0.4")]
	[BepInProcess("valheim.exe")]
	public sealed class ProximityInteractionPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "nussbacker.valheim.proximityinteraction";

		public const string PluginName = "Proximity Interaction";

		public const string PluginVersion = "1.0.4";

		public const string PluginCredits = "Made by nussbacker";

		internal static ConfigEntry<bool> ModEnabled;

		internal static ConfigEntry<float> InteractionRadius;

		internal static ConfigEntry<bool> PrioritizeCrosshairTarget;

		internal static ConfigEntry<bool> RequireLineOfSight;

		internal static ConfigEntry<bool> DisableWhileBuilding;

		internal static ConfigEntry<bool> DebugLogging;

		internal static ManualLogSource Log;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected O, but got Unknown
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			ModEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable proximity-based interaction targeting.");
			InteractionRadius = ((BaseUnityPlugin)this).Config.Bind<float>("General", "InteractionRadius", 3f, new ConfigDescription("How close an interactable must be to the player to become a proximity fallback target, in metres.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 8f), Array.Empty<object>()));
			PrioritizeCrosshairTarget = ((BaseUnityPlugin)this).Config.Bind<bool>("Targeting", "PrioritizeCrosshairTarget", true, "If the center-screen physical ray directly hits a valid interactable inside the configured radius, prefer that exact target over proximity selection.");
			RequireLineOfSight = ((BaseUnityPlugin)this).Config.Bind<bool>("Targeting", "RequireLineOfSight", true, "Only allow proximity fallback targets with an unobstructed line of sight from the player. Solid walls, floors, rocks, closed structure pieces, and other colliders block proximity interaction.");
			DisableWhileBuilding = ((BaseUnityPlugin)this).Config.Bind<bool>("Compatibility", "DisableWhileBuilding", true, "Keep vanilla crosshair targeting while the player is in build/place mode so repairing, dismantling, and placing pieces are not redirected by proximity.");
			DebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "DebugLogging", false, "Log targeting changes to BepInEx/LogOutput.log. Useful for testing crosshair priority, visibility filtering, and proximity priority.");
			_harmony = new Harmony("nussbacker.valheim.proximityinteraction");
			_harmony.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Proximity Interaction 1.0.4 loaded. Made by nussbacker. Authoritative physical crosshair targeting has priority; otherwise the closest useful visible interactable wins inside " + InteractionRadius.Value.ToString("0.0") + "m."));
		}

		private void OnDestroy()
		{
			if (_harmony != null)
			{
				_harmony.UnpatchSelf();
				_harmony = null;
			}
		}
	}
	[HarmonyPatch(typeof(Player), "UpdateHover")]
	internal static class PlayerUpdateHoverPatch
	{
		private const float ScanInterval = 0.05f;

		private const float VerticalOriginOffset = 1f;

		private const float RaycastEndPadding = 0.05f;

		private static readonly MethodInfo InPlaceModeMethod = AccessTools.Method(typeof(Player), "InPlaceMode", (Type[])null, (Type[])null);

		private static bool _physicsFailureLogged;

		private static bool _visibilityFailureLogged;

		private static bool _buildModeFailureLogged;

		private static bool _hoverProbeFailureLogged;

		private static GameObject _lastDecisionTarget;

		private static string _lastDecisionSummary;

		private static float _nextScanTime;

		private static GameObject _cachedTarget;

		private static Character _cachedCreature;

		private static GameObject _lastLoggedTarget;

		private static string _lastLoggedSource;

		[HarmonyPostfix]
		[HarmonyPriority(0)]
		private static void Postfix(Player __instance, ref GameObject ___m_hovering, ref Character ___m_hoveringCreature)
		{
			if (ProximityInteractionPlugin.ModEnabled == null || !ProximityInteractionPlugin.ModEnabled.Value)
			{
				ClearCache();
			}
			else
			{
				if ((Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)Player.m_localPlayer != (Object)(object)__instance)
				{
					return;
				}
				if (ProximityInteractionPlugin.DisableWhileBuilding.Value && IsInPlaceMode(__instance))
				{
					ClearCache();
					LogTargetChange(null, "build-mode vanilla");
					return;
				}
				if (IsTombStoneTarget(___m_hovering))
				{
					ClearCache();
					LogTargetChange(___m_hovering, "vanilla-tombstone");
					return;
				}
				if (ProximityInteractionPlugin.PrioritizeCrosshairTarget.Value)
				{
					Character creature;
					GameObject physicalCrosshairTarget = GetPhysicalCrosshairTarget(__instance, out creature);
					if ((Object)(object)physicalCrosshairTarget != (Object)null)
					{
						___m_hovering = physicalCrosshairTarget;
						___m_hoveringCreature = creature;
						LogTargetChange(physicalCrosshairTarget, "crosshair-physical-authoritative");
						return;
					}
				}
				Character creature2;
				GameObject nearestInteractable = GetNearestInteractable(__instance, out creature2);
				if ((Object)(object)nearestInteractable != (Object)null)
				{
					___m_hovering = nearestInteractable;
					___m_hoveringCreature = creature2;
					LogTargetChange(nearestInteractable, "proximity");
				}
				else
				{
					___m_hovering = null;
					___m_hoveringCreature = null;
					LogTargetChange(null, "authoritative-none");
				}
			}
		}

		internal static GameObject GetNearestInteractable(Player player, out Character creature)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: 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_01f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
			creature = null;
			float unscaledTime = Time.unscaledTime;
			if (unscaledTime < _nextScanTime)
			{
				creature = _cachedCreature;
				return _cachedTarget;
			}
			_nextScanTime = unscaledTime + 0.05f;
			_cachedTarget = null;
			_cachedCreature = null;
			float num = Mathf.Clamp(ProximityInteractionPlugin.InteractionRadius.Value, 0.5f, 8f);
			float num2 = num * num;
			Vector3 interactionOrigin = GetInteractionOrigin(player);
			Collider[] array;
			try
			{
				array = Physics.OverlapSphere(interactionOrigin, num, -5, (QueryTriggerInteraction)2);
			}
			catch (Exception ex)
			{
				if (!_physicsFailureLogged)
				{
					_physicsFailureLogged = true;
					ProximityInteractionPlugin.Log.LogWarning((object)("Proximity physics query failed; vanilla interaction will remain available. " + ex));
				}
				return null;
			}
			if (array == null || array.Length == 0)
			{
				return null;
			}
			GameObject val = null;
			Character val2 = null;
			int num3 = int.MaxValue;
			float num4 = float.PositiveInfinity;
			string hoverText = null;
			HashSet<int> hashSet = new HashSet<int>();
			foreach (Collider val3 in array)
			{
				if ((Object)(object)val3 == (Object)null)
				{
					continue;
				}
				MonoBehaviour val4 = FindNearestInteractableComponent(val3);
				if ((Object)(object)val4 == (Object)null)
				{
					continue;
				}
				int instanceID = ((Object)val4).GetInstanceID();
				if (!hashSet.Add(instanceID))
				{
					continue;
				}
				GameObject gameObject = ((Component)val4).gameObject;
				if ((Object)(object)gameObject == (Object)null || (Object)(object)gameObject == (Object)(object)((Component)player).gameObject || !TryGetPhysicalClosestPoint(val4, interactionOrigin, out var closestPoint, out var sqrDistance))
				{
					continue;
				}
				if (sqrDistance > num2)
				{
					LogRejectedCandidate(gameObject, "physical distance " + Mathf.Sqrt(sqrDistance).ToString("0.00") + "m exceeds radius");
					continue;
				}
				int num5 = 0;
				string hoverText2 = null;
				if (TryGetHoverText(val4, out hoverText2))
				{
					if (string.IsNullOrWhiteSpace(hoverText2))
					{
						LogRejectedCandidate(gameObject, "blank hover text");
						continue;
					}
					if (ContainsIgnoreCase(hoverText2, "Too far"))
					{
						LogRejectedCandidate(gameObject, "hover reports Too far");
						continue;
					}
					if (ContainsIgnoreCase(hoverText2, "( EMPTY )"))
					{
						num5 = 1;
					}
				}
				if (num5 <= num3 && (num5 != num3 || !(sqrDistance >= num4)))
				{
					if (ProximityInteractionPlugin.RequireLineOfSight.Value && !HasLineOfSight(player, val4, interactionOrigin, closestPoint))
					{
						LogRejectedCandidate(gameObject, "blocked line of sight");
						continue;
					}
					num3 = num5;
					num4 = sqrDistance;
					val = gameObject;
					val2 = ((Component)val4).GetComponentInParent<Character>();
					hoverText = hoverText2;
				}
			}
			_cachedTarget = val;
			_cachedCreature = val2;
			creature = val2;
			LogSelectionDecision(val, num3, num4, hoverText);
			return val;
		}

		private static Vector3 GetSafeClosestPoint(Collider collider, Vector3 origin)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: 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_0022: Unknown result type (might be due to invalid IL or missing references)
			MeshCollider val = (MeshCollider)(object)((collider is MeshCollider) ? collider : null);
			if ((Object)(object)val != (Object)null && !val.convex)
			{
				Bounds bounds = ((Collider)val).bounds;
				return ((Bounds)(ref bounds)).ClosestPoint(origin);
			}
			return collider.ClosestPoint(origin);
		}

		private static bool IsSuspiciousContainingCollider(MonoBehaviour interactionComponent, Collider collider, Vector3 origin, Vector3 closestPoint)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = closestPoint - origin;
			if (((Vector3)(ref val)).sqrMagnitude > 1E-06f)
			{
				return false;
			}
			val = ((Component)interactionComponent).transform.position - origin;
			return ((Vector3)(ref val)).sqrMagnitude > 0.25f;
		}

		private static bool TryGetPhysicalClosestPoint(MonoBehaviour interactionComponent, Vector3 origin, out Vector3 closestPoint, out float sqrDistance)
		{
			//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_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: 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)
			//IL_0205: Unknown result type (might be due to invalid IL or missing references)
			//IL_0206: Unknown result type (might be due to invalid IL or missing references)
			//IL_020b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0219: Unknown result type (might be due to invalid IL or missing references)
			//IL_021a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			//IL_0252: Unknown result type (might be due to invalid IL or missing references)
			//IL_0257: Unknown result type (might be due to invalid IL or missing references)
			//IL_025e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0263: Unknown result type (might be due to invalid IL or missing references)
			//IL_0264: Unknown result type (might be due to invalid IL or missing references)
			//IL_0269: Unknown result type (might be due to invalid IL or missing references)
			//IL_022e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0230: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			closestPoint = ((Component)interactionComponent).transform.position;
			sqrDistance = float.PositiveInfinity;
			bool flag = false;
			ZNetView componentInParent = ((Component)interactionComponent).GetComponentInParent<ZNetView>();
			Vector3 val3;
			if ((Object)(object)componentInParent != (Object)null)
			{
				Collider[] array = null;
				try
				{
					array = ((Component)componentInParent).GetComponentsInChildren<Collider>(true);
				}
				catch
				{
					array = null;
				}
				if (array != null)
				{
					foreach (Collider val in array)
					{
						if ((Object)(object)val == (Object)null || val.isTrigger || !val.enabled)
						{
							continue;
						}
						ZNetView componentInParent2 = ((Component)val).GetComponentInParent<ZNetView>();
						if ((Object)(object)componentInParent2 == (Object)null || ((Object)componentInParent2).GetInstanceID() != ((Object)componentInParent).GetInstanceID())
						{
							continue;
						}
						Vector3 val2;
						try
						{
							val2 = GetSafeClosestPoint(val, origin);
						}
						catch
						{
							val2 = ((Component)val).transform.position;
						}
						val3 = val2 - origin;
						float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude;
						if (!IsSuspiciousContainingCollider(interactionComponent, val, origin, val2))
						{
							if (sqrMagnitude < sqrDistance)
							{
								sqrDistance = sqrMagnitude;
								closestPoint = val2;
							}
							flag = true;
						}
					}
				}
			}
			if (!flag)
			{
				Collider[] array2 = null;
				try
				{
					array2 = ((Component)interactionComponent).GetComponentsInChildren<Collider>(true);
				}
				catch
				{
					array2 = null;
				}
				if (array2 != null)
				{
					foreach (Collider val4 in array2)
					{
						if ((Object)(object)val4 == (Object)null || val4.isTrigger || !val4.enabled)
						{
							continue;
						}
						Vector3 val5;
						try
						{
							val5 = GetSafeClosestPoint(val4, origin);
						}
						catch
						{
							val5 = ((Component)val4).transform.position;
						}
						val3 = val5 - origin;
						float sqrMagnitude2 = ((Vector3)(ref val3)).sqrMagnitude;
						if (!IsSuspiciousContainingCollider(interactionComponent, val4, origin, val5))
						{
							if (sqrMagnitude2 < sqrDistance)
							{
								sqrDistance = sqrMagnitude2;
								closestPoint = val5;
							}
							flag = true;
						}
					}
				}
			}
			if (!flag)
			{
				Collider[] array3 = null;
				try
				{
					array3 = ((Component)interactionComponent).GetComponentsInParent<Collider>(true);
				}
				catch
				{
					array3 = null;
				}
				if (array3 != null)
				{
					foreach (Collider val6 in array3)
					{
						if ((Object)(object)val6 == (Object)null || val6.isTrigger || !val6.enabled)
						{
							continue;
						}
						Vector3 val7;
						try
						{
							val7 = GetSafeClosestPoint(val6, origin);
						}
						catch
						{
							val7 = ((Component)val6).transform.position;
						}
						val3 = val7 - origin;
						float sqrMagnitude3 = ((Vector3)(ref val3)).sqrMagnitude;
						if (!IsSuspiciousContainingCollider(interactionComponent, val6, origin, val7))
						{
							if (sqrMagnitude3 < sqrDistance)
							{
								sqrDistance = sqrMagnitude3;
								closestPoint = val7;
							}
							flag = true;
						}
					}
				}
			}
			if (!flag)
			{
				closestPoint = ((Component)interactionComponent).transform.position;
				val3 = closestPoint - origin;
				sqrDistance = ((Vector3)(ref val3)).sqrMagnitude;
			}
			if (!float.IsNaN(sqrDistance))
			{
				return !float.IsInfinity(sqrDistance);
			}
			return false;
		}

		private static bool TryGetHoverText(MonoBehaviour interactionComponent, out string hoverText)
		{
			hoverText = null;
			if ((Object)(object)interactionComponent == (Object)null)
			{
				return false;
			}
			try
			{
				MethodInfo methodInfo = AccessTools.Method(((object)interactionComponent).GetType(), "GetHoverText", Type.EmptyTypes, (Type[])null);
				if (methodInfo == null || methodInfo.ReturnType != typeof(string))
				{
					return false;
				}
				hoverText = methodInfo.Invoke(interactionComponent, null) as string;
				return true;
			}
			catch (Exception ex)
			{
				if (!_hoverProbeFailureLogged)
				{
					_hoverProbeFailureLogged = true;
					ProximityInteractionPlugin.Log.LogWarning((object)("Could not inspect an interactable's hover text; it will remain eligible by distance. " + ex));
				}
				return false;
			}
		}

		private static bool ContainsIgnoreCase(string text, string value)
		{
			if (text != null && value != null)
			{
				return text.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0;
			}
			return false;
		}

		private static void LogRejectedCandidate(GameObject candidate, string reason)
		{
			if (ProximityInteractionPlugin.DebugLogging != null && ProximityInteractionPlugin.DebugLogging.Value)
			{
				ProximityInteractionPlugin.Log.LogDebug((object)("Proximity rejected " + (((Object)(object)candidate != (Object)null) ? ((Object)candidate).name : "<null>") + ": " + reason));
			}
		}

		private static void LogSelectionDecision(GameObject target, int priority, float sqrDistance, string hoverText)
		{
			if (ProximityInteractionPlugin.DebugLogging == null || !ProximityInteractionPlugin.DebugLogging.Value)
			{
				_lastDecisionTarget = target;
				_lastDecisionSummary = null;
				return;
			}
			string text;
			if ((Object)(object)target == (Object)null)
			{
				text = "<none>";
			}
			else
			{
				string text2 = ((priority <= 0) ? "useful" : "low-priority");
				text = ((Object)target).name + " | " + text2 + " | physicalDistance=" + Mathf.Sqrt(sqrDistance).ToString("0.00") + "m | hover=\"" + (hoverText ?? "<unavailable>").Replace("\n", "\\n") + "\"";
			}
			if (!((Object)(object)_lastDecisionTarget == (Object)(object)target) || !(_lastDecisionSummary == text))
			{
				_lastDecisionTarget = target;
				_lastDecisionSummary = text;
				ProximityInteractionPlugin.Log.LogInfo((object)("Proximity decision: " + text));
			}
		}

		private static bool IsWaterGeometry(Collider collider)
		{
			if ((Object)(object)collider == (Object)null)
			{
				return false;
			}
			try
			{
				MonoBehaviour[] componentsInParent = ((Component)collider).GetComponentsInParent<MonoBehaviour>(true);
				foreach (MonoBehaviour val in componentsInParent)
				{
					if ((Object)(object)val != (Object)null && string.Equals(((object)val).GetType().Name, "WaterVolume", StringComparison.Ordinal))
					{
						return true;
					}
				}
			}
			catch
			{
			}
			try
			{
				string text = LayerMask.LayerToName(((Component)collider).gameObject.layer);
				if (!string.IsNullOrEmpty(text) && text.IndexOf("water", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return true;
				}
			}
			catch
			{
			}
			return false;
		}

		internal static GameObject GetPhysicalCrosshairTarget(Player player, out Character creature)
		{
			//IL_0026: 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_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			creature = null;
			if ((Object)(object)player == (Object)null)
			{
				return null;
			}
			Camera main = Camera.main;
			if ((Object)(object)main == (Object)null)
			{
				return null;
			}
			RaycastHit[] array;
			try
			{
				array = Physics.RaycastAll(((Component)main).transform.position, ((Component)main).transform.forward, 12f, -5, (QueryTriggerInteraction)1);
			}
			catch (Exception ex)
			{
				if (!_visibilityFailureLogged)
				{
					_visibilityFailureLogged = true;
					ProximityInteractionPlugin.Log.LogWarning((object)("Authoritative crosshair ray failed; proximity targeting will be used instead. " + ex));
				}
				return null;
			}
			if (array == null || array.Length == 0)
			{
				return null;
			}
			Array.Sort(array, (RaycastHit a, RaycastHit b) => ((RaycastHit)(ref a)).distance.CompareTo(((RaycastHit)(ref b)).distance));
			Transform transform = ((Component)player).transform;
			Vector3 interactionOrigin = GetInteractionOrigin(player);
			float num = Mathf.Clamp(ProximityInteractionPlugin.InteractionRadius.Value, 0.5f, 8f);
			float num2 = num * num;
			for (int num3 = 0; num3 < array.Length; num3++)
			{
				Collider collider = ((RaycastHit)(ref array[num3])).collider;
				if ((Object)(object)collider == (Object)null || collider.isTrigger || IsWaterGeometry(collider))
				{
					continue;
				}
				Transform transform2 = ((Component)collider).transform;
				if (!((Object)(object)transform2 == (Object)(object)transform) && !transform2.IsChildOf(transform))
				{
					MonoBehaviour val = FindInteractableOwnedByCollider(collider);
					if ((Object)(object)val == (Object)null)
					{
						return null;
					}
					if (!TryGetPhysicalClosestPoint(val, interactionOrigin, out var _, out var sqrDistance) || sqrDistance > num2)
					{
						LogRejectedCandidate(((Component)val).gameObject, "physical crosshair hit is outside InteractionRadius");
						return null;
					}
					creature = ((Component)val).GetComponentInParent<Character>();
					return ((Component)val).gameObject;
				}
			}
			return null;
		}

		private static MonoBehaviour FindInteractableOwnedByCollider(Collider collider)
		{
			MonoBehaviour val = FindNearestInteractableComponent(collider);
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			ZNetView componentInParent = ((Component)collider).GetComponentInParent<ZNetView>();
			if ((Object)(object)componentInParent == (Object)null)
			{
				return null;
			}
			MonoBehaviour[] componentsInChildren = ((Component)componentInParent).GetComponentsInChildren<MonoBehaviour>(true);
			foreach (MonoBehaviour val2 in componentsInChildren)
			{
				if ((Object)(object)val2 != (Object)null && val2 is Interactable)
				{
					return val2;
				}
			}
			return null;
		}

		private static float InteractionRadiusWithPadding()
		{
			return Mathf.Clamp((ProximityInteractionPlugin.InteractionRadius != null) ? ProximityInteractionPlugin.InteractionRadius.Value : 3f, 0.5f, 8f) + 1f;
		}

		private static MonoBehaviour FindInteractableComponent(GameObject target)
		{
			if ((Object)(object)target == (Object)null)
			{
				return null;
			}
			MonoBehaviour[] componentsInParent = target.GetComponentsInParent<MonoBehaviour>(true);
			foreach (MonoBehaviour val in componentsInParent)
			{
				if ((Object)(object)val != (Object)null && val is Interactable)
				{
					return val;
				}
			}
			return null;
		}

		internal static bool IsTombStoneTarget(GameObject target)
		{
			if ((Object)(object)target == (Object)null)
			{
				return false;
			}
			try
			{
				return (Object)(object)target.GetComponentInParent<TombStone>(true) != (Object)null;
			}
			catch
			{
				return false;
			}
		}

		internal static bool IsInteractableTarget(GameObject target)
		{
			if ((Object)(object)target == (Object)null)
			{
				return false;
			}
			MonoBehaviour[] componentsInParent = target.GetComponentsInParent<MonoBehaviour>(true);
			foreach (MonoBehaviour val in componentsInParent)
			{
				if ((Object)(object)val != (Object)null && val is Interactable)
				{
					return true;
				}
			}
			return false;
		}

		private static Vector3 GetInteractionOrigin(Player player)
		{
			//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_0015: 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)
			return ((Component)player).transform.position + Vector3.up * 1f;
		}

		private static bool HasLineOfSight(Player player, MonoBehaviour interactionComponent, Vector3 origin, Vector3 targetPoint)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: 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_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			List<Vector3> list = new List<Vector3>();
			list.Add(targetPoint);
			ZNetView componentInParent = ((Component)interactionComponent).GetComponentInParent<ZNetView>();
			Collider[] array = null;
			try
			{
				array = ((!((Object)(object)componentInParent != (Object)null)) ? ((Component)interactionComponent).GetComponentsInChildren<Collider>(true) : ((Component)componentInParent).GetComponentsInChildren<Collider>(true));
			}
			catch
			{
				array = null;
			}
			if (array != null)
			{
				foreach (Collider val in array)
				{
					if ((Object)(object)val == (Object)null || val.isTrigger || !val.enabled)
					{
						continue;
					}
					if ((Object)(object)componentInParent != (Object)null)
					{
						ZNetView componentInParent2 = ((Component)val).GetComponentInParent<ZNetView>();
						if ((Object)(object)componentInParent2 == (Object)null || ((Object)componentInParent2).GetInstanceID() != ((Object)componentInParent).GetInstanceID())
						{
							continue;
						}
					}
					Bounds bounds = val.bounds;
					list.Add(((Bounds)(ref bounds)).center);
					Vector3 item = ((Bounds)(ref bounds)).ClosestPoint(new Vector3(origin.x, origin.y, origin.z));
					list.Add(item);
					list.Add(new Vector3(((Bounds)(ref bounds)).center.x, Mathf.Lerp(((Bounds)(ref bounds)).center.y, ((Bounds)(ref bounds)).max.y, 0.75f), ((Bounds)(ref bounds)).center.z));
				}
			}
			for (int j = 0; j < list.Count; j++)
			{
				if (IsLineClearToCandidate(player, interactionComponent, origin, list[j], componentInParent))
				{
					return true;
				}
			}
			return false;
		}

		private static bool IsLineClearToCandidate(Player player, MonoBehaviour interactionComponent, Vector3 origin, Vector3 targetPoint, ZNetView targetNetView)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: 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_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = targetPoint - origin;
			float magnitude = ((Vector3)(ref val)).magnitude;
			if (magnitude <= 0.05f)
			{
				return true;
			}
			RaycastHit[] array;
			try
			{
				array = Physics.RaycastAll(origin, val / magnitude, magnitude + 0.05f, -5, (QueryTriggerInteraction)1);
			}
			catch (Exception ex)
			{
				if (!_visibilityFailureLogged)
				{
					_visibilityFailureLogged = true;
					ProximityInteractionPlugin.Log.LogWarning((object)("Proximity line-of-sight query failed; proximity candidates will be rejected for safety. " + ex));
				}
				return false;
			}
			if (array == null || array.Length == 0)
			{
				return true;
			}
			Array.Sort(array, (RaycastHit a, RaycastHit b) => ((RaycastHit)(ref a)).distance.CompareTo(((RaycastHit)(ref b)).distance));
			Transform transform = ((Component)player).transform;
			Transform transform2 = ((Component)interactionComponent).transform;
			for (int num = 0; num < array.Length; num++)
			{
				Collider collider = ((RaycastHit)(ref array[num])).collider;
				if ((Object)(object)collider == (Object)null || IsWaterGeometry(collider))
				{
					continue;
				}
				Transform transform3 = ((Component)collider).transform;
				if ((Object)(object)transform3 == (Object)(object)transform || transform3.IsChildOf(transform))
				{
					continue;
				}
				if ((Object)(object)transform3 == (Object)(object)transform2 || transform3.IsChildOf(transform2))
				{
					return true;
				}
				MonoBehaviour val2 = FindNearestInteractableComponent(collider);
				if ((Object)(object)val2 != (Object)null && ((Object)val2).GetInstanceID() == ((Object)interactionComponent).GetInstanceID())
				{
					return true;
				}
				if ((Object)(object)targetNetView != (Object)null)
				{
					ZNetView componentInParent = ((Component)collider).GetComponentInParent<ZNetView>();
					if ((Object)(object)componentInParent != (Object)null && ((Object)componentInParent).GetInstanceID() == ((Object)targetNetView).GetInstanceID())
					{
						return true;
					}
				}
				if (((RaycastHit)(ref array[num])).distance <= magnitude + 0.05f)
				{
					return false;
				}
			}
			return true;
		}

		private static MonoBehaviour FindNearestInteractableComponent(Collider collider)
		{
			MonoBehaviour[] componentsInParent = ((Component)collider).GetComponentsInParent<MonoBehaviour>(true);
			foreach (MonoBehaviour val in componentsInParent)
			{
				if ((Object)(object)val != (Object)null && val is Interactable)
				{
					return val;
				}
			}
			return null;
		}

		internal static bool IsInPlaceMode(Player player)
		{
			if (InPlaceModeMethod == null)
			{
				if (!_buildModeFailureLogged)
				{
					_buildModeFailureLogged = true;
					ProximityInteractionPlugin.Log.LogWarning((object)"Player.InPlaceMode() was not found. Build-mode protection could not be verified for this Valheim version.");
				}
				return false;
			}
			try
			{
				object obj = InPlaceModeMethod.Invoke(player, null);
				return obj is bool && (bool)obj;
			}
			catch (Exception ex)
			{
				if (!_buildModeFailureLogged)
				{
					_buildModeFailureLogged = true;
					ProximityInteractionPlugin.Log.LogWarning((object)("Player.InPlaceMode() check failed. " + ex));
				}
				return false;
			}
		}

		private static void ClearCache()
		{
			_cachedTarget = null;
			_cachedCreature = null;
			_nextScanTime = 0f;
			_lastDecisionTarget = null;
			_lastDecisionSummary = null;
		}

		private static void LogTargetChange(GameObject target, string source)
		{
			if (ProximityInteractionPlugin.DebugLogging == null || !ProximityInteractionPlugin.DebugLogging.Value)
			{
				_lastLoggedTarget = target;
				_lastLoggedSource = source;
			}
			else if (!((Object)(object)_lastLoggedTarget == (Object)(object)target) || !(_lastLoggedSource == source))
			{
				_lastLoggedTarget = target;
				_lastLoggedSource = source;
				if ((Object)(object)target == (Object)null)
				{
					ProximityInteractionPlugin.Log.LogInfo((object)("Interaction target [" + source + "]: <none>"));
				}
				else
				{
					ProximityInteractionPlugin.Log.LogInfo((object)("Interaction target [" + source + "]: " + ((Object)target).name));
				}
			}
		}
	}
	[HarmonyPatch(typeof(Player), "Interact")]
	internal static class PlayerInteractPatch
	{
		[HarmonyPrefix]
		[HarmonyPriority(0)]
		private static void Prefix(Player __instance, ref GameObject go)
		{
			if (ProximityInteractionPlugin.ModEnabled == null || !ProximityInteractionPlugin.ModEnabled.Value || (Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)Player.m_localPlayer != (Object)(object)__instance || (ProximityInteractionPlugin.DisableWhileBuilding.Value && PlayerUpdateHoverPatch.IsInPlaceMode(__instance)) || PlayerUpdateHoverPatch.IsTombStoneTarget(go))
			{
				return;
			}
			if (ProximityInteractionPlugin.PrioritizeCrosshairTarget.Value)
			{
				Character creature;
				GameObject physicalCrosshairTarget = PlayerUpdateHoverPatch.GetPhysicalCrosshairTarget(__instance, out creature);
				if ((Object)(object)physicalCrosshairTarget != (Object)null)
				{
					go = physicalCrosshairTarget;
					return;
				}
			}
			Character creature2;
			GameObject nearestInteractable = PlayerUpdateHoverPatch.GetNearestInteractable(__instance, out creature2);
			go = nearestInteractable;
		}
	}
}