Decompiled source of ParrySense v0.1.1

plugins\ParrySense.dll

Decompiled 3 weeks ago
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
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("ParrySense")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+9f35119cefcfc6190dd341aab0aa4fc7b8cf959f")]
[assembly: AssemblyProduct("ParrySense")]
[assembly: AssemblyTitle("ParrySense")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.0.0")]
[module: UnverifiableCode]
[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 ParrySense
{
	[BepInPlugin("ParrySense", "ParrySense", "0.1.0")]
	public class Plugin : BaseUnityPlugin
	{
		private enum TimingResult
		{
			None,
			TooEarly,
			Parry,
			TooLate
		}

		internal static ManualLogSource Logger;

		private static Plugin _instance;

		private Harmony _harmony;

		private static float _lastTimingMs;

		private static TimingResult _lastResult = TimingResult.None;

		private static float _displayUntil;

		private static bool _hasPendingMissedImpact;

		private static float _lastMissedImpactTime;

		private static bool _processingLocalDamage;

		private static bool _blockAttackSeenDuringDamage;

		private static string _toggleMessage = "";

		private static float _toggleMessageUntil;

		private ConfigEntry<bool> _enabled;

		private ConfigEntry<KeyboardShortcut> _toggleKey;

		private ConfigEntry<float> _positionX;

		private ConfigEntry<float> _positionY;

		private ConfigEntry<float> _displayDuration;

		private ConfigEntry<float> _panelWidth;

		private ConfigEntry<float> _panelHeight;

		private ConfigEntry<float> _backgroundOpacity;

		private ConfigEntry<float> _tooEarlyLimit;

		private ConfigEntry<float> _tooLateLimit;

		private ConfigEntry<float> _outsideDisplayRange;

		private GUIStyle _titleStyle;

		private GUIStyle _labelStyle;

		private GUIStyle _statusStyle;

		private GUIStyle _toggleMessageStyle;

		private void Awake()
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			Logger = ((BaseUnityPlugin)this).Logger;
			_instance = this;
			BindConfig();
			_harmony = new Harmony("ParrySense");
			_harmony.PatchAll();
			Logger.LogInfo((object)"ParrySense 0.1.0 loaded");
		}

		private void Update()
		{
			//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_000e: Unknown result type (might be due to invalid IL or missing references)
			KeyboardShortcut value = _toggleKey.Value;
			if (Input.GetKeyDown(((KeyboardShortcut)(ref value)).MainKey))
			{
				_enabled.Value = !_enabled.Value;
				ClearRuntimeState();
				ShowToggleMessage(_enabled.Value);
				string text = (_enabled.Value ? "enabled" : "disabled");
				Logger.LogInfo((object)("ParrySense " + text));
			}
			if (_hasPendingMissedImpact && Time.unscaledTime - _lastMissedImpactTime > GetTooLateLimitSeconds())
			{
				_hasPendingMissedImpact = false;
			}
		}

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

		private void BindConfig()
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			_enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable or disable ParrySense.");
			_toggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "ToggleKey", new KeyboardShortcut((KeyCode)289, Array.Empty<KeyCode>()), "Keyboard shortcut used to enable or disable ParrySense.");
			_positionX = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "PositionX", 45f, "Horizontal HUD position in pixels from the left edge of the screen.");
			_positionY = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "PositionY", 160f, "Vertical HUD position in pixels from the top edge of the screen.");
			_displayDuration = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "DisplayDuration", 3f, "How long the timing result remains visible, in seconds.");
			_panelWidth = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "PanelWidth", 360f, "Width of the HUD panel in pixels.");
			_panelHeight = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "PanelHeight", 75f, "Height of the HUD panel in pixels.");
			_backgroundOpacity = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "BackgroundOpacity", 0.45f, "Background opacity of the HUD panel. Valid range: 0 to 1.");
			_tooEarlyLimit = ((BaseUnityPlugin)this).Config.Bind<float>("Training", "TooEarlyLimit", 1f, "Maximum time in seconds before impact that can be reported as TOO EARLY. Blocks started earlier than this are ignored.");
			_tooLateLimit = ((BaseUnityPlugin)this).Config.Bind<float>("Training", "TooLateLimit", 0.5f, "Maximum time in seconds after impact that can be reported as TOO LATE.");
			_outsideDisplayRange = ((BaseUnityPlugin)this).Config.Bind<float>("Training", "OutsideDisplayRangeMs", 250f, "Amount of time represented by each dotted area outside the parry window. Larger errors are visually clamped, but the real timing value is still displayed.");
		}

		internal static bool IsEnabled()
		{
			if ((Object)(object)_instance != (Object)null)
			{
				return _instance._enabled.Value;
			}
			return false;
		}

		private static float GetTooLateLimitSeconds()
		{
			if ((Object)(object)_instance == (Object)null)
			{
				return 0.5f;
			}
			return Mathf.Max(0f, _instance._tooLateLimit.Value);
		}

		private static float GetTooEarlyLimitSeconds()
		{
			if ((Object)(object)_instance == (Object)null)
			{
				return 1f;
			}
			return Mathf.Max(0.25f, _instance._tooEarlyLimit.Value);
		}

		private static void ClearRuntimeState()
		{
			_lastResult = TimingResult.None;
			_displayUntil = 0f;
			_hasPendingMissedImpact = false;
			_lastMissedImpactTime = 0f;
			_processingLocalDamage = false;
			_blockAttackSeenDuringDamage = false;
		}

		private static void ShowToggleMessage(bool enabled)
		{
			if (IsFrench())
			{
				_toggleMessage = (enabled ? "ENTRAÎNEMENT À LA PARADE : ACTIVÉ" : "ENTRAÎNEMENT À LA PARADE : DÉSACTIVÉ");
			}
			else
			{
				_toggleMessage = (enabled ? "PARRY TRAINING: ENABLED" : "PARRY TRAINING: DISABLED");
			}
			_toggleMessageUntil = Time.unscaledTime + 1.5f;
		}

		internal static void ShowBlockTiming(float milliseconds)
		{
			if (!IsEnabled())
			{
				return;
			}
			_hasPendingMissedImpact = false;
			if (milliseconds < 250f)
			{
				ShowResult(TimingResult.Parry, milliseconds);
				return;
			}
			float num = GetTooEarlyLimitSeconds() * 1000f;
			if (milliseconds <= num)
			{
				ShowResult(TimingResult.TooEarly, milliseconds);
			}
		}

		internal static void BeginLocalDamage()
		{
			if (IsEnabled())
			{
				_processingLocalDamage = true;
				_blockAttackSeenDuringDamage = false;
			}
		}

		internal static void MarkBlockAttackSeen()
		{
			if (IsEnabled() && _processingLocalDamage)
			{
				_blockAttackSeenDuringDamage = true;
			}
		}

		internal static void EndLocalDamage(bool hasAttacker)
		{
			if (IsEnabled() && _processingLocalDamage)
			{
				if (!_blockAttackSeenDuringDamage && hasAttacker)
				{
					_lastMissedImpactTime = Time.unscaledTime;
					_hasPendingMissedImpact = true;
				}
				_processingLocalDamage = false;
				_blockAttackSeenDuringDamage = false;
			}
		}

		internal static void OnBlockStarted()
		{
			if (IsEnabled() && _hasPendingMissedImpact)
			{
				float num = Time.unscaledTime - _lastMissedImpactTime;
				float tooLateLimitSeconds = GetTooLateLimitSeconds();
				if (num < 0f || num > tooLateLimitSeconds)
				{
					_hasPendingMissedImpact = false;
					return;
				}
				float milliseconds = 0f - num * 1000f;
				_hasPendingMissedImpact = false;
				ShowResult(TimingResult.TooLate, milliseconds);
			}
		}

		private static void ShowResult(TimingResult result, float milliseconds)
		{
			if (IsEnabled())
			{
				_lastResult = result;
				_lastTimingMs = Mathf.Round(milliseconds);
				_displayUntil = Time.unscaledTime + Mathf.Max(0.1f, _instance._displayDuration.Value);
				Logger.LogInfo((object)$"{result} | timing = {_lastTimingMs:F0} ms");
			}
		}

		private void OnGUI()
		{
			bool flag = !string.IsNullOrEmpty(_toggleMessage) && Time.unscaledTime <= _toggleMessageUntil;
			bool flag2 = _enabled.Value && _lastResult != TimingResult.None && Time.unscaledTime <= _displayUntil;
			if (flag || flag2)
			{
				EnsureStyles();
				if (flag)
				{
					DrawToggleMessage();
				}
				if (flag2)
				{
					float value = _positionX.Value;
					float value2 = _positionY.Value;
					float num = Mathf.Max(280f, _panelWidth.Value);
					float height = Mathf.Max(70f, _panelHeight.Value);
					DrawBackground(value, value2, num, height);
					DrawTitle(value, value2, num);
					DrawTimeline(value, value2, num);
				}
			}
		}

		private void DrawToggleMessage()
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			float num = ((float)Screen.width - 600f) * 0.5f;
			float num2 = ((float)Screen.height - 50f) * 0.5f;
			GUI.Label(new Rect(num, num2, 600f, 50f), _toggleMessage, _toggleMessageStyle);
		}

		private void DrawBackground(float x, float y, float width, float height)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			Color color = GUI.color;
			float num = Mathf.Clamp01(_backgroundOpacity.Value);
			GUI.color = new Color(0f, 0f, 0f, num);
			GUI.DrawTexture(new Rect(x, y, width, height), (Texture)(object)Texture2D.whiteTexture);
			GUI.color = color;
		}

		private void DrawTitle(float panelX, float panelY, float panelWidth)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			string resultText = GetResultText(_lastResult);
			string text = $"{_lastTimingMs:F0} ms";
			_statusStyle.normal.textColor = GetResultColor(_lastResult);
			GUI.Label(new Rect(panelX + 8f, panelY + 4f, panelWidth - 16f, 25f), resultText + "  " + text, _statusStyle);
		}

		private void DrawTimeline(float panelX, float panelY, float panelWidth)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: 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_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: 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_016b: Unknown result type (might be due to invalid IL or missing references)
			float num = panelX + 14f;
			float num2 = panelX + panelWidth - 14f;
			float num3 = num2 - num;
			float num4 = num3 * 0.2f;
			float num5 = num3 * 0.6f;
			float num6 = num + num4;
			float num7 = num6 + num5;
			float num8 = panelY + 43f;
			Color color = GUI.color;
			GUI.color = GetResultColor(TimingResult.TooEarly);
			DrawDottedLine(num, num6, num8);
			GUI.color = GetResultColor(TimingResult.Parry);
			GUI.DrawTexture(new Rect(num6, num8, num5, 2f), (Texture)(object)Texture2D.whiteTexture);
			GUI.color = GetResultColor(TimingResult.TooLate);
			DrawDottedLine(num7, num2, num8);
			GUI.color = Color.white;
			GUI.DrawTexture(new Rect(num6, num8 - 4f, 2f, 10f), (Texture)(object)Texture2D.whiteTexture);
			GUI.DrawTexture(new Rect(num7, num8 - 4f, 2f, 10f), (Texture)(object)Texture2D.whiteTexture);
			float num9 = CalculateMarkerPosition(num, num6, num7, num2);
			GUI.color = GetResultColor(_lastResult);
			GUI.DrawTexture(new Rect(num9 - 4f, num8 - 4f, 8f, 8f), (Texture)(object)Texture2D.whiteTexture);
			GUI.color = color;
			GUI.Label(new Rect(num6 - 27f, num8 + 6f, 58f, 18f), "250 ms", _labelStyle);
			GUI.Label(new Rect(num7 - 28f, num8 + 6f, 58f, 18f), "Impact", _labelStyle);
		}

		private float CalculateMarkerPosition(float timelineLeft, float windowStart, float impactX, float timelineRight)
		{
			if (_lastTimingMs >= 0f && _lastTimingMs <= 250f)
			{
				float num = _lastTimingMs / 250f;
				return Mathf.Lerp(impactX, windowStart, num);
			}
			if (_lastTimingMs > 250f)
			{
				float num2 = _lastTimingMs - 250f;
				float num3 = Mathf.Max(1f, _outsideDisplayRange.Value);
				float num4 = Mathf.Clamp01(num2 / num3);
				return Mathf.Lerp(windowStart, timelineLeft, num4);
			}
			float num5 = Mathf.Abs(_lastTimingMs);
			float num6 = Mathf.Max(1f, _outsideDisplayRange.Value);
			float num7 = Mathf.Clamp01(num5 / num6);
			return Mathf.Lerp(impactX, timelineRight, num7);
		}

		private static void DrawDottedLine(float startX, float endX, float y)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			for (float num = startX; num < endX; num += 8f)
			{
				float num2 = Mathf.Min(3f, endX - num);
				GUI.DrawTexture(new Rect(num, y, num2, 2f), (Texture)(object)Texture2D.whiteTexture);
			}
		}

		private void EnsureStyles()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: 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_0066: Expected O, but got Unknown
			//IL_0079: 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_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Expected O, but got Unknown
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: 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_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Expected O, but got Unknown
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			if (_titleStyle == null)
			{
				_titleStyle = new GUIStyle(GUI.skin.label)
				{
					alignment = (TextAnchor)3,
					fontSize = 16,
					fontStyle = (FontStyle)1
				};
			}
			if (_statusStyle == null)
			{
				_statusStyle = new GUIStyle(GUI.skin.label)
				{
					alignment = (TextAnchor)3,
					fontSize = 16,
					fontStyle = (FontStyle)1
				};
			}
			if (_labelStyle == null)
			{
				_labelStyle = new GUIStyle(GUI.skin.label)
				{
					alignment = (TextAnchor)4,
					fontSize = 10
				};
			}
			if (_toggleMessageStyle == null)
			{
				_toggleMessageStyle = new GUIStyle(GUI.skin.label)
				{
					alignment = (TextAnchor)4,
					fontSize = 20,
					fontStyle = (FontStyle)1
				};
				_toggleMessageStyle.normal.textColor = Color.yellow;
			}
		}

		private static Color GetResultColor(TimingResult result)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: 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_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			return (Color)(result switch
			{
				TimingResult.TooEarly => new Color(0.35f, 0.7f, 1f, 1f), 
				TimingResult.Parry => new Color(0.35f, 1f, 0.45f, 1f), 
				TimingResult.TooLate => new Color(1f, 0.35f, 0.35f, 1f), 
				_ => Color.white, 
			});
		}

		private static string GetResultText(TimingResult result)
		{
			if (IsFrench())
			{
				return result switch
				{
					TimingResult.TooEarly => "TROP TÔT", 
					TimingResult.Parry => "PARADE", 
					TimingResult.TooLate => "TROP TARD", 
					_ => "", 
				};
			}
			return result switch
			{
				TimingResult.TooEarly => "TOO EARLY", 
				TimingResult.Parry => "PARRY", 
				TimingResult.TooLate => "TOO LATE", 
				_ => "", 
			};
		}

		private static bool IsFrench()
		{
			try
			{
				Type type = null;
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				for (int i = 0; i < assemblies.Length; i++)
				{
					type = assemblies[i].GetType("Localization");
					if (type != null)
					{
						break;
					}
				}
				if (type == null)
				{
					return false;
				}
				object obj = null;
				FieldInfo field = type.GetField("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					obj = field.GetValue(null);
				}
				else
				{
					PropertyInfo property = type.GetProperty("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					if (property != null)
					{
						obj = property.GetValue(null, null);
					}
				}
				if (obj == null)
				{
					return false;
				}
				MethodInfo method = type.GetMethod("GetSelectedLanguage", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (method == null)
				{
					return false;
				}
				string text = method.Invoke(obj, null) as string;
				if (string.IsNullOrEmpty(text))
				{
					return false;
				}
				return text.IndexOf("French", StringComparison.OrdinalIgnoreCase) >= 0;
			}
			catch (Exception ex)
			{
				ManualLogSource logger = Logger;
				if (logger != null)
				{
					logger.LogWarning((object)("Could not detect Valheim language: " + ex.Message));
				}
				return false;
			}
		}
	}
	[HarmonyPatch(typeof(Humanoid), "BlockAttack")]
	internal static class HumanoidBlockAttackPatch
	{
		private static void Prefix(Humanoid __instance, float ___m_blockTimer, out float __state)
		{
			__state = -1f;
			if (Plugin.IsEnabled() && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
			{
				Plugin.MarkBlockAttackSeen();
				__state = ___m_blockTimer;
			}
		}

		private static void Postfix(Humanoid __instance, bool __result, float __state)
		{
			if (Plugin.IsEnabled() && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && __result && !(__state < 0f))
			{
				Plugin.ShowBlockTiming(__state * 1000f);
			}
		}
	}
	[HarmonyPatch(typeof(Humanoid), "UpdateBlock")]
	internal static class HumanoidUpdateBlockPatch
	{
		private static void Prefix(Humanoid __instance, float ___m_blockTimer, out float __state)
		{
			__state = ___m_blockTimer;
		}

		private static void Postfix(Humanoid __instance, float ___m_blockTimer, float __state)
		{
			if (Plugin.IsEnabled() && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && __state < 0f && ___m_blockTimer >= 0f)
			{
				Plugin.OnBlockStarted();
			}
		}
	}
	[HarmonyPatch(typeof(Character), "RPC_Damage", new Type[]
	{
		typeof(long),
		typeof(HitData)
	})]
	internal static class CharacterRpcDamagePatch
	{
		private static void Prefix(Character __instance)
		{
			if (Plugin.IsEnabled() && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
			{
				Plugin.BeginLocalDamage();
			}
		}

		private static void Postfix(Character __instance, HitData hit)
		{
			if (Plugin.IsEnabled() && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
			{
				bool flag = false;
				try
				{
					Character val = ((hit != null) ? hit.GetAttacker() : null);
					flag = (Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)__instance;
				}
				catch
				{
					flag = false;
				}
				Plugin.EndLocalDamage(flag);
			}
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "ParrySense";

		public const string PLUGIN_NAME = "ParrySense";

		public const string PLUGIN_VERSION = "0.1.0";
	}
}