Decompiled source of StopPullingArrows v1.0.0

plugins/StopPullingArrows.dll

Decompiled a day 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 Peak;
using StopPullingArrows.Patches;
using StopPullingArrows.Utils;
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("StopPullingArrows")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("StopPullingArrows")]
[assembly: AssemblyTitle("StopPullingArrows")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace StopPullingArrows
{
	[BepInPlugin("StopPullingArrows", "StopPullingArrows", "1.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "StopPullingArrows";

		public const string PluginName = "StopPullingArrows";

		public const string PluginVersion = "1.0.0";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<bool> DebugLogging;

		private static bool disabledByError;

		internal static bool Active
		{
			get
			{
				if (!disabledByError && Enabled != null)
				{
					return Enabled.Value;
				}
				return false;
			}
		}

		private void Awake()
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Let interactables (chains, ropes, vines, pitons, items...) take priority over arrows stuck in your own character.");
			DebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DebugLogging", false, "Log when an embedded arrow is skipped in favor of another interactable.");
			Log.LogInfo((object)"Loaded.");
			try
			{
				InteractionPatch.TryApply(new Harmony("StopPullingArrows"));
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Failed to apply interaction patch: " + ex));
				Log.LogError((object)"Patch disabled; game behavior remains unchanged.");
			}
		}

		internal static void DisableAfterError(Exception e)
		{
			if (!disabledByError)
			{
				disabledByError = true;
				Log.LogError((object)("Unexpected error while adjusting interaction priority: " + e));
				Log.LogError((object)"Patch disabled; game behavior remains unchanged.");
			}
		}
	}
}
namespace StopPullingArrows.Utils
{
	internal static class ArrowInteractionUtils
	{
		private static readonly RaycastHit[] lineHits = (RaycastHit[])(object)new RaycastHit[64];

		private static readonly RaycastHit[] sphereHits = (RaycastHit[])(object)new RaycastHit[100];

		private static FieldRef<CharacterRagdoll, List<Collider>> ragdollColliders;

		private static Object lastLoggedTarget;

		internal static bool Initialize(out string missing)
		{
			missing = null;
			if (AccessTools.Field(typeof(Interaction), "distance") == null)
			{
				missing = "Interaction.distance";
			}
			else if (AccessTools.Field(typeof(Interaction), "area") == null)
			{
				missing = "Interaction.area";
			}
			else if (AccessTools.Field(typeof(ThornOnMe), "character") == null)
			{
				missing = "ThornOnMe.character";
			}
			else if (AccessTools.Field(typeof(ThornOnMe), "stuckIn") == null)
			{
				missing = "ThornOnMe.stuckIn";
			}
			else if (AccessTools.PropertyGetter(typeof(ThornOnMe), "isArrow") == null)
			{
				missing = "ThornOnMe.isArrow";
			}
			else if (AccessTools.Field(typeof(Character), "localCharacter") == null)
			{
				missing = "Character.localCharacter";
			}
			else if (AccessTools.Field(typeof(MainCamera), "instance") == null)
			{
				missing = "MainCamera.instance";
			}
			else if (AccessTools.Field(typeof(CharacterRagdoll), "colliderList") == null)
			{
				missing = "CharacterRagdoll.colliderList";
			}
			else if (AccessTools.Method(typeof(Item), "TryGetItemFromCollider", (Type[])null, (Type[])null) == null)
			{
				missing = "Item.TryGetItemFromCollider";
			}
			else if (AccessTools.Method(typeof(HelperFunctions), "LineCheckAll", (Type[])null, (Type[])null) == null)
			{
				missing = "HelperFunctions.LineCheckAll";
			}
			if (missing != null)
			{
				return false;
			}
			ragdollColliders = AccessTools.FieldRefAccess<CharacterRagdoll, List<Collider>>("colliderList");
			return true;
		}

		internal static void ApplyPriority(Interaction interaction, ref IInteractible result, bool ignoreInteractable)
		{
			Character localCharacter = Character.localCharacter;
			if ((Object)(object)localCharacter == (Object)null || !IsLocalEmbeddedArrow(result, localCharacter))
			{
				lastLoggedTarget = null;
			}
			else
			{
				if ((Object)(object)MainCamera.instance == (Object)null)
				{
					return;
				}
				IInteractible val = FindWorldInteractable(interaction, localCharacter, ignoreInteractable);
				if (val == null)
				{
					lastLoggedTarget = null;
					return;
				}
				if (Plugin.DebugLogging.Value)
				{
					LogReplacement(val);
				}
				result = val;
			}
		}

		internal static bool IsLocalEmbeddedArrow(IInteractible interactible, Character local)
		{
			ThornOnMe val = (ThornOnMe)(object)((interactible is ThornOnMe) ? interactible : null);
			if ((Object)(object)val != (Object)null && (val is ArrowOnMe || val.isArrow) && val.stuckIn)
			{
				return (Object)(object)val.character == (Object)(object)local;
			}
			return false;
		}

		internal static bool IsClimbable(IInteractible interactible)
		{
			if (!(interactible is JungleVine) && !(interactible is RopeSegment))
			{
				return interactible is ClimbHandle;
			}
			return true;
		}

		private static IInteractible FindWorldInteractable(Interaction interaction, Character local, bool ignoreInteractable)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: 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)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: 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_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: 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_01ed: 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_01f3: 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_021c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0228: Unknown result type (might be due to invalid IL or missing references)
			//IL_022d: Unknown result type (might be due to invalid IL or missing references)
			Transform transform = ((Component)MainCamera.instance).transform;
			Vector3 position = transform.position;
			Vector3 forward = transform.forward;
			float distance = interaction.distance;
			float area = interaction.area;
			List<Collider> list = ((local.refs != null && (Object)(object)local.refs.ragdoll != (Object)null) ? ragdollColliders.Invoke(local.refs.ragdoll) : null);
			Item heldItem = (((Object)(object)local.data != (Object)null) ? local.data.currentItem : null);
			int num = HelperFunctions.LineCheckAll(position, position + forward * distance, (LayerType)0, lineHits, 0f, (QueryTriggerInteraction)2);
			Collider val = null;
			float num2 = distance;
			for (int i = 0; i < num; i++)
			{
				RaycastHit val2 = lineHits[i];
				Collider collider = ((RaycastHit)(ref val2)).collider;
				if (!((Object)(object)collider == (Object)null) && Object.op_Implicit((Object)(object)((RaycastHit)(ref val2)).transform) && !(((RaycastHit)(ref val2)).distance >= num2) && (list == null || !list.Contains(collider)) && !IsHeldItem(collider, heldItem) && !IsArrowCollider(collider, local))
				{
					num2 = ((RaycastHit)(ref val2)).distance;
					val = collider;
				}
			}
			if ((Object)(object)val != (Object)null)
			{
				IInteractible componentInParent = ((Component)val).GetComponentInParent<IInteractible>();
				if (IsWorldCandidate(componentInParent) && (ignoreInteractable || componentInParent.IsInteractible(local)))
				{
					return componentInParent;
				}
			}
			int num3 = Physics.SphereCastNonAlloc(position + forward * (area / 2f), area, forward, sphereHits, num2, LayerMask.op_Implicit(HelperFunctions.GetMask((LayerType)0)), (QueryTriggerInteraction)2);
			IInteractible result = null;
			bool flag = false;
			float num4 = float.MaxValue;
			for (int j = 0; j < num3; j++)
			{
				RaycastHit val3 = sphereHits[j];
				Collider collider2 = ((RaycastHit)(ref val3)).collider;
				if ((Object)(object)collider2 == (Object)null || IsHeldItem(collider2, heldItem) || IsArrowCollider(collider2, local))
				{
					continue;
				}
				IInteractible componentInParent2 = ((Component)collider2).GetComponentInParent<IInteractible>();
				if (!IsWorldCandidate(componentInParent2))
				{
					continue;
				}
				bool flag2 = IsClimbable(componentInParent2);
				float num5 = Vector3.Angle(((RaycastHit)(ref val3)).point - position, forward);
				if ((!flag || flag2) && (flag2 != flag || !(num5 >= num4)) && componentInParent2.IsInteractible(local))
				{
					RaycastHit val4 = HelperFunctions.LineCheck(position, ((RaycastHit)(ref val3)).point, (LayerType)1, 0f, (QueryTriggerInteraction)2);
					if (!((Object)(object)((RaycastHit)(ref val4)).collider != (Object)null) || ((Component)((RaycastHit)(ref val4)).collider).GetComponentInParent<IInteractible>() == componentInParent2)
					{
						result = componentInParent2;
						flag = flag2;
						num4 = num5;
					}
				}
			}
			return result;
		}

		private static bool IsWorldCandidate(IInteractible interactible)
		{
			if (interactible != null)
			{
				return !(interactible is ThornOnMe);
			}
			return false;
		}

		private static bool IsHeldItem(Collider col, Item heldItem)
		{
			Item val = default(Item);
			if ((Object)(object)heldItem != (Object)null && Item.TryGetItemFromCollider(col, ref val))
			{
				return (Object)(object)val == (Object)(object)heldItem;
			}
			return false;
		}

		private static bool IsArrowCollider(Collider col, Character local)
		{
			Transform parent = ((Component)col).transform.parent;
			ArrowOnMe val = default(ArrowOnMe);
			if ((Object)(object)parent != (Object)null && ((Component)parent).TryGetComponent<ArrowOnMe>(ref val))
			{
				return true;
			}
			ThornOnMe componentInParent = ((Component)col).GetComponentInParent<ThornOnMe>();
			if ((Object)(object)componentInParent != (Object)null && componentInParent.isArrow)
			{
				return (Object)(object)componentInParent.character == (Object)(object)local;
			}
			return false;
		}

		private static void LogReplacement(IInteractible target)
		{
			Transform transform = target.GetTransform();
			Object val = (Object)(object)(((Object)(object)transform != (Object)null) ? transform : null);
			if (!(val != (Object)null) || !(val == lastLoggedTarget))
			{
				lastLoggedTarget = val;
				string text = ((target is JungleVine) ? "Chain/Vine" : ((target is RopeSegment) ? "Rope" : ((target is ClimbHandle) ? "ClimbHandle" : ((object)target).GetType().Name)));
				string text2 = (((Object)(object)transform != (Object)null) ? ((Object)((Component)transform).gameObject).name : "?");
				Plugin.Log.LogInfo((object)("Ignored local embedded arrow in favor of " + text + " (" + text2 + ")."));
			}
		}
	}
}
namespace StopPullingArrows.Patches
{
	internal static class InteractionPatch
	{
		private const string TargetMethod = "DoInteractableRaycasts";

		internal static bool TryApply(Harmony harmony)
		{
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(typeof(Interaction), "DoInteractableRaycasts", new Type[3]
			{
				typeof(IInteractible).MakeByRefType(),
				typeof(float),
				typeof(bool)
			}, (Type[])null);
			if (methodInfo == null)
			{
				Plugin.Log.LogError((object)"Failed to locate interaction target method.");
				Plugin.Log.LogError((object)"Patch disabled; game behavior remains unchanged.");
				return false;
			}
			if (!ArrowInteractionUtils.Initialize(out var missing))
			{
				Plugin.Log.LogError((object)("Failed to locate required game member: " + missing));
				Plugin.Log.LogError((object)"Patch disabled; game behavior remains unchanged.");
				return false;
			}
			harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(InteractionPatch), "Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Plugin.Log.LogInfo((object)"Interaction priority patch applied.");
			return true;
		}

		private static void Postfix(Interaction __instance, ref IInteractible interactableResult, float overrideDistance, bool ignoreInteractable)
		{
			if (!Plugin.Active || overrideDistance != -1f)
			{
				return;
			}
			try
			{
				ArrowInteractionUtils.ApplyPriority(__instance, ref interactableResult, ignoreInteractable);
			}
			catch (Exception e)
			{
				Plugin.DisableAfterError(e);
			}
		}
	}
}