Decompiled source of DodgeCancel v0.2.2

DodgeCancel.dll

Decompiled a week ago
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace DodgeCancel;

[BepInPlugin("com.burks.dodgecancel", "Dodge Cancel", "0.2.2")]
public class DodgeCancelPlugin : BaseUnityPlugin
{
	public const string GUID = "com.burks.dodgecancel";

	public const string NAME = "Dodge Cancel";

	public const string VERSION = "0.2.2";

	public static ManualLogSource Log;

	public static ConfigEntry<bool> Enabled;

	public static ConfigEntry<bool> OnlyBeforeHit;

	public static ConfigEntry<bool> BlockCancel;

	public static ConfigEntry<float> ExtraStamina;

	private bool _on;

	private void Awake()
	{
		//IL_008f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0095: Invalid comparison between Unknown and I4
		//IL_009d: Unknown result type (might be due to invalid IL or missing references)
		Log = ((BaseUnityPlugin)this).Logger;
		Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Dodge rolling during an attack cancels the attack.");
		BlockCancel = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "BlockCancels", true, "Pressing block during an attack also cancels the attack and raises your guard. Only a fresh press counts: holding block before you swing does not cancel the swing.");
		OnlyBeforeHit = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "OnlyBeforeTheHit", false, "true = you can only cancel during the wind-up, before the swing connects. false = any time during the attack.");
		ExtraStamina = ((BaseUnityPlugin)this).Config.Bind<float>("General", "ExtraStaminaCost", 0f, "Extra stamina a cancelling roll costs, on top of the normal roll.");
		if ((int)SystemInfo.graphicsDeviceType != 4)
		{
			new Harmony("com.burks.dodgecancel").PatchAll(Assembly.GetExecutingAssembly());
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Dodge Cancel 0.2.2 loaded.");
			_on = true;
		}
	}

	private void Update()
	{
		if (_on)
		{
			try
			{
				Patches.Tick();
			}
			catch (Exception)
			{
			}
		}
	}

	private void LateUpdate()
	{
		if (_on)
		{
			try
			{
				Patches.Tick();
			}
			catch (Exception)
			{
			}
		}
	}
}
[HarmonyPatch]
public static class Patches
{
	private static readonly FieldRef<Player, float> QueuedDodge = AccessTools.FieldRefAccess<Player, float>("m_queuedDodgeTimer");

	private static readonly FieldRef<Humanoid, Attack> CurrentAttack = AccessTools.FieldRefAccess<Humanoid, Attack>("m_currentAttack");

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

	private static bool _bypass;

	private static Attack _landed;

	private static bool _warned;

	private static readonly FieldRef<Character, bool> Blocking = AccessTools.FieldRefAccess<Character, bool>("m_blocking");

	private static readonly FieldRef<Character, ZSyncAnimation> Zanim = AccessTools.FieldRefAccess<Character, ZSyncAnimation>("m_zanim");

	private static readonly MethodInfo Blocker = AccessTools.Method(typeof(Humanoid), "GetCurrentBlocker", (Type[])null, (Type[])null);

	private static bool _raw;

	private static bool _wasBlocking;

	private static float _guardUntil;

	private static readonly FieldRef<Character, Animator> Anim = AccessTools.FieldRefAccess<Character, Animator>("m_animator");

	private static readonly FieldRef<CharacterAnimEvent, Animator> EventAnim = AccessTools.FieldRefAccess<CharacterAnimEvent, Animator>("m_animator");

	public static bool FastForwarding => _guardUntil > Time.time;

	[HarmonyPatch(typeof(Player), "UpdateDodge")]
	[HarmonyPrefix]
	public static void UpdateDodge_Prefix(Player __instance)
	{
		_bypass = false;
		try
		{
			if (!DodgeCancelPlugin.Enabled.Value || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || QueuedDodge.Invoke(__instance) <= 0f || !((Character)__instance).InAttack() || !((Character)__instance).IsOnGround() || ((Character)__instance).IsDead() || ((Character)__instance).IsEncumbered() || ((Character)__instance).InDodge() || ((Character)__instance).IsStaggering())
			{
				return;
			}
			Attack val = CurrentAttack.Invoke((Humanoid)(object)__instance);
			if (DodgeCancelPlugin.OnlyBeforeHit.Value && val != null && val == _landed)
			{
				return;
			}
			float num = Mathf.Max(0f, DodgeCancelPlugin.ExtraStamina.Value);
			float num2 = (float)DodgeCost.Invoke(__instance, null) + num;
			if (((Character)__instance).HaveStamina(num2))
			{
				if (val != null)
				{
					val.Stop();
					CurrentAttack.Invoke((Humanoid)(object)__instance) = null;
				}
				if (num > 0f)
				{
					((Character)__instance).UseStamina(num);
				}
				_bypass = true;
			}
		}
		catch (Exception ex)
		{
			_bypass = false;
			if (!_warned)
			{
				_warned = true;
				DodgeCancelPlugin.Log.LogWarning((object)("Dodge cancel failed: " + ex));
			}
		}
	}

	[HarmonyPatch(typeof(Player), "UpdateDodge")]
	[HarmonyFinalizer]
	public static void UpdateDodge_Finalizer()
	{
		_bypass = false;
	}

	[HarmonyPatch(typeof(Player), "InAttack")]
	[HarmonyPrefix]
	public static bool InAttack_Prefix(Player __instance, ref bool __result)
	{
		if (_raw || (Object)(object)__instance != (Object)(object)Player.m_localPlayer)
		{
			return true;
		}
		if (!_bypass && Time.time >= _guardUntil)
		{
			return true;
		}
		__result = false;
		return false;
	}

	private static bool ReallyAttacking(Player p)
	{
		_raw = true;
		try
		{
			return ((Character)p).InAttack();
		}
		finally
		{
			_raw = false;
		}
	}

	[HarmonyPatch(typeof(Humanoid), "UpdateBlock")]
	[HarmonyPrefix]
	public static void UpdateBlock_Prefix(Humanoid __instance)
	{
		try
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)__instance != (Object)(object)localPlayer)
			{
				return;
			}
			bool num = Blocking.Invoke((Character)(object)localPlayer);
			bool flag = num && !_wasBlocking;
			_wasBlocking = num;
			bool flag2 = ReallyAttacking(localPlayer);
			if (_guardUntil > Time.time && !flag2)
			{
				EndFastForward(localPlayer);
			}
			if (!flag || !flag2 || _guardUntil > Time.time || !DodgeCancelPlugin.Enabled.Value || !DodgeCancelPlugin.BlockCancel.Value || ((Character)localPlayer).IsDead() || ((Character)localPlayer).InDodge() || ((Character)localPlayer).IsStaggering() || ((Character)localPlayer).IsEncumbered() || ((Character)localPlayer).InPlaceMode() || Blocker.Invoke(localPlayer, null) == null)
			{
				return;
			}
			Attack val = CurrentAttack.Invoke((Humanoid)(object)localPlayer);
			if (DodgeCancelPlugin.OnlyBeforeHit.Value && val != null && val == _landed)
			{
				return;
			}
			float num2 = Mathf.Max(0f, DodgeCancelPlugin.ExtraStamina.Value);
			if (num2 > 0f)
			{
				if (!((Character)localPlayer).HaveStamina(num2))
				{
					return;
				}
				((Character)localPlayer).UseStamina(num2);
			}
			if (val != null)
			{
				val.Stop();
				CurrentAttack.Invoke((Humanoid)(object)localPlayer) = null;
			}
			_guardUntil = Time.time + 2f;
			FastForward(localPlayer);
		}
		catch (Exception ex)
		{
			_guardUntil = 0f;
			if (!_warned)
			{
				_warned = true;
				DodgeCancelPlugin.Log.LogWarning((object)("Block cancel failed: " + ex));
			}
		}
	}

	public static void FastForward(Player me)
	{
		Animator val = (((Object)(object)me != (Object)null) ? Anim.Invoke((Character)(object)me) : null);
		if ((Object)(object)val != (Object)null && FastForwarding)
		{
			val.speed = 25f;
		}
	}

	private static void EndFastForward(Player me)
	{
		_guardUntil = 0f;
		Animator val = (((Object)(object)me != (Object)null) ? Anim.Invoke((Character)(object)me) : null);
		if ((Object)(object)val != (Object)null)
		{
			val.speed = 1f;
		}
	}

	public static void Tick()
	{
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			_guardUntil = 0f;
		}
		else if (!(_guardUntil <= 0f))
		{
			if (_guardUntil <= Time.time || !ReallyAttacking(localPlayer))
			{
				EndFastForward(localPlayer);
			}
			else
			{
				FastForward(localPlayer);
			}
		}
	}

	[HarmonyPatch(typeof(CharacterAnimEvent), "OnAnimatorMove")]
	[HarmonyPrefix]
	public static bool OnAnimatorMove_Prefix(CharacterAnimEvent __instance)
	{
		if (!FastForwarding || (Object)(object)Player.m_localPlayer == (Object)null)
		{
			return true;
		}
		try
		{
			return (Object)(object)EventAnim.Invoke(__instance) != (Object)(object)Anim.Invoke((Character)(object)Player.m_localPlayer);
		}
		catch
		{
			return true;
		}
	}

	[HarmonyPatch(typeof(CharacterAnimEvent), "CustomFixedUpdate")]
	[HarmonyPostfix]
	public static void AnimFixedUpdate_Postfix(CharacterAnimEvent __instance)
	{
		try
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || !FastForwarding)
			{
				return;
			}
			Animator val = Anim.Invoke((Character)(object)localPlayer);
			if (!((Object)(object)val == (Object)null) && !((Object)(object)EventAnim.Invoke(__instance) != (Object)(object)val))
			{
				if (ReallyAttacking(localPlayer))
				{
					val.speed = 25f;
				}
				else
				{
					EndFastForward(localPlayer);
				}
			}
		}
		catch
		{
		}
	}

	[HarmonyPatch(typeof(CharacterAnimEvent), "Speed")]
	[HarmonyPrefix]
	public static bool Speed_Prefix(CharacterAnimEvent __instance)
	{
		return OnAnimatorMove_Prefix(__instance);
	}

	[HarmonyPatch(typeof(CharacterAnimEvent), "FreezeFrame")]
	[HarmonyPrefix]
	public static bool FreezeFrame_Prefix(CharacterAnimEvent __instance)
	{
		return OnAnimatorMove_Prefix(__instance);
	}

	[HarmonyPatch(typeof(Attack), "OnAttackTrigger")]
	[HarmonyPostfix]
	public static void OnAttackTrigger_Postfix(Attack __instance)
	{
		_landed = __instance;
	}
}