Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ProximityInteraction v1.0.4
plugins/ProximityInteraction.dll
Decompiled a month agousing 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; } } }