Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ParrySense v0.1.1
plugins\ParrySense.dll
Decompiled 3 weeks agousing 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"; } }