Decompiled source of GrapplingHookTweaks v0.1.0

plugins/GrapplingHookTweaks.dll

Decompiled 2 weeks ago
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using GrapplingHookTweaks.Config;
using GrapplingHookTweaks.Grapple;
using HarmonyLib;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("GrapplingHookTweaks")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("GrapplingHookTweaks")]
[assembly: AssemblyCopyright("Copyright ©  2021")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")]
[assembly: AssemblyFileVersion("0.1.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.0.0")]
namespace GrapplingHookTweaks
{
	[BepInPlugin("orax.GrapplingHookTweaks", "GrapplingHookTweaks", "0.1.0")]
	internal sealed class GrapplingHookTweaks : BaseUnityPlugin
	{
		public const string PluginGUID = "orax.GrapplingHookTweaks";

		public const string PluginName = "GrapplingHookTweaks";

		public const string PluginVersion = "0.1.0";

		private Harmony _harmony;

		private bool _loggedPolling;

		internal static GrapplingHookTweaks Instance { get; private set; }

		internal PluginConfig Settings { get; private set; }

		private void Awake()
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			Settings = new PluginConfig(((BaseUnityPlugin)this).Config);
			_harmony = new Harmony("orax.GrapplingHookTweaks");
			_harmony.PatchAll(Assembly.GetExecutingAssembly());
			((BaseUnityPlugin)this).Logger.LogInfo((object)(string.Format("{0} {1} loaded: shortcut {2}, ", "GrapplingHookTweaks", "0.1.0", Settings.PullShortcut) + "jump trigger " + (Settings.AllowJumpToPull ? "kept" : "replaced") + ", auto pull " + (Settings.AutoPullOnSpecialLaunch ? "on" : "off") + ", ground pull " + (Settings.GroundPull ? "on" : "off")));
		}

		internal void LogInfo(string message)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)message);
		}

		private void Update()
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || !Settings.Enabled)
			{
				return;
			}
			if (!_loggedPolling)
			{
				_loggedPolling = true;
				((BaseUnityPlugin)this).Logger.LogInfo((object)($"Shortcut polling started: {Settings.PullShortcut} " + "(Debug logging in the config shows what a press does)."));
			}
			KeyboardShortcut pullShortcut = Settings.PullShortcut;
			if (!PullShortcutInput.IsMainKeyDown(pullShortcut))
			{
				return;
			}
			bool flag = PullShortcutInput.AreModifiersHeld(pullShortcut);
			string text = BlockedReason();
			bool flag2 = (Object)(object)GrapplingPoint.m_localGrappler != (Object)null;
			if (Settings.DebugLogging)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)($"Shortcut main key down: modifiers held {flag}, " + string.Format("hook attached {0}, blocked {1}.", flag2, text ?? "nothing")));
			}
			if (!flag || text != null)
			{
				return;
			}
			if (GrapplePuller.TryPull(localPlayer, out var reason))
			{
				if (Settings.DebugLogging)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)"Reeled in by the pull shortcut.");
				}
			}
			else if (Settings.DebugLogging)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Pull shortcut pressed: " + reason + "."));
			}
		}

		private static string BlockedReason()
		{
			if (Game.IsPaused())
			{
				return "the game is paused";
			}
			if (Menu.IsVisible())
			{
				return "the menu is open";
			}
			if (InventoryGui.IsVisible())
			{
				return "the inventory is open";
			}
			if (Console.IsVisible())
			{
				return "the console is open";
			}
			if (TextInput.IsVisible())
			{
				return "a text input is open";
			}
			if ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus())
			{
				return "the chat has focus";
			}
			return null;
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}
	}
}
namespace GrapplingHookTweaks.Patches
{
	[HarmonyPatch]
	internal static class CharacterJumpPatch
	{
		[HarmonyPatch(typeof(Character), "Jump")]
		private static void Prefix(Character __instance, out GrapplingPoint __state)
		{
			__state = null;
			PluginConfig pluginConfig = GrapplingHookTweaks.Instance?.Settings;
			if (pluginConfig != null && pluginConfig.Enabled && !pluginConfig.AllowJumpToPull && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
			{
				__state = GrapplingPoint.m_localGrappler;
				if ((Object)(object)__state != (Object)null)
				{
					GrapplingPoint.m_localGrappler = null;
				}
			}
		}

		[HarmonyPatch(typeof(Character), "Jump")]
		private static void Postfix(GrapplingPoint __state)
		{
			if ((Object)(object)__state != (Object)null)
			{
				GrapplingPoint.m_localGrappler = __state;
			}
		}
	}
	[HarmonyPatch]
	internal static class CharacterUpdateWalkingPatch
	{
		[HarmonyPatch(typeof(Character), "UpdateWalking")]
		private static void Prefix(Character __instance)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected O, but got Unknown
			if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
			{
				PluginConfig pluginConfig = GrapplingHookTweaks.Instance?.Settings;
				if (pluginConfig != null)
				{
					GrapplePuller.KeepReelingOnGround((Player)__instance, pluginConfig);
				}
			}
		}
	}
	[HarmonyPatch]
	internal static class GrapplingPointActivatePatch
	{
		[HarmonyPatch(typeof(GrapplingPoint), "Activate")]
		private static void Postfix(GrapplingPoint __instance, Character character)
		{
			PluginConfig pluginConfig = GrapplingHookTweaks.Instance?.Settings;
			if (pluginConfig == null || !pluginConfig.Enabled)
			{
				return;
			}
			bool flag = (Object)(object)character == (Object)(object)Player.m_localPlayer;
			ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
			bool flag2 = (Object)(object)component != (Object)null && component.IsOwner();
			bool secondary = __instance.m_secondary;
			bool launchedWithSpecial = SpecialLaunchTracker.LaunchedWithSpecial;
			if (pluginConfig.DebugLogging)
			{
				GrapplingHookTweaks.Instance.LogInfo($"Hook attached: local player {flag}, owned {flag2}, " + $"special attack hook {secondary}, recorded launch {launchedWithSpecial}, " + $"auto pull {pluginConfig.AutoPullOnSpecialLaunch}.");
			}
			if (!flag)
			{
				return;
			}
			SpecialLaunchTracker.Clear();
			if (pluginConfig.AutoPullOnSpecialLaunch && flag2 && (secondary || launchedWithSpecial))
			{
				GrapplePuller.ReelIn(Player.m_localPlayer, __instance, chargeStamina: false);
				if (pluginConfig.DebugLogging)
				{
					GrapplingHookTweaks.Instance.LogInfo("Hook fired with the special attack: pulled in automatically.");
				}
			}
		}
	}
	[HarmonyPatch]
	internal static class HumanoidStartAttackPatch
	{
		[HarmonyPatch(typeof(Humanoid), "StartAttack")]
		private static bool Prefix(Humanoid __instance, bool secondaryAttack)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer)
			{
				return true;
			}
			PluginConfig pluginConfig = GrapplingHookTweaks.Instance?.Settings;
			if (pluginConfig == null)
			{
				return true;
			}
			if (pluginConfig.Enabled && PullShortcutInput.IsHeld(pluginConfig.PullShortcut) && (Object)(object)GrapplingPoint.m_localGrappler != (Object)null)
			{
				if (pluginConfig.DebugLogging)
				{
					GrapplingHookTweaks.Instance.LogInfo("Attack swallowed: the pull shortcut was pressed while hanging.");
				}
				SpecialLaunchTracker.Clear();
				return false;
			}
			bool launchedWithSpecial = SpecialLaunchTracker.LaunchedWithSpecial;
			SpecialLaunchTracker.Record(secondaryAttack);
			if (pluginConfig.DebugLogging && launchedWithSpecial != SpecialLaunchTracker.LaunchedWithSpecial)
			{
				GrapplingHookTweaks.Instance.LogInfo(SpecialLaunchTracker.LaunchedWithSpecial ? "Special attack started: the next hook reels in automatically." : "Special attack forgotten: a normal attack was started.");
			}
			return true;
		}
	}
}
namespace GrapplingHookTweaks.Grapple
{
	internal static class GrapplePuller
	{
		private const float LiftOffSpeed = 4f;

		private static GrapplingPoint _liftedFrom;

		public static bool TryPull(Player player, out string reason)
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			GrapplingPoint localGrappler = GrapplingPoint.m_localGrappler;
			if ((Object)(object)localGrappler == (Object)null)
			{
				reason = "no hook is attached";
				return false;
			}
			if ((Object)(object)player == (Object)null)
			{
				reason = "there is no local player";
				return false;
			}
			if (((Character)player).IsDead() || ((Character)player).IsEncumbered() || ((Character)player).InDodge() || ((Character)player).IsKnockedBack() || ((Character)player).IsStaggering())
			{
				reason = "the player cannot reel in right now";
				return false;
			}
			float num = Vector3.Distance(((Component)localGrappler).transform.position, ((Component)player).transform.position);
			if (num <= localGrappler.m_closePullDist)
			{
				reason = $"the point is only {num:0.0} m away, nothing to reel in";
				return false;
			}
			ReelIn(player, localGrappler, chargeStamina: true);
			reason = null;
			return true;
		}

		public static void ReelIn(Player player, GrapplingPoint point, bool chargeStamina)
		{
			if (chargeStamina)
			{
				if (!((Character)player).HaveStamina(((Character)player).m_jumpStaminaUsage) && (Object)(object)Hud.instance != (Object)null)
				{
					Hud.instance.StaminaBarEmptyFlash();
				}
				float num = ((Character)player).m_jumpStaminaUsage - ((Character)player).m_jumpStaminaUsage * ((Character)player).GetEquipmentMovementModifier() + ((Character)player).m_jumpStaminaUsage * ((Character)player).GetEquipmentJumpStaminaModifier();
				((Character)player).UseStamina(num);
			}
			point.Pull();
		}

		public static void KeepReelingOnGround(Player player, PluginConfig settings)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: 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_00e6: Unknown result type (might be due to invalid IL or missing references)
			GrapplingPoint localGrappler = GrapplingPoint.m_localGrappler;
			if (!settings.GroundPull || (Object)(object)localGrappler == (Object)null || localGrappler.m_secondary || !((Character)player).IsOnGround())
			{
				_liftedFrom = null;
				return;
			}
			Vector3 val = ((Component)localGrappler).transform.position - ((Component)player).transform.position;
			float magnitude = ((Vector3)(ref val)).magnitude;
			if (magnitude <= localGrappler.m_closePullDist)
			{
				return;
			}
			float num = (((int)localGrappler.Method == 0) ? (localGrappler.m_pullForce + localGrappler.m_distanceForceMultiplier * magnitude) : localGrappler.m_pullForce);
			Vector3 val2 = val / magnitude * num;
			val2.y = Mathf.Max(val2.y, 4f);
			if ((Object)(object)_liftedFrom == (Object)(object)localGrappler)
			{
				((Character)player).SetVelocity(val2);
				return;
			}
			_liftedFrom = localGrappler;
			((Character)player).ForceJump(val2, false);
			if (settings.DebugLogging)
			{
				GrapplingHookTweaks.Instance.LogInfo("Ground pull: lifted off the ground toward the point " + $"({magnitude:0.0} m, {localGrappler.Method}, speed {num:0.0}).");
			}
		}
	}
	internal static class PullShortcutInput
	{
		public static bool IsPressed(KeyboardShortcut shortcut)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			if (IsMainKeyDown(shortcut))
			{
				return AreModifiersHeld(shortcut);
			}
			return false;
		}

		public static bool IsHeld(KeyboardShortcut shortcut)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			if (IsMainKeyHeld(shortcut))
			{
				return AreModifiersHeld(shortcut);
			}
			return false;
		}

		public static bool IsMainKeyDown(KeyboardShortcut shortcut)
		{
			//IL_0002: 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)
			if ((int)((KeyboardShortcut)(ref shortcut)).MainKey != 0)
			{
				return Input.GetKeyDown(((KeyboardShortcut)(ref shortcut)).MainKey);
			}
			return false;
		}

		public static bool IsMainKeyHeld(KeyboardShortcut shortcut)
		{
			//IL_0002: 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)
			if ((int)((KeyboardShortcut)(ref shortcut)).MainKey != 0)
			{
				return Input.GetKey(((KeyboardShortcut)(ref shortcut)).MainKey);
			}
			return false;
		}

		public static bool AreModifiersHeld(KeyboardShortcut shortcut)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			foreach (KeyCode modifier in ((KeyboardShortcut)(ref shortcut)).Modifiers)
			{
				if (!Input.GetKey(modifier))
				{
					return false;
				}
			}
			return true;
		}
	}
	internal static class SpecialLaunchTracker
	{
		private const float WindowSeconds = 5f;

		private static float _launchedAt = -1f;

		public static bool LaunchedWithSpecial
		{
			get
			{
				if (_launchedAt >= 0f)
				{
					return Time.time - _launchedAt <= 5f;
				}
				return false;
			}
		}

		public static void Record(bool secondaryAttack)
		{
			_launchedAt = (secondaryAttack ? Time.time : (-1f));
		}

		public static void Clear()
		{
			_launchedAt = -1f;
		}
	}
}
namespace GrapplingHookTweaks.Config
{
	public sealed class PluginConfig
	{
		private const string GeneralSection = "General";

		private const string PullSection = "Pull";

		private readonly ConfigEntry<bool> _enabled;

		private readonly ConfigEntry<bool> _debugLogging;

		private readonly ConfigEntry<KeyboardShortcut> _pullShortcut;

		private readonly ConfigEntry<bool> _allowJumpToPull;

		private readonly ConfigEntry<bool> _autoPullOnSpecialLaunch;

		private readonly ConfigEntry<bool> _groundPull;

		public bool Enabled => _enabled.Value;

		public bool DebugLogging => _debugLogging.Value;

		public KeyboardShortcut PullShortcut => _pullShortcut.Value;

		public bool AllowJumpToPull => _allowJumpToPull.Value;

		public bool AutoPullOnSpecialLaunch => _autoPullOnSpecialLaunch.Value;

		public bool GroundPull => _groundPull.Value;

		public PluginConfig(ConfigFile file)
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Expected O, but got Unknown
			_enabled = Bind(file, "General", "Enabled", defaultValue: true, "Enable the grappling hook tweaks (key combination, automatic pull, ground pull). When off, the game behaves exactly like vanilla.");
			_debugLogging = Bind(file, "General", "DebugLogging", defaultValue: false, "Log every reel-in (shortcut, automatic pull, swallowed attack). Verbose, intended for troubleshooting only.");
			_pullShortcut = Bind<KeyboardShortcut>(file, "Pull", "PullShortcut", new KeyboardShortcut((KeyCode)308, Array.Empty<KeyCode>()), new ConfigDescription("Key combination that reels the player toward the point the hook is attached to, replacing the double jump. Works on the ground and in the air. Default: Left Alt (pressed alone). Set it to None to disable it.", (AcceptableValueBase)null, Array.Empty<object>()));
			_allowJumpToPull = Bind(file, "Pull", "AllowJumpToPull", defaultValue: false, "Keep the vanilla trigger as well: pressing jump while hanging also reels the player in. When off (default), only the pull shortcut and the automatic pull can reel the player in.");
			_autoPullOnSpecialLaunch = Bind(file, "Pull", "AutoPullOnSpecialLaunch", defaultValue: true, "Reel in automatically when the hook is fired with the special attack, exactly like the attack key already does. When off, the special attack leaves the player hanging until the pull shortcut is used.");
			_groundPull = Bind(file, "Pull", "GroundPull", defaultValue: true, "Reel in even while standing on the ground. The game only reels you in while you are off the ground: it brakes every movement that does not come from your own keys as long as your feet touch the ground, so a hook fired on flat ground in front of you stays still. When on, the reel-in lifts you off your feet first, the way the game already does when the hook lands above you.");
		}

		private static ConfigEntry<T> Bind<T>(ConfigFile file, string section, string identifier, T defaultValue, string description)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			return Bind(file, section, identifier, defaultValue, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>()));
		}

		private static ConfigEntry<T> Bind<T>(ConfigFile file, string section, string identifier, T defaultValue, ConfigDescription description)
		{
			return file.Bind<T>(section, ReadableKey(identifier), defaultValue, description);
		}

		private static string ReadableKey(string identifier)
		{
			if (string.IsNullOrEmpty(identifier))
			{
				return identifier;
			}
			StringBuilder stringBuilder = new StringBuilder(identifier.Length + 4);
			stringBuilder.Append(char.ToUpperInvariant(identifier[0]));
			for (int i = 1; i < identifier.Length; i++)
			{
				char c = identifier[i];
				if (char.IsUpper(c))
				{
					stringBuilder.Append(' ');
				}
				stringBuilder.Append(char.ToLowerInvariant(c));
			}
			return stringBuilder.ToString();
		}
	}
}