Decompiled source of BetterCombat v0.5.0

JG224.BetterCombat.dll

Decompiled 8 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using JG224.BetterCombat.Patches;
using JG224.ModCore.API;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: InternalsVisibleTo("JG224.BetterCombat.CoreTests")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("JG224.BetterCombat")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.5.0.0")]
[assembly: AssemblyInformationalVersion("0.5.0")]
[assembly: AssemblyProduct("JG224.BetterCombat")]
[assembly: AssemblyTitle("JG224.BetterCombat")]
[assembly: AssemblyVersion("0.5.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace JG224.BetterCombat
{
	internal static class DefenseDefaultsMigration
	{
		internal const int CurrentRevision = 2;

		internal static float PerfectBlockWindow(int revision, float current)
		{
			if (revision < 1 && Math.Abs(current - 0.25f) < 0.0001f)
			{
				current = 0.15f;
			}
			if (revision >= 2 || !(Math.Abs(current - 0.15f) < 0.0001f))
			{
				return current;
			}
			return 0.25f;
		}

		internal static BlockCancelMode BlockCancel(int revision, BlockCancelMode current)
		{
			if (revision < 1 && current == BlockCancelMode.Full)
			{
				current = BlockCancelMode.Off;
			}
			if (revision >= 2 || current != BlockCancelMode.Off)
			{
				return current;
			}
			return BlockCancelMode.RecoveryOnly;
		}

		internal static float BossKnockback(int revision, float current)
		{
			if (revision >= 2 || !(Math.Abs(current - 0.15f) < 0.0001f))
			{
				return current;
			}
			return 0.35f;
		}

		internal static float CombatClearDelay(int revision, float current)
		{
			if (revision >= 2 || !(Math.Abs(current - 8f) < 0.0001f))
			{
				return current;
			}
			return 4f;
		}

		internal static float BowZoomFov(int revision, float current)
		{
			if (revision >= 2 || !(Math.Abs(current - 52f) < 0.0001f))
			{
				return current;
			}
			return 40f;
		}

		internal static float BowZoomDrawSpeed(int revision, float current)
		{
			if (revision >= 2 || !(Math.Abs(current - 8f) < 0.0001f))
			{
				return current;
			}
			return 3f;
		}

		internal static float BowZoomReleaseSpeed(int revision, float current)
		{
			if (revision >= 2 || !(Math.Abs(current - 14f) < 0.0001f))
			{
				return current;
			}
			return 2f;
		}
	}
	internal sealed class BetterCombatConfig
	{
		private readonly ConfigFile _file;

		private readonly List<Tuple<ConfigEntryBase, ConfigScope>> _entries = new List<Tuple<ConfigEntryBase, ConfigScope>>();

		internal ConfigEntry<bool> Enabled { get; }

		internal ConfigEntry<CombatPreset> Preset { get; }

		internal ConfigEntry<bool> TrainingFeedback { get; }

		internal ConfigEntry<float> PerfectBlockWindow { get; }

		internal ConfigEntry<float> BlockStaminaMultiplier { get; }

		internal ConfigEntry<float> PerfectBlockStaminaMultiplier { get; }

		internal ConfigEntry<BlockCancelMode> BlockCancel { get; }

		internal ConfigEntry<bool> InstantBlockFacing { get; }

		internal ConfigEntry<float> PlayerKnockbackMultiplier { get; }

		internal ConfigEntry<float> EnemyKnockbackMultiplier { get; }

		internal ConfigEntry<float> BossKnockbackMultiplier { get; }

		internal ConfigEntry<float> KnockbackSpeed { get; }

		internal ConfigEntry<float> KnockbackDurationLimit { get; }

		internal ConfigEntry<float> DodgeStaminaMultiplier { get; }

		internal ConfigEntry<float> PerfectDodgeRefundMultiplier { get; }

		internal ConfigEntry<float> MeleeStaminaMultiplier { get; }

		internal ConfigEntry<float> BowStaminaMultiplier { get; }

		internal ConfigEntry<float> SpearStaminaMultiplier { get; }

		internal ConfigEntry<float> MeleeVerticalAngle { get; }

		internal ConfigEntry<float> MeleeCharacterRadiusBonus { get; }

		internal ConfigEntry<BowTrajectoryMode> BowTrajectory { get; }

		internal ConfigEntry<float> ProgressiveBowVelocityBonus { get; }

		internal ConfigEntry<float> ProgressiveBowGravityMultiplier { get; }

		internal ConfigEntry<float> ProgressiveBowAimElevation { get; }

		internal ConfigEntry<float> BowVelocityMultiplier { get; }

		internal ConfigEntry<float> BowGravityMultiplier { get; }

		internal ConfigEntry<float> SpearVelocityMultiplier { get; }

		internal ConfigEntry<float> SpearGravityMultiplier { get; }

		internal ConfigEntry<float> ArrowRecoveryChance { get; }

		internal ConfigEntry<float> CombatClearDelaySeconds { get; }

		internal ConfigEntry<KeyboardShortcut> DodgeHotkey { get; }

		internal ConfigEntry<KeyboardShortcut> BowDrawCancelHotkey { get; }

		internal ConfigEntry<bool> AutoEquipShield { get; }

		internal ConfigEntry<bool> RestoreWeaponsAfterSwimming { get; }

		internal ConfigEntry<BowZoomMode> BowZoom { get; }

		internal ConfigEntry<float> BowZoomFov { get; }

		internal ConfigEntry<float> BowZoomDrawSpeed { get; }

		internal ConfigEntry<float> BowZoomReleaseSpeed { get; }

		internal ConfigEntry<bool> DebugLogging { get; }

		internal BetterCombatConfig(ConfigFile file)
		{
			//IL_04cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f3: Unknown result type (might be due to invalid IL or missing references)
			_file = file;
			RemoveObsoletePoiseSettings();
			Enabled = Server("General", "Enabled", value: true, "Master switch for BetterCombat gameplay changes.");
			Preset = Server("General", "PolicyPreset", CombatPreset.Custom, "Custom uses saved settings. Standard uses current defaults. Relaxed widens perfect blocks and reduces stamina costs; Precise narrows blocks and uses native bow trajectories. Switching presets preserves all custom values.");
			TrainingFeedback = Local("HUD", "TrainingFeedback", value: false, "Show local perfect-block timing and perfect-dodge feedback during gameplay.");
			PerfectBlockWindow = ServerRange("Defense", "PerfectBlockWindow", 0.25f, 0.05f, 1f, "Seconds after raising a block that count as a perfect block. The default 0.25 matches Valheim.");
			BlockStaminaMultiplier = ServerRange("Defense", "BlockStaminaMultiplier", 1f, 0f, 5f, "Normal block stamina cost multiplier.");
			PerfectBlockStaminaMultiplier = ServerRange("Defense", "PerfectBlockStaminaMultiplier", 1f, 0f, 5f, "Perfect block stamina cost multiplier.");
			BlockCancel = Server("Defense", "BlockCancelMode", BlockCancelMode.RecoveryOnly, "Off never interrupts attacks; RecoveryOnly requires a fresh block press after the hit commits and cancels only recovery; Full cancels wind-up or recovery immediately. Completed hits and projectiles are never reversed.");
			InstantBlockFacing = Server("Defense", "InstantBlockFacing", value: true, "Immediately face horizontal camera aim while blocking instead of using Valheim's slow combat turn.");
			PlayerKnockbackMultiplier = ServerRange("Knockback", "PlayerKnockbackMultiplier", 0.35f, 0f, 2f, "Multiplier for knockback received by players before the duration cap is applied.");
			EnemyKnockbackMultiplier = ServerRange("Knockback", "EnemyKnockbackMultiplier", 0.65f, 0f, 2f, "Multiplier for knockback received by ordinary enemies before the duration cap is applied.");
			BossKnockbackMultiplier = ServerRange("Knockback", "BossKnockbackMultiplier", 0.35f, 0f, 2f, "Multiplier for knockback received by bosses before the duration cap is applied.");
			KnockbackSpeed = ServerRange("Knockback", "KnockbackSpeed", 8f, 0f, 20f, "Horizontal push speed while knockback is active. Valheim normally forces 20.");
			KnockbackDurationLimit = ServerRange("Knockback", "KnockbackDurationLimit", 0.25f, 0f, 1f, "Maximum seconds a single combat push can remain active. At defaults, displacement is about two metres.");
			DodgeStaminaMultiplier = ServerRange("Defense", "DodgeStaminaMultiplier", 1f, 0f, 5f, "Dodge stamina cost multiplier.");
			PerfectDodgeRefundMultiplier = ServerRange("Defense", "PerfectDodgeRefundMultiplier", 1f, 0f, 5f, "Multiplier for Valheim's native perfect-dodge stamina refund.");
			MeleeStaminaMultiplier = ServerRange("Offense", "MeleeStaminaMultiplier", 1f, 0f, 5f, "Attack stamina multiplier for ordinary melee weapons.");
			BowStaminaMultiplier = ServerRange("Offense", "BowStaminaMultiplier", 1f, 0f, 5f, "Attack stamina multiplier for bows and crossbows.");
			SpearStaminaMultiplier = ServerRange("Offense", "SpearStaminaMultiplier", 1f, 0f, 5f, "Attack stamina multiplier for spear melee and thrown attacks.");
			MeleeVerticalAngle = ServerRange("Targeting", "MeleeVerticalAngle", 55f, 0f, 89f, "Maximum vertical angle in degrees for player melee aim correction.");
			MeleeCharacterRadiusBonus = ServerRange("Targeting", "MeleeCharacterRadiusBonus", 0.08f, 0f, 1f, "Extra character-only melee trace radius, improving hits on slopes without hitting through walls.");
			BowTrajectory = Server("Projectiles", "BowTrajectoryMode", BowTrajectoryMode.Progressive, "Native preserves Valheim bow ballistics. Progressive adds a tier-preserving velocity bonus, stronger gravity, and a small aim lift while retaining the native draw curve and bow progression.");
			ProgressiveBowVelocityBonus = ServerRange("Projectiles", "ProgressiveBowVelocityBonus", 10f, 0f, 50f, "Velocity added to a draw bow's native full-draw maximum in Progressive mode. Native bow differences are retained.");
			ProgressiveBowGravityMultiplier = ServerRange("Projectiles", "ProgressiveBowGravityMultiplier", 2.75f, 0f, 4f, "Additional arrow gravity multiplier in Progressive mode, applied before BowGravityMultiplier.");
			ProgressiveBowAimElevation = ServerRange("Projectiles", "ProgressiveBowAimElevation", 0.05f, -0.2f, 0.2f, "Small normalized world-up aim correction in Progressive mode. The default is 0.05.");
			BowVelocityMultiplier = ServerRange("Projectiles", "BowVelocityMultiplier", 1f, 0.25f, 4f, "Final bow and crossbow projectile velocity multiplier, applied after the selected trajectory mode.");
			BowGravityMultiplier = ServerRange("Projectiles", "BowGravityMultiplier", 1f, 0f, 4f, "Final bow and crossbow projectile gravity multiplier, applied after the selected trajectory mode.");
			SpearVelocityMultiplier = ServerRange("Projectiles", "SpearVelocityMultiplier", 1f, 0.25f, 4f, "Thrown spear velocity multiplier.");
			SpearGravityMultiplier = ServerRange("Projectiles", "SpearGravityMultiplier", 1f, 0f, 4f, "Thrown spear gravity multiplier.");
			ArrowRecoveryChance = ServerRange("Projectiles", "ArrowRecoveryChance", 0.25f, 0f, 1f, "Chance for a physical arrow or bolt to drop on impact. Recovery is decided by the projectile owner.");
			CombatClearDelaySeconds = ServerRange("Integration", "CombatClearDelaySeconds", 4f, 0f, 60f, "Seconds without hostile contact before Mod Core reports the player out of combat.");
			DodgeHotkey = Local<KeyboardShortcut>("Dodge", "DodgeHotkey", new KeyboardShortcut((KeyCode)308, Array.Empty<KeyCode>()), "Highest-priority local dodge shortcut. Short taps are latched; directional movement rolls that way, while no direction rolls backward from camera aim like default Valheim.");
			BowDrawCancelHotkey = Local<KeyboardShortcut>("Archery", "BowDrawCancelHotkey", new KeyboardShortcut((KeyCode)101, Array.Empty<KeyCode>()), "Local key that safely cancels an active draw-bow attack before the arrow fires. Set to None to disable.");
			AutoEquipShield = Local("Equipment", "AutoEquipShieldWithOneHandedWeapon", value: true, "Automatically equip the last used usable shield, or the first usable shield in inventory, after equipping a one-handed weapon into an empty off hand.");
			RestoreWeaponsAfterSwimming = Local("Equipment", "RestoreWeaponsAfterSwimming", value: true, "Remember native hand items hidden for swimming and retry equipping those exact inventory items after returning to ground.");
			BowZoom = Local("Camera", "BowZoom", BowZoomMode.WhileDrawing, "Off, WhileDrawing, or WhileEquipped. This is visual and never server-authoritative.");
			BowZoomFov = LocalRange("Camera", "BowZoomFov", 40f, 30f, 80f, "Camera field of view while bow zoom is active.");
			BowZoomDrawSpeed = LocalRange("Camera", "BowZoomDrawSpeed", 3f, 1f, 30f, "Zoom-in interpolation speed while an arrow is nocked/drawing.");
			BowZoomReleaseSpeed = LocalRange("Camera", "BowZoomReleaseSpeed", 2f, 1f, 30f, "Zoom-out interpolation speed after firing, cancelling, or putting the bow away.");
			DebugLogging = Developer("Diagnostics", "DebugLogging", value: false, "Enable verbose BetterCombat diagnostics.");
			ApplyDefaultMigrations();
		}

		private void ApplyDefaultMigrations()
		{
			ConfigEntry<int> val = _file.Bind<int>("Migration", "DefenseDefaultsRevision", 0, "Internal one-time migration marker for Better Combat defaults.");
			if (val.Value < 2)
			{
				PerfectBlockWindow.Value = DefenseDefaultsMigration.PerfectBlockWindow(val.Value, PerfectBlockWindow.Value);
				BlockCancel.Value = DefenseDefaultsMigration.BlockCancel(val.Value, BlockCancel.Value);
				BossKnockbackMultiplier.Value = DefenseDefaultsMigration.BossKnockback(val.Value, BossKnockbackMultiplier.Value);
				CombatClearDelaySeconds.Value = DefenseDefaultsMigration.CombatClearDelay(val.Value, CombatClearDelaySeconds.Value);
				BowZoomFov.Value = DefenseDefaultsMigration.BowZoomFov(val.Value, BowZoomFov.Value);
				BowZoomDrawSpeed.Value = DefenseDefaultsMigration.BowZoomDrawSpeed(val.Value, BowZoomDrawSpeed.Value);
				BowZoomReleaseSpeed.Value = DefenseDefaultsMigration.BowZoomReleaseSpeed(val.Value, BowZoomReleaseSpeed.Value);
				val.Value = 2;
				_file.Save();
			}
		}

		internal IEnumerable<IDisposable> RegisterMetadata(ICoreServices services, ModuleId owner)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			foreach (Tuple<ConfigEntryBase, ConfigScope> entry2 in _entries)
			{
				ConfigEntryBase entry = entry2.Item1;
				yield return services.Configuration.Register(new ConfigSettingDescriptor(owner, entry.Definition.Section, entry.Definition.Key, entry2.Item2, (Func<string>)(() => Convert.ToString(entry.BoxedValue, CultureInfo.InvariantCulture)), 1));
			}
		}

		internal CombatPolicy CreatePolicy()
		{
			CombatPolicy custom = new CombatPolicy
			{
				Enabled = Enabled.Value,
				PerfectBlockWindow = PerfectBlockWindow.Value,
				BlockStaminaMultiplier = BlockStaminaMultiplier.Value,
				PerfectBlockStaminaMultiplier = PerfectBlockStaminaMultiplier.Value,
				BlockCancel = BlockCancel.Value,
				InstantBlockFacing = InstantBlockFacing.Value,
				PlayerKnockbackMultiplier = PlayerKnockbackMultiplier.Value,
				EnemyKnockbackMultiplier = EnemyKnockbackMultiplier.Value,
				BossKnockbackMultiplier = BossKnockbackMultiplier.Value,
				KnockbackSpeed = KnockbackSpeed.Value,
				KnockbackDurationLimit = KnockbackDurationLimit.Value,
				DodgeStaminaMultiplier = DodgeStaminaMultiplier.Value,
				PerfectDodgeRefundMultiplier = PerfectDodgeRefundMultiplier.Value,
				MeleeStaminaMultiplier = MeleeStaminaMultiplier.Value,
				BowStaminaMultiplier = BowStaminaMultiplier.Value,
				SpearStaminaMultiplier = SpearStaminaMultiplier.Value,
				MeleeVerticalAngle = MeleeVerticalAngle.Value,
				MeleeCharacterRadiusBonus = MeleeCharacterRadiusBonus.Value,
				BowTrajectory = BowTrajectory.Value,
				ProgressiveBowVelocityBonus = ProgressiveBowVelocityBonus.Value,
				ProgressiveBowGravityMultiplier = ProgressiveBowGravityMultiplier.Value,
				ProgressiveBowAimElevation = ProgressiveBowAimElevation.Value,
				BowVelocityMultiplier = BowVelocityMultiplier.Value,
				BowGravityMultiplier = BowGravityMultiplier.Value,
				SpearVelocityMultiplier = SpearVelocityMultiplier.Value,
				SpearGravityMultiplier = SpearGravityMultiplier.Value,
				ArrowRecoveryChance = ArrowRecoveryChance.Value,
				CombatClearDelaySeconds = CombatClearDelaySeconds.Value
			};
			return CombatPresets.Apply(Preset.Value, custom);
		}

		private ConfigEntry<T> Server<T>(string section, string key, T value, string description)
		{
			return Add<T>(_file.Bind<T>(section, key, value, description), (ConfigScope)2);
		}

		private ConfigEntry<float> ServerRange(string section, string key, float value, float min, float max, string description)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected O, but got Unknown
			return Add<float>(_file.Bind<float>(section, key, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max), Array.Empty<object>())), (ConfigScope)2);
		}

		private ConfigEntry<T> Local<T>(string section, string key, T value, string description)
		{
			return Add<T>(_file.Bind<T>(section, key, value, description), (ConfigScope)1);
		}

		private ConfigEntry<float> LocalRange(string section, string key, float value, float min, float max, string description)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected O, but got Unknown
			return Add<float>(_file.Bind<float>(section, key, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max), Array.Empty<object>())), (ConfigScope)1);
		}

		private ConfigEntry<T> Developer<T>(string section, string key, T value, string description)
		{
			return Add<T>(_file.Bind<T>(section, key, value, description), (ConfigScope)5);
		}

		private ConfigEntry<T> Add<T>(ConfigEntry<T> entry, ConfigScope scope)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			_entries.Add(Tuple.Create<ConfigEntryBase, ConfigScope>((ConfigEntryBase)(object)entry, scope));
			return entry;
		}

		private void RemoveObsoletePoiseSettings()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected O, but got Unknown
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Expected O, but got Unknown
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Expected O, but got Unknown
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Expected O, but got Unknown
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Expected O, but got Unknown
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Expected O, but got Unknown
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Expected O, but got Unknown
			bool flag = false;
			ConfigDefinition[] array = (ConfigDefinition[])(object)new ConfigDefinition[8]
			{
				new ConfigDefinition("Poise", "EnemyPoiseMultiplier"),
				new ConfigDefinition("Poise", "BossPoise"),
				new ConfigDefinition("Poise", "BossPoiseHealthFactor"),
				new ConfigDefinition("HUD", "ShowPoiseBars"),
				new ConfigDefinition("HUD", "PoiseBarHeight"),
				new ConfigDefinition("HUD", "PoiseBarColor"),
				new ConfigDefinition("HUD", "PoiseBreakColor"),
				new ConfigDefinition("HUD", "PoiseFadeSeconds")
			};
			foreach (ConfigDefinition val in array)
			{
				_file.Bind<string>(val.Section, val.Key, string.Empty, (ConfigDescription)null);
				flag |= _file.Remove(val);
			}
			if (flag)
			{
				_file.Save();
			}
		}
	}
	internal sealed class BetterCombatRuntime : IDisposable
	{
		private readonly ICoreServices _core;

		private readonly CombatNetwork _network;

		internal BetterCombatRuntime(ICoreServices core, BetterCombatConfig config, IList<IDisposable> ownerRegistrations)
		{
			//IL_0021: 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)
			_core = core;
			_network = new CombatNetwork(core, config);
			ownerRegistrations.Add(core.Lifecycle.Subscribe(Plugin.ModuleId, (LifecycleEventKind)9, (Action<LifecycleEvent>)delegate
			{
				Reset();
			}, 0));
			ownerRegistrations.Add(core.Lifecycle.Subscribe(Plugin.ModuleId, (LifecycleEventKind)3, (Action<LifecycleEvent>)delegate
			{
				Reset();
			}, 0));
		}

		internal void Tick(double now)
		{
			_network.Tick(now);
			_core.CombatState.Tick(now);
		}

		internal void ObserveCombat(Character first, Character second)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Expected O, but got Unknown
			//IL_0099: 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_00ec: Expected O, but got Unknown
			double num = Time.unscaledTime;
			CombatContextFlags val = CombatIdentity.Context(first, second);
			string text = CombatIdentity.PlayerId(first);
			string text2 = CombatIdentity.PlayerId(second);
			string text3 = (Object.op_Implicit((Object)(object)second) ? CombatIdentity.Actor(second).ActorId : string.Empty);
			string text4 = ((Object.op_Implicit((Object)(object)second) && second.IsBoss()) ? text3 : string.Empty);
			if (text.Length != 0)
			{
				_core.CombatState.Observe(new CombatObservation(text, num, val, text3, text4, (double)Plugin.Policy.CombatClearDelaySeconds, true));
			}
			if (text2.Length != 0)
			{
				_core.CombatState.Observe(new CombatObservation(text2, num, val, Object.op_Implicit((Object)(object)first) ? CombatIdentity.Actor(first).ActorId : string.Empty, (Object.op_Implicit((Object)(object)first) && first.IsBoss()) ? CombatIdentity.Actor(first).ActorId : string.Empty, (double)Plugin.Policy.CombatClearDelaySeconds, true));
			}
		}

		internal void Reset()
		{
			_network.ResetConnection();
			ResponsiveBlock.Clear();
			EquipmentAutomation.Clear();
			DodgeHotkeyInput.Clear();
		}

		public void Dispose()
		{
			Reset();
			_network.Dispose();
		}
	}
	internal static class CombatIdentity
	{
		internal static string Key(ZDOID id)
		{
			return ((ZDOID)(ref id)).UserID + ":" + ((ZDOID)(ref id)).ID;
		}

		internal static CombatActor Actor(Character character)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Expected O, but got Unknown
			if (!Object.op_Implicit((Object)(object)character))
			{
				return CombatActor.None;
			}
			ZDOID zDOID = character.GetZDOID();
			string obj = (((ZDOID)(ref zDOID)).IsNone() ? ("scene:" + ((Object)character).GetInstanceID()) : ("zdo:" + Key(zDOID)));
			string prefabName = Utils.GetPrefabName(((Component)character).gameObject);
			return new CombatActor(obj, character, prefabName, character.GetHoverName(), character.IsPlayer(), character.IsBoss());
		}

		internal static CombatContextFlags Context(Character first, Character second = null)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			CombatContextFlags val = (CombatContextFlags)1;
			if ((Object.op_Implicit((Object)(object)first) && first.IsBoss()) || (Object.op_Implicit((Object)(object)second) && second.IsBoss()))
			{
				val = (CombatContextFlags)(val | 2);
			}
			if (Object.op_Implicit((Object)(object)first) && Object.op_Implicit((Object)(object)second) && first.IsPlayer() && second.IsPlayer())
			{
				val = (CombatContextFlags)(val | 4);
			}
			return val;
		}

		internal static string PlayerId(Character character)
		{
			//IL_0017: 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_002b: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)character) || !character.IsPlayer())
			{
				return string.Empty;
			}
			ZDOID zDOID = character.GetZDOID();
			if (!((ZDOID)(ref zDOID)).IsNone())
			{
				return "player:" + Key(zDOID);
			}
			return "local:" + ((Object)character).GetInstanceID();
		}
	}
	internal sealed class CombatNetwork : IDisposable
	{
		private static readonly NetworkMessageDescriptor PolicyRequest = new NetworkMessageDescriptor(Plugin.ModuleId, (ushort)1, 8, (NetworkDirection)1, 32, 0uL);

		private static readonly NetworkMessageDescriptor PolicySnapshot = new NetworkMessageDescriptor(Plugin.ModuleId, (ushort)2, 8, (NetworkDirection)2, 1056, 0uL);

		private readonly IAuthoritativePolicyRegistry _policies;

		private readonly INetworkRouter _network;

		private readonly Func<CombatPolicy> _createPolicy;

		private readonly List<IDisposable> _registrations = new List<IDisposable>();

		private IDisposable _policyRegistration;

		private byte[] _lastServerPolicy = Array.Empty<byte>();

		private long _policyRevision;

		private double _nextRequest;

		private bool _clientHasPolicy;

		private bool _clientPolicySuppressed;

		private bool _disposed;

		internal CombatNetwork(ICoreServices core, BetterCombatConfig config)
			: this(core.Policies, core.Network, config.CreatePolicy)
		{
		}

		internal CombatNetwork(IAuthoritativePolicyRegistry policies, INetworkRouter network, Func<CombatPolicy> createPolicy)
		{
			_policies = policies;
			_network = network;
			_createPolicy = createPolicy;
			try
			{
				RegisterPolicy();
				_registrations.Add(network.Register(PolicyRequest, (Action<NetworkMessageContext, ZPackage>)OnPolicyRequest));
				_registrations.Add(network.Register(PolicySnapshot, (Action<NetworkMessageContext, ZPackage>)OnPolicySnapshot));
			}
			catch
			{
				Dispose();
				throw;
			}
		}

		internal void Tick(double now)
		{
			if (!_disposed && Object.op_Implicit((Object)(object)ZNet.instance))
			{
				Tick(now, ZNet.instance.IsServer());
			}
		}

		internal void Tick(double now, bool isServer)
		{
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Expected O, but got Unknown
			if (_disposed)
			{
				return;
			}
			if (isServer)
			{
				CombatPolicy combatPolicy = _createPolicy();
				byte[] array = combatPolicy.Serialize();
				if (!Equal(array, _lastServerPolicy))
				{
					_lastServerPolicy = array;
					_policyRevision = Math.Max(_policyRevision + 1, DateTime.UtcNow.Ticks);
					Plugin.Policy = combatPolicy;
					PolicySnapshot val = default(PolicySnapshot);
					string text = default(string);
					if (!_policies.TryApply(Plugin.ModuleId, _policyRevision, array, true, ref val, ref text))
					{
						Plugin.Log.LogError((object)("Server combat policy rejected: " + text));
					}
					else
					{
						_network.Broadcast(PolicySnapshot, PolicyPackage(_policyRevision, array), 0, (NetworkMessageFlags)9);
					}
				}
			}
			else if (!_clientHasPolicy && now >= _nextRequest)
			{
				if (!_clientPolicySuppressed)
				{
					Plugin.Policy = new CombatPolicy
					{
						Enabled = false
					};
					_clientPolicySuppressed = true;
				}
				_nextRequest = now + 1.0;
				_network.SendToServer(PolicyRequest, new ZPackage(), _network.NextCorrelationId(), (NetworkMessageFlags)2);
			}
		}

		private void OnPolicyRequest(NetworkMessageContext context, ZPackage payload)
		{
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			if (!_disposed && Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsServer())
			{
				if (_lastServerPolicy.Length == 0)
				{
					Plugin.Policy = _createPolicy();
					_lastServerPolicy = Plugin.Policy.Serialize();
					_policyRevision = Math.Max(_policyRevision + 1, DateTime.UtcNow.Ticks);
					PolicySnapshot val = default(PolicySnapshot);
					string text = default(string);
					_policies.TryApply(Plugin.ModuleId, _policyRevision, _lastServerPolicy, true, ref val, ref text);
				}
				_network.SendToPeer(context.PeerId, PolicySnapshot, PolicyPackage(_policyRevision, _lastServerPolicy), context.CorrelationId, (NetworkMessageFlags)5);
			}
		}

		private void OnPolicySnapshot(NetworkMessageContext context, ZPackage payload)
		{
			if (_disposed || (Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsServer()))
			{
				return;
			}
			try
			{
				long revision = payload.ReadLong();
				byte[] bytes = payload.ReadByteArray();
				if (!TryApplyPolicy(revision, bytes, out var error))
				{
					Plugin.Log.LogWarning((object)("Mod Core rejected server combat policy: " + error));
				}
				else
				{
					Plugin.Debug("Applied server combat policy revision " + revision + ".");
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Malformed combat policy packet rejected: " + ex.Message));
			}
		}

		internal bool TryApplyPolicy(long revision, byte[] bytes, out string error)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			error = string.Empty;
			if (_disposed)
			{
				error = "Combat policy connection is disposed.";
				return false;
			}
			PolicySnapshot val = default(PolicySnapshot);
			if (!CombatPolicy.TryDeserialize(bytes, out var policy, out error) || !_policies.TryApply(Plugin.ModuleId, revision, bytes, true, ref val, ref error))
			{
				return false;
			}
			Plugin.Policy = policy;
			_clientHasPolicy = true;
			_clientPolicySuppressed = false;
			return true;
		}

		private static ZPackage PolicyPackage(long revision, byte[] bytes)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write(revision);
			val.Write(bytes);
			return val;
		}

		private static bool Equal(byte[] first, byte[] second)
		{
			if (first == second)
			{
				return true;
			}
			if (first == null || second == null || first.Length != second.Length)
			{
				return false;
			}
			for (int i = 0; i < first.Length; i++)
			{
				if (first[i] != second[i])
				{
					return false;
				}
			}
			return true;
		}

		internal void ResetConnection()
		{
			if (!_disposed)
			{
				_clientHasPolicy = false;
				_clientPolicySuppressed = false;
				_nextRequest = 0.0;
				_lastServerPolicy = Array.Empty<byte>();
				_policyRevision = 0L;
				Plugin.Policy = new CombatPolicy
				{
					Enabled = false
				};
				_policyRegistration?.Dispose();
				_policyRegistration = null;
				RegisterPolicy();
			}
		}

		private void RegisterPolicy()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Expected O, but got Unknown
			_policyRegistration = _policies.Register(new PolicyDescriptor(Plugin.ModuleId, 6, 1024, (Func<byte[], string>)delegate(byte[] bytes)
			{
				CombatPolicy.TryDeserialize(bytes, out var _, out var error);
				return error;
			}));
		}

		public void Dispose()
		{
			if (!_disposed)
			{
				_disposed = true;
				for (int num = _registrations.Count - 1; num >= 0; num--)
				{
					_registrations[num].Dispose();
				}
				_registrations.Clear();
				_policyRegistration?.Dispose();
				_policyRegistration = null;
			}
		}
	}
	public enum BowZoomMode : byte
	{
		Off,
		WhileDrawing,
		WhileEquipped
	}
	public enum BlockCancelMode : byte
	{
		Off,
		RecoveryOnly,
		Full
	}
	public enum BowTrajectoryMode : byte
	{
		Native,
		Progressive
	}
	public sealed class CombatPolicy
	{
		public const int SchemaVersion = 6;

		public const int MaximumBytes = 1024;

		public bool Enabled { get; set; } = true;

		public float PerfectBlockWindow { get; set; } = 0.25f;

		public float BlockStaminaMultiplier { get; set; } = 1f;

		public float PerfectBlockStaminaMultiplier { get; set; } = 1f;

		public BlockCancelMode BlockCancel { get; set; } = BlockCancelMode.RecoveryOnly;

		public bool InstantBlockFacing { get; set; } = true;

		public float PlayerKnockbackMultiplier { get; set; } = 0.35f;

		public float EnemyKnockbackMultiplier { get; set; } = 0.65f;

		public float BossKnockbackMultiplier { get; set; } = 0.35f;

		public float KnockbackSpeed { get; set; } = 8f;

		public float KnockbackDurationLimit { get; set; } = 0.25f;

		public float DodgeStaminaMultiplier { get; set; } = 1f;

		public float PerfectDodgeRefundMultiplier { get; set; } = 1f;

		public float MeleeStaminaMultiplier { get; set; } = 1f;

		public float BowStaminaMultiplier { get; set; } = 1f;

		public float SpearStaminaMultiplier { get; set; } = 1f;

		public float MeleeVerticalAngle { get; set; } = 55f;

		public float MeleeCharacterRadiusBonus { get; set; } = 0.08f;

		public BowTrajectoryMode BowTrajectory { get; set; } = BowTrajectoryMode.Progressive;

		public float ProgressiveBowVelocityBonus { get; set; } = 10f;

		public float ProgressiveBowGravityMultiplier { get; set; } = 2.75f;

		public float ProgressiveBowAimElevation { get; set; } = 0.05f;

		public float BowVelocityMultiplier { get; set; } = 1f;

		public float BowGravityMultiplier { get; set; } = 1f;

		public float SpearVelocityMultiplier { get; set; } = 1f;

		public float SpearGravityMultiplier { get; set; } = 1f;

		public float ArrowRecoveryChance { get; set; } = 0.25f;

		public float CombatClearDelaySeconds { get; set; } = 4f;

		public byte[] Serialize()
		{
			using MemoryStream memoryStream = new MemoryStream();
			using BinaryWriter binaryWriter = new BinaryWriter(memoryStream);
			binaryWriter.Write(6);
			binaryWriter.Write(Enabled);
			binaryWriter.Write(PerfectBlockWindow);
			binaryWriter.Write(BlockStaminaMultiplier);
			binaryWriter.Write(PerfectBlockStaminaMultiplier);
			binaryWriter.Write((byte)BlockCancel);
			binaryWriter.Write(InstantBlockFacing);
			binaryWriter.Write(PlayerKnockbackMultiplier);
			binaryWriter.Write(EnemyKnockbackMultiplier);
			binaryWriter.Write(BossKnockbackMultiplier);
			binaryWriter.Write(KnockbackSpeed);
			binaryWriter.Write(KnockbackDurationLimit);
			binaryWriter.Write(DodgeStaminaMultiplier);
			binaryWriter.Write(PerfectDodgeRefundMultiplier);
			binaryWriter.Write(MeleeStaminaMultiplier);
			binaryWriter.Write(BowStaminaMultiplier);
			binaryWriter.Write(SpearStaminaMultiplier);
			binaryWriter.Write(MeleeVerticalAngle);
			binaryWriter.Write(MeleeCharacterRadiusBonus);
			binaryWriter.Write((byte)BowTrajectory);
			binaryWriter.Write(ProgressiveBowVelocityBonus);
			binaryWriter.Write(ProgressiveBowGravityMultiplier);
			binaryWriter.Write(ProgressiveBowAimElevation);
			binaryWriter.Write(BowVelocityMultiplier);
			binaryWriter.Write(BowGravityMultiplier);
			binaryWriter.Write(SpearVelocityMultiplier);
			binaryWriter.Write(SpearGravityMultiplier);
			binaryWriter.Write(ArrowRecoveryChance);
			binaryWriter.Write(CombatClearDelaySeconds);
			return memoryStream.ToArray();
		}

		public static bool TryDeserialize(byte[] bytes, out CombatPolicy policy, out string error)
		{
			policy = null;
			error = ValidatePayload(bytes);
			if (error.Length != 0)
			{
				return false;
			}
			try
			{
				using BinaryReader binaryReader = new BinaryReader(new MemoryStream(bytes, writable: false));
				int num = binaryReader.ReadInt32();
				if (num != 6)
				{
					error = "Unsupported policy schema " + num + ".";
					return false;
				}
				CombatPolicy combatPolicy = new CombatPolicy
				{
					Enabled = binaryReader.ReadBoolean(),
					PerfectBlockWindow = binaryReader.ReadSingle(),
					BlockStaminaMultiplier = binaryReader.ReadSingle(),
					PerfectBlockStaminaMultiplier = binaryReader.ReadSingle(),
					BlockCancel = (BlockCancelMode)binaryReader.ReadByte(),
					InstantBlockFacing = binaryReader.ReadBoolean(),
					PlayerKnockbackMultiplier = binaryReader.ReadSingle(),
					EnemyKnockbackMultiplier = binaryReader.ReadSingle(),
					BossKnockbackMultiplier = binaryReader.ReadSingle(),
					KnockbackSpeed = binaryReader.ReadSingle(),
					KnockbackDurationLimit = binaryReader.ReadSingle(),
					DodgeStaminaMultiplier = binaryReader.ReadSingle(),
					PerfectDodgeRefundMultiplier = binaryReader.ReadSingle(),
					MeleeStaminaMultiplier = binaryReader.ReadSingle(),
					BowStaminaMultiplier = binaryReader.ReadSingle(),
					SpearStaminaMultiplier = binaryReader.ReadSingle(),
					MeleeVerticalAngle = binaryReader.ReadSingle(),
					MeleeCharacterRadiusBonus = binaryReader.ReadSingle(),
					BowTrajectory = (BowTrajectoryMode)binaryReader.ReadByte(),
					ProgressiveBowVelocityBonus = binaryReader.ReadSingle(),
					ProgressiveBowGravityMultiplier = binaryReader.ReadSingle(),
					ProgressiveBowAimElevation = binaryReader.ReadSingle(),
					BowVelocityMultiplier = binaryReader.ReadSingle(),
					BowGravityMultiplier = binaryReader.ReadSingle(),
					SpearVelocityMultiplier = binaryReader.ReadSingle(),
					SpearGravityMultiplier = binaryReader.ReadSingle(),
					ArrowRecoveryChance = binaryReader.ReadSingle(),
					CombatClearDelaySeconds = binaryReader.ReadSingle()
				};
				if (binaryReader.BaseStream.Position != binaryReader.BaseStream.Length)
				{
					error = "Policy contains trailing data.";
					return false;
				}
				error = combatPolicy.Validate();
				if (error.Length != 0)
				{
					return false;
				}
				policy = combatPolicy;
				return true;
			}
			catch (Exception ex)
			{
				error = "Policy could not be decoded: " + ex.Message;
				return false;
			}
		}

		public static string ValidatePayload(byte[] bytes)
		{
			if (bytes == null || bytes.Length == 0)
			{
				return "Policy payload is empty.";
			}
			if (bytes.Length > 1024)
			{
				return "Policy payload exceeds " + 1024 + " bytes.";
			}
			return string.Empty;
		}

		public string Validate()
		{
			string result;
			if ((result = Range(PerfectBlockWindow, 0.05f, 1f, "PerfectBlockWindow")).Length != 0)
			{
				return result;
			}
			if ((result = Range(BlockStaminaMultiplier, 0f, 5f, "BlockStaminaMultiplier")).Length != 0)
			{
				return result;
			}
			if ((result = Range(PerfectBlockStaminaMultiplier, 0f, 5f, "PerfectBlockStaminaMultiplier")).Length != 0)
			{
				return result;
			}
			if (!Enum.IsDefined(typeof(BlockCancelMode), BlockCancel))
			{
				return "BlockCancel is invalid.";
			}
			if ((result = Range(PlayerKnockbackMultiplier, 0f, 2f, "PlayerKnockbackMultiplier")).Length != 0)
			{
				return result;
			}
			if ((result = Range(EnemyKnockbackMultiplier, 0f, 2f, "EnemyKnockbackMultiplier")).Length != 0)
			{
				return result;
			}
			if ((result = Range(BossKnockbackMultiplier, 0f, 2f, "BossKnockbackMultiplier")).Length != 0)
			{
				return result;
			}
			if ((result = Range(KnockbackSpeed, 0f, 20f, "KnockbackSpeed")).Length != 0)
			{
				return result;
			}
			if ((result = Range(KnockbackDurationLimit, 0f, 1f, "KnockbackDurationLimit")).Length != 0)
			{
				return result;
			}
			if ((result = Range(DodgeStaminaMultiplier, 0f, 5f, "DodgeStaminaMultiplier")).Length != 0)
			{
				return result;
			}
			if ((result = Range(PerfectDodgeRefundMultiplier, 0f, 5f, "PerfectDodgeRefundMultiplier")).Length != 0)
			{
				return result;
			}
			if ((result = Range(MeleeStaminaMultiplier, 0f, 5f, "MeleeStaminaMultiplier")).Length != 0)
			{
				return result;
			}
			if ((result = Range(BowStaminaMultiplier, 0f, 5f, "BowStaminaMultiplier")).Length != 0)
			{
				return result;
			}
			if ((result = Range(SpearStaminaMultiplier, 0f, 5f, "SpearStaminaMultiplier")).Length != 0)
			{
				return result;
			}
			if ((result = Range(MeleeVerticalAngle, 0f, 89f, "MeleeVerticalAngle")).Length != 0)
			{
				return result;
			}
			if ((result = Range(MeleeCharacterRadiusBonus, 0f, 1f, "MeleeCharacterRadiusBonus")).Length != 0)
			{
				return result;
			}
			if (!Enum.IsDefined(typeof(BowTrajectoryMode), BowTrajectory))
			{
				return "BowTrajectory is invalid.";
			}
			if ((result = Range(ProgressiveBowVelocityBonus, 0f, 50f, "ProgressiveBowVelocityBonus")).Length != 0)
			{
				return result;
			}
			if ((result = Range(ProgressiveBowGravityMultiplier, 0f, 4f, "ProgressiveBowGravityMultiplier")).Length != 0)
			{
				return result;
			}
			if ((result = Range(ProgressiveBowAimElevation, -0.2f, 0.2f, "ProgressiveBowAimElevation")).Length != 0)
			{
				return result;
			}
			if ((result = Range(BowVelocityMultiplier, 0.25f, 4f, "BowVelocityMultiplier")).Length != 0)
			{
				return result;
			}
			if ((result = Range(BowGravityMultiplier, 0f, 4f, "BowGravityMultiplier")).Length != 0)
			{
				return result;
			}
			if ((result = Range(SpearVelocityMultiplier, 0.25f, 4f, "SpearVelocityMultiplier")).Length != 0)
			{
				return result;
			}
			if ((result = Range(SpearGravityMultiplier, 0f, 4f, "SpearGravityMultiplier")).Length != 0)
			{
				return result;
			}
			if ((result = Range(ArrowRecoveryChance, 0f, 1f, "ArrowRecoveryChance")).Length != 0)
			{
				return result;
			}
			return Range(CombatClearDelaySeconds, 0f, 60f, "CombatClearDelaySeconds");
		}

		private static string Range(float value, float minimum, float maximum, string name)
		{
			if (!float.IsNaN(value) && !float.IsInfinity(value) && !(value < minimum) && !(value > maximum))
			{
				return string.Empty;
			}
			return name + " must be between " + minimum + " and " + maximum + ".";
		}
	}
	public enum CombatPreset : byte
	{
		Custom,
		Standard,
		Relaxed,
		Precise
	}
	internal static class CombatPresets
	{
		internal static CombatPolicy Apply(CombatPreset preset, CombatPolicy custom)
		{
			if (preset == CombatPreset.Custom)
			{
				return custom;
			}
			CombatPolicy combatPolicy = new CombatPolicy
			{
				Enabled = custom.Enabled
			};
			switch (preset)
			{
			case CombatPreset.Relaxed:
			{
				combatPolicy.PerfectBlockWindow = 0.4f;
				float num = (combatPolicy.SpearStaminaMultiplier = 0.75f);
				float num3 = (combatPolicy.BowStaminaMultiplier = num);
				float num5 = (combatPolicy.MeleeStaminaMultiplier = num3);
				float blockStaminaMultiplier = (combatPolicy.DodgeStaminaMultiplier = num5);
				combatPolicy.BlockStaminaMultiplier = blockStaminaMultiplier;
				combatPolicy.PerfectDodgeRefundMultiplier = 1.25f;
				break;
			}
			case CombatPreset.Precise:
				combatPolicy.PerfectBlockWindow = 0.15f;
				combatPolicy.BlockCancel = BlockCancelMode.Off;
				combatPolicy.MeleeCharacterRadiusBonus = 0f;
				combatPolicy.BowTrajectory = BowTrajectoryMode.Native;
				break;
			}
			return combatPolicy;
		}
	}
	internal static class CombatRulesBridge
	{
		private static long _action;

		internal static double Now => Time.unscaledTime;

		internal static string Action(string kind)
		{
			return kind + ":" + Interlocked.Increment(ref _action);
		}

		internal static CombatItem Item(ItemData item)
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Expected O, but got Unknown
			if (item == null)
			{
				return CombatItem.None;
			}
			string text = (Object.op_Implicit((Object)(object)item.m_dropPrefab) ? Utils.GetPrefabName(item.m_dropPrefab) : item.m_shared.m_name);
			return new CombatItem(text + ":q" + item.m_quality + ":" + ((object)item).GetHashCode(), text, item);
		}

		internal static void ApplyDurability(Character actor, ItemData item, float before, DurabilityUseKind kind, string action)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Expected O, but got Unknown
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Expected O, but got Unknown
			if (item != null && !(before < 0f))
			{
				float num = Mathf.Max(0f, before - item.m_durability);
				if (!(num <= 0f))
				{
					DurabilityRuleContext val = new DurabilityRuleContext(action, CombatIdentity.Actor(actor), Item(item), kind, before, num, CombatIdentity.Context(actor), Now, true);
					DurabilityRuleDecision val2 = new DurabilityRuleDecision(num, true, "");
					PipelineEvaluation<DurabilityRuleDecision> val3 = CombatRulePipelineExtensions.GetDurabilityPipeline(Plugin.Core.Rules).Evaluate(val, val2);
					DurabilityRuleDecision val4 = val3.Decision ?? val2;
					item.m_durability = ((val4.Apply && !val3.Vetoed) ? Mathf.Max(0f, before - val4.DurabilityLoss) : before);
				}
			}
		}
	}
	internal static class ConfigFileMigration
	{
		internal const string CurrentFileName = "jg224.bettercombat.cfg";

		internal const string LegacyFileName = "com.jg224.bettercombat.cfg";

		internal static ConfigFile Open(BaseUnityPlugin plugin, string configDirectory, ManualLogSource log)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			if (MoveLegacy(configDirectory) && log != null)
			{
				log.LogInfo((object)"Renamed legacy config com.jg224.bettercombat.cfg to jg224.bettercombat.cfg.");
			}
			return PluginConfigFiles.Attach(plugin, new ConfigFile(Path.Combine(configDirectory, "jg224.bettercombat.cfg"), true, plugin.Info.Metadata));
		}

		internal static bool MoveLegacy(string configDirectory)
		{
			if (!Directory.Exists(configDirectory))
			{
				Directory.CreateDirectory(configDirectory);
			}
			if (ExactPath(configDirectory, "jg224.bettercombat.cfg") != null)
			{
				return false;
			}
			string text = ExactPath(configDirectory, "com.jg224.bettercombat.cfg");
			if (text == null)
			{
				return false;
			}
			string text2 = Path.Combine(configDirectory, "jg224.bettercombat.cfg");
			if (!string.Equals(text, text2, StringComparison.OrdinalIgnoreCase))
			{
				File.Move(text, text2);
				return true;
			}
			string text3 = text2 + ".rename-" + Guid.NewGuid().ToString("N") + ".tmp";
			File.Move(text, text3);
			try
			{
				File.Move(text3, text2);
			}
			catch
			{
				if (File.Exists(text3) && !File.Exists(text))
				{
					File.Move(text3, text);
				}
				throw;
			}
			return true;
		}

		private static string ExactPath(string directory, string fileName)
		{
			return Directory.EnumerateFiles(directory, "*.cfg", SearchOption.TopDirectoryOnly).FirstOrDefault((string path) => string.Equals(Path.GetFileName(path), fileName, StringComparison.Ordinal));
		}
	}
	[BepInPlugin("com.jg224.bettercombat", "Better Combat", "0.5.0")]
	[BepInDependency("com.jg224.modcore", "0.5.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.jg224.bettercombat";

		public const string PluginName = "Better Combat";

		public const string PluginVersion = "0.5.0";

		public const string ModCoreGuid = "com.jg224.modcore";

		public const int ProtocolVersion = 8;

		public static readonly ModuleId ModuleId = new ModuleId("bettercombat");

		private readonly List<IDisposable> _registrations = new List<IDisposable>();

		private Harmony _harmony;

		private BetterCombatRuntime _runtime;

		internal static Plugin Instance { get; private set; }

		internal static ManualLogSource Log { get; private set; }

		internal static BetterCombatConfig Settings { get; private set; }

		internal static CombatPolicy Policy { get; set; } = new CombatPolicy();

		internal static ICoreServices Core { get; private set; }

		internal static BetterCombatRuntime Runtime => Instance?._runtime;

		private void Awake()
		{
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Expected O, but got Unknown
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			Settings = new BetterCombatConfig(ConfigFileMigration.Open((BaseUnityPlugin)(object)this, Paths.ConfigPath, ((BaseUnityPlugin)this).Logger));
			try
			{
				if (!ModCoreApi.IsAvailable)
				{
					throw new InvalidOperationException("ModCore did not initialize before Better Combat.");
				}
				Core = ModCoreApi.Services;
				RegisterWithCore();
				Policy = Settings.CreatePolicy();
				_runtime = new BetterCombatRuntime(Core, Settings, _registrations);
				_harmony = new Harmony("com.jg224.bettercombat");
				_harmony.PatchAll(Assembly.GetExecutingAssembly());
				Core.Modules.SetState(ModuleId, (ModuleRuntimeState)2, "Combat hooks and protocol ready.");
				Game.isModded = true;
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Better Combat 0.5.0 loaded; server policy schema " + 6 + ", protocol " + 8 + "."));
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Better Combat failed to initialize safely: " + ex));
				if (Core != null)
				{
					Core.Modules.SetState(ModuleId, (ModuleRuntimeState)5, ex.Message);
				}
				Shutdown();
				throw;
			}
		}

		private void Update()
		{
			BowDrawCancelInput.Poll();
			DodgeHotkeyInput.Poll();
			_runtime?.Tick(Time.unscaledTimeAsDouble);
		}

		private void OnDestroy()
		{
			Shutdown();
		}

		private void RegisterWithCore()
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			SemanticVersion val = default(SemanticVersion);
			if (!SemanticVersion.TryParse("0.5.0", ref val))
			{
				throw new InvalidOperationException("Better Combat has invalid release version metadata.");
			}
			_registrations.Add(Core.Modules.Register(new ModuleDescriptor(ModuleId, "com.jg224.bettercombat", "Better Combat", val, 8, (ModuleSide)3, (ModuleRequirement)4, 0uL, 1, 1)));
			foreach (IDisposable item in Settings.RegisterMetadata(Core, ModuleId))
			{
				_registrations.Add(item);
			}
			_registrations.Add(Core.Namespaces.Register(ModuleId, (NamespaceKind)1, "jg224.bc.", 1, Array.Empty<string>()));
			_registrations.Add(Core.Namespaces.Register(ModuleId, (NamespaceKind)5, "jg224.bc.net.", 1, Array.Empty<string>()));
			_registrations.Add(Core.Namespaces.Register(ModuleId, (NamespaceKind)9, "jg224.bc.", 1, Array.Empty<string>()));
			CombatRulePipelineExtensions.GetDamagePipeline(Core.Rules);
			CombatRulePipelineExtensions.GetBlockPipeline(Core.Rules);
			CombatRulePipelineExtensions.GetDeathPipeline(Core.Rules);
			CombatRulePipelineExtensions.GetDurabilityPipeline(Core.Rules);
			CombatRulePipelineExtensions.GetSkillGainPipeline(Core.Rules);
		}

		private void Shutdown()
		{
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			BowDrawCancelInput.Reset();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			_harmony = null;
			_runtime?.Dispose();
			_runtime = null;
			for (int num = _registrations.Count - 1; num >= 0; num--)
			{
				try
				{
					_registrations[num].Dispose();
				}
				catch (Exception ex)
				{
					ManualLogSource logger = ((BaseUnityPlugin)this).Logger;
					if (logger != null)
					{
						logger.LogWarning((object)("Registration cleanup failed: " + ex.Message));
					}
				}
			}
			_registrations.Clear();
			if (Core != null)
			{
				Core.Metrics.RemoveOwner(ModuleId);
			}
			Core = null;
			Settings = null;
			Log = null;
			Instance = null;
		}

		internal static void Debug(string message)
		{
			BetterCombatConfig settings = Settings;
			if (settings != null && settings.DebugLogging.Value)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogDebug((object)message);
				}
			}
		}
	}
}
namespace JG224.BetterCombat.Patches
{
	internal static class BowDrawCancelRules
	{
		internal static bool CanCancel(bool policyEnabled, bool localOwner, bool gameplayInputAvailable, bool hotkeyDown, bool drawingBow, bool drawBowWeapon)
		{
			return policyEnabled && localOwner && gameplayInputAvailable && hotkeyDown && drawingBow && drawBowWeapon;
		}
	}
	internal sealed class BowDrawCancelLatch
	{
		private bool _armed;

		internal void Arm()
		{
			_armed = true;
		}

		internal bool ShouldSuppress(bool attack, bool attackHold)
		{
			if (!_armed)
			{
				return false;
			}
			if (attack || attackHold)
			{
				return true;
			}
			_armed = false;
			return false;
		}

		internal void Reset()
		{
			_armed = false;
		}
	}
	internal static class BowDrawCancelInput
	{
		private static readonly FieldRef<Humanoid, float> AttackDrawTime = AccessTools.FieldRefAccess<Humanoid, float>("m_attackDrawTime");

		private static readonly BowDrawCancelLatch HeldAttackLatch = new BowDrawCancelLatch();

		private static Player _cancelledPlayer;

		private static int _cancelledFrame = -1;

		internal static void Poll()
		{
			TryCancel(Player.m_localPlayer);
		}

		internal static bool TryCancel(Player player)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			KeyboardShortcut val = (KeyboardShortcut)(((??)Plugin.Settings?.BowDrawCancelHotkey.Value) ?? new KeyboardShortcut((KeyCode)0, Array.Empty<KeyCode>()));
			ItemData val2 = (Object.op_Implicit((Object)(object)player) ? ((Humanoid)player).GetCurrentWeapon() : null);
			if (!BowDrawCancelRules.CanCancel(Plugin.Policy.Enabled, ResponsiveBlock.IsLocalOwner(player), DodgeHotkeyInput.GameplayInputAvailable(), ((KeyboardShortcut)(ref val)).IsDown(), Object.op_Implicit((Object)(object)player) && ((Character)player).IsDrawingBow(), val2 != null && val2.m_shared != null && WeaponKinds.IsDrawBow(val2.m_shared.m_skillType)))
			{
				return false;
			}
			AttackDrawTime.Invoke((Humanoid)(object)player) = -1f;
			string drawAnimationState = val2.m_shared.m_attack.m_drawAnimationState;
			ZSyncAnimation zAnim = ((Character)player).GetZAnim();
			if ((Object)(object)zAnim != (Object)null && !string.IsNullOrEmpty(drawAnimationState))
			{
				zAnim.SetBool(drawAnimationState, false);
			}
			ICoreServices core = Plugin.Core;
			if (core != null)
			{
				core.Metrics.Increment(Plugin.ModuleId, "bow_draws_cancelled", 1L);
			}
			_cancelledPlayer = player;
			_cancelledFrame = Time.frameCount;
			HeldAttackLatch.Arm();
			return true;
		}

		internal static bool ShouldSuppressControls(Player player, bool attack, bool attackHold)
		{
			if (TryCancel(player))
			{
				return true;
			}
			if (_cancelledPlayer != player)
			{
				return false;
			}
			bool num = HeldAttackLatch.ShouldSuppress(attack, attackHold);
			if (!num && !attack && !attackHold)
			{
				_cancelledPlayer = null;
			}
			return num;
		}

		internal static bool ShouldAllowInteraction(Player player)
		{
			if (_cancelledPlayer == player && _cancelledFrame == Time.frameCount)
			{
				return false;
			}
			return !TryCancel(player);
		}

		internal static void Reset()
		{
			HeldAttackLatch.Reset();
			_cancelledPlayer = null;
			_cancelledFrame = -1;
		}
	}
	[HarmonyPatch(typeof(Player), "SetControls")]
	internal static class BowDrawCancelControlsPatch
	{
		private static void Prefix(Player __instance, ref bool attack, ref bool attackHold)
		{
			if (BowDrawCancelInput.ShouldSuppressControls(__instance, attack, attackHold))
			{
				attack = false;
				attackHold = false;
			}
		}
	}
	[HarmonyPatch(typeof(Player), "Interact")]
	internal static class BowDrawCancelInteractionPatch
	{
		private static bool Prefix(Player __instance)
		{
			return BowDrawCancelInput.ShouldAllowInteraction(__instance);
		}
	}
	internal sealed class DamageState
	{
		internal bool Processed;

		internal string Action;

		internal Character Attacker;

		internal Character Defender;

		internal HitData Effective;

		internal float HealthBefore;

		internal bool StaggeringBefore;
	}
	[HarmonyPatch(typeof(Character), "RPC_Damage")]
	internal static class DamagePipelinePatch
	{
		private static bool Prefix(Character __instance, ref HitData hit, out DamageState __state)
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Expected O, but got Unknown
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			__state = new DamageState();
			if (hit == null || !Plugin.Policy.Enabled || Plugin.Core == null)
			{
				return true;
			}
			ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
			if (!Object.op_Implicit((Object)(object)component) || !component.IsValid() || !component.IsOwner())
			{
				return true;
			}
			Character attacker = hit.GetAttacker();
			string text = CombatRulesBridge.Action("damage");
			DamageRuleContext val = new DamageRuleContext(text, CombatIdentity.Actor(attacker), CombatIdentity.Actor(__instance), hit, (DamageRulePhase)1, CombatIdentity.Context(attacker, __instance), CombatRulesBridge.Now, true);
			DamageRuleDecision val2 = DamageRuleDecision.From(hit);
			PipelineEvaluation<DamageRuleDecision> obj = CombatRulePipelineExtensions.GetDamagePipeline(Plugin.Core.Rules).Evaluate(val, val2);
			DamageRuleDecision val3 = obj.Decision ?? val2;
			if (obj.Vetoed || !val3.ApplyDamage)
			{
				Plugin.Core.Metrics.Increment(Plugin.ModuleId, "damage_vetoed", 1L);
				return false;
			}
			hit = val3.EffectiveHit;
			__state.Processed = true;
			__state.Action = text;
			__state.Attacker = attacker;
			__state.Defender = __instance;
			__state.Effective = hit.Clone();
			__state.HealthBefore = Mathf.Max(0f, __instance.GetHealth());
			__state.StaggeringBefore = __instance.IsStaggering();
			return true;
		}

		private static void Postfix(DamageState __state)
		{
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Expected O, but got Unknown
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Expected O, but got Unknown
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			if (__state != null && __state.Processed && Object.op_Implicit((Object)(object)__state.Defender))
			{
				float num = Mathf.Max(0f, __state.Defender.GetHealth());
				bool flag = !__state.StaggeringBefore && __state.Defender.IsStaggering();
				bool flag2 = __state.Effective.m_dodgeable && __state.Defender.IsDodgeInvincible();
				Plugin.Core.Events.Publish<CombatHitResolvedEvent>(Plugin.ModuleId, new CombatHitResolvedEvent(__state.Action, CombatActorSnapshot.From(CombatIdentity.Actor(__state.Attacker)), CombatActorSnapshot.From(CombatIdentity.Actor(__state.Defender)), new CombatHitSnapshot(__state.Effective), CombatIdentity.Context(__state.Attacker, __state.Defender), __state.HealthBefore, num, num == __state.HealthBefore && !flag2, flag2, flag, CombatRulesBridge.Now, true));
				if (num < __state.HealthBefore)
				{
					Plugin.Runtime?.ObserveCombat(__state.Attacker, __state.Defender);
					Plugin.Core.Metrics.Increment(Plugin.ModuleId, "hits_resolved", 1L);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Character), "CheckDeath")]
	internal static class DeathPipelinePatch
	{
		private static bool Prefix(Character __instance)
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Expected O, but got Unknown
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Expected O, but got Unknown
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)__instance) || __instance.GetHealth() > 0f || Plugin.Core == null)
			{
				return true;
			}
			ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
			if (!Object.op_Implicit((Object)(object)component) || !component.IsValid() || !component.IsOwner())
			{
				return true;
			}
			DeathRuleContext val = new DeathRuleContext(CombatRulesBridge.Action("death"), CombatIdentity.Actor(__instance), CombatActor.None, (HitData)null, (CombatContextFlags)(CombatIdentity.Context(__instance) | 0x80), CombatRulesBridge.Now, true);
			DeathRuleDecision val2 = new DeathRuleDecision(true, (CombatPolicyOverride)0, (CombatPolicyOverride)0, (CombatPolicyOverride)0, "");
			PipelineEvaluation<DeathRuleDecision> obj = CombatRulePipelineExtensions.GetDeathPipeline(Plugin.Core.Rules).Evaluate(val, val2);
			DeathRuleDecision val3 = obj.Decision ?? val2;
			if (!obj.Vetoed && val3.AllowDeath)
			{
				return true;
			}
			__instance.SetHealth(1f);
			Plugin.Core.Metrics.Increment(Plugin.ModuleId, "deaths_vetoed", 1L);
			return false;
		}
	}
	[HarmonyPatch(typeof(Skills), "RaiseSkill")]
	internal static class SkillGainPipelinePatch
	{
		private static readonly FieldRef<Skills, Player> PlayerRef = AccessTools.FieldRefAccess<Skills, Player>("m_player");

		private static bool Prefix(Skills __instance, SkillType skillType, ref float factor)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Expected O, but got Unknown
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Expected O, but got Unknown
			if (Plugin.Core == null || factor < 0f)
			{
				return true;
			}
			Player val = PlayerRef.Invoke(__instance);
			SkillGainRuleContext val2 = new SkillGainRuleContext(CombatRulesBridge.Action("skill"), CombatIdentity.Actor((Character)(object)val), skillType, factor, CombatIdentity.Context((Character)(object)val), CombatRulesBridge.Now, true);
			SkillGainRuleDecision val3 = new SkillGainRuleDecision(factor, true, "");
			PipelineEvaluation<SkillGainRuleDecision> obj = CombatRulePipelineExtensions.GetSkillGainPipeline(Plugin.Core.Rules).Evaluate(val2, val3);
			SkillGainRuleDecision val4 = obj.Decision ?? val3;
			if (obj.Vetoed || !val4.Apply)
			{
				return false;
			}
			factor = val4.Amount;
			return true;
		}
	}
	internal static class DefenseRules
	{
		internal static bool CanPerfectBlock(bool ranged)
		{
			return !ranged;
		}

		internal static bool ShouldCancelIntoBlock(BlockCancelMode mode, bool newBlockPress, bool isBlocking, bool inAttack, bool committed)
		{
			if (!isBlocking || !inAttack)
			{
				return false;
			}
			if (mode == BlockCancelMode.Full)
			{
				return newBlockPress;
			}
			return mode == BlockCancelMode.RecoveryOnly && committed && newBlockPress;
		}
	}
	internal static class ResponsiveBlock
	{
		private static readonly FieldRef<Character, bool> RawBlocking = AccessTools.FieldRefAccess<Character, bool>("m_blocking");

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

		private static readonly FieldRef<Attack, bool> AttackAborted = AccessTools.FieldRefAccess<Attack, bool>("m_abortAttack");

		private static readonly FieldRef<Player, float> QueuedPrimary = AccessTools.FieldRefAccess<Player, float>("m_queuedAttackTimer");

		private static readonly FieldRef<Player, float> QueuedSecondary = AccessTools.FieldRefAccess<Player, float>("m_queuedSecondAttackTimer");

		private static readonly HashSet<Attack> CommittedAttacks = new HashSet<Attack>();

		internal static bool IsRawBlocking(Player player)
		{
			if (Object.op_Implicit((Object)(object)player))
			{
				return RawBlocking.Invoke((Character)(object)player);
			}
			return false;
		}

		internal static void SetRawBlocking(Player player, bool value)
		{
			if (Object.op_Implicit((Object)(object)player))
			{
				RawBlocking.Invoke((Character)(object)player) = value;
			}
		}

		internal static bool IsLocalOwner(Player player)
		{
			if (!Object.op_Implicit((Object)(object)player) || (Object)(object)player != (Object)(object)Player.m_localPlayer)
			{
				return false;
			}
			ZNetView component = ((Component)player).GetComponent<ZNetView>();
			if (Object.op_Implicit((Object)(object)component) && component.IsValid())
			{
				return component.IsOwner();
			}
			return true;
		}

		internal static bool CanBeginBlock(Player player)
		{
			if (Object.op_Implicit((Object)(object)player) && !((Character)player).IsDead() && !((Character)player).InDodge() && !((Character)player).InPlaceMode() && !((Character)player).IsEncumbered() && !((Character)player).InMinorAction())
			{
				return !((Character)player).IsStaggering();
			}
			return false;
		}

		internal static void ApplyInput(Player player, bool wasBlocking)
		{
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)player))
			{
				return;
			}
			bool flag = IsRawBlocking(player);
			if (!flag || !Plugin.Policy.Enabled || !IsLocalOwner(player) || !CanBeginBlock(player))
			{
				return;
			}
			bool flag2 = ((Character)player).InAttack();
			Attack val = (flag2 ? CurrentAttack.Invoke((Humanoid)(object)player) : null);
			bool newBlockPress = !wasBlocking;
			bool committed = val != null && CommittedAttacks.Contains(val);
			bool flag3 = val != null && DefenseRules.ShouldCancelIntoBlock(Plugin.Policy.BlockCancel, newBlockPress, flag, flag2, committed);
			if (flag2 && !flag3)
			{
				return;
			}
			if (Plugin.Policy.InstantBlockFacing)
			{
				player.FaceLookDirection();
			}
			if (flag3)
			{
				val.Abort();
				QueuedPrimary.Invoke(player) = 0f;
				QueuedSecondary.Invoke(player) = 0f;
				ZSyncAnimation zAnim = ((Character)player).GetZAnim();
				if ((Object)(object)zAnim != (Object)null)
				{
					zAnim.SetTrigger("attack_abort");
				}
				ICoreServices core = Plugin.Core;
				if (core != null)
				{
					core.Metrics.Increment(Plugin.ModuleId, "attacks_cancelled_into_block", 1L);
				}
			}
		}

		internal static bool IsCancelling(Player player)
		{
			if (!Plugin.Policy.Enabled || Plugin.Policy.BlockCancel == BlockCancelMode.Off || !IsLocalOwner(player) || !IsRawBlocking(player))
			{
				return false;
			}
			Attack val = CurrentAttack.Invoke((Humanoid)(object)player);
			if (val != null)
			{
				return AttackAborted.Invoke(val);
			}
			return false;
		}

		internal static bool SuppressAttackWhileBlocking(Player player)
		{
			if (Plugin.Policy.Enabled && Plugin.Policy.BlockCancel != BlockCancelMode.Off && IsLocalOwner(player) && IsRawBlocking(player))
			{
				return CanBeginBlock(player);
			}
			return false;
		}

		internal static void MarkCommitted(Attack attack, Character character)
		{
			if (attack != null)
			{
				Player val = (Player)(object)((character is Player) ? character : null);
				if (val != null && IsLocalOwner(val))
				{
					CommittedAttacks.Add(attack);
				}
			}
		}

		internal static void ClearAttack(Attack attack)
		{
			if (attack != null)
			{
				CommittedAttacks.Remove(attack);
			}
		}

		internal static void Clear()
		{
			CommittedAttacks.Clear();
		}
	}
	[HarmonyPatch(typeof(Player), "SetControls")]
	internal static class BlockInputPatch
	{
		private static void Prefix(Player __instance, out bool __state)
		{
			__state = ResponsiveBlock.IsRawBlocking(__instance);
		}

		private static void Postfix(Player __instance, bool __state)
		{
			ResponsiveBlock.ApplyInput(__instance, __state);
		}
	}
	[HarmonyPatch(typeof(Player), "InAttack")]
	internal static class BlockCancelAttackStatePatch
	{
		private static void Postfix(Player __instance, ref bool __result)
		{
			if (__result && ResponsiveBlock.IsCancelling(__instance))
			{
				__result = false;
			}
		}
	}
	[HarmonyPatch(typeof(Humanoid), "StartAttack")]
	internal static class BlockPriorityPatch
	{
		private static bool Prefix(Humanoid __instance, ref bool __result)
		{
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if (val == null || !ResponsiveBlock.SuppressAttackWhileBlocking(val))
			{
				return true;
			}
			__result = false;
			return false;
		}
	}
	internal sealed class BlockState
	{
		internal bool Changed;

		internal bool Vetoed;

		internal float Timer;

		internal float Drain;

		internal float PerfectDrain;

		internal float StaminaBefore;

		internal float DurabilityBefore = -1f;

		internal float Incoming;

		internal bool Perfect;

		internal string Action;

		internal ItemData Item;

		internal Character Attacker;

		internal Humanoid Defender;
	}
	[HarmonyPatch(typeof(Humanoid), "BlockAttack")]
	internal static class BlockAttackPatch
	{
		private static readonly FieldRef<Humanoid, float> BlockTimer = AccessTools.FieldRefAccess<Humanoid, float>("m_blockTimer");

		private static bool Prefix(Humanoid __instance, HitData hit, Character attacker, ref bool __result, out BlockState __state)
		{
			//IL_016a: 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_017b: Expected O, but got Unknown
			//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Expected O, but got Unknown
			//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f9: Invalid comparison between Unknown and I4
			//IL_020f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0215: Invalid comparison between Unknown and I4
			__state = new BlockState();
			if (!Plugin.Policy.Enabled || hit == null || Plugin.Core == null)
			{
				return true;
			}
			__state.Changed = true;
			__state.Defender = __instance;
			__state.Attacker = attacker;
			__state.Timer = BlockTimer.Invoke(__instance);
			__state.Drain = __instance.m_blockStaminaDrain;
			__state.PerfectDrain = __instance.m_perfectBlockStaminaDrain;
			BlockState obj = __state;
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			obj.StaminaBefore = ((val != null) ? val.GetStamina() : 0f);
			__state.Incoming = hit.GetTotalDamage();
			__state.Perfect = DefenseRules.CanPerfectBlock(hit.m_ranged) && __state.Timer >= 0f && __state.Timer < Plugin.Policy.PerfectBlockWindow;
			__state.Action = CombatRulesBridge.Action("block");
			__state.Item = Traverse.Create((object)__instance).Method("GetCurrentBlocker", Array.Empty<object>()).GetValue<ItemData>();
			if (__state.Item != null)
			{
				__state.DurabilityBefore = __state.Item.m_durability;
			}
			BlockRuleContext val2 = new BlockRuleContext(__state.Action, CombatIdentity.Actor(attacker), CombatIdentity.Actor((Character)(object)__instance), CombatRulesBridge.Item(__state.Item), hit, (BlockRulePhase)1, __state.Perfect, Mathf.Max(0f, __state.Timer), 0f, CombatIdentity.Context(attacker, (Character)(object)__instance), CombatRulesBridge.Now, true);
			BlockRuleDecision val3 = new BlockRuleDecision(true, (CombatPolicyOverride)0, 1f, __state.Perfect ? Plugin.Policy.PerfectBlockStaminaMultiplier : Plugin.Policy.BlockStaminaMultiplier, 1f, true, "");
			PipelineEvaluation<BlockRuleDecision> obj2 = CombatRulePipelineExtensions.GetBlockPipeline(Plugin.Core.Rules).Evaluate(val2, val3);
			BlockRuleDecision val4 = obj2.Decision ?? val3;
			if (obj2.Vetoed || !val4.AllowBlock)
			{
				__state.Vetoed = true;
				__result = false;
				return false;
			}
			if ((int)val4.PerfectBlock == 1)
			{
				__state.Perfect = DefenseRules.CanPerfectBlock(hit.m_ranged);
			}
			if ((int)val4.PerfectBlock == 2)
			{
				__state.Perfect = false;
			}
			BlockTimer.Invoke(__instance) = (__state.Perfect ? 0f : Mathf.Max(0.251f, __state.Timer));
			__instance.m_blockStaminaDrain *= val4.StaminaCostMultiplier;
			__instance.m_perfectBlockStaminaDrain *= val4.StaminaCostMultiplier;
			return true;
		}

		private static void Postfix(Humanoid __instance, HitData hit, bool __result, BlockState __state)
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Expected O, but got Unknown
			if (__state == null || !__state.Changed)
			{
				return;
			}
			BlockTimer.Invoke(__instance) = __state.Timer;
			__instance.m_blockStaminaDrain = __state.Drain;
			__instance.m_perfectBlockStaminaDrain = __state.PerfectDrain;
			if (!__state.Vetoed)
			{
				CombatRulesBridge.ApplyDurability((Character)(object)__instance, __state.Item, __state.DurabilityBefore, (DurabilityUseKind)2, __state.Action);
				float num = ((hit != null) ? hit.GetTotalDamage() : __state.Incoming);
				IGameEventBus events = Plugin.Core.Events;
				ModuleId moduleId = Plugin.ModuleId;
				string action = __state.Action;
				CombatActorSnapshot obj = CombatActorSnapshot.From(CombatIdentity.Actor(__state.Attacker));
				CombatActorSnapshot obj2 = CombatActorSnapshot.From(CombatIdentity.Actor((Character)(object)__instance));
				CombatItemSnapshot obj3 = CombatItemSnapshot.From(CombatRulesBridge.Item(__state.Item));
				bool perfect = __state.Perfect;
				float num2 = Mathf.Max(0f, __state.Incoming);
				float num3 = Mathf.Max(0f, __state.Incoming - num);
				float staminaBefore = __state.StaminaBefore;
				Player val = (Player)(object)((__instance is Player) ? __instance : null);
				events.Publish<CombatBlockResolvedEvent>(moduleId, new CombatBlockResolvedEvent(action, obj, obj2, obj3, perfect, num2, num3, Mathf.Max(0f, staminaBefore - ((val != null) ? val.GetStamina() : 0f)), CombatIdentity.Context(__state.Attacker, (Character)(object)__instance), CombatRulesBridge.Now, true));
				if (__result)
				{
					Plugin.Runtime?.ObserveCombat(__state.Attacker, (Character)(object)__instance);
				}
				if (__result && __state.Perfect)
				{
					TrainingFeedback.Show(__instance, "Perfect block · " + Mathf.RoundToInt(__state.Timer * 1000f) + " ms");
				}
			}
		}
	}
	[HarmonyPatch(typeof(Player), "GetDodgeStaminaUse")]
	internal static class DodgeStaminaPatch
	{
		private static void Postfix(ref float __result)
		{
			if (Plugin.Policy.Enabled)
			{
				__result = Mathf.Max(0f, __result * Plugin.Policy.DodgeStaminaMultiplier);
			}
		}
	}
	[HarmonyPatch(typeof(Player), "RPC_HitWhileDodging")]
	internal static class PerfectDodgePatch
	{
		private static void Prefix(Player __instance, out float __state)
		{
			__state = __instance.m_perfectDodgeStaminaReturnMultiplier;
			if (Plugin.Policy.Enabled)
			{
				__instance.m_perfectDodgeStaminaReturnMultiplier *= Plugin.Policy.PerfectDodgeRefundMultiplier;
			}
		}

		private static void Postfix(Player __instance, float __state)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Expected O, but got Unknown
			__instance.m_perfectDodgeStaminaReturnMultiplier = __state;
			TrainingFeedback.Show((Humanoid)(object)__instance, "Perfect dodge");
			if (Plugin.Core != null)
			{
				Plugin.Core.Events.Publish<CombatDodgeResolvedEvent>(Plugin.ModuleId, new CombatDodgeResolvedEvent(CombatRulesBridge.Action("dodge"), CombatActorSnapshot.From(CombatIdentity.Actor((Character)(object)__instance)), CombatActorSnapshot.None, true, 0f, 0f, CombatIdentity.Context((Character)(object)__instance), CombatRulesBridge.Now, true));
			}
		}
	}
	internal static class DodgeDirection
	{
		internal static bool HasDirection(Vector3 direction)
		{
			return ((Vector3)(ref direction)).sqrMagnitude >= 0.01f;
		}

		internal static Vector3 ResolveWorld(Vector3 currentMoveDirection, Vector3 lookDirection)
		{
			//IL_000c: 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_0016: 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_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			currentMoveDirection.y = 0f;
			if (HasDirection(currentMoveDirection))
			{
				return ((Vector3)(ref currentMoveDirection)).normalized;
			}
			lookDirection.y = 0f;
			if (HasDirection(lookDirection))
			{
				return -((Vector3)(ref lookDirection)).normalized;
			}
			return Vector3.back;
		}
	}
	internal static class DodgePriorityRules
	{
		internal static bool IsNativeRequest(bool dodge, bool jump, bool blockPressed, bool blockHeld, bool alreadyBlocking)
		{
			if (!dodge)
			{
				if (jump)
				{
					return blockPressed || blockHeld || alreadyBlocking;
				}
				return false;
			}
			return true;
		}
	}
	internal sealed class DodgeHotkeyState
	{
		internal bool Requested;

		internal float QueuedBefore;

		internal double CapturedAt;
	}
	internal sealed class DodgePressBuffer
	{
		private readonly double _retentionSeconds;

		private bool _wasHeld;

		private bool _pending;

		private double _capturedAt;

		internal DodgePressBuffer(double retentionSeconds)
		{
			_retentionSeconds = Math.Max(0.0, retentionSeconds);
		}

		internal void Poll(bool held, bool inputAvailable, double now)
		{
			if (!inputAvailable)
			{
				_wasHeld = held;
				_pending = false;
				return;
			}
			if (held && !_wasHeld)
			{
				_pending = true;
				_capturedAt = now;
			}
			_wasHeld = held;
			if (_pending && now - _capturedAt > _retentionSeconds)
			{
				_pending = false;
			}
		}

		internal bool TryConsume(double now, out double capturedAt)
		{
			capturedAt = _capturedAt;
			if (!_pending || now - _capturedAt > _retentionSeconds)
			{
				_pending = false;
				return false;
			}
			_pending = false;
			return true;
		}

		internal void Discard()
		{
			_pending = false;
		}

		internal void Reset()
		{
			_wasHeld = false;
			_pending = false;
			_capturedAt = 0.0;
		}
	}
	internal static class DodgeHotkeyInput
	{
		private const double PressRetentionSeconds = 0.15;

		private static readonly FieldRef<Player, float> QueuedDodge = AccessTools.FieldRefAccess<Player, float>("m_queuedDodgeTimer");

		private static readonly FieldRef<Character, Vector3> MoveDirection = AccessTools.FieldRefAccess<Character, Vector3>("m_moveDir");

		private static readonly MethodInfo NativeDodge = AccessTools.DeclaredMethod(typeof(Player), "Dodge", new Type[1] { typeof(Vector3) }, (Type[])null);

		private static readonly DodgePressBuffer PressBuffer = new DodgePressBuffer(0.15);

		private static KeyboardShortcut _trackedShortcut = new KeyboardShortcut((KeyCode)0, Array.Empty<KeyCode>());

		internal static bool GameplayInputAvailable()
		{
			if (!Console.IsVisible() && !Menu.IsVisible() && !TextInput.IsVisible() && !InventoryGui.IsVisible() && !StoreGui.IsVisible() && !Minimap.IsOpen())
			{
				if (Object.op_Implicit((Object)(object)Chat.instance))
				{
					return !Chat.instance.HasFocus();
				}
				return true;
			}
			return false;
		}

		internal static DodgeHotkeyState Request(Player player, bool nativeDodgePressed)
		{
			DodgeHotkeyState dodgeHotkeyState = new DodgeHotkeyState();
			Poll();
			if (Plugin.Settings == null || !Plugin.Policy.Enabled || !ResponsiveBlock.IsLocalOwner(player) || !GameplayInputAvailable())
			{
				return dodgeHotkeyState;
			}
			double unscaledTimeAsDouble = Time.unscaledTimeAsDouble;
			if (!PressBuffer.TryConsume(unscaledTimeAsDouble, out var capturedAt) && !nativeDodgePressed)
			{
				return dodgeHotkeyState;
			}
			if (nativeDodgePressed)
			{
				capturedAt = unscaledTimeAsDouble;
			}
			dodgeHotkeyState.Requested = true;
			dodgeHotkeyState.QueuedBefore = QueuedDodge.Invoke(player);
			dodgeHotkeyState.CapturedAt = capturedAt;
			DodgePriority.CancelCompetingActions(player);
			DodgePriority.Mark(player);
			return dodgeHotkeyState;
		}

		internal static void Complete(Player player, DodgeHotkeyState state)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			if (state == null || !state.Requested || QueuedDodge.Invoke(player) > state.QueuedBefore)
			{
				return;
			}
			Vector3 val = DodgeDirection.ResolveWorld(MoveDirection.Invoke((Character)(object)player), ((Character)player).GetLookDir());
			try
			{
				DodgePriority.CancelCompetingActions(player);
				DodgePriority.EnterLockOverride(player);
				try
				{
					NativeDodge.Invoke(player, new object[1] { val });
				}
				finally
				{
					DodgePriority.ExitLockOverride();
				}
				if (QueuedDodge.Invoke(player) > state.QueuedBefore)
				{
					ICoreServices core = Plugin.Core;
					if (core != null)
					{
						core.Metrics.Increment(Plugin.ModuleId, "hotkey_dodges_queued", 1L);
					}
				}
				Plugin.Debug("Dodge hotkey consumed after " + ((Time.unscaledTimeAsDouble - state.CapturedAt) * 1000.0).ToString("0.0") + " ms.");
			}
			catch (TargetInvocationException ex)
			{
				Plugin.Log.LogError((object)("Native dodge invocation failed: " + (ex.InnerException ?? ex)));
			}
		}

		internal static void Poll()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			BetterCombatConfig settings = Plugin.Settings;
			if (settings == null)
			{
				Clear();
				return;
			}
			KeyboardShortcut value = settings.DodgeHotkey.Value;
			object obj = value;
			if (!((object)Unsafe.As<KeyboardShortcut, KeyboardShortcut>(ref _trackedShortcut)/*cast due to .constrained prefix*/).Equals(obj))
			{
				_trackedShortcut = value;
				PressBuffer.Reset();
			}
			bool held = IsShortcutHeld(value);
			bool inputAvailable = Plugin.Policy.Enabled && Object.op_Implicit((Object)(object)Player.m_localPlayer) && GameplayInputAvailable();
			PressBuffer.Poll(held, inputAvailable, Time.unscaledTimeAsDouble);
		}

		internal static void Clear()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			_trackedShortcut = new KeyboardShortcut((KeyCode)0, Array.Empty<KeyCode>());
			PressBuffer.Reset();
			DodgePriority.Clear();
		}

		private static bool IsShortcutHeld(KeyboardShortcut shortcut)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			if ((int)((KeyboardShortcut)(ref shortcut)).MainKey == 0 || !ZInput.GetKey(((KeyboardShortcut)(ref shortcut)).MainKey, true))
			{
				return false;
			}
			foreach (KeyCode modifier in ((KeyboardShortcut)(ref shortcut)).Modifiers)
			{
				if (!ZInput.GetKey(modifier, true))
				{
					return false;
				}
			}
			return true;
		}
	}
	internal static class DodgePriority
	{
		private static readonly FieldRef<Humanoid, Attack> CurrentAttack = AccessTools.FieldRefAccess<Humanoid, Attack>("m_currentAttack");

		private static readonly FieldRef<Player, float> QueuedPrimary = AccessTools.FieldRefAccess<Player, float>("m_queuedAttackTimer");

		private static readonly FieldRef<Player, float> QueuedSecondary = AccessTools.FieldRefAccess<Player, float>("m_queuedSecondAttackTimer");

		private static readonly FieldRef<Player, float> QueuedDodge = AccessTools.FieldRefAccess<Player, float>("m_queuedDodgeTimer");

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

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

		[ThreadStatic]
		private static Player _lockOverridePlayer;

		[ThreadStatic]
		private static int _lockOverrideDepth;

		private static Player _priorityPlayer;

		internal static void Mark(Player player)
		{
			_priorityPlayer = player;
		}

		internal static bool IsMarked(Player player)
		{
			if (Object.op_Implicit((Object)(object)player))
			{
				return _priorityPlayer == player;
			}
			return false;
		}

		internal static void CancelCompetingActions(Player player)
		{
			if (Object.op_Implicit((Object)(object)player))
			{
				Attack val = CurrentAttack.Invoke((Humanoid)(object)player);
				if (val != null)
				{
					val.Abort();
				}
				QueuedPrimary.Invoke(player) = 0f;
				QueuedSecondary.Invoke(player) = 0f;
				ClearActionQueue.Invoke(player, null);
				StopEmote.Invoke(player, null);
				ResponsiveBlock.SetRawBlocking(player, value: false);
				ZSyncAnimation zAnim = ((Character)player).GetZAnim();
				if (val != null && (Object)(object)zAnim != (Object)null)
				{
					zAnim.SetTrigger("attack_abort");
				}
			}
		}

		internal static bool BeginUpdate(Player player)
		{
			if (!IsMarked(player))
			{
				return false;
			}
			CancelCompetingActions(player);
			EnterLockOverride(player);
			return true;
		}

		internal static void EndUpdate(Player player, bool active)
		{
			if (active)
			{
				ExitLockOverride();
				if (!Object.op_Implicit((Object)(object)player) || QueuedDodge.Invoke(player) <= 0f)
				{
					_priorityPlayer = null;
				}
			}
		}

		internal static void EnterLockOverride(Player player)
		{
			_lockOverridePlayer = player;
			_lockOverrideDepth++;
		}

		internal static void ExitLockOverride()
		{
			if (_lockOverrideDepth > 0)
			{
				_lockOverrideDepth--;
			}
			if (_lockOverrideDepth == 0)
			{
				_lockOverridePlayer = null;
			}
		}

		internal static bool Overrides(Character character)
		{
			if (_lockOverrideDepth > 0 && Object.op_Implicit((Object)(object)character))
			{
				return (object)_lockOverridePlayer == character;
			}
			return false;
		}

		internal static void Clear()
		{
			_priorityPlayer = null;
			_lockOverridePlayer = null;
			_lockOverrideDepth = 0;
		}
	}
	[HarmonyPatch(typeof(Player), "SetControls")]
	internal static class DodgeHotkeyPatch
	{
		private static void Prefix(Player __instance, ref bool attack, ref bool attackHold, ref bool secondaryAttack, ref bool secondaryAttackHold, ref bool block, ref bool blockHold, ref bool jump, ref bool dodge, out DodgeHotkeyState __state)
		{
			bool nativeDodgePressed = DodgePriorityRules.IsNativeRequest(dodge, jump, block, blockHold, ResponsiveBlock.IsRawBlocking(__instance));
			__state = DodgeHotkeyInput.Request(__instance, nativeDodgePressed);
			if (__state != null && __state.Requested)
			{
				attack = false;
				attackHold = false;
				secondaryAttack = false;
				secondaryAttackHold = false;
				block = false;
				blockHold = false;
				jump = false;
				dodge = false;
			}
		}

		private static void Postfix(Player __instance, DodgeHotkeyState __state)
		{
			DodgeHotkeyInput.Complete(__instance, __state);
		}
	}
	[HarmonyPatch(typeof(Player), "UpdateDodge")]
	internal static class PriorityDodgeUpdatePatch
	{
		private static void Prefix(Player __instance, out bool __state)
		{
			__state = DodgePriority.BeginUpdate(__instance);
		}

		private static Exception Finalizer(Player __instance, bool __state, Exception __exception)
		{
			DodgePriority.EndUpdate(__instance, __state);
			return __exception;
		}
	}
	[HarmonyPatch(typeof(Character), "IsEncumbered")]
	internal static class PriorityDodgeEncumbrancePatch
	{
		private static void Postfix(Character __instance, ref bool __result)
		{
			if (__result && DodgePriority.Overrides(__instance))
			{
				__result = false;
			}
		}
	}
	[HarmonyPatch(typeof(Character), "IsStaggering")]
	internal static class PriorityDodgeStaggerPatch
	{
		private static void Postfix(Character __instance, ref bool __result)
		{
			if (__result && DodgePriority.Overrides(__instance))
			{
				__result = false;
			}
		}
	}
	[HarmonyPatch(typeof(Player), "InAttack")]
	internal static class PriorityDodgeAttackPatch
	{
		private static void Postfix(Player __instance, ref bool __result)
		{
			if (__result && DodgePriority.Overrides((Character)(object)__instance))
			{
				__result = false;
			}
		}
	}
	internal static class EquipmentRules
	{
		internal static bool IsOneHandedWeapon(ItemType type)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			return (int)type == 3;
		}

		internal static bool IsOneHandedWeapon(ItemData item)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			if (item != null && item.m_shared != null)
			{
				return IsOneHandedWeapon(item.m_shared.m_itemType);
			}
			return false;
		}

		internal static bool IsShield(ItemType type)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			return (int)type == 5;
		}

		internal static bool IsShield(ItemData item)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			if (item != null && item.m_shared != null)
			{
				return IsShield(item.m_shared.m_itemType);
			}
			return false;
		}

		internal static bool IsUsable(ItemData item)
		{
			if (item != null && item.m_shared != null)
			{
				if (item.m_shared.m_useDurability)
				{
					return item.m_durability > 0f;
				}
				return true;
			}
			return false;
		}

		internal static bool CanRestore(bool swimming, bool onGround, bool dead, bool inAttack, bool inDodge)
		{
			if ((!swimming || onGround) && !dead && !inAttack)
			{
				return !inDodge;
			}
			return false;
		}

		internal static bool IsDeliberateReplacement(ItemData current, ItemData remembered)
		{
			if (current != null)
			{
				return current != remembered;
			}
			return false;
		}
	}
	internal static class EquipmentAutomation
	{
		private static readonly FieldRef<Humanoid, ItemData> RightItem = AccessTools.FieldRefAccess<Humanoid, ItemData>("m_rightItem");

		private static readonly FieldRef<Humanoid, ItemData> LeftItem = AccessTools.FieldRefAccess<Humanoid, ItemData>("m_leftItem");

		private static ItemData _lastShield;

		private static ItemData _waterRight;

		private static ItemData _waterLeft;

		private static bool _restoringWaterItems;

		internal static void OnEquipped(Player player, ItemData item, bool equipped)
		{
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			BetterCombatConfig settings = Plugin.Settings;
			if (!equipped || settings == null || !Plugin.Policy.Enabled || !ResponsiveBlock.IsLocalOwner(player))
			{
				return;
			}
			if (EquipmentRules.IsShield(item))
			{
				_lastShield = item;
			}
			else
			{
				if (_restoringWaterItems || !settings.AutoEquipShield.Value || !EquipmentRules.IsOneHandedWeapon(item) || GetLeftItem((Humanoid)(object)player) != null)
				{
					return;
				}
				ItemData val = FindShield(((Humanoid)player).GetInventory());
				if (val != null && ((Humanoid)player).EquipItem(val, true))
				{
					ICoreServices core = Plugin.Core;
					if (core != null)
					{
						core.Metrics.Increment(Plugin.ModuleId, "shields_auto_equipped", 1L);
					}
				}
			}
		}

		internal static void CaptureSwimmingItems(Player player, bool onlyRightHand)
		{
			BetterCombatConfig settings = Plugin.Settings;
			if (settings != null && settings.RestoreWeaponsAfterSwimming.Value && Plugin.Policy.Enabled && ResponsiveBlock.IsLocalOwner(player) && ((Character)player).IsSwimming() && !((Character)player).IsOnGround())
			{
				if (_waterRight == null && GetRightItem((Humanoid)(object)player) != null)
				{
					_waterRight = GetRightItem((Humanoid)(object)player);
				}
				if (!onlyRightHand && _waterLeft == null && GetLeftItem((Humanoid)(object)player) != null)
				{
					_waterLeft = GetLeftItem((Humanoid)(object)player);
				}
			}
		}

		internal static void TryRestoreSwimmingItems(Player player)
		{
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			if (!ResponsiveBlock.IsLocalOwner(player) || (_waterRight == null && _waterLeft == null))
			{
				return;
			}
			BetterCombatConfig settings = Plugin.Settings;
			if (settings == null || !settings.RestoreWeaponsAfterSwimming.Value || !Plugin.Policy.Enabled)
			{
				ClearWaterItems();
			}
			else
			{
				if (!EquipmentRules.CanRestore(((Character)player).IsSwimming(), ((Character)player).IsOnGround(), ((Character)player).IsDead(), ((Character)player).InAttack(), ((Character)player).InDodge()))
				{
					return;
				}
				ItemData rightItem = GetRightItem((Humanoid)(object)player);
				ItemData leftItem = GetLeftItem((Humanoid)(object)player);
				if (EquipmentRules.IsDeliberateReplacement(rightItem, _waterRight) || EquipmentRules.IsDeliberateReplacement(leftItem, _waterLeft))
				{
					ClearWaterItems();
					return;
				}
				Inventory inventory = ((Humanoid)player).GetInventory();
				_restoringWaterItems = true;
				try
				{
					ItemData obj = RestoreHand(player, inventory, _waterRight, rightHand: true);
					ItemData val = RestoreHand(player, inventory, _waterLeft, rightHand: false);
					if (obj == null && val == null)
					{
						ClearWaterItems();
					}
				}
				finally
				{
					_restoringWaterItems = false;
				}
				if (_waterRight == null && _waterLeft == null)
				{
					ICoreServices core = Plugin.Core;
					if (core != null)
					{
						core.Metrics.Increment(Plugin.ModuleId, "swim_loadouts_restored", 1L);
					}
				}
			}
		}

		internal static void Clear()
		{
			_lastShield = null;
			ClearWaterItems();
			_restoringWaterItems = false;
		}

		private static ItemData FindShield(Inventory inventory)
		{
			if (inventory == null)
			{
				return null;
			}
			if (_lastShield != null && inventory.ContainsItem(_lastShield) && EquipmentRules.IsUsable(_lastShield))
			{
				return _lastShield;
			}
			foreach (ItemData item in inventory.GetAllItemsInGridOrder())
			{
				if (EquipmentRules.IsShield(item) && EquipmentRules.IsUsable(item))
				{
					return item;
				}
			}
			return null;
		}

		private static ItemData RestoreHand(Player player, Inventory inventory, ItemData remembered, bool rightHand)
		{
			if (remembered == null)
			{
				return null;
			}
			if (inventory == null || !inventory.ContainsItem(remembered) || !EquipmentRules.IsUsable(remembered))
			{
				return null;
			}
			ItemData val = (rightHand ? GetRightItem((Humanoid)(object)player) : GetLeftItem((Humanoid)(object)player));
			if (val == remembered)
			{
				return null;
			}
			if (val != null)
			{
				return null;
			}
			if (!((Humanoid)player).EquipItem(remembered, true))
			{
				return remembered;
			}
			return null;
		}

		private static ItemData GetRightItem(Humanoid humanoid)
		{
			return RightItem.Invoke(humanoid);
		}

		private static ItemData GetLeftItem(Humanoid humanoid)
		{
			return LeftItem.Invoke(humanoid);
		}

		private static void ClearWaterItems()
		{
			_waterRight = null;
			_waterLeft = null;
		}
	}
	[HarmonyPatch(typeof(Humanoid), "EquipItem")]
	internal static class AutoEquipShieldPatch
	{
		private static void Postfix(Humanoid __instance, ItemData item, bool __result)
		{
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if (val != null)
			{
				EquipmentAutomation.OnEquipped(val, item, __result);
			}
		}
	}
	[HarmonyPatch(typeof(Humanoid), "HideHandItems")]
	internal static class RememberSwimmingItemsPatch
	{
		private static void Prefix(Humanoid __instance, bool onlyRightHand)
		{
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if (val != null)
			{
				EquipmentAutomation.CaptureSwimmingItems(val, onlyRightHand);
			}
		}
	}
	[HarmonyPatch(typeof(Player), "Update")]
	internal static class RestoreSwimmingItemsPatch
	{
		private static void Postfix(Player __instance)
		{
			EquipmentAutomation.TryRestoreSwimmingItems(__instance);
		}
	}
	internal static class KnockbackMath
	{
		internal const float NativeForceScale = 2.5f;

		internal const float NativeForceDissipation = 100f;

		internal static float ScaleAndCap(float pushForce, float targetMultiplier, float movementModifier, float bodyMass, float durationLimit)
		{
			if (!Finite(pushForce) || !Finite(targetMultiplier) || !Finite(movementModifier) || !Finite(bodyMass) || !Finite(durationLimit))
			{
				return 0f;
			}
			if (pushForce <= 0f || targetMultiplier <= 0f || bodyMass <= 0f || durationLimit <= 0f)
			{
				return 0f;
			}
			float num = Mathf.Clamp01(1f + movementModifier);
			if (num <= 0f)
			{
				return 0f;
			}
			float num2 = pushForce * targetMultiplier;
			float num3 = 100f * durationLimit * bodyMass / (num * 2.5f);
			return Mathf.Min(num2, num3);
		}

		internal static void ApplyVelocity(ref Vector3 velocity, Vector3 storedPushForce, float targetSpeed, bool dampened)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			if (Finite(storedPushForce.x) && Finite(storedPushForce.y) && Finite(storedPushForce.z) && Finite(targetSpeed) && !(storedPushForce == Vector3.zero) && !(targetSpeed <= 0f))
			{
				Vector3 normalized = ((Vector3)(ref storedPushForce)).normalized;
				float num = Vector3.Dot(normalized, velocity);
				if (num < targetSpeed)
				{
					velocity += normalized * (targetSpeed - num);
				}
				if (dampened)
				{
					velocity *= 0.5f;
				}
			}
		}