Decompiled source of Footwork v1.0.0

Footwork.dll

Decompiled 2 weeks ago
using System;
using System.Diagnostics;
using System.IO;
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(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("0.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 Footwork
{
	internal sealed class ConfigurationManagerAttributes
	{
		public string Category;

		public string DispName;

		public int? Order;
	}
	[HarmonyPatch(typeof(Character), "ApplyLiquidResistance")]
	internal static class BaseSpeedPatch
	{
		private static void Prefix(Character __instance, ref float speed)
		{
			if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				AttackSpeedPatch.BaseSpeed = speed;
			}
		}
	}
	[HarmonyPatch(typeof(Humanoid), "GetAttackSpeedFactorMovement")]
	internal static class AttackSpeedPatch
	{
		internal static float BaseSpeed;

		private static Attack _logged;

		private static void Postfix(Humanoid __instance, Attack ___m_currentAttack, ref float __result)
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Invalid comparison between Unknown and I4
			if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			float baseSpeed = BaseSpeed;
			BaseSpeed = 0f;
			if (Plugin.Enabled.Value && !(baseSpeed <= 0f) && ___m_currentAttack != null && ((Character)__instance).InAttack() && ((Character)__instance).IsOnGround())
			{
				ItemData currentWeapon = __instance.GetCurrentWeapon();
				bool flag = currentWeapon != null && (int)currentWeapon.m_shared.m_itemType == 3;
				float num = (flag ? Plugin.OneHandedSpeed.Value : Plugin.Speed.Value);
				float num2 = Mathf.Min(1f, ((Character)__instance).m_crouchSpeed * num / baseSpeed);
				if (Plugin.DebugLog.Value && ___m_currentAttack != _logged)
				{
					_logged = ___m_currentAttack;
					Plugin.Log.LogInfo((object)("Attack " + ___m_currentAttack.m_attackAnimation + " (" + (flag ? "one-handed" : "other") + ", " + $"{num:0.##}× crouch speed {((Character)__instance).m_crouchSpeed:0.##}): base speed {baseSpeed:0.##} m/s, " + $"vanilla factor {__result:0.##} ({baseSpeed * __result:0.##} m/s), " + $"mod factor {num2:0.##} ({baseSpeed * num2:0.##} m/s)."));
				}
				__result = num2;
			}
		}
	}
	internal static class Aim
	{
		private static readonly FieldRef<Humanoid, Attack> CurrentAttack = AccessTools.FieldRefAccess<Humanoid, Attack>("m_currentAttack");

		internal static bool IsMelee(Attack attack)
		{
			//IL_0004: 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_0012: Invalid comparison between Unknown and I4
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Invalid comparison between Unknown and I4
			if (attack != null)
			{
				if ((int)attack.m_attackType != 0 && (int)attack.m_attackType != 1)
				{
					return (int)attack.m_attackType == 4;
				}
				return true;
			}
			return false;
		}

		internal static bool InMeleeAttack(Character character)
		{
			Humanoid val = (Humanoid)(object)((character is Humanoid) ? character : null);
			if (val != null && ((Character)val).InAttack())
			{
				return IsMelee(CurrentAttack.Invoke(val));
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(Character), "UpdateRotation")]
	internal static class FacingPatch
	{
		[HarmonyPriority(800)]
		private static bool Prefix(Character __instance, Quaternion ___m_lookYaw, ref float __result)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.AimAtCamera.Value || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || !Aim.InMeleeAttack(__instance))
			{
				return true;
			}
			((Component)__instance).transform.rotation = ___m_lookYaw;
			__result = 0f;
			return false;
		}
	}
	[HarmonyPatch(typeof(Attack), "OnAttackTrigger")]
	internal static class HitAimPatch
	{
		private static void Prefix(Attack __instance, Humanoid ___m_character)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//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)
			if (Plugin.AimAtCamera.Value && !((Object)(object)___m_character == (Object)null) && !((Object)(object)___m_character != (Object)(object)Player.m_localPlayer) && Aim.IsMelee(__instance))
			{
				Quaternion lookYaw = ((Character)___m_character).GetLookYaw();
				if (Plugin.DebugLog.Value)
				{
					Plugin.Log.LogInfo((object)($"Hit {__instance.m_attackAnimation}: facing {Quaternion.Angle(((Component)___m_character).transform.rotation, lookYaw):0.#}° " + "off the camera, lined up."));
				}
				((Component)___m_character).transform.rotation = lookYaw;
			}
		}
	}
	[BepInPlugin("vh.footwork", "Footwork", "1.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "vh.footwork";

		public const string Name = "Footwork";

		public const string Version = "1.0.0";

		private const string Attacks = "1. Moving while attacking";

		private const string Aiming = "2. Melee aim";

		private const string Debug = "3. Debug";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<float> OneHandedSpeed;

		internal static ConfigEntry<float> Speed;

		internal static ConfigEntry<bool> AimAtCamera;

		internal static ConfigEntry<bool> DebugLog;

		private Harmony _harmony;

		private FileSystemWatcher _watcher;

		private DateTime _lastReload;

		private static ConfigurationManagerAttributes Display(string category, string name, int order)
		{
			return new ConfigurationManagerAttributes
			{
				Category = category,
				DispName = name,
				Order = order
			};
		}

		private void Awake()
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Expected O, but got Unknown
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Expected O, but got Unknown
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Expected O, but got Unknown
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Expected O, but got Unknown
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Expected O, but got Unknown
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("1. Moving while attacking", "Enabled", true, new ConfigDescription("Master switch for moving while attacking. Off is vanilla, where you creep at a fifth of your speed at the start of a swing and stop dead once it lands.", (AcceptableValueBase)null, new object[1] { Display("1. Moving while attacking", "Enabled", 3) }));
			OneHandedSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("1. Moving while attacking", "OneHandedSpeed", 1.5f, new ConfigDescription("How fast you move for the whole attack with a one-handed weapon (swords, one-handed axes, maces, knives, spears), as a share of your crouch (sneak) speed: 1 is crouch speed, 0.5 half of it, 2 double. Never faster than you'd move without attacking. Default 1.5.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), new object[1] { Display("1. Moving while attacking", "Speed, one-handed (× crouch speed)", 2) }));
			Speed = ((BaseUnityPlugin)this).Config.Bind<float>("1. Moving while attacking", "Speed", 1f, new ConfigDescription("The same for every other attack: two-handed weapons, bows, crossbows, staffs, torches and fists. Never faster than you'd move without attacking, so walking stays at walk speed. Lunges and steps built into the attack animation still carry you when they're faster. Default 1.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), new object[1] { Display("1. Moving while attacking", "Speed, other attacks (× crouch speed)", 1) }));
			AimAtCamera = ((BaseUnityPlugin)this).Config.Bind<bool>("2. Melee aim", "AimAtCamera", true, new ConfigDescription("Melee swings and area attacks (sledge slams, atgeir spins) go exactly where the camera points: your character turns with the camera for the whole attack, every hit of a combo included, and is lined up again at the moment the hit lands. Up and down aim stays vanilla, within each weapon's limit. Off is vanilla, where only the first hit of a combo faces the camera and you barely turn during a swing.", (AcceptableValueBase)null, new object[1] { Display("2. Melee aim", "Aim at camera", 1) }));
			DebugLog = ((BaseUnityPlugin)this).Config.Bind<bool>("3. Debug", "DebugLog", false, new ConfigDescription("Write the start of every attack to BepInEx\\LogOutput.log, with your speed before the attack, the vanilla attack factor and the speed the mod sets, and every melee hit, with how far off the camera direction your character was facing. For troubleshooting; leave it off otherwise.", (AcceptableValueBase)null, new object[1] { Display("3. Debug", "Debug log", 1) }));
			_harmony = new Harmony("vh.footwork");
			_harmony.PatchAll();
			SetupWatcher();
			Log.LogInfo((object)($"Loaded: attacks at {OneHandedSpeed.Value:0.##}× crouch speed one-handed, " + string.Format("{0:0.##}× otherwise; aim at camera {1}.", Speed.Value, AimAtCamera.Value ? "on" : "off")));
		}

		private void OnDestroy()
		{
			_watcher?.Dispose();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		private void SetupWatcher()
		{
			_watcher = new FileSystemWatcher(Paths.ConfigPath, Path.GetFileName(((BaseUnityPlugin)this).Config.ConfigFilePath));
			_watcher.Changed += ReloadConfig;
			_watcher.Created += ReloadConfig;
			_watcher.Renamed += ReloadConfig;
			_watcher.SynchronizingObject = ThreadingHelper.SynchronizingObject;
			_watcher.EnableRaisingEvents = true;
		}

		private void ReloadConfig(object sender, FileSystemEventArgs e)
		{
			if ((DateTime.Now - _lastReload).TotalSeconds < 1.0 || !File.Exists(((BaseUnityPlugin)this).Config.ConfigFilePath))
			{
				return;
			}
			_lastReload = DateTime.Now;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			try
			{
				((BaseUnityPlugin)this).Config.Reload();
				Log.LogInfo((object)"Configuration reloaded.");
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Error reloading configuration: " + ex.Message));
			}
			finally
			{
				((BaseUnityPlugin)this).Config.SaveOnConfigSet = true;
			}
		}
	}
}