Decompiled source of PerfectBlock v1.6.8

ChineseCanFly.PerfectBlock.dll

Decompiled 4 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("ChineseCanFly.PerfectBlock")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.6.8.0")]
[assembly: AssemblyInformationalVersion("1.6.8")]
[assembly: AssemblyProduct("ChineseCanFly.PerfectBlock")]
[assembly: AssemblyTitle("ChineseCanFly.PerfectBlock")]
[assembly: AssemblyVersion("1.6.8.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 ChineseCanFly.PerfectBlock
{
	[BepInPlugin("ChineseCanFly.PerfectBlock", "Perfect Block", "1.6.8")]
	public class Plugin : BaseUnityPlugin
	{
		private sealed class BuffRow
		{
			public string Token;

			public string Name;

			public Sprite Icon;

			public float End;

			public float Seen;

			public int Stacks;

			public bool Temporary;
		}

		private sealed class HitText
		{
			public string Text;

			public string Label;

			public Color Color;

			public Sprite Icon;

			public float Born;

			public int Size;

			public bool Taken;

			public bool Crit;

			public bool Avoid;

			public float FinishX;

			public float FinishY;

			public float RandY;

			public float RootA;

			public float RootB;

			public float Angle;

			public float NextSquiggle;

			public float SquiggleX;

			public float SquiggleY;
		}

		private sealed class Flash
		{
			public float Started;
		}

		[Serializable]
		public class Record
		{
			public string prefab;

			public int stateHash;

			public float[] hits = new float[0];

			public float leadMs;

			public bool learned;

			public int successes;

			public int early;

			public int late;
		}

		public const string Guid = "ChineseCanFly.PerfectBlock";

		private static Plugin _instance;

		private static ConfigEntry<bool> _autoBlock;

		private static ConfigEntry<float> _detectRange;

		private static ConfigEntry<bool> _logLearning;

		private static ConfigEntry<bool> _logAttempts;

		private static ConfigEntry<float> _leadMs;

		private static ConfigEntry<int> _minSamples;

		private static ConfigEntry<float> _maxSpread;

		private static ConfigEntry<bool> _forceBlockTest;

		private static bool _testLogged;

		private static ConfigEntry<bool> _releaseOnAttack;

		private static ConfigEntry<bool> _showFlash;

		private static ConfigEntry<bool> _reliable;

		private static ConfigEntry<bool> _cancelOnManualBlock;

		private static ConfigEntry<int> _holdAfterPerfectMs;

		private static ConfigEntry<string> _afterPerfect;

		private static ConfigEntry<float> _pressTailMs;

		private static ConfigEntry<bool> _showCombatText;

		private static ConfigEntry<bool> _showVanillaNumbers;

		private static ConfigEntry<string> _fontName;

		private static ConfigEntry<string> _labelFontName;

		private static ConfigEntry<int> _baseFontSize;

		private static ConfigEntry<float> _critScale;

		private static ConfigEntry<float> _takenX;

		private static ConfigEntry<float> _takenY;

		private static ConfigEntry<float> _dealtX;

		private static ConfigEntry<float> _dealtY;

		private static ConfigEntry<bool> _testCombatText;

		private static ConfigEntry<bool> _showWeaponIcon;

		private static ConfigEntry<string> _animStyle;

		private static ConfigEntry<string> _animDirection;

		private static ConfigEntry<float> _scrollSize;

		private static ConfigEntry<float> _lineSpacing;

		private static ConfigEntry<int> _maxLines;

		private static ConfigEntry<float> _fadeStart;

		private static ConfigEntry<float> _textDuration;

		private static ConfigEntry<bool> _showBlockText;

		private static ConfigEntry<bool> _englishLabels;

		private static ConfigEntry<float> _flashX;

		private static ConfigEntry<float> _flashY;

		private static ConfigEntry<float> _flashSize;

		private static ConfigEntry<float> _flashDuration;

		private static ConfigEntry<string> _flashAnimation;

		private static ConfigEntry<string> _flashColor;

		private static ConfigEntry<bool> _showBuffPanel;

		private static ConfigEntry<bool> _buffOnlyBlock;

		private static ConfigEntry<bool> _showCounterWindow;

		private static ConfigEntry<bool> _logPerfectBuffs;

		private static ConfigEntry<float> _tempBuffX;

		private static ConfigEntry<float> _tempBuffY;

		private static ConfigEntry<float> _buffScale;

		private static ConfigEntry<float> _buffLinger;

		private static ConfigEntry<int> _buffMaxRows;

		private static readonly List<BuffRow> BuffRows = new List<BuffRow>();

		private static readonly Dictionary<string, float> SeenEffects = new Dictionary<string, float>();

		private static GUIStyle _buffNameStyle;

		private static GUIStyle _buffTimeStyle;

		private static GUIStyle _counterStyle;

		private static float _lastPerfectAt = -999f;

		private static float _adrenalineGain;

		private static float _adrenalineGainAt = -999f;

		private static float _adrenalineBefore = -1f;

		private static float _adrenalinePrev = -1f;

		private static MethodInfo _localizeMethod;

		private static bool _localizeProbed;

		private static int _pressStartFrame = -1;

		private static float _pressUntil;

		private static float _holdUntil;

		private static bool _postPerfect;

		private static bool _toggleOnPending;

		private static bool _toggleOffPending;

		private static bool _wantBlockPrev;

		private static readonly List<HitText> HitTexts = new List<HitText>();

		private static readonly Dictionary<string, Font> Fonts = new Dictionary<string, Font>();

		private static GUIStyle _numberStyle;

		private static GUIStyle _labelStyle;

		private static bool _attackCancelLogged;

		private static bool _staggerBypassLogged;

		private static int _perfectTotal;

		private static int _earlyTotal;

		private static int _lateTotal;

		private static int _damageLogged;

		private static readonly List<Flash> Flashes = new List<Flash>();

		private static GUIStyle _flashStyle;

		private static readonly FieldInfo CurrentAttackField = typeof(Humanoid).GetField("m_currentAttack", BindingFlags.Instance | BindingFlags.NonPublic);

		private static readonly FieldInfo PreviousAttackField = typeof(Humanoid).GetField("m_previousAttack", BindingFlags.Instance | BindingFlags.NonPublic);

		private static readonly FieldInfo CurrentAttackSecondaryField = typeof(Humanoid).GetField("m_currentAttackIsSecondary", BindingFlags.Instance | BindingFlags.NonPublic);

		private static bool _cancelSuppress;

		private static bool _forceBlockState;

		private static float _cancelStateAt;

		private static readonly Dictionary<string, Record> Records = new Dictionary<string, Record>();

		private static readonly Regex RecordBlock = new Regex("\\{[^{}]*\\}", RegexOptions.Compiled);

		private static readonly Regex FieldString = new Regex("\"prefab\"\\s*:\\s*\"([^\"]*)\"", RegexOptions.Compiled);

		private static readonly Regex FieldInt = new Regex("\"state\"\\s*:\\s*(-?\\d+)", RegexOptions.Compiled);

		private static readonly Regex FieldHits = new Regex("\"hits\"\\s*:\\s*\\[([^\\]]*)\\]", RegexOptions.Compiled);

		private static readonly Regex FieldLead = new Regex("\"lead\"\\s*:\\s*(-?[0-9.]+)", RegexOptions.Compiled);

		private static readonly Regex FieldLearned = new Regex("\"learned\"\\s*:\\s*(true|false)", RegexOptions.Compiled);

		private static readonly Regex FieldOk = new Regex("\"ok\"\\s*:\\s*(-?\\d+)", RegexOptions.Compiled);

		private static readonly Regex FieldEarly = new Regex("\"early\"\\s*:\\s*(-?\\d+)", RegexOptions.Compiled);

		private static readonly Regex FieldLate = new Regex("\"late\"\\s*:\\s*(-?\\d+)", RegexOptions.Compiled);

		private static readonly Dictionary<Character, Animator> Animators = new Dictionary<Character, Animator>();

		private static readonly List<Character> Stale = new List<Character>();

		private static bool _wantBlock;

		private static bool _simulated;

		private static string _wantKey;

		private static readonly FieldInfo BlockingField = typeof(Humanoid).GetField("m_blocking", BindingFlags.Instance | BindingFlags.NonPublic);

		internal static int PressFrames;

		internal static int ForcedReturns;

		private static readonly HashSet<string> InfoSeen = new HashSet<string>();

		private static readonly FieldInfo BlockTimerField = typeof(Humanoid).GetField("m_blockTimer", BindingFlags.Instance | BindingFlags.NonPublic);

		private static readonly MethodInfo StaggerBlockerMethod = typeof(Humanoid).GetMethod("GetCurrentBlocker", BindingFlags.Instance | BindingFlags.NonPublic);

		private static readonly HashSet<string> OsFontNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private static bool _osFontsRead;

		private static readonly string[] NumberFontChoices = new string[25]
		{
			"Norsebold", "Norse", "AveriaSerifLibre-Bold", "AveriaSerifLibre-Regular", "AveriaSerifLibre-Light", "AveriaSerifLibre-Italic", "AveriaSerifLibre-BoldItalic", "AveriaSerifLibre-LightItalic", "AveriaSansLibre-Bold", "AveriaSansLibre-Regular",
			"AveriaSansLibre-Light", "AveriaSansLibre-Italic", "AveriaSansLibre-BoldItalic", "AveriaSansLibre-LightItalic", "prstartk", "rune", "Impact", "Arial Black", "NotoSansSC-Regular", "NotoSansSC-Thin",
			"NotoSerifSC-Regular", "NotoSans-Regular", "NotoSerif-Regular", "Microsoft YaHei", "Arial"
		};

		private static readonly string[] LabelFontChoices = new string[17]
		{
			"Norsebold", "Norse", "AveriaSerifLibre-Bold", "AveriaSansLibre-Bold", "prstartk", "NotoSansSC-Regular", "NotoSansSC-Thin", "NotoSerifSC-Regular", "NotoSansJP-Regular", "NotoSansKR-Regular",
			"NotoSerifJP-Regular", "NotoSerifKR-Regular", "NotoSans-Regular", "NotoSerif-Regular", "Microsoft YaHei", "Impact", "Arial"
		};

		private static Dictionary<string, Font> _gameFonts;

		private static float _gameFontsScanAt = -999f;

		private static readonly Dictionary<int, bool> GlyphCache = new Dictionary<int, bool>();

		private static object _localizeInstance;

		private static readonly FieldInfo AttackChainLevelField = typeof(Attack).GetField("m_currentAttackCainLevel", BindingFlags.Instance | BindingFlags.NonPublic);

		private static bool _pendingTaken;

		private static bool _pendingCrit;

		private static int _pendingSize;

		private static Sprite _pendingIcon;

		private static string _pendingLabel = "";

		private static bool _pendingPlus;

		private static float _pendingAt = -999f;

		private static GUIStyle _avoidStyle;

		private static float _testNextAt;

		private static int _testTick;

		private static float _blockedValue = -1f;

		private static float _blockedAt = -999f;

		private static Dictionary<string, string> _lang = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

		private static string _languageName = "English";

		internal static bool CancelSuppress => _cancelSuppress;

		internal static bool ForceBlockState => _forceBlockState;

		private static string SavePath => Path.Combine(Paths.ConfigPath, "PerfectBlock", "learned.json");

		internal static bool WantBlock => _wantBlock;

		internal static bool Simulated => _simulated;

		internal static bool TestMode
		{
			get
			{
				if (_forceBlockTest != null)
				{
					return _forceBlockTest.Value;
				}
				return false;
			}
		}

		internal static bool LogAttemptsOn
		{
			get
			{
				if (_logAttempts != null)
				{
					return _logAttempts.Value;
				}
				return false;
			}
		}

		internal static string AfterPerfectMode
		{
			get
			{
				if (_afterPerfect == null || string.IsNullOrEmpty(_afterPerfect.Value))
				{
					return "Release";
				}
				return _afterPerfect.Value;
			}
		}

		internal static bool KeepMode => string.Equals(AfterPerfectMode, "Keep", StringComparison.OrdinalIgnoreCase);

		internal static float LeadMs
		{
			get
			{
				if (_leadMs == null)
				{
					return 70f;
				}
				return Mathf.Clamp(_leadMs.Value, 1f, 500f);
			}
		}

		internal static float TailMs
		{
			get
			{
				if (_pressTailMs == null)
				{
					return 150f;
				}
				return Mathf.Clamp(_pressTailMs.Value, 0f, 1000f);
			}
		}

		internal static float LeadSeconds => LeadMs / 1000f;

		internal static float TailSeconds => TailMs / 1000f;

		internal static int HoldMs
		{
			get
			{
				if (_holdAfterPerfectMs == null)
				{
					return 500;
				}
				return Mathf.Max(0, _holdAfterPerfectMs.Value);
			}
		}

		internal static bool ToggleMode
		{
			get
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer != (Object)null)
				{
					return localPlayer.ToggleBlock;
				}
				return false;
			}
		}

		internal static bool ToggleOnPending => _toggleOnPending;

		internal static bool ToggleOffPending => _toggleOffPending;

		internal static bool CombatTextEnabled
		{
			get
			{
				if (_showCombatText != null)
				{
					return _showCombatText.Value;
				}
				return false;
			}
		}

		internal static bool VanillaNumbersShown
		{
			get
			{
				if (_showVanillaNumbers != null)
				{
					return _showVanillaNumbers.Value;
				}
				return false;
			}
		}

		internal static bool EnglishLabels
		{
			get
			{
				if (_englishLabels != null)
				{
					return _englishLabels.Value;
				}
				return false;
			}
		}

		internal static bool ShowWeaponIcons
		{
			get
			{
				if (_showWeaponIcon != null)
				{
					return _showWeaponIcon.Value;
				}
				return true;
			}
		}

		internal static int CombatFontSize
		{
			get
			{
				if (_baseFontSize == null)
				{
					return 30;
				}
				return _baseFontSize.Value;
			}
		}

		internal static float BigHitScale
		{
			get
			{
				if (_critScale == null)
				{
					return 2.4f;
				}
				return _critScale.Value;
			}
		}

		internal static string LabelFontName
		{
			get
			{
				if (_labelFontName == null || string.IsNullOrEmpty(_labelFontName.Value))
				{
					return "Norsebold";
				}
				return _labelFontName.Value;
			}
		}

		internal static float TakenAnchorX
		{
			get
			{
				if (_takenX == null)
				{
					return 0.3f;
				}
				return Mathf.Clamp01(_takenX.Value);
			}
		}

		internal static float TakenAnchorY
		{
			get
			{
				if (_takenY == null)
				{
					return 0.6f;
				}
				return Mathf.Clamp01(_takenY.Value);
			}
		}

		internal static float DealtAnchorX
		{
			get
			{
				if (_dealtX == null)
				{
					return 0.7f;
				}
				return Mathf.Clamp01(_dealtX.Value);
			}
		}

		internal static float DealtAnchorY
		{
			get
			{
				if (_dealtY == null)
				{
					return 0.6f;
				}
				return Mathf.Clamp01(_dealtY.Value);
			}
		}

		internal static void ClearCancelState()
		{
			_cancelSuppress = false;
			_forceBlockState = false;
		}

		internal static bool BlockingFieldIsSet(Character c)
		{
			try
			{
				if (BlockingField == null || (Object)(object)c == (Object)null)
				{
					return false;
				}
				return (bool)BlockingField.GetValue(c);
			}
			catch
			{
				return false;
			}
		}

		private void Awake()
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected O, but got Unknown
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Expected O, but got Unknown
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Expected O, but got Unknown
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Expected O, but got Unknown
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Expected O, but got Unknown
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Expected O, but got Unknown
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Expected O, but got Unknown
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c1: Expected O, but got Unknown
			//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f6: Expected O, but got Unknown
			//IL_0233: Unknown result type (might be due to invalid IL or missing references)
			//IL_023d: Expected O, but got Unknown
			//IL_027a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0284: Expected O, but got Unknown
			//IL_02c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cb: Expected O, but got Unknown
			//IL_0308: Unknown result type (might be due to invalid IL or missing references)
			//IL_0312: Expected O, but got Unknown
			//IL_0373: Unknown result type (might be due to invalid IL or missing references)
			//IL_037d: Expected O, but got Unknown
			//IL_03ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b6: Expected O, but got Unknown
			//IL_03e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03eb: Expected O, but got Unknown
			//IL_0416: Unknown result type (might be due to invalid IL or missing references)
			//IL_0420: Expected O, but got Unknown
			//IL_0459: Unknown result type (might be due to invalid IL or missing references)
			//IL_0463: Expected O, but got Unknown
			//IL_04ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b6: Expected O, but got Unknown
			//IL_04e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_04eb: Expected O, but got Unknown
			//IL_0516: Unknown result type (might be due to invalid IL or missing references)
			//IL_0520: Expected O, but got Unknown
			//IL_054b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0555: Expected O, but got Unknown
			//IL_0580: Unknown result type (might be due to invalid IL or missing references)
			//IL_058a: Expected O, but got Unknown
			//IL_05c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d1: Expected O, but got Unknown
			//IL_060e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0618: Expected O, but got Unknown
			//IL_0655: Unknown result type (might be due to invalid IL or missing references)
			//IL_065f: Expected O, but got Unknown
			//IL_069c: Unknown result type (might be due to invalid IL or missing references)
			//IL_06a6: Expected O, but got Unknown
			//IL_06d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_06e1: Expected O, but got Unknown
			//IL_070c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0716: Expected O, but got Unknown
			//IL_074e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0758: Expected O, but got Unknown
			//IL_078c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0796: Expected O, but got Unknown
			//IL_07d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_07dd: Expected O, but got Unknown
			//IL_0808: Unknown result type (might be due to invalid IL or missing references)
			//IL_0812: Expected O, but got Unknown
			//IL_084a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0854: Expected O, but got Unknown
			//IL_0891: Unknown result type (might be due to invalid IL or missing references)
			//IL_089b: Expected O, but got Unknown
			//IL_08d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_08e2: Expected O, but got Unknown
			//IL_091f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0929: Expected O, but got Unknown
			//IL_0966: Unknown result type (might be due to invalid IL or missing references)
			//IL_0970: Expected O, but got Unknown
			//IL_0a28: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a32: Expected O, but got Unknown
			//IL_0ac6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0ad0: Expected O, but got Unknown
			//IL_0b0d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0b17: Expected O, but got Unknown
			//IL_0b54: Unknown result type (might be due to invalid IL or missing references)
			//IL_0b5e: Expected O, but got Unknown
			//IL_0b90: Unknown result type (might be due to invalid IL or missing references)
			//IL_0b9a: Expected O, but got Unknown
			//IL_0bd7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0be1: Expected O, but got Unknown
			//IL_0c1e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0c28: Expected O, but got Unknown
			//IL_0c53: Unknown result type (might be due to invalid IL or missing references)
			//IL_0c5d: Expected O, but got Unknown
			//IL_0c88: Unknown result type (might be due to invalid IL or missing references)
			//IL_0c92: Expected O, but got Unknown
			//IL_0ccf: Unknown result type (might be due to invalid IL or missing references)
			//IL_0cd9: Expected O, but got Unknown
			//IL_0d16: Unknown result type (might be due to invalid IL or missing references)
			//IL_0d20: Expected O, but got Unknown
			//IL_0d52: Unknown result type (might be due to invalid IL or missing references)
			//IL_0d5c: Expected O, but got Unknown
			//IL_0d99: Unknown result type (might be due to invalid IL or missing references)
			//IL_0da3: Expected O, but got Unknown
			_instance = this;
			LoadLanguage();
			_autoBlock = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enable Auto Block", true, new ConfigDescription(T("EnableAutoBlock", "Take over the block key when an attack the mod has learned is about to land. On by default; turning it off keeps learning and logging running."), (AcceptableValueBase)null, Array.Empty<object>()));
			_detectRange = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Detect Range", 10f, new ConfigDescription(T("DetectRange", "Only enemies within this many metres (m) are watched and blocked against."), (AcceptableValueBase)(object)new AcceptableValueRange<float>(3f, 60f), Array.Empty<object>()));
			_logLearning = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Log Learning", true, new ConfigDescription(T("LogLearning", "Write one log line per learned attack, with the sample count and spread."), (AcceptableValueBase)null, Array.Empty<object>()));
			_logAttempts = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Log Attempts", true, new ConfigDescription(T("LogAttempts", "Log every simulated block and its result. Noisy; for tuning."), (AcceptableValueBase)null, Array.Empty<object>()));
			_forceBlockTest = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Force Block Test", false, new ConfigDescription(T("ForceBlockTest", "Diagnostic only: hold the block key on every frame, ignoring enemies and learning."), (AcceptableValueBase)null, Array.Empty<object>()));
			_showVanillaNumbers = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Show Vanilla Numbers", false, new ConfigDescription(T("VanillaNumbers", "Keep the game's own floating damage numbers for hits our text also draws. Off by default: ours replaces them. Mining, chopping and building hits are taken over too, so this switch covers them as well."), (AcceptableValueBase)null, Array.Empty<object>()));
			_testCombatText = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Test All UI", false, new ConfigDescription(T("TestCombat", "Diagnostic: spawn a real batch of entries every 0.8 s (taken/dealt/crit numbers, block and parry text, sometimes the flash) through the normal pipeline, so style and direction changes show up immediately. Only runs inside a world."), (AcceptableValueBase)null, Array.Empty<object>()));
			_releaseOnAttack = ((BaseUnityPlugin)this).Config.Bind<bool>("Feedback", "Release Block On Attack", true, new ConfigDescription(T("ReleaseOnAttack", "After a perfect block, release the forced block as soon as you attack, until the next automatic block."), (AcceptableValueBase)null, Array.Empty<object>()));
			_showFlash = ((BaseUnityPlugin)this).Config.Bind<bool>("Feedback", "Show Perfect Flash", true, new ConfigDescription(T("ShowFlash", "Show PERFECT BLOCK in the middle of the screen when a perfect block lands; several in a row stack upwards."), (AcceptableValueBase)null, Array.Empty<object>()));
			_flashX = ((BaseUnityPlugin)this).Config.Bind<float>("Feedback", "Flash Position X", 0.5f, new ConfigDescription(T("FlashX", "PERFECT BLOCK: horizontal anchor, 0 = left edge, 1 = right edge."), (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			_flashY = ((BaseUnityPlugin)this).Config.Bind<float>("Feedback", "Flash Position Y", 0.216f, new ConfigDescription(T("FlashY", "PERFECT BLOCK: vertical anchor, 0 = top, 1 = bottom."), (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			_flashSize = ((BaseUnityPlugin)this).Config.Bind<float>("Feedback", "Flash Font Size", 6f, new ConfigDescription(T("FlashSize", "PERFECT BLOCK: font size as a percentage of the screen height (5.5 = the old fixed value)."), (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 14f), Array.Empty<object>()));
			_flashDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Feedback", "Flash Duration (s)", 2f, new ConfigDescription(T("FlashDuration", "How long the PERFECT BLOCK text stays on screen."), (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.4f, 2.5f), Array.Empty<object>()));
			_flashAnimation = ((BaseUnityPlugin)this).Config.Bind<string>("Feedback", "Flash Animation", "Punch", new ConfigDescription(T("FlashAnimation", "Animation of the PERFECT BLOCK text. Punch = slam in huge and snap back like a fighting game hit (default, the author's pick). Pop = drop in and fade (the old look). Slam = fall from above with a shake on impact. Slide = fly in from the right with after-images. Shake = sit still then rattle and settle."), (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[5] { "Punch", "Pop", "Slam", "Slide", "Shake" }), Array.Empty<object>()));
			_flashColor = ((BaseUnityPlugin)this).Config.Bind<string>("Feedback", "Flash Color", "#FFD21A", new ConfigDescription(T("FlashColor", "Colour of the PERFECT BLOCK text, HTML hex (#RRGGBB)."), (AcceptableValueBase)null, Array.Empty<object>()));
			_reliable = ((BaseUnityPlugin)this).Config.Bind<bool>("Feedback", "Reliable Perfect Block", true, new ConfigDescription(T("Reliable", "On a perfect block, ignore the vanilla rule that a hit bigger than your block armor plus stagger threshold breaks the block (what the ReliableBlock mod does, but only on perfect blocks)."), (AcceptableValueBase)null, Array.Empty<object>()));
			_cancelOnManualBlock = ((BaseUnityPlugin)this).Config.Bind<bool>("Feedback", "Cancel Swing On Manual Block", true, new ConfigDescription(T("CancelManual", "Pressing block while you are mid-swing cancels the swing so the block starts immediately (AttackCancel behaviour)."), (AcceptableValueBase)null, Array.Empty<object>()));
			_holdAfterPerfectMs = ((BaseUnityPlugin)this).Config.Bind<int>("Feedback", "Hold After Perfect (ms)", 500, new ConfigDescription(T("HoldAfterPerfect", "After a perfect block, keep holding the block for this many milliseconds before letting go. 0-150 behaves the same as 'Press Tail Ms' (the press already stays down that long); higher values keep the shield up longer. 500 is the value the author tuned with."), (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1000), Array.Empty<object>()));
			_afterPerfect = ((BaseUnityPlugin)this).Config.Bind<string>("Feedback", "After Perfect Block", "Release", new ConfigDescription(T("AfterPerfect", "What to do after a perfect block. Release = hold for 'Hold After Perfect (ms)', then let go. Keep = keep blocking until you attack or press block yourself (room for a future parry)."), (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[2] { "Release", "Keep" }), Array.Empty<object>()));
			_showBuffPanel = ((BaseUnityPlugin)this).Config.Bind<bool>("Buff Panel", "Show Buff Panel", true, new ConfigDescription(T("ShowBuffPanel", "Show what a perfect block gave you: the status effects that appeared right after it (icon, localised name, remaining time, stacks), plus the adrenaline it added."), (AcceptableValueBase)null, Array.Empty<object>()));
			_buffOnlyBlock = ((BaseUnityPlugin)this).Config.Bind<bool>("Buff Panel", "Only Buffs From Perfect Block", true, new ConfigDescription(T("BuffOnlyBlock", "On = only effects that appeared within a moment of the perfect block. Off = every status effect currently on you."), (AcceptableValueBase)null, Array.Empty<object>()));
			_showCounterWindow = ((BaseUnityPlugin)this).Config.Bind<bool>("Buff Panel", "Show Counter Window", true, new ConfigDescription(T("ShowCounterWindow", "After a perfect block, show the counter window (the time the block stays held and your swings keep their chain) and the current attack chain stage."), (AcceptableValueBase)null, Array.Empty<object>()));
			_logPerfectBuffs = ((BaseUnityPlugin)this).Config.Bind<bool>("Buff Panel", "Log Perfect Block Buffs", true, new ConfigDescription(T("BuffLog", "Diagnostic: write the status effects seen right after every perfect block, with their remaining time, to the log. Use it to find out which buff the block actually gives."), (AcceptableValueBase)null, Array.Empty<object>()));
			_tempBuffX = ((BaseUnityPlugin)this).Config.Bind<float>("Buff Panel", "Temp Buff X", 0.624f, new ConfigDescription(T("TempBuffX", "Temporary buffs (short, cooldown-driven combat buffs such as Bloodrage): horizontal anchor, 0 = left edge, 1 = right edge."), (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			_tempBuffY = ((BaseUnityPlugin)this).Config.Bind<float>("Buff Panel", "Temp Buff Y", 0.4f, new ConfigDescription(T("TempBuffY", "Temporary buffs: vertical anchor, 0 = top, 1 = bottom."), (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			_buffScale = ((BaseUnityPlugin)this).Config.Bind<float>("Buff Panel", "Buff Scale", 1.5f, new ConfigDescription(T("BuffScale", "Size multiplier of the buff panel."), (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.6f, 1.8f), Array.Empty<object>()));
			_buffLinger = ((BaseUnityPlugin)this).Config.Bind<float>("Buff Panel", "Buff Linger (s)", 1f, new ConfigDescription(T("BuffLinger", "How long a buff row stays after its effect ran out."), (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>()));
			_buffMaxRows = ((BaseUnityPlugin)this).Config.Bind<int>("Buff Panel", "Buff Max Rows", 1, new ConfigDescription(T("BuffMaxRows", "Maximum buff rows shown at once; the newest win."), (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 8), Array.Empty<object>()));
			_showCombatText = ((BaseUnityPlugin)this).Config.Bind<bool>("Combat", "Show Combat Text", true, new ConfigDescription(T("ShowCombat", "Draw damage numbers: damage you take on the left, damage you deal on the right."), (AcceptableValueBase)null, Array.Empty<object>()));
			_fontName = ((BaseUnityPlugin)this).Config.Bind<string>("Combat", "Font", "Norsebold", new ConfigDescription(T("Font", "Font used by the combat text and the PERFECT BLOCK flash. Norsebold (first in the list) is the game's own sign and runestone face and is what the author uses - strongly recommended. Everything starting with Averia / Noto / prstartk is shipped with the game too, so nothing has to be installed; Impact and Arial come from Windows."), (AcceptableValueBase)(object)new AcceptableValueList<string>(NumberFontChoices), Array.Empty<object>()));
			_baseFontSize = ((BaseUnityPlugin)this).Config.Bind<int>("Combat", "Base Font Size", 30, new ConfigDescription(T("BaseSize", "Font size of a normal damage number."), (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 60), Array.Empty<object>()));
			_critScale = ((BaseUnityPlugin)this).Config.Bind<float>("Combat", "Big Hit Scale", 2.4f, new ConfigDescription(T("CritScale", "Size multiplier for the last hit of a combo or a doubled hit."), (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 3f), Array.Empty<object>()));
			_showWeaponIcon = ((BaseUnityPlugin)this).Config.Bind<bool>("Combat", "Show Weapon Icon", true, new ConfigDescription(T("WeaponIcon", "Draw the weapon icon in front of every damage number: your weapon for damage you deal, the attacker's weapon for damage you take."), (AcceptableValueBase)null, Array.Empty<object>()));
			_labelFontName = ((BaseUnityPlugin)this).Config.Bind<string>("Combat", "Label Font", "Norsebold", new ConfigDescription(T("LabelFont", "Font for the damage-type label in brackets. Norsebold (first in the list) is the game's own sign face and the recommended one; it only covers English, so Chinese labels fall back to the game's Noto face automatically."), (AcceptableValueBase)(object)new AcceptableValueList<string>(LabelFontChoices), Array.Empty<object>()));
			_takenX = ((BaseUnityPlugin)this).Config.Bind<float>("Combat", "Taken Position X", 0.3f, new ConfigDescription(T("TakenX", "Damage you take: horizontal anchor, 0 = left edge, 1 = right edge."), (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			_takenY = ((BaseUnityPlugin)this).Config.Bind<float>("Combat", "Taken Position Y", 0.6f, new ConfigDescription(T("TakenY", "Damage you take: vertical anchor, 0 = top, 1 = bottom."), (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			_dealtX = ((BaseUnityPlugin)this).Config.Bind<float>("Combat", "Dealt Position X", 0.7f, new ConfigDescription(T("DealtX", "Damage you deal: horizontal anchor, 0 = left edge, 1 = right edge."), (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			_dealtY = ((BaseUnityPlugin)this).Config.Bind<float>("Combat", "Dealt Position Y", 0.6f, new ConfigDescription(T("DealtY", "Damage you deal: vertical anchor, 0 = top, 1 = bottom."), (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			_animStyle = ((BaseUnityPlugin)this).Config.Bind<string>("Combat", "Animation Style", "Static", new ConfigDescription(T("AnimStyle", "Scrolling-text animation, ported from the Parrot addon: Straight, Parabola, Semicircle, Pow, Pow2, Static, Static2, Rainbow, Horizontal, Action, Action Sticky, Angled, Angled2, Sprinkler, Sprinkler2."), (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[15]
			{
				"Straight", "Parabola", "Semicircle", "Pow", "Pow2", "Static", "Static2", "Rainbow", "Horizontal", "Action",
				"Action Sticky", "Angled", "Angled2", "Sprinkler", "Sprinkler2"
			}), Array.Empty<object>()));
			_animDirection = ((BaseUnityPlugin)this).Config.Bind<string>("Combat", "Scroll Direction", "UP;RIGHT", new ConfigDescription(T("AnimDir", "Which way the numbers travel, same scheme as Parrot: UP/DOWN plus LEFT/RIGHT/CENTER/ALT (ALT = alternating)."), (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[11]
			{
				"UP;RIGHT", "UP;LEFT", "UP;CENTER", "UP;ALT", "DOWN;RIGHT", "DOWN;LEFT", "DOWN;CENTER", "DOWN;ALT", "LEFT", "RIGHT",
				"ALT"
			}), Array.Empty<object>()));
			_scrollSize = ((BaseUnityPlugin)this).Config.Bind<float>("Combat", "Scroll Size", 1f, new ConfigDescription(T("ScrollSize", "Travel distance multiplier. The base distance is 40 px per unit, so this is pixels, not font sizes."), (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.2f, 4f), Array.Empty<object>()));
			_lineSpacing = ((BaseUnityPlugin)this).Config.Bind<float>("Combat", "Line Spacing", 1f, new ConfigDescription(T("LineSpacing", "Vertical distance between stacked lines, as a multiple of one line slot."), (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 3f), Array.Empty<object>()));
			_maxLines = ((BaseUnityPlugin)this).Config.Bind<int>("Combat", "Max Lines", 8, new ConfigDescription(T("MaxLines", "How many lines are shown at once in the Static / Static2 styles. The other styles fade the oldest line out instead of capping the count."), (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20), Array.Empty<object>()));
			_fadeStart = ((BaseUnityPlugin)this).Config.Bind<float>("Combat", "Fade Start (%)", 60f, new ConfigDescription(T("FadeStart", "Percentage of the lifetime after which the text starts fading out."), (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 95f), Array.Empty<object>()));
			_textDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Combat", "Text Duration (s)", 1.2f, new ConfigDescription(T("TextDuration", "How long one number lives."), (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.4f, 4f), Array.Empty<object>()));
			_showBlockText = ((BaseUnityPlugin)this).Config.Bind<bool>("Combat", "Show Block Text", true, new ConfigDescription(T("ShowBlockText", "Show the avoidance text in the taken column, e.g. \"Block(12)\" for an ordinary block and \"Parry(0.5)\" for a perfect one (Parrot's avoid events). The value is the game's own blocked number."), (AcceptableValueBase)null, Array.Empty<object>()));
			_englishLabels = ((BaseUnityPlugin)this).Config.Bind<bool>("Combat", "English Labels", true, new ConfigDescription(T("EnglishLabels", "Draw the block line and the damage-type tag in English (Block / Parry / Slash / Blunt ...) instead of the game language. Useful for comparing the typography, since Latin words sit differently next to a condensed number font."), (AcceptableValueBase)null, Array.Empty<object>()));
			_leadMs = ((BaseUnityPlugin)this).Config.Bind<float>("Learning", "Lead Ms", 70f, new ConfigDescription(T("LeadMs", "How early (milliseconds) to press block before the learned landing point. Fixed on purpose: the block timer is counted from the press, so a larger lead pushes you out of the 0.25 s perfect window."), (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 300f), Array.Empty<object>()));
			_pressTailMs = ((BaseUnityPlugin)this).Config.Bind<float>("Learning", "Press Tail Ms", 150f, new ConfigDescription(T("PressTail", "How long (milliseconds) to keep the simulated block down after the learned landing point. Covers prediction error; a late press is still an ordinary block instead of a free hit. Press length = Lead Ms + Press Tail Ms."), (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 500f), Array.Empty<object>()));
			_minSamples = ((BaseUnityPlugin)this).Config.Bind<int>("Learning", "Min Samples", 5, new ConfigDescription(T("MinSamples", "Samples needed before an attack counts as learned."), (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20), Array.Empty<object>()));
			_maxSpread = ((BaseUnityPlugin)this).Config.Bind<float>("Learning", "Max Spread", 0.05f, new ConfigDescription(T("MaxSpread", "Largest allowed spread of the recorded landing points, as animation progress (0.05 = 5%). Wider = treated as an unstable attack and ignored."), (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.005f, 0.3f), Array.Empty<object>()));
			LoadRecords();
			ApplyPatches();
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Perfect Block " + ((BaseUnityPlugin)this).Info.Metadata.Version?.ToString() + " loaded. Learned attacks: " + Records.Count + ", auto block: " + _autoBlock.Value + ", lead: " + LeadMs + " ms, tail: " + TailMs + " ms, after perfect: " + AfterPerfectMode + " (" + HoldMs + " ms hold)"));
		}

		private void ApplyPatches()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			Harmony harmony;
			try
			{
				harmony = new Harmony("ChineseCanFly.PerfectBlock");
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Harmony unavailable, mod does nothing: " + ex));
				return;
			}
			TryPatch(harmony, typeof(PatchAttackLanded), T("PatchLearn", "attack learning"));
			TryPatch(harmony, typeof(PatchSetControls), T("PatchSetControls", "input boundary"));
			TryPatch(harmony, typeof(PatchStaggerBypass), T("PatchStagger", "reliable perfect block"));
			TryPatch(harmony, typeof(PatchPlayerInAttack), T("PatchInAttack", "attack cancel transition"));
			TryPatch(harmony, typeof(PatchIsBlocking), T("PatchIsBlocking", "block during transition"));
			TryPatch(harmony, typeof(PatchBlockKey), T("PatchBlockKey", "block key"));
			TryPatch(harmony, typeof(PatchBlockResult), T("PatchBlockResult", "block result"));
			TryPatch(harmony, typeof(PatchBlockedText), T("PatchBlocked", "blocked number"));
			TryPatch(harmony, typeof(PatchObjectDamage), T("PatchObjects", "object damage"));
			TryPatch(harmony, typeof(PatchDamageTaken), T("PatchDamage", "damage taken"));
			TryPatch(harmony, typeof(PatchVanillaDamageText), T("PatchVanillaText", "vanilla damage text"));
			TryPatch(harmony, typeof(PatchCombatText), T("PatchCombat", "combat text"));
		}

		private void TryPatch(Harmony harmony, Type patch, string label)
		{
			try
			{
				harmony.PatchAll(patch);
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Patch applied: " + label));
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Patch failed (" + label + "): " + ex));
			}
		}

		private void Update()
		{
			//IL_0273: Unknown result type (might be due to invalid IL or missing references)
			//IL_027e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0283: Unknown result type (might be due to invalid IL or missing references)
			//IL_0288: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f2: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				_wantBlock = false;
				_wantKey = null;
				_simulated = false;
				TickBuffPanel();
				TickTestOverlay();
				if (_forceBlockTest != null && _forceBlockTest.Value)
				{
					_wantBlock = true;
					_simulated = true;
					PressFrames++;
					if (!_testLogged)
					{
						_testLogged = true;
						((BaseUnityPlugin)this).Logger.LogInfo((object)"TEST MODE on: forcing the block key every frame. The character should hold its block.");
					}
					return;
				}
				if (_testLogged)
				{
					_testLogged = false;
					((BaseUnityPlugin)this).Logger.LogInfo((object)"TEST MODE off.");
				}
				if (!_autoBlock.Value)
				{
					_postPerfect = false;
					return;
				}
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead())
				{
					_postPerfect = false;
					return;
				}
				if (_postPerfect)
				{
					bool num = ((Character)localPlayer).InAttack() || ZInput.GetButton("Attack") || ZInput.GetButton("JoyAttack");
					bool flag = ZInput.GetButtonDown("Block") || ZInput.GetButtonDown("JoyBlock");
					if (num && (KeepMode || _releaseOnAttack == null || _releaseOnAttack.Value))
					{
						EndAfterPerfect();
						if (BlockingField != null)
						{
							BlockingField.SetValue(localPlayer, false);
						}
						Diag("after perfect: released because you attacked.");
						return;
					}
					if (KeepMode)
					{
						if (!flag)
						{
							_wantBlock = true;
							_simulated = true;
							PressFrames++;
							return;
						}
						EndAfterPerfect();
						Diag("after perfect: released because you pressed block yourself.");
					}
					else
					{
						if (Time.time < _holdUntil)
						{
							_wantBlock = true;
							_simulated = true;
							PressFrames++;
							return;
						}
						EndAfterPerfect();
						Diag("after perfect: hold finished, releasing.");
					}
				}
				if (((Character)localPlayer).InAttack())
				{
					return;
				}
				if (((Character)localPlayer).IsBlocking() && !_wantBlockPrev)
				{
					LogOnce("[diag] player is holding block on their own; auto block stays idle until that block drops.");
					return;
				}
				float value = _detectRange.Value;
				float num2 = value * value;
				float leadSeconds = LeadSeconds;
				float tailSeconds = TailSeconds;
				List<Character> allCharacters = Character.GetAllCharacters();
				for (int i = 0; i < allCharacters.Count; i++)
				{
					Character val = allCharacters[i];
					if ((Object)(object)val == (Object)null || (Object)(object)val == (Object)(object)localPlayer || val.IsPlayer() || val.IsTamed() || val.IsDead() || !val.InAttack())
					{
						continue;
					}
					Vector3 val2 = ((Component)val).transform.position - ((Component)localPlayer).transform.position;
					if (((Vector3)(ref val2)).sqrMagnitude > num2)
					{
						continue;
					}
					Animator val3 = AnimatorOf(val);
					if ((Object)(object)val3 == (Object)null)
					{
						continue;
					}
					AnimatorStateInfo currentAnimatorStateInfo = val3.GetCurrentAnimatorStateInfo(0);
					if (((AnimatorStateInfo)(ref currentAnimatorStateInfo)).length <= 0.01f)
					{
						continue;
					}
					string text = Key(val, currentAnimatorStateInfo);
					if (!Records.TryGetValue(text, out var value2) || !value2.learned)
					{
						continue;
					}
					float num3 = Normalised(currentAnimatorStateInfo);
					if (num3 < 0f)
					{
						continue;
					}
					float num4 = (Average(value2.hits) - num3) * ((AnimatorStateInfo)(ref currentAnimatorStateInfo)).length;
					if (!(num4 <= leadSeconds) || !(num4 >= 0f - tailSeconds))
					{
						continue;
					}
					if (!_wantBlockPrev)
					{
						PressFrames = 0;
						_pressStartFrame = Time.frameCount;
						if (_logAttempts != null && _logAttempts.Value)
						{
							((BaseUnityPlugin)this).Logger.LogInfo((object)$"attempt: {text}, press at {num4 * 1000f:F0} ms before the learned hit (lead {LeadMs:F0}, tail {TailMs:F0})");
						}
					}
					_wantBlock = true;
					_simulated = true;
					_wantKey = text;
					PressFrames++;
					_pressUntil = Mathf.Max(_pressUntil, Time.time + Mathf.Max(num4, 0f) + tailSeconds);
					break;
				}
				if (!_wantBlock && Time.time < _pressUntil)
				{
					_wantBlock = true;
					_simulated = true;
					PressFrames++;
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("update failed: " + ex));
			}
		}

		private void LateUpdate()
		{
			try
			{
				if (_wantBlock && !_wantBlockPrev)
				{
					_toggleOnPending = true;
					_pressStartFrame = Time.frameCount;
				}
				else if (!_wantBlock && _wantBlockPrev)
				{
					_toggleOnPending = false;
					if (ToggleMode)
					{
						_toggleOffPending = true;
					}
				}
				_wantBlockPrev = _wantBlock;
				if (!_wantBlock && (_cancelSuppress || _forceBlockState) && Time.time - _cancelStateAt > 1f)
				{
					ClearCancelState();
				}
			}
			catch
			{
			}
		}

		private void OnApplicationQuit()
		{
			SaveRecords();
		}

		internal static void LearnFrom(Humanoid attacker)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: 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_005a: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)attacker == (Object)null || ((Character)attacker).IsPlayer() || ((Character)attacker).IsTamed())
				{
					return;
				}
				Animator val = AnimatorOf((Character)(object)attacker);
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				AnimatorStateInfo currentAnimatorStateInfo = val.GetCurrentAnimatorStateInfo(0);
				float num = Normalised(currentAnimatorStateInfo);
				if (!(num < 0.02f) && !(num > 1.2f))
				{
					string text = Key((Character)(object)attacker, currentAnimatorStateInfo);
					if (!Records.TryGetValue(text, out var value))
					{
						value = new Record
						{
							prefab = PrefabName((Character)(object)attacker),
							stateHash = ((AnimatorStateInfo)(ref currentAnimatorStateInfo)).shortNameHash,
							leadMs = LeadMs
						};
						Records[text] = value;
					}
					float[] array = new float[Mathf.Min(value.hits.Length + 1, 20)];
					Array.Copy(value.hits, array, value.hits.Length);
					array[^1] = num;
					value.hits = array;
					bool learned = value.learned;
					value.learned = value.hits.Length >= ((_minSamples != null) ? _minSamples.Value : 5) && Spread(value.hits) <= ((_maxSpread != null) ? _maxSpread.Value : 0.05f);
					if (_logLearning != null && _logLearning.Value && (Object)(object)_instance != (Object)null)
					{
						((BaseUnityPlugin)_instance).Logger.LogInfo((object)$"[learn] {text} | samples {value.hits.Length} | hit {Average(value.hits):F3} | spread {Spread(value.hits):F3} | learned {value.learned}");
					}
					if (value.learned && !learned && (Object)(object)_instance != (Object)null)
					{
						((BaseUnityPlugin)_instance).Logger.LogInfo((object)("[learned] " + text + " is now stable enough to block against."));
						_instance.SaveRecords();
					}
				}
			}
			catch (Exception ex)
			{
				if ((Object)(object)_instance != (Object)null)
				{
					((BaseUnityPlugin)_instance).Logger.LogError((object)("learning failed: " + ex));
				}
			}
		}

		internal static void NotePerfect()
		{
			if (TestMode)
			{
				return;
			}
			if (_showFlash != null && _showFlash.Value)
			{
				AddFlash();
			}
			if (_simulated)
			{
				_perfectTotal++;
				PostStat();
				if (_wantKey != null && Records.TryGetValue(_wantKey, out var value))
				{
					value.successes++;
				}
				_postPerfect = true;
				_holdUntil = Time.time + (float)HoldMs / 1000f;
				_lastPerfectAt = Time.time;
				_adrenalineBefore = ((_adrenalinePrev >= 0f) ? _adrenalinePrev : AdrenalineOf(Player.m_localPlayer));
				if (_logPerfectBuffs != null && _logPerfectBuffs.Value)
				{
					LogPerfectBuffs();
				}
				Diag("perfect block #" + _perfectTotal);
			}
		}

		internal static void NoteEarly()
		{
			if (_simulated && !TestMode)
			{
				_earlyTotal++;
				Diag($"ordinary block: blockTimer {BlockTimerOf((Character)(object)Player.m_localPlayer):F3}s, blocker='{CurrentBlockerName((Humanoid)(object)Player.m_localPlayer)}' (perfect needs <0.25 s and a parry weapon)");
				PostStat();
				if (_wantKey != null && Records.TryGetValue(_wantKey, out var value))
				{
					value.early++;
				}
			}
		}

		private static string CurrentBlockerName(Humanoid humanoid)
		{
			try
			{
				if ((Object)(object)humanoid == (Object)null || StaggerBlockerMethod == null)
				{
					return "none";
				}
				object? obj = StaggerBlockerMethod.Invoke(humanoid, null);
				ItemData val = (ItemData)((obj is ItemData) ? obj : null);
				return (val != null) ? val.m_shared.m_name : "none";
			}
			catch
			{
				return "none";
			}
		}

		internal static void NoteLate(Character attacker)
		{
			//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_0039: Unknown result type (might be due to invalid IL or missing references)
			if (!_simulated || TestMode || (Object)(object)attacker == (Object)null)
			{
				return;
			}
			Animator val = AnimatorOf(attacker);
			if (!((Object)(object)val == (Object)null))
			{
				AnimatorStateInfo currentAnimatorStateInfo = val.GetCurrentAnimatorStateInfo(0);
				if (Records.TryGetValue(Key(attacker, currentAnimatorStateInfo), out var value))
				{
					_lateTotal++;
					PostStat();
					value.late++;
				}
			}
		}

		private static void PostStat()
		{
			int num = _perfectTotal + _earlyTotal + _lateTotal;
			if (num != 0 && num % 10 == 0 && !((Object)(object)_instance == (Object)null))
			{
				((BaseUnityPlugin)_instance).Logger.LogInfo((object)$"[stat] {num} blocks: perfect={_perfectTotal} ordinary={_earlyTotal} late={_lateTotal}");
			}
		}

		private static void EndAfterPerfect()
		{
			_postPerfect = false;
			_holdUntil = 0f;
		}

		internal static float BlockTimerOf(Character c)
		{
			try
			{
				if ((Object)(object)c == (Object)null || BlockTimerField == null)
				{
					return -999f;
				}
				return (float)BlockTimerField.GetValue(c);
			}
			catch
			{
				return -999f;
			}
		}

		private static float Normalised(AnimatorStateInfo state)
		{
			float num = ((AnimatorStateInfo)(ref state)).normalizedTime;
			if (num < 0f)
			{
				return -1f;
			}
			if (num > 1.5f)
			{
				num = Mathf.Repeat(num, 1f);
			}
			return num;
		}

		private static string Key(Character c, AnimatorStateInfo state)
		{
			return PrefabName(c) + "|" + ((AnimatorStateInfo)(ref state)).shortNameHash;
		}

		private static Animator AnimatorOf(Character c)
		{
			if ((Object)(object)c == (Object)null)
			{
				return null;
			}
			if (Animators.TryGetValue(c, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			value = ((Component)c).GetComponentInChildren<Animator>();
			if ((Object)(object)value == (Object)null)
			{
				return null;
			}
			if (Animators.Count > 400)
			{
				Stale.Clear();
				foreach (KeyValuePair<Character, Animator> animator in Animators)
				{
					if ((Object)(object)animator.Key == (Object)null || (Object)(object)animator.Value == (Object)null)
					{
						Stale.Add(animator.Key);
					}
				}
				for (int i = 0; i < Stale.Count; i++)
				{
					Animators.Remove(Stale[i]);
				}
			}
			Animators[c] = value;
			return value;
		}

		private static string PrefabName(Character c)
		{
			string text = ((Object)((Component)c).gameObject).name;
			if (text.EndsWith("(Clone)", StringComparison.Ordinal))
			{
				text = text.Substring(0, text.Length - 7);
			}
			return text.Trim();
		}

		private static float Average(float[] values)
		{
			if (values == null || values.Length == 0)
			{
				return 0f;
			}
			float num = 0f;
			for (int i = 0; i < values.Length; i++)
			{
				num += values[i];
			}
			return num / (float)values.Length;
		}

		private static float Spread(float[] values)
		{
			if (values == null || values.Length == 0)
			{
				return 0f;
			}
			float num = values[0];
			float num2 = values[0];
			for (int i = 1; i < values.Length; i++)
			{
				if (values[i] < num)
				{
					num = values[i];
				}
				if (values[i] > num2)
				{
					num2 = values[i];
				}
			}
			return num2 - num;
		}

		private static void LoadRecords()
		{
			try
			{
				string savePath = SavePath;
				if (!File.Exists(savePath))
				{
					return;
				}
				string input = File.ReadAllText(savePath, Encoding.UTF8);
				int num = 0;
				foreach (Match item in RecordBlock.Matches(input))
				{
					string value = item.Value;
					Match match = FieldString.Match(value);
					Match match2 = FieldInt.Match(value);
					if (!match.Success || !match2.Success)
					{
						continue;
					}
					Record record = new Record
					{
						prefab = match.Groups[1].Value,
						stateHash = int.Parse(match2.Groups[1].Value, CultureInfo.InvariantCulture)
					};
					Match match3 = FieldHits.Match(value);
					if (match3.Success)
					{
						List<float> list = new List<float>();
						string[] array = match3.Groups[1].Value.Split(new char[1] { ',' });
						for (int i = 0; i < array.Length; i++)
						{
							if (float.TryParse(array[i].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
							{
								list.Add(result);
							}
						}
						record.hits = list.ToArray();
					}
					Match match4 = FieldLead.Match(value);
					if (match4.Success)
					{
						float.TryParse(match4.Groups[1].Value, NumberStyles.Float, CultureInfo.InvariantCulture, out record.leadMs);
					}
					Match match5 = FieldLearned.Match(value);
					record.learned = match5.Success && match5.Groups[1].Value == "true";
					Match match6 = FieldOk.Match(value);
					if (match6.Success)
					{
						int.TryParse(match6.Groups[1].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out record.successes);
					}
					Match match7 = FieldEarly.Match(value);
					if (match7.Success)
					{
						int.TryParse(match7.Groups[1].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out record.early);
					}
					Match match8 = FieldLate.Match(value);
					if (match8.Success)
					{
						int.TryParse(match8.Groups[1].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out record.late);
					}
					Records[record.prefab + "|" + record.stateHash] = record;
					num++;
				}
				if ((Object)(object)_instance != (Object)null)
				{
					((BaseUnityPlugin)_instance).Logger.LogInfo((object)("Loaded " + num + " learned attack(s) from " + savePath));
				}
			}
			catch (Exception ex)
			{
				if ((Object)(object)_instance != (Object)null)
				{
					((BaseUnityPlugin)_instance).Logger.LogError((object)("could not read " + SavePath + ": " + ex.Message));
				}
			}
		}

		private void SaveRecords()
		{
			try
			{
				string savePath = SavePath;
				string directoryName = Path.GetDirectoryName(savePath);
				if (!string.IsNullOrEmpty(directoryName) && !Directory.Exists(directoryName))
				{
					Directory.CreateDirectory(directoryName);
				}
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.Append("{\n");
				stringBuilder.Append("  \"version\": \"").Append(((BaseUnityPlugin)this).Info.Metadata.Version).Append("\",\n");
				stringBuilder.Append("  \"note\": \"Learned attack landing points. hits = animation progress (0-1) at the moment damage fires; lead is informational only (the press lead is a fixed config value now).\",\n");
				stringBuilder.Append("  \"records\": [");
				bool flag = true;
				foreach (Record value in Records.Values)
				{
					if (!flag)
					{
						stringBuilder.Append(',');
					}
					flag = false;
					stringBuilder.Append("\n    {\"prefab\": \"").Append(Escape(value.prefab)).Append("\"");
					stringBuilder.Append(", \"state\": ").Append(value.stateHash);
					stringBuilder.Append(", \"hits\": [");
					for (int i = 0; i < value.hits.Length; i++)
					{
						if (i > 0)
						{
							stringBuilder.Append(", ");
						}
						stringBuilder.Append(value.hits[i].ToString("0.####", CultureInfo.InvariantCulture));
					}
					stringBuilder.Append(']');
					stringBuilder.Append(", \"lead\": ").Append(value.leadMs.ToString("0.##", CultureInfo.InvariantCulture));
					stringBuilder.Append(", \"learned\": ").Append(value.learned ? "true" : "false");
					stringBuilder.Append(", \"ok\": ").Append(value.successes);
					stringBuilder.Append(", \"early\": ").Append(value.early);
					stringBuilder.Append(", \"late\": ").Append(value.late);
					stringBuilder.Append('}');
				}
				stringBuilder.Append("\n  ]\n}\n");
				File.WriteAllText(savePath, stringBuilder.ToString(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("could not write " + SavePath + ": " + ex.Message));
			}
		}

		private static string Escape(string value)
		{
			if (string.IsNullOrEmpty(value))
			{
				return "";
			}
			return value.Replace("\\", "\\\\").Replace("\"", "\\\"");
		}

		internal static void LogOnce(string message)
		{
			if (!((Object)(object)_instance == (Object)null) && InfoSeen.Add(message))
			{
				((BaseUnityPlugin)_instance).Logger.LogInfo((object)message);
			}
		}

		internal static void Diag(string message)
		{
			if (!((Object)(object)_instance == (Object)null) && _logAttempts != null && _logAttempts.Value)
			{
				((BaseUnityPlugin)_instance).Logger.LogInfo((object)("[diag] " + message));
			}
		}

		internal static void DiagDamage(Character victim, HitData hit, float delta)
		{
			if ((Object)(object)_instance == (Object)null || _damageLogged >= 5)
			{
				return;
			}
			_damageLogged++;
			try
			{
				Character attacker = hit.GetAttacker();
				((BaseUnityPlugin)_instance).Logger.LogInfo((object)string.Format("[diag] ApplyDamage #{0}: victim={1} player={2} attacker={3} total={4:F1} healthDelta={5:F1}", _damageLogged, ((Object)(object)victim != (Object)null) ? ((Object)((Component)victim).gameObject).name : "null", (Object)(object)victim != (Object)null && (Object)(object)victim == (Object)(object)Player.m_localPlayer, ((Object)(object)attacker != (Object)null) ? ((Object)((Component)attacker).gameObject).name : "null", hit.GetTotalDamage(), delta));
			}
			catch
			{
			}
		}

		internal static bool ShouldSkipStagger(Character target)
		{
			try
			{
				if (_reliable == null || !_reliable.Value)
				{
					return false;
				}
				if ((Object)(object)target == (Object)null || (Object)(object)target != (Object)(object)Player.m_localPlayer)
				{
					return false;
				}
				if (BlockTimerField == null)
				{
					return false;
				}
				float num = (float)BlockTimerField.GetValue(target);
				if (num < 0f || num >= 0.25f)
				{
					return false;
				}
				if (StaggerBlockerMethod != null)
				{
					object? obj = StaggerBlockerMethod.Invoke(target, null);
					ItemData val = (ItemData)((obj is ItemData) ? obj : null);
					if (val == null || val.m_shared.m_timedBlockBonus <= 1f)
					{
						return false;
					}
				}
				if (!_staggerBypassLogged)
				{
					_staggerBypassLogged = true;
					LogOnce("[diag] stagger rule bypassed on a perfect block.");
				}
				return true;
			}
			catch
			{
				return false;
			}
		}

		private static void CancelPlayerAttack(Player player)
		{
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (CurrentAttackField == null)
				{
					return;
				}
				object? value = CurrentAttackField.GetValue(player);
				Attack val = (Attack)((value is Attack) ? value : null);
				if (val != null)
				{
					val.Stop();
					if (PreviousAttackField != null)
					{
						PreviousAttackField.SetValue(player, val);
					}
					CurrentAttackField.SetValue(player, null);
					if (CurrentAttackSecondaryField != null)
					{
						CurrentAttackSecondaryField.SetValue(player, false);
					}
					_cancelSuppress = true;
					_forceBlockState = true;
					_cancelStateAt = Time.time;
					((Character)player).ForceJump(new Vector3(0f, -20f, 0f), true);
					if (!_attackCancelLogged)
					{
						_attackCancelLogged = true;
						LogOnce("[diag] attack cancelled (Stop + ForceJump) so the block could start.");
					}
				}
			}
			catch
			{
			}
		}

		internal static void ClearToggleOn()
		{
			_toggleOnPending = false;
		}

		internal static void ClearToggleOff()
		{
			_toggleOffPending = false;
		}

		internal static void ForceBlocking(bool value)
		{
			try
			{
				Player localPlayer = Player.m_localPlayer;
				if (!((Object)(object)localPlayer == (Object)null) && !(BlockingField == null))
				{
					BlockingField.SetValue(localPlayer, value);
				}
			}
			catch
			{
			}
		}

		internal static bool ShouldCancelManualBlock()
		{
			if (_cancelOnManualBlock != null)
			{
				return _cancelOnManualBlock.Value;
			}
			return false;
		}

		internal static void CancelSwingNow()
		{
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null) && !((Character)localPlayer).IsDead() && !(CurrentAttackField == null) && CurrentAttackField.GetValue(localPlayer) != null)
			{
				CancelPlayerAttack(localPlayer);
			}
		}

		private static void AddFlash()
		{
			Flashes.Add(new Flash
			{
				Started = Time.time
			});
			if (Flashes.Count > 6)
			{
				Flashes.RemoveAt(0);
			}
		}

		private static bool IsInstalledFont(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return false;
			}
			if (!_osFontsRead)
			{
				_osFontsRead = true;
				try
				{
					string[] oSInstalledFontNames = Font.GetOSInstalledFontNames();
					if (oSInstalledFontNames != null)
					{
						for (int i = 0; i < oSInstalledFontNames.Length; i++)
						{
							if (!string.IsNullOrEmpty(oSInstalledFontNames[i]))
							{
								OsFontNames.Add(oSInstalledFontNames[i]);
							}
						}
					}
				}
				catch
				{
				}
			}
			return OsFontNames.Contains(name.Trim());
		}

		private static Font GameFont()
		{
			try
			{
				if ((Object)(object)GUI.skin != (Object)null && GUI.skin.label != null)
				{
					return GUI.skin.label.font;
				}
			}
			catch
			{
			}
			return null;
		}

		private static string FontKey(string name)
		{
			if (name != null)
			{
				return name.Replace(" ", "").Replace("-", "").Replace("_", "")
					.ToLowerInvariant();
			}
			return "";
		}

		private static void ScanGameFonts(bool force)
		{
			if (!force && Time.realtimeSinceStartup - _gameFontsScanAt < 2f)
			{
				return;
			}
			_gameFontsScanAt = Time.realtimeSinceStartup;
			if (_gameFonts == null)
			{
				_gameFonts = new Dictionary<string, Font>();
			}
			int count = _gameFonts.Count;
			try
			{
				Font[] array = Resources.FindObjectsOfTypeAll<Font>();
				foreach (Font val in array)
				{
					if (!((Object)(object)val == (Object)null) && !string.IsNullOrEmpty(((Object)val).name))
					{
						string key = FontKey(((Object)val).name);
						if (!_gameFonts.ContainsKey(key))
						{
							_gameFonts[key] = val;
						}
					}
				}
				if (_gameFonts.Count > count)
				{
					string[] array2 = new string[_gameFonts.Count];
					_gameFonts.Keys.CopyTo(array2, 0);
					Array.Sort(array2);
					LogOnce("[diag] game fonts available: " + string.Join(", ", array2));
				}
			}
			catch
			{
			}
		}

		private static Font FindGameFont(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return null;
			}
			if (_gameFonts != null && _gameFonts.TryGetValue(FontKey(name), out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			ScanGameFonts(force: false);
			if (_gameFonts != null && _gameFonts.TryGetValue(FontKey(name), out value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			try
			{
				Font val = Resources.Load<Font>(name.Trim());
				if ((Object)(object)val != (Object)null)
				{
					_gameFonts[FontKey(name)] = val;
					LogOnce("[diag] game font loaded on demand: " + ((Object)val).name);
					return val;
				}
			}
			catch
			{
			}
			return null;
		}

		private static Font FontFor(int size)
		{
			return FontFor(size, (_fontName != null) ? _fontName.Value : "Norsebold");
		}

		private static bool CanDraw(Font font, string text)
		{
			if ((Object)(object)font == (Object)null || string.IsNullOrEmpty(text))
			{
				return true;
			}
			foreach (char c in text)
			{
				if (!char.IsWhiteSpace(c) && !HasGlyph(font, c))
				{
					return false;
				}
			}
			return true;
		}

		private static bool HasGlyph(Font font, char c)
		{
			int key = (((Object)font).GetInstanceID() * 397) ^ c;
			if (GlyphCache.TryGetValue(key, out var value))
			{
				return value;
			}
			try
			{
				value = font.HasCharacter(c);
			}
			catch
			{
				value = true;
			}
			if (GlyphCache.Count > 4000)
			{
				GlyphCache.Clear();
			}
			GlyphCache[key] = value;
			return value;
		}

		private static string Bracket(string inner, Font font)
		{
			if (!CanDraw(font, "()"))
			{
				return "(" + inner + ")";
			}
			return "(" + inner + ")";
		}

		private static Font LabelFontFor(int size, string text)
		{
			Font val = FontFor(size, LabelFontName);
			if (CanDraw(val, text))
			{
				return val;
			}
			Font val2 = FindGameFont("NotoSansSC-Regular");
			if ((Object)(object)val2 != (Object)null && CanDraw(val2, text))
			{
				return val2;
			}
			Font val3 = FontFor(size, "Microsoft YaHei");
			if (CanDraw(val3, text))
			{
				return val3;
			}
			return val;
		}

		private static Font FontFor(int size, string requested)
		{
			if (string.IsNullOrEmpty(requested))
			{
				requested = "Norsebold";
			}
			string key = requested.Trim() + "#" + size;
			if (Fonts.TryGetValue(key, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			Font val = FindGameFont(requested);
			if ((Object)(object)val != (Object)null)
			{
				LogOnce($"[diag] font for size {size}: using game font '{((Object)val).name}'");
				Fonts[key] = val;
				return val;
			}
			bool flag = IsInstalledFont(requested);
			value = null;
			if (flag)
			{
				try
				{
					value = Font.CreateDynamicFontFromOSFont(requested.Trim(), size);
				}
				catch
				{
					value = null;
				}
			}
			if ((Object)(object)value == (Object)null)
			{
				value = GameFont();
			}
			LogOnce(string.Format("[diag] font for size {0}: requested '{1}' installed={2}, using '{3}'", size, requested, flag, ((Object)(object)value != (Object)null && !string.IsNullOrEmpty(((Object)value).name)) ? ((Object)value).name : "null"));
			Fonts[key] = value;
			return value;
		}

		private static void DrawBold(Rect rect, string text, GUIStyle style, Color color, int outline)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: 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_007e: 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_00d6: Unknown result type (might be due to invalid IL or missing references)
			Color color2 = GUI.color;
			if (outline > 0)
			{
				GUI.color = new Color(0f, 0f, 0f, color.a * 0.85f);
				GUI.Label(new Rect(((Rect)(ref rect)).x - (float)outline, ((Rect)(ref rect)).y, ((Rect)(ref rect)).width, ((Rect)(ref rect)).height), text, style);
				GUI.Label(new Rect(((Rect)(ref rect)).x + (float)outline, ((Rect)(ref rect)).y, ((Rect)(ref rect)).width, ((Rect)(ref rect)).height), text, style);
				GUI.Label(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y - (float)outline, ((Rect)(ref rect)).width, ((Rect)(ref rect)).height), text, style);
				GUI.Label(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y + (float)outline, ((Rect)(ref rect)).width, ((Rect)(ref rect)).height), text, style);
			}
			GUI.color = color;
			GUI.Label(rect, text, style);
			GUI.Label(new Rect(((Rect)(ref rect)).x + 1f, ((Rect)(ref rect)).y, ((Rect)(ref rect)).width, ((Rect)(ref rect)).height), text, style);
			GUI.Label(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y + 1f, ((Rect)(ref rect)).width, ((Rect)(ref rect)).height), text, style);
			GUI.color = color2;
		}

		private void OnGUI()
		{
			try
			{
				if (Flashes.Count > 0)
				{
					DrawFlashes();
				}
				if (_showBuffPanel != null && _showBuffPanel.Value)
				{
					DrawBuffPanel();
				}
				DrawCounterWindow();
				if (_showCombatText != null && _showCombatText.Value && HitTexts.Count > 0)
				{
					DrawHitTexts();
				}
			}
			catch
			{
			}
		}

		private void DrawFlashes()
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Expected O, but got Unknown
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_048d: Unknown result type (might be due to invalid IL or missing references)
			//IL_04cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_044b: Unknown result type (might be due to invalid IL or missing references)
			//IL_045a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0461: Unknown result type (might be due to invalid IL or missing references)
			//IL_0468: Unknown result type (might be due to invalid IL or missing references)
			//IL_0477: Unknown result type (might be due to invalid IL or missing references)
			float time = Time.time;
			float num = ((_flashDuration != null) ? _flashDuration.Value : 2f);
			for (int num2 = Flashes.Count - 1; num2 >= 0; num2--)
			{
				if (time - Flashes[num2].Started > num)
				{
					Flashes.RemoveAt(num2);
				}
			}
			if (Flashes.Count == 0)
			{
				return;
			}
			if (_flashStyle == null)
			{
				_flashStyle = new GUIStyle(GUI.skin.label);
				_flashStyle.alignment = (TextAnchor)4;
			}
			float num3 = (float)Screen.width * ((_flashX != null) ? _flashX.Value : 0.5f);
			float num4 = (float)Screen.height * ((_flashY != null) ? _flashY.Value : 0.216f);
			float num5 = ((_flashSize != null) ? _flashSize.Value : 6f);
			int num6 = Mathf.Max(8, Mathf.RoundToInt((float)Screen.height * num5 / 100f));
			Color val = ParseColor((_flashColor != null) ? _flashColor.Value : "#FFD21A", new Color(1f, 0.82f, 0.1f, 1f));
			string text = ((_flashAnimation != null) ? _flashAnimation.Value : "Punch");
			_flashStyle.fontSize = num6;
			_flashStyle.font = FontFor(num6);
			Color val2 = default(Color);
			Rect rect = default(Rect);
			Vector2 val3 = default(Vector2);
			for (int i = 0; i < Flashes.Count; i++)
			{
				float num7 = time - Flashes[i].Started;
				float num8 = Mathf.Max(0.15f, num * 0.6f);
				float num9 = ((num7 < num8) ? 1f : Mathf.Clamp01(1f - (num7 - num8) / Mathf.Max(0.05f, num - num8)));
				float num10 = num4 - (float)i * ((float)num6 + 14f);
				float num11 = 1f;
				float num12 = 0f;
				float num13 = 0f;
				float num14 = 0f;
				int num15 = 0;
				switch (text)
				{
				case "Punch":
				{
					float num17 = Mathf.Clamp01(num7 / 0.09f);
					num11 = 1f + 0.9f * (1f - num17) * (1f - num17);
					if (num7 > 0.09f && num7 < 0.16f)
					{
						num11 = 1f + 0.06f * (1f - (num7 - 0.09f) / 0.07f);
					}
					break;
				}
				case "Slam":
				{
					float num16 = Mathf.Clamp01(num7 / 0.14f);
					num13 = Mathf.Lerp((float)(-Screen.height) * 0.18f, 0f, num16 * num16);
					if (num7 >= 0.14f && num7 < 0.34f)
					{
						num14 = 5f * (1f - (num7 - 0.14f) / 0.2f);
					}
					break;
				}
				case "Slide":
				{
					float num18 = Mathf.Clamp01(num7 / 0.18f);
					num12 = Mathf.Lerp((float)Screen.width * 0.35f, 0f, num18 * num18 * (3f - 2f * num18));
					num15 = 3;
					break;
				}
				case "Shake":
					if (num7 >= 0.05f && num7 < 0.3f)
					{
						num14 = 7f * (1f - (num7 - 0.05f) / 0.25f);
					}
					break;
				default:
					num13 = 0f - Mathf.Min(num7 * 40f, 10f);
					break;
				}
				if (num14 > 0f)
				{
					num12 += Random.Range(0f - num14, num14);
					num13 += Random.Range(0f - num14, num14);
				}
				((Color)(ref val2))..ctor(val.r, val.g, val.b, num9);
				((Rect)(ref rect))..ctor(num3 - 520f + num12, num10 + num13, 1040f, (float)num6 * 1.6f);
				if (num15 > 0 && Mathf.Abs(num12) > 0.5f)
				{
					for (int num19 = num15; num19 >= 1; num19--)
					{
						DrawBold(new Rect(((Rect)(ref rect)).x + num12 * (float)num19 * 0.6f, ((Rect)(ref rect)).y, ((Rect)(ref rect)).width, ((Rect)(ref rect)).height), "PERFECT BLOCK !!!", _flashStyle, new Color(val2.r, val2.g, val2.b, num9 * 0.18f), 0);
					}
				}
				Matrix4x4 matrix = GUI.matrix;
				if (!Mathf.Approximately(num11, 1f))
				{
					((Vector2)(ref val3))..ctor(num3, ((Rect)(ref rect)).y + ((Rect)(ref rect)).height * 0.5f);
					GUIUtility.ScaleAroundPivot(new Vector2(num11, num11), val3);
				}
				DrawBold(rect, "PERFECT BLOCK !!!", _flashStyle, val2, 3);
				GUI.matrix = matrix;
			}
		}

		private static float AdrenalineOf(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return -1f;
			}
			try
			{
				return player.GetAdrenaline();
			}
			catch
			{
				return -1f;
			}
		}

		private static string LocalizeEffectName(string token)
		{
			if (string.IsNullOrEmpty(token))
			{
				return "";
			}
			if (!_localizeProbed)
			{
				try
				{
					Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
					for (int i = 0; i < assemblies.Length; i++)
					{
						Type type = assemblies[i].GetType("Localization");
						if (type == null)
						{
							continue;
						}
						PropertyInfo property = type.GetProperty("instance", BindingFlags.Static | BindingFlags.Public);
						MethodInfo method = type.GetMethod("Localize", new Type[1] { typeof(string) });
						if (property != null && method != null)
						{
							object value = property.GetValue(null, null);
							if (value != null)
							{
								_localizeInstance = value;
								_localizeMethod = method;
								break;
							}
						}
					}
					if (_localizeMethod != null && _localizeInstance != null)
					{
						_localizeProbed = true;
					}
				}
				catch
				{
					_localizeMethod = null;
				}
			}
			if (_localizeMethod == null || _localizeInstance == null)
			{
				return token.TrimStart(new char[1] { '$' });
			}
			try
			{
				string text = _localizeMethod.Invoke(_localizeInstance, new object[1] { token }) as string;
				return string.IsNullOrEmpty(text) ? token.TrimStart(new char[1] { '$' }) : text;
			}
			catch
			{
				return token.TrimStart(new char[1] { '$' });
			}
		}

		private static List<StatusEffect> StatusEffectsOf(Player player)
		{
			try
			{
				if ((Object)(object)player == (Object)null || ((Character)player).GetSEMan() == null)
				{
					return null;
				}
				return ((Character)player).GetSEMan().GetStatusEffects();
			}
			catch
			{
				return null;
			}
		}

		private static int StacksOf(StatusEffect effect)
		{
			if ((Object)(object)effect == (Object)null)
			{
				return 1;
			}
			try
			{
				FieldInfo field = ((object)effect).GetType().GetField("Stacks", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null && field.FieldType == typeof(int))
				{
					return Mathf.Max(1, (int)field.GetValue(effect));
				}
			}
			catch
			{
			}
			return 1;
		}

		private static void LogPerfectBuffs()
		{
			try
			{
				Player localPlayer = Player.m_localPlayer;
				List<StatusEffect> list = StatusEffectsOf(localPlayer);
				if (list == null)
				{
					return;
				}
				StringBuilder stringBuilder = new StringBuilder("[PB] perfect block buffs:");
				for (int i = 0; i < list.Count; i++)
				{
					StatusEffect val = list[i];
					if (!((Object)(object)val == (Object)null))
					{
						stringBuilder.Append(" ").Append(val.m_name).Append("(")
							.Append(val.GetRemaningTime().ToString("F1"))
							.Append("s)");
					}
				}
				stringBuilder.Append(" | adrenaline ").Append(AdrenalineOf(localPlayer).ToString("F1"));
				if ((Object)(object)_instance != (Object)null)
				{
					((BaseUnityPlugin)_instance).Logger.LogInfo((object)stringBuilder.ToString());
				}
			}
			catch
			{
			}
		}

		private static void TickTestOverlay()
		{
			if (_testCombatText == null || !_testCombatText.Value || (Object)(object)Player.m_localPlayer == (Object)null)
			{
				return;
			}
			float time = Time.time;
			if (!(time < _testNextAt))
			{
				_testNextAt = time + 0.8f;
				_testTick++;
				AddHitText(taken: true, "37", "", CombatFontSize, null);
				AddHitText(taken: false, "78", "", CombatFontSize, null);
				AddHitText(taken: false, "142", "", Mathf.RoundToInt((float)CombatFontSize * BigHitScale), null, crit: true);
				if (_showBlockText == null || _showBlockText.Value)
				{
					NoteBlock(perfect: false);
					NoteBlock(perfect: true);
				}
				if (_testTick % 3 == 0)
				{
					AddFlash();
				}
			}
		}

		private static void TickBuffPanel()
		{
			float time = Time.time;
			bool flag = _showBuffPanel != null && _showBuffPanel.Value;
			bool flag2 = _buffOnlyBlock == null || _buffOnlyBlock.Value;
			float num = ((_buffLinger != null) ? _buffLinger.Value : 1f);
			Player localPlayer = Player.m_localPlayer;
			float num2 = AdrenalineOf(localPlayer);
			if ((Object)(object)localPlayer != (Object)null && _adrenalineBefore >= 0f && time - _lastPerfectAt < 1.5f && num2 > _adrenalineBefore + 0.05f)
			{
				_adrenalineGain = num2 - _adrenalineBefore;
				_adrenalineGainAt = time;
			}
			if (num2 >= 0f)
			{
				_adrenalinePrev = num2;
			}
			List<StatusEffect> list = StatusEffectsOf(localPlayer);
			if (list != null)
			{
				for (int i = 0; i < list.Count; i++)
				{
					StatusEffect val = list[i];
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					float num3 = 0f;
					float num4 = 0f;
					try
					{
						num3 = val.m_ttl;
						num4 = Mathf.Max(0f, val.GetRemaningTime());
					}
					catch
					{
						num3 = 0f;
					}
					if (num3 <= 0.01f)
					{
						continue;
					}
					string text = val.m_name;
					if (string.IsNullOrEmpty(text))
					{
						text = "(unnamed)";
					}
					if (!SeenEffects.TryGetValue(text, out var value))
					{
						value = time;
						if (SeenEffects.Count > 200)
						{
							SeenEffects.Clear();
						}
						SeenEffects[text] = value;
					}
					bool flag3 = value >= _lastPerfectAt - 0.2f && value <= _lastPerfectAt + 1.5f;
					if (flag2 && !flag3)
					{
						continue;
					}
					int num5 = -1;
					for (int j = 0; j < BuffRows.Count; j++)
					{
						if (BuffRows[j].Token == text)
						{
							num5 = j;
							break;
						}
					}
					if (num5 < 0)
					{
						BuffRow buffRow = new BuffRow();
						buffRow.Token = text;
						buffRow.Seen = value;
						BuffRows.Add(buffRow);
						num5 = BuffRows.Count - 1;
					}
					BuffRow buffRow2 = BuffRows[num5];
					buffRow2.Name = LocalizeEffectName(text);
					buffRow2.Icon = val.m_icon;
					buffRow2.Stacks = StacksOf(val);
					buffRow2.Temporary = true;
					buffRow2.End = time + num4;
				}
			}
			for (int num6 = BuffRows.Count - 1; num6 >= 0; num6--)
			{
				BuffRow buffRow3 = BuffRows[num6];
				if (time > buffRow3.End + num)
				{
					BuffRows.RemoveAt(num6);
				}
			}
			if (BuffRows.Count > 12)
			{
				BuffRows.Sort((BuffRow a, BuffRow b) => b.Seen.CompareTo(a.Seen));
				BuffRows.RemoveRange(12, BuffRows.Count - 12);
			}
			if (!flag)
			{
				BuffRows.Clear();
			}
		}

		private static void DrawBuffPanel()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Expected O, but got Unknown
			if (_buffNameStyle == null)
			{
				_buffNameStyle = new GUIStyle(GUI.skin.label);
				_buffNameStyle.alignment = (TextAnchor)3;
				_buffNameStyle.wordWrap = false;
				_buffTimeStyle = new GUIStyle(GUI.skin.label);
				_buffTimeStyle.alignment = (TextAnchor)5;
				_buffTimeStyle.wordWrap = false;
			}
			DrawBuffColumn();
		}

		private static bool DrawBuffColumn()
		{
			//IL_019d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_021b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0384: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_023f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0261: Unknown result type (might be due to invalid IL or missing references)
			float time = Time.time;
			float num = ((_buffScale != null) ? _buffScale.Value : 1.5f);
			float num2 = (float)Screen.width * ((_tempBuffX != null) ? _tempBuffX.Value : 0.624f);
			float num3 = (float)Screen.height * ((_tempBuffY != null) ? _tempBuffY.Value : 0.4f);
			int num4 = ((_buffMaxRows == null) ? 1 : _buffMaxRows.Value);
			float num5 = ((_buffLinger != null) ? _buffLinger.Value : 1f);
			int num6 = Mathf.RoundToInt(38f * num);
			int num7 = Mathf.RoundToInt(18f * num);
			int num8 = Mathf.RoundToInt(16f * num);
			float num9 = (float)num6 + 8f * num;
			float num10 = 300f * num;
			_buffNameStyle.fontSize = num7;
			_buffNameStyle.font = LabelFontFor(num7, "状态效果");
			_buffTimeStyle.fontSize = num8;
			_buffTimeStyle.font = LabelFontFor(num8, "0.0s");
			int num11 = 0;
			for (int i = 0; i < BuffRows.Count; i++)
			{
				if (num11 >= num4)
				{
					break;
				}
				BuffRow buffRow = BuffRows[i];
				if (buffRow != null)
				{
					float num12 = Mathf.Max(0f, buffRow.End - time);
					float num13 = ((num12 > 0f) ? 1f : Mathf.Clamp01(1f - (time - buffRow.End) / Mathf.Max(0.1f, num5)));
					float num14 = num3 + (float)num11 * num9;
					if ((Object)(object)buffRow.Icon != (Object)null)
					{
						DrawIcon(new Rect(num2, num14, (float)num6, (float)num6), buffRow.Icon, num13);
					}
					string text = ((buffRow.Stacks > 1) ? (buffRow.Name + " x" + buffRow.Stacks) : buffRow.Name);
					DrawBold(new Rect(num2 + (float)num6 + 8f, num14, num10 - (float)num6 - 14f, (float)num6), text, _buffNameStyle, new Color(1f, 0.93f, 0.72f, num13), 1);
					if (num12 > 0f)
					{
						DrawBold(new Rect(num2 + num10, num14, 70f * num, (float)num6), FormatSeconds(num12), _buffTimeStyle, new Color(1f, 0.82f, 0.35f, num13), 1);
					}
					num11++;
				}
			}
			if (_adrenalineGain > 0.05f && time - _adrenalineGainAt < 5f && num11 < num4)
			{
				float num15 = Mathf.Clamp01(1f - (time - _adrenalineGainAt - 4f) / 1f);
				Player localPlayer = Player.m_localPlayer;
				float num16 = (((Object)(object)localPlayer != (Object)null) ? localPlayer.GetAdrenaline() : 0f);
				float num17 = (((Object)(object)localPlayer != (Object)null) ? ((Character)localPlayer).GetMaxAdrenaline() : 100f);
				if (num17 <= 0f)
				{
					num17 = 100f;
				}
				string text2 = string.Format("{0} +{1:F0}  ({2:F0}/{3:F0})", T("BuffAdrenaline", "Adrenaline"), _adrenalineGain, num16, num17);
				float num18 = num3 + (float)num11 * num9;
				DrawBold(new Rect(num2 + (float)num6 + 8f, num18, num10, (float)num6), text2, _buffNameStyle, new Color(1f, 0.45f, 0.35f, num15), 1);
				num11++;
			}
			return num11 > 0;
		}

		private static string FormatSeconds(float seconds)
		{
			if (seconds >= 60f)
			{
				return Mathf.FloorToInt(seconds / 60f) + ":" + Mathf.FloorToInt(seconds % 60f).ToString("00");
			}
			return seconds.ToString("0.0") + "s";
		}

		private void DrawCounterWindow()
		{
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Expected O, but got Unknown
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0218: Unknown result type (might be due to invalid IL or missing references)
			//IL_0227: Unknown result type (might be due to invalid IL or missing references)
			if (_showCounterWindow == null || !_showCounterWindow.Value || !_postPerfect)
			{
				return;
			}
			float time = Time.time;
			if (!(time >= _holdUntil))
			{
				float num = Mathf.Max(0.05f, (float)HoldMs / 1000f);
				float num2 = Mathf.Clamp01((_holdUntil - time) / num);
				float num3 = 220f;
				float num4 = (float)Screen.width * 0.5f - num3 * 0.5f;
				float num5 = (float)Screen.height * 0.5f;
				if (_buffNameStyle == null)
				{
					_counterStyle = new GUIStyle(GUI.skin.label);
					_counterStyle.alignment = (TextAnchor)4;
					_counterStyle.wordWrap = false;
				}
				int num6 = Mathf.RoundToInt((float)Screen.height * 0.018f);
				_counterStyle.fontSize = num6;
				string text = T("CounterWindow", "反击窗口");
				if (CurrentChain(out var level, out var levels) && levels > 1)
				{
					text = text + "  " + (level + 1) + "/" + levels;
				}
				_counterStyle.font = LabelFontFor(num6, text);
				DrawBold(new Rect(num4 - 40f, num5 - (float)num6 * 1.4f, num3 + 80f, (float)num6 * 1.5f), text, _counterStyle, new Color(0.45f, 0.95f, 1f, 0.95f), 1);
				Color color = GUI.color;
				GUI.color = new Color(0f, 0f, 0f, 0.55f);
				GUI.DrawTexture(new Rect(num4 - 2f, num5 + (float)num6 * 0.2f - 2f, num3 + 4f, 10f), (Texture)(object)Texture2D.whiteTexture);
				GUI.color = new Color(0.35f, 0.9f, 1f, 0.9f);
				GUI.DrawTexture(new Rect(num4, num5 + (float)num6 * 0.2f, num3 * num2, 6f), (Texture)(object)Texture2D.whiteTexture);
				GUI.color = color;
			}
		}

		private static bool CurrentChain(out int level, out int levels)
		{
			level = 0;
			levels = 1;
			try
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer == (Object)null || CurrentAttackField == null || AttackChainLevelField == null)
				{
					return false;
				}
				object? value = CurrentAttackField.GetValue(localPlayer);
				Attack val = (Attack)((value is Attack) ? value : null);
				if (val == null)
				{
					return false;
				}
				levels = val.m_attackChainLevels;
				level = (int)AttackChainLevelField.GetValue(val);
				return levels > 0;
			}
			catch
			{
				return false;
			}
		}

		private static Color ParseColor(string hex, Color fallback)
		{
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: 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)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: 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_0045: 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)
			if (string.IsNullOrEmpty(hex))
			{
				return fallback;
			}
			try
			{
				string text = hex.Trim();
				if (text.StartsWith("#"))
				{
					text = text.Substring(1);
				}
				if (text.Length == 6)
				{
					text += "FF";
				}
				if (text.Length != 8)
				{
					return fallback;
				}
				int num = Convert.ToInt32(text, 16);
				return new Color((float)((num >> 16) & 0xFF) / 255f, (float)((num >> 8) & 0xFF) / 255f, (float)(num & 0xFF) / 255f, 1f);
			}
			catch
			{
				return fallback;
			}
		}

		private void DrawHitTexts()
		{
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Expected O, but got Unknown
			//IL_02fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0311: Unknown result type (might be due to invalid IL or missing references)
			//IL_031a: Unknown result type (might be due to invalid IL or missing references)
			//IL_03dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0369: Unknown result type (might be due to invalid IL or missing references)
			//IL_0373: Expected O, but got Unknown
			//IL_036e: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b3: Unknown result type (might be due to invalid IL or missing references)
			float time = Time.time;
			float num = ((_textDuration != null) ? _textDuration.Value : 1.2f);
			for (int num2 = HitTexts.Count - 1; num2 >= 0; num2--)
			{
				if (time - HitTexts[num2].Born > num)
				{
					HitTexts.RemoveAt(num2);
				}
			}
			if (HitTexts.Count == 0)
			{
				return;
			}
			int num3 = 0;
			int num4 = 0;
			int num5 = 0;
			int num6 = 0;
			for (int i = 0; i < HitTexts.Count; i++)
			{
				HitText hitText = HitTexts[i];
				if (hitText.Taken || hitText.Avoid)
				{
					num3++;
					if (hitText.Size > num5)
					{
						num5 = hitText.Size;
					}
				}
				else
				{
					num4++;
					if (hitText.Size > num6)
					{
						num6 = hitText.Size;
					}
				}
			}
			int num7 = 0;
			int num8 = 0;
			string style = ((_animStyle != null) ? _animStyle.Value : "Static");
			string direction = ((_animDirection != null) ? _animDirection.Value : "UP;RIGHT");
			float area = ((_scrollSize != null) ? _scrollSize.Value : 1f) * 40f;
			float num9 = ((_fadeStart != null) ? _fadeStart.Value : 60f) / 100f;
			int maxLines = ((_maxLines != null) ? _maxLines.Value : 8);
			if (_avoidStyle == null)
			{
				_avoidStyle = new GUIStyle(GUI.skin.label);
				_avoidStyle.alignment = (TextAnchor)4;
				_avoidStyle.wordWrap = false;
			}
			Color color = default(Color);
			for (int j = 0; j < HitTexts.Count; j++)
			{
				HitText hitText2 = HitTexts[j];
				float num10 = Mathf.Clamp01((time - hitText2.Born) / num);
				float num11 = ((num10 < num9) ? 1f : Mathf.Clamp01(1f - (num10 - num9) / Mathf.Max(0.05f, 1f - num9)));
				bool flag = hitText2.Taken || hitText2.Avoid;
				int index = (flag ? num7++ : num8++);
				int count = (flag ? num3 : num4);
				int lineSize = Mathf.Max(CombatFontSize, flag ? num5 : num6);
				float num12 = (float)Screen.width * (hitText2.Taken ? TakenAnchorX : DealtAnchorX);
				float num13 = (float)Screen.height * (hitText2.Taken ? TakenAnchorY : DealtAnchorY);
				if (StyleOffset(style, direction, num10, area, hitText2, lineSize, index, count, maxLines, out var dx, out var dy, out var scale))
				{
					if (hitText2.Crit && num10 < 0.1f)
					{
						scale *= 20f * (0.1f - num10) + 1f;
					}
					((Color)(ref color))..ctor(hitText2.Color.r, hitText2.Color.g, hitText2.Color.b, num11);
					Matrix4x4 matrix = GUI.matrix;
					if (!Mathf.Approximately(scale, 1f))
					{
						GUIUtility.ScaleAroundPivot(new Vector2(scale, scale), new Vector2(num12, num13));
					}
					if (hitText2.Avoid)
					{
						int size = hitText2.Size;
						_avoidStyle.fontSize = size;
						_avoidStyle.font = LabelFontFor(size, hitText2.Text);
						float x = _avoidStyle.CalcSize(new GUIContent(hitText2.Text)).x;
						DrawBold(new Rect(num12 - x + dx, num13 + dy - (float)size * 0.75f, x + 8f, (float)size * 1.5f), hitText2.Text, _avoidStyle, color, 2);
					}
					else
					{
						DrawHitEntry(num12 + dx, num13 + dy, hitText2.Text, hitText2.Label, hitText2.Size, color, hitText2.Icon);
					}
					GUI.matrix = matrix;
				}
			}
		}

		private static bool StyleOffset(string style, string direction, float p, float area, HitText text, int lineSize, int index, int count, int maxLines, out float dx, out float dy, out float scale)
		{
			dx = 0f;
			dy = 0f;
			scale = 1f;
			string text2 = direction;
			string text3 = "RIGHT";
			int num = direction.IndexOf(';');
			if (num >= 0)
			{
				text2 = direction.Substring(0, num);
				text3 = direction.Substring(num + 1);
			}
			else
			{
				switch (direction)
				{
				case "LEFT":
				case "RIGHT":
				case "ALT":
					text2 = "UP";
					text3 = direction;
					break;
				}
			}
			bool flag = (text2 == "UP" || text2 == "DOWN") && text2 == "UP";
			bool flag2 = text3 == "LEFT" || (text3 == "ALT" && index % 2 == 0);
			float num2 = (flag ? (-1f) : 1f);
			float num3 = (float)Mathf.Max(CombatFontSize, lineSize) * ((_lineSpacing != null) ? _lineSpacing.Value : 1f);
			switch (style)
			{
			case "Straight":
				dy = num2 * (p - 0.5f) * area;
				break;
			case "Parabola":
				dx = (1f - 4f * (p - 0.5f) * (p - 0.5f)) * area / 3f;
				dy = num2 * (p - 0.5f) * area;
				break;
			case "Semicircle":
				dy = area / 2f * Mathf.Sin((float)Math.PI / 180f * (90f + 180f * p));
				dx = (0f - area) / 2f * Mathf.Cos((float)Math.PI / 180f * (90f + 180f * p));
				if (flag)
				{
					dy = 0f - dy;
				}
				break;
			case "Pow":
			case "Pow2":
				dy = ((float)index - (float)(count - 1) * 0.5f) * num3 * (0f - num2);
				if (p < 0.1f)
				{
					scale = 20f * (0.1f - p) + 1f;
				}
				else if (style == "Pow")
				{
					Squiggle(text, ref dx, ref dy);
				}
				break;
			case "Static":
			case "Static2":
			{
				int num5 = Mathf.Max(1, Mathf.FloorToInt(area / Mathf.Max(1f, num3)));
				if (index >= num5 && index >= maxLines)
				{
					return false;
				}
				float num6 = (float)(Mathf.Min(count, num5) - 1) * 0.5f * num3;
				float num7 = (float)(-index) * num3;
				dy = num2 * (0f - (num6 + num7));
				if (style == "Static2")
				{
					dy = 0f - dy;
				}
				break;
			}
			case "Rainbow":
				dx = area / 2f * p;
				dy = area * (-1.7777778f * (p - text.RootA) * (p - text.RootB) - 0.5f);
				if (flag)
				{
					dy = 0f - dy;
				}
				break;
			case "Horizontal":
				dx = area * p;
				dy = area * text.RandY;
				break;
			case "Action":
			case "Action Sticky":
				dx = area * Mathf.Pow(p, 6f) * 6f;
				dy = area * text.RandY;
				scale = ((style == "Action") ? Mathf.Max(0.1f, Mathf.Min(p / 0.1f, 1f)) : Mathf.Max(1f, 1f + (0.1f - p) / 0.1f));
				break;
			case "Angled":
			case "Angled2":
			{
				float finishX = text.FinishX;
				float finishY = text.FinishY;
				if (p < 0.3f)
				{
					dx = p / 0.3f * finishX;
					dy = (0f - p) / 0.3f * finishY;
				}
				else if (p < 0.8f)
				{
					dx = finishX;
					dy = 0f - finishY;
					if (style == "Angled")
					{
						Squiggle(text, ref dx, ref dy);
					}
				}
				else
				{
					dx = ((p - 0.8f) / 0.2f + 1f) * finishX;
					dy = (0f - (1f - p)) / 0.2f * finishY;
				}
				if (flag)
				{
					dy = 0f - dy;
				}
				break;
			}
			case "Sprinkler2":
			case "Sprinkler":
			{
				float num4 = area / 2f;
				if (p < 0.3f)
				{
					dx = p / 0.3f * num4 * Mathf.Cos((float)Math.PI / 180f * text.Angle);
					dy = p / 0.3f * num4 * Mathf.Sin((float)Math.PI / 180f * text.Angle);
				}
				else if (p < 0.8f)
				{
					dx = num4 * Mathf.Cos((float)Math.PI / 180f * text.Angle);
					dy = num4 * Mathf.Sin((float)Math.PI / 180f * text.Angle);
					if (style == "Sprinkler")
					{
						Squiggle(text, ref dx, ref dy);
					}
				}
				else
				{
					dx = ((p - 0.8f) / 0.2f + 1f) * num4 * Mathf.Cos((float)Math.PI / 180f * text.Angle);
					dy = ((p - 0.8f) / 0.2f + 1f) * num4 * Mathf.Sin((float)Math.PI / 180f * text.Angle);
				}
				break;
			}
			default:
				dy = num2 * (p - 0.5f) * area;
				break;
			}
			if (flag2 && style != "Sprinkler" && style != "Sprinkler2")
			{
				dx = 0f - dx;
			}
			return true;
		}

		private static void Squiggle(HitText text, ref float dx, ref float dy)
		{
			float time = Time.time;
			if (time >= text.NextSquiggle)
			{
				text.NextSquiggle = time + 0.05f;
				text.SquiggleX = Random.Range(-1f, 1f);
				text.SquiggleY = Random.Range(-1f, 1f);
			}
			dx += text.SquiggleX;
			dy += text.SquiggleY;
		}

		private static void DrawHitEntry(float anchorX, float y, string number, string label, int size, Color color, Sprite icon)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Expected O, but got Unknown
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Expected O, but got Unknown
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Expected O, but got Unknown
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01db: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fd: Unknown result type (might be due to invalid IL or missing references)
			if (_numberStyle == null)
			{
				_numberStyle = new GUIStyle(GUI.skin.label);
				_numberStyle.alignment = (TextAnchor)3;
				_numberStyle.wordWrap = false;
				_labelStyle = new GUIStyle(GUI.skin.label);
				_labelStyle.alignment = (TextAnchor)3;
				_labelStyle.wordWrap = false;
			}
			int num = Mathf.Max(10, Mathf.RoundToInt((float)size * 0.72f));
			_numberStyle.fontSize = size;
			_numberStyle.font = FontFor(size);
			_la