Decompiled source of GoosCombatOverhaul v1.0.0

BepInEx/plugins/GooCombatOverhaul/GooCombatOverhaul.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("GooCombatOverhaul")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("GooCombatOverhaul")]
[assembly: AssemblyTitle("GooCombatOverhaul")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace GooCombatOverhaul
{
	[BepInPlugin("goo.valheim.gooscombatoverhaul", "Goo's Combat Overhaul", "1.0.0")]
	public sealed class GooCombatOverhaulPlugin : BaseUnityPlugin
	{
		internal sealed class CategoryConfig
		{
			public ConfigEntry<StaggerApplicationMode> PrimaryStaggerMode { get; }

			public ConfigEntry<float> PrimaryStaggerPowerValue { get; }

			public ConfigEntry<StaggerApplicationMode> SecondaryStaggerMode { get; }

			public ConfigEntry<float> SecondaryStaggerPowerValue { get; }

			public ConfigEntry<HyperArmorMode> PrimaryHyperArmorMode { get; }

			public ConfigEntry<HyperArmorMode> SecondaryHyperArmorMode { get; }

			public ConfigEntry<float> PrimaryDamageTakenMultiplier { get; }

			public ConfigEntry<float> SecondaryDamageTakenMultiplier { get; }

			public ConfigEntry<float> PrimaryStaggerTakenMultiplier { get; }

			public ConfigEntry<float> SecondaryStaggerTakenMultiplier { get; }

			public ConfigEntry<float> PrimaryKnockbackTakenMultiplier { get; }

			public ConfigEntry<float> SecondaryKnockbackTakenMultiplier { get; }

			public ConfigEntry<bool> EnableHitStopOnHit { get; }

			public ConfigEntry<bool> PrimaryCounterDamageEnabled { get; }

			public ConfigEntry<float> PrimaryCounterDamageMultiplier { get; }

			public ConfigEntry<bool> SecondaryCounterDamageEnabled { get; }

			public ConfigEntry<float> SecondaryCounterDamageMultiplier { get; }

			public ConfigEntry<float> PrimaryDamageRateMultiplier { get; }

			public ConfigEntry<float> SecondaryDamageRateMultiplier { get; }

			public ConfigEntry<float> PrimaryStaminaRateMultiplier { get; }

			public ConfigEntry<float> SecondaryStaminaRateMultiplier { get; }

			public ConfigEntry<float> PrimaryAttackMovementSpeedMultiplier { get; }

			public ConfigEntry<float> SecondaryAttackMovementSpeedMultiplier { get; }

			public ConfigEntry<float> PrimaryAttackAnimationSpeedMultiplier { get; }

			public ConfigEntry<float> SecondaryAttackAnimationSpeedMultiplier { get; }

			public ConfigEntry<float> PrimaryAttackRotationFactor { get; }

			public ConfigEntry<float> SecondaryAttackRotationFactor { get; }

			public ConfigEntry<bool> PrimaryLockRotationAfterAttackTrigger { get; }

			public ConfigEntry<bool> SecondaryLockRotationAfterAttackTrigger { get; }

			public ConfigEntry<float> PrimaryAdrenalineMultiplier { get; }

			public ConfigEntry<float> SecondaryAdrenalineMultiplier { get; }

			public ConfigEntry<float> PrimaryPvpDamageMultiplier { get; }

			public ConfigEntry<float> SecondaryPvpDamageMultiplier { get; }

			public ConfigEntry<float> PrimaryFullAttackDuration { get; }

			public ConfigEntry<float> SecondaryFullAttackDuration { get; }

			public ConfigEntry<float> PrimaryActiveHitboxDuration { get; }

			public ConfigEntry<float> SecondaryActiveHitboxDuration { get; }

			public CategoryConfig(ConfigEntry<StaggerApplicationMode> primaryStaggerMode, ConfigEntry<float> primaryStaggerPowerValue, ConfigEntry<StaggerApplicationMode> secondaryStaggerMode, ConfigEntry<float> secondaryStaggerPowerValue, ConfigEntry<HyperArmorMode> primaryHyperArmorMode, ConfigEntry<HyperArmorMode> secondaryHyperArmorMode, ConfigEntry<float> primaryDamageTakenMultiplier, ConfigEntry<float> secondaryDamageTakenMultiplier, ConfigEntry<float> primaryStaggerTakenMultiplier, ConfigEntry<float> secondaryStaggerTakenMultiplier, ConfigEntry<float> primaryKnockbackTakenMultiplier, ConfigEntry<float> secondaryKnockbackTakenMultiplier, ConfigEntry<bool> enableHitStopOnHit, ConfigEntry<bool> primaryCounterDamageEnabled, ConfigEntry<float> primaryCounterDamageMultiplier, ConfigEntry<bool> secondaryCounterDamageEnabled, ConfigEntry<float> secondaryCounterDamageMultiplier, ConfigEntry<float> primaryDamageRateMultiplier, ConfigEntry<float> secondaryDamageRateMultiplier, ConfigEntry<float> primaryStaminaRateMultiplier, ConfigEntry<float> secondaryStaminaRateMultiplier, ConfigEntry<float> primaryAttackMovementSpeedMultiplier, ConfigEntry<float> secondaryAttackMovementSpeedMultiplier, ConfigEntry<float> primaryAttackAnimationSpeedMultiplier, ConfigEntry<float> secondaryAttackAnimationSpeedMultiplier, ConfigEntry<float> primaryAttackRotationFactor, ConfigEntry<float> secondaryAttackRotationFactor, ConfigEntry<bool> primaryLockRotationAfterAttackTrigger, ConfigEntry<bool> secondaryLockRotationAfterAttackTrigger, ConfigEntry<float> primaryAdrenalineMultiplier, ConfigEntry<float> secondaryAdrenalineMultiplier, ConfigEntry<float> primaryPvpDamageMultiplier, ConfigEntry<float> secondaryPvpDamageMultiplier, ConfigEntry<float> primaryFullAttackDuration, ConfigEntry<float> secondaryFullAttackDuration, ConfigEntry<float> primaryActiveHitboxDuration, ConfigEntry<float> secondaryActiveHitboxDuration)
			{
				PrimaryStaggerMode = primaryStaggerMode;
				PrimaryStaggerPowerValue = primaryStaggerPowerValue;
				SecondaryStaggerMode = secondaryStaggerMode;
				SecondaryStaggerPowerValue = secondaryStaggerPowerValue;
				PrimaryHyperArmorMode = primaryHyperArmorMode;
				SecondaryHyperArmorMode = secondaryHyperArmorMode;
				PrimaryDamageTakenMultiplier = primaryDamageTakenMultiplier;
				SecondaryDamageTakenMultiplier = secondaryDamageTakenMultiplier;
				PrimaryStaggerTakenMultiplier = primaryStaggerTakenMultiplier;
				SecondaryStaggerTakenMultiplier = secondaryStaggerTakenMultiplier;
				PrimaryKnockbackTakenMultiplier = primaryKnockbackTakenMultiplier;
				SecondaryKnockbackTakenMultiplier = secondaryKnockbackTakenMultiplier;
				EnableHitStopOnHit = enableHitStopOnHit;
				PrimaryCounterDamageEnabled = primaryCounterDamageEnabled;
				PrimaryCounterDamageMultiplier = primaryCounterDamageMultiplier;
				SecondaryCounterDamageEnabled = secondaryCounterDamageEnabled;
				SecondaryCounterDamageMultiplier = secondaryCounterDamageMultiplier;
				PrimaryDamageRateMultiplier = primaryDamageRateMultiplier;
				SecondaryDamageRateMultiplier = secondaryDamageRateMultiplier;
				PrimaryStaminaRateMultiplier = primaryStaminaRateMultiplier;
				SecondaryStaminaRateMultiplier = secondaryStaminaRateMultiplier;
				PrimaryAttackMovementSpeedMultiplier = primaryAttackMovementSpeedMultiplier;
				SecondaryAttackMovementSpeedMultiplier = secondaryAttackMovementSpeedMultiplier;
				PrimaryAttackAnimationSpeedMultiplier = primaryAttackAnimationSpeedMultiplier;
				SecondaryAttackAnimationSpeedMultiplier = secondaryAttackAnimationSpeedMultiplier;
				PrimaryAttackRotationFactor = primaryAttackRotationFactor;
				SecondaryAttackRotationFactor = secondaryAttackRotationFactor;
				PrimaryLockRotationAfterAttackTrigger = primaryLockRotationAfterAttackTrigger;
				SecondaryLockRotationAfterAttackTrigger = secondaryLockRotationAfterAttackTrigger;
				PrimaryAdrenalineMultiplier = primaryAdrenalineMultiplier;
				SecondaryAdrenalineMultiplier = secondaryAdrenalineMultiplier;
				PrimaryPvpDamageMultiplier = primaryPvpDamageMultiplier;
				SecondaryPvpDamageMultiplier = secondaryPvpDamageMultiplier;
				PrimaryFullAttackDuration = primaryFullAttackDuration;
				SecondaryFullAttackDuration = secondaryFullAttackDuration;
				PrimaryActiveHitboxDuration = primaryActiveHitboxDuration;
				SecondaryActiveHitboxDuration = secondaryActiveHitboxDuration;
			}

			public StaggerApplicationMode GetStaggerMode(AttackRole role)
			{
				if (role != AttackRole.Secondary)
				{
					return PrimaryStaggerMode.Value;
				}
				return SecondaryStaggerMode.Value;
			}

			public float GetStaggerPowerValue(AttackRole role)
			{
				if (role != AttackRole.Secondary)
				{
					return PrimaryStaggerPowerValue.Value;
				}
				return SecondaryStaggerPowerValue.Value;
			}

			public HyperArmorMode GetHyperArmorMode(AttackRole role)
			{
				if (role != AttackRole.Secondary)
				{
					return PrimaryHyperArmorMode.Value;
				}
				return SecondaryHyperArmorMode.Value;
			}

			public float GetDamageTakenMultiplier(AttackRole role)
			{
				if (role != AttackRole.Secondary)
				{
					return PrimaryDamageTakenMultiplier.Value;
				}
				return SecondaryDamageTakenMultiplier.Value;
			}

			public float GetStaggerTakenMultiplier(AttackRole role)
			{
				if (role != AttackRole.Secondary)
				{
					return PrimaryStaggerTakenMultiplier.Value;
				}
				return SecondaryStaggerTakenMultiplier.Value;
			}

			public float GetKnockbackTakenMultiplier(AttackRole role)
			{
				if (role != AttackRole.Secondary)
				{
					return PrimaryKnockbackTakenMultiplier.Value;
				}
				return SecondaryKnockbackTakenMultiplier.Value;
			}

			public bool GetCounterDamageEnabled(AttackRole role)
			{
				if (role != AttackRole.Secondary)
				{
					return PrimaryCounterDamageEnabled.Value;
				}
				return SecondaryCounterDamageEnabled.Value;
			}

			public float GetCounterDamageMultiplier(AttackRole role)
			{
				if (role != AttackRole.Secondary)
				{
					return PrimaryCounterDamageMultiplier.Value;
				}
				return SecondaryCounterDamageMultiplier.Value;
			}

			public float GetDamageRateMultiplier(AttackRole role)
			{
				if (role != AttackRole.Secondary)
				{
					return PrimaryDamageRateMultiplier.Value;
				}
				return SecondaryDamageRateMultiplier.Value;
			}

			public float GetStaminaRateMultiplier(AttackRole role)
			{
				if (role != AttackRole.Secondary)
				{
					return PrimaryStaminaRateMultiplier.Value;
				}
				return SecondaryStaminaRateMultiplier.Value;
			}

			public float GetAttackMovementSpeedMultiplier(AttackRole role)
			{
				if (role != AttackRole.Secondary)
				{
					return PrimaryAttackMovementSpeedMultiplier.Value;
				}
				return SecondaryAttackMovementSpeedMultiplier.Value;
			}

			public float GetAttackAnimationSpeedMultiplier(AttackRole role)
			{
				if (role != AttackRole.Secondary)
				{
					return PrimaryAttackAnimationSpeedMultiplier.Value;
				}
				return SecondaryAttackAnimationSpeedMultiplier.Value;
			}

			public float GetAttackRotationFactor(AttackRole role)
			{
				if (role != AttackRole.Secondary)
				{
					return PrimaryAttackRotationFactor.Value;
				}
				return SecondaryAttackRotationFactor.Value;
			}

			public bool GetLockRotationAfterAttackTrigger(AttackRole role)
			{
				if (role != AttackRole.Secondary)
				{
					return PrimaryLockRotationAfterAttackTrigger.Value;
				}
				return SecondaryLockRotationAfterAttackTrigger.Value;
			}

			public float GetAdrenalineMultiplier(AttackRole role)
			{
				if (role != AttackRole.Secondary)
				{
					return PrimaryAdrenalineMultiplier.Value;
				}
				return SecondaryAdrenalineMultiplier.Value;
			}

			public float GetPvpDamageMultiplier(AttackRole role)
			{
				if (role != AttackRole.Secondary)
				{
					return PrimaryPvpDamageMultiplier.Value;
				}
				return SecondaryPvpDamageMultiplier.Value;
			}

			public float GetFullAttackDuration(AttackRole role)
			{
				if (role != AttackRole.Secondary)
				{
					return PrimaryFullAttackDuration.Value;
				}
				return SecondaryFullAttackDuration.Value;
			}

			public float GetActiveHitboxDuration(AttackRole role)
			{
				if (role != AttackRole.Secondary)
				{
					return PrimaryActiveHitboxDuration.Value;
				}
				return SecondaryActiveHitboxDuration.Value;
			}
		}

		private readonly struct SuppressedFieldValue
		{
			public object Target { get; }

			public FieldInfo Field { get; }

			public object? OriginalValue { get; }

			public SuppressedFieldValue(object target, FieldInfo field, object? originalValue)
			{
				Target = target;
				Field = field;
				OriginalValue = originalValue;
			}
		}

		private readonly struct CounterVulnerableState
		{
			public object? AttackInstance { get; }

			public CounterVulnerableState(object? attackInstance)
			{
				AttackInstance = attackInstance;
			}
		}

		private readonly struct AttackRuntimeState
		{
			public WeaponCategory Category { get; }

			public AttackRole Role { get; }

			public float EndTime { get; }

			public object? AttackInstance { get; }

			public AttackRuntimeState(WeaponCategory category, AttackRole role, float endTime, object? attackInstance)
			{
				Category = category;
				Role = role;
				EndTime = endTime;
				AttackInstance = attackInstance;
			}
		}

		private readonly struct PendingAttackRoleState
		{
			public AttackRole Role { get; }

			public float EndTime { get; }

			public PendingAttackRoleState(AttackRole role, float endTime)
			{
				Role = role;
				EndTime = endTime;
			}
		}

		private readonly struct HyperArmorRuntimeState
		{
			public WeaponCategory Category { get; }

			public AttackRole Role { get; }

			public HyperArmorMode Mode { get; }

			public float EndTime { get; }

			public object? AttackInstance { get; }

			public HyperArmorRuntimeState(WeaponCategory category, AttackRole role, HyperArmorMode mode, float endTime, object? attackInstance)
			{
				Category = category;
				Role = role;
				Mode = mode;
				EndTime = endTime;
				AttackInstance = attackInstance;
			}
		}

		private readonly struct AnimatorSpeedValue
		{
			private readonly Animator? _animator;

			private readonly object? _target;

			private readonly FieldInfo? _field;

			private readonly float _originalSpeed;

			private readonly float _multiplier;

			public AnimatorSpeedValue(Animator animator, float originalSpeed, float multiplier)
			{
				_animator = animator;
				_target = null;
				_field = null;
				_originalSpeed = originalSpeed;
				_multiplier = multiplier;
			}

			public AnimatorSpeedValue(object target, FieldInfo field, float originalSpeed, float multiplier)
			{
				_animator = null;
				_target = target;
				_field = field;
				_originalSpeed = originalSpeed;
				_multiplier = multiplier;
			}

			public void Restore()
			{
				if ((Object)(object)_animator != (Object)null)
				{
					_animator.speed = _originalSpeed;
				}
				else if (_target != null && _field != null)
				{
					_field.SetValue(_target, _originalSpeed);
				}
			}

			public void Reapply()
			{
				float num = _originalSpeed * _multiplier;
				if ((Object)(object)_animator != (Object)null)
				{
					_animator.speed = num;
				}
				else if (_target != null && _field != null)
				{
					_field.SetValue(_target, num);
				}
			}
		}

		public sealed class DamagePatchState
		{
			public Character Victim { get; }

			public float HealthBefore { get; }

			public float DamageMultiplier { get; }

			public float TargetHealthAfterMitigation { get; set; }

			public DamagePatchState(Character victim, float healthBefore, float damageMultiplier)
			{
				Victim = victim;
				HealthBefore = healthBefore;
				DamageMultiplier = damageMultiplier;
				TargetHealthAfterMitigation = -1f;
			}
		}

		[CompilerGenerated]
		private sealed class <ApplyHyperArmorFinalDamageReductionDelayed>d__141 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public DamagePatchState state;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <ApplyHyperArmorFinalDamageReductionDelayed>d__141(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0018: Unknown result type (might be due to invalid IL or missing references)
				//IL_0022: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = (object)new WaitForEndOfFrame();
					<>1__state = 1;
					return true;
				case 1:
				{
					<>1__state = -1;
					if (state == null || (Object)(object)state.Victim == (Object)null || IsCharacterDead(state.Victim))
					{
						return false;
					}
					if (state.TargetHealthAfterMitigation <= 0f)
					{
						return false;
					}
					float characterHealth = GetCharacterHealth(state.Victim);
					if (characterHealth >= 0f && characterHealth + 0.0001f < state.TargetHealthAfterMitigation)
					{
						SetCharacterHealthDirect(state.Victim, state.TargetHealthAfterMitigation);
					}
					return false;
				}
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		[CompilerGenerated]
		private sealed class <GetAllInstanceFields>d__114 : IEnumerable<FieldInfo>, IEnumerable, IEnumerator<FieldInfo>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private FieldInfo <>2__current;

			private int <>l__initialThreadId;

			private Type type;

			public Type <>3__type;

			private Type <t>5__2;

			private FieldInfo[] <>7__wrap2;

			private int <>7__wrap3;

			FieldInfo IEnumerator<FieldInfo>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <GetAllInstanceFields>d__114(int <>1__state)
			{
				this.<>1__state = <>1__state;
				<>l__initialThreadId = Environment.CurrentManagedThreadId;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<t>5__2 = null;
				<>7__wrap2 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				int num = <>1__state;
				if (num != 0)
				{
					if (num != 1)
					{
						return false;
					}
					<>1__state = -1;
					<>7__wrap3++;
					goto IL_0074;
				}
				<>1__state = -1;
				<t>5__2 = type;
				goto IL_009c;
				IL_0074:
				if (<>7__wrap3 < <>7__wrap2.Length)
				{
					FieldInfo fieldInfo = <>7__wrap2[<>7__wrap3];
					<>2__current = fieldInfo;
					<>1__state = 1;
					return true;
				}
				<>7__wrap2 = null;
				<t>5__2 = <t>5__2.BaseType;
				goto IL_009c;
				IL_009c:
				if (<t>5__2 != null)
				{
					<>7__wrap2 = <t>5__2.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					<>7__wrap3 = 0;
					goto IL_0074;
				}
				<t>5__2 = null;
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}

			[DebuggerHidden]
			IEnumerator<FieldInfo> IEnumerable<FieldInfo>.GetEnumerator()
			{
				<GetAllInstanceFields>d__114 <GetAllInstanceFields>d__;
				if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId)
				{
					<>1__state = 0;
					<GetAllInstanceFields>d__ = this;
				}
				else
				{
					<GetAllInstanceFields>d__ = new <GetAllInstanceFields>d__114(0);
				}
				<GetAllInstanceFields>d__.type = <>3__type;
				return <GetAllInstanceFields>d__;
			}

			[DebuggerHidden]
			IEnumerator IEnumerable.GetEnumerator()
			{
				return ((IEnumerable<FieldInfo>)this).GetEnumerator();
			}
		}

		public const string ModGuid = "goo.valheim.gooscombatoverhaul";

		public const string ModName = "Goo's Combat Overhaul";

		public const string ModVersion = "1.0.0";

		private readonly Harmony _harmony = new Harmony("goo.valheim.gooscombatoverhaul");

		internal static ConfigEntry<bool> Enabled = null;

		internal static ConfigEntry<bool> ApplyGoosSettings = null;

		internal static ConfigEntry<bool> ResetToVanillaSettings = null;

		internal static ConfigEntry<bool> AffectPlayersOnlyForOutgoingStagger = null;

		internal static ConfigEntry<bool> DebugLogging = null;

		internal static ConfigEntry<bool> DebugAttackLifecycle = null;

		internal static ConfigEntry<bool> DebugCounterDamage = null;

		internal static ConfigEntry<bool> DebugHitStop = null;

		internal static ConfigEntry<bool> DebugMovement = null;

		internal static ConfigEntry<bool> DebugHyperArmorLifecycle = null;

		internal static ConfigEntry<bool> EnableCounterDamage = null;

		internal static ConfigEntry<bool> OnlyPlayersCanDealCounterDamage = null;

		internal static ConfigEntry<bool> EnableCounterDamageDing = null;

		internal static ConfigEntry<HyperArmorDamageMitigationMode> HyperArmorMitigationMode = null;

		internal static ConfigEntry<bool> MitigateUnknownAttackerDamageDuringHyperArmor = null;

		internal static ConfigEntry<bool> DebugHyperArmorDamageMitigation = null;

		internal static ConfigEntry<bool> EnablePvpDamageModifiers = null;

		internal static ConfigEntry<float> HitStopDurationMultiplier = null;

		internal static ConfigEntry<float> HitStopDurationOverrideSeconds = null;

		internal static ConfigEntry<float> AttackMovementSpeedActiveWindowSeconds = null;

		internal static ConfigEntry<bool> ActiveHitboxEndsOnDoMeleeAttackPostfix = null;

		internal static readonly Dictionary<WeaponCategory, CategoryConfig> CategoryConfigs = new Dictionary<WeaponCategory, CategoryConfig>();

		private static readonly Dictionary<Character, AttackRuntimeState> AttackStates = new Dictionary<Character, AttackRuntimeState>();

		private static readonly Dictionary<object, Character> AttackInstanceOwners = new Dictionary<object, Character>();

		private static readonly Dictionary<object, ItemData> AttackInstanceWeapons = new Dictionary<object, ItemData>();

		private static readonly Dictionary<object, AttackRole> AttackInstanceRoles = new Dictionary<object, AttackRole>();

		private static readonly HashSet<object> BalancedHyperArmorCompletedAttacks = new HashSet<object>();

		private static readonly Dictionary<Character, AttackRuntimeState> MeleeHitStopContexts = new Dictionary<Character, AttackRuntimeState>();

		private static readonly Dictionary<Character, HyperArmorRuntimeState> HyperArmorStates = new Dictionary<Character, HyperArmorRuntimeState>();

		private static readonly Dictionary<Character, PendingAttackRoleState> PendingAttackRoles = new Dictionary<Character, PendingAttackRoleState>();

		private static readonly Dictionary<Character, CounterVulnerableState> CounterVulnerableStates = new Dictionary<Character, CounterVulnerableState>();

		private static readonly Dictionary<object, List<SuppressedFieldValue>> AttackMovementSpeedContexts = new Dictionary<object, List<SuppressedFieldValue>>();

		private static readonly Dictionary<object, float> AttackMovementSpeedEndTimes = new Dictionary<object, float>();

		private static readonly Dictionary<object, List<SuppressedFieldValue>> AttackRotationContexts = new Dictionary<object, List<SuppressedFieldValue>>();

		private static readonly Dictionary<object, List<AnimatorSpeedValue>> AttackAnimationSpeedContexts = new Dictionary<object, List<AnimatorSpeedValue>>();

		private static readonly Dictionary<string, FieldInfo?> FieldCache = new Dictionary<string, FieldInfo>();

		private static readonly Dictionary<string, MethodInfo?> MethodCache = new Dictionary<string, MethodInfo>();

		private static bool _processingPresetToggle;

		private static MethodInfo? _hitDataGetAttacker;

		private static MethodInfo? CharacterInAttackMethod;

		internal static GooCombatOverhaulPlugin Instance { get; private set; } = null;


		internal static ManualLogSource Log => ((BaseUnityPlugin)Instance).Logger;

		private static bool ModActive
		{
			get
			{
				if (Enabled != null)
				{
					return Enabled.Value;
				}
				return false;
			}
		}

		private void Awake()
		{
			Instance = this;
			BindConfig();
			_hitDataGetAttacker = AccessTools.Method(typeof(HitData), "GetAttacker", (Type[])null, (Type[])null);
			PatchCoreDamageAndStagger();
			PatchAttackLifecycle();
			((BaseUnityPlugin)this).Config.SettingChanged += delegate
			{
				AttackStates.Clear();
				AttackInstanceOwners.Clear();
				AttackInstanceWeapons.Clear();
				AttackInstanceRoles.Clear();
				BalancedHyperArmorCompletedAttacks.Clear();
				MeleeHitStopContexts.Clear();
				HyperArmorStates.Clear();
				PendingAttackRoles.Clear();
				CounterVulnerableStates.Clear();
				RestoreAllAttackMovementSpeedTweaks();
				RestoreAllAttackRotationTweaks();
				RestoreAllAttackAnimationSpeedTweaks();
				ProcessGeneralPresetToggles();
			};
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Goo's Combat Overhaul 1.0.0 loaded.");
		}

		private void OnDestroy()
		{
			_harmony.UnpatchSelf();
			AttackStates.Clear();
			AttackInstanceOwners.Clear();
			AttackInstanceWeapons.Clear();
			AttackInstanceRoles.Clear();
			BalancedHyperArmorCompletedAttacks.Clear();
			MeleeHitStopContexts.Clear();
			HyperArmorStates.Clear();
			PendingAttackRoles.Clear();
			CounterVulnerableStates.Clear();
			RestoreAllAttackMovementSpeedTweaks();
			RestoreAllAttackRotationTweaks();
			RestoreAllAttackAnimationSpeedTweaks();
		}

		private void Update()
		{
			if (!ModActive)
			{
				AttackStates.Clear();
				AttackInstanceOwners.Clear();
				AttackInstanceWeapons.Clear();
				AttackInstanceRoles.Clear();
				BalancedHyperArmorCompletedAttacks.Clear();
				MeleeHitStopContexts.Clear();
				HyperArmorStates.Clear();
				PendingAttackRoles.Clear();
				CounterVulnerableStates.Clear();
				RestoreAllAttackMovementSpeedTweaks();
				RestoreAllAttackRotationTweaks();
				RestoreAllAttackAnimationSpeedTweaks();
			}
			else
			{
				MaintainAttackMovementSpeedTweaks();
			}
		}

		private void BindConfig()
		{
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ApplyModOptions", true, "Master switch. If false, disables every gameplay feature, runtime state check, and live modifier this mod offers without unloading the Harmony patches.");
			ApplyGoosSettings = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ApplyGoosSettings", false, "One-shot preset button. Fresh configs already default to Goo's curated settings; set true to rewrite combat/balance entries back to Goo's preset after manual edits or ResetToVanilla. This automatically returns to false. ApplyModOptions controls whether the mod is active.");
			ResetToVanillaSettings = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ResetToVanilla", false, "One-shot reset button. Set true to rewrite combat/balance config entries back to vanilla/no-op Valheim behavior, then this value automatically returns to false. Debug/general runtime toggles are preserved.");
			AffectPlayersOnlyForOutgoingStagger = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "AffectPlayersOnlyForOutgoingStagger", true, "If true, stagger-power calibration only applies to outgoing attacks made by players.");
			DebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DebugLogging", false, "Legacy master debug switch. If true, enables all extra debug logs. Prefer the categorized toggles in the Debug section for normal testing.");
			DebugAttackLifecycle = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugAttackLifecycle", false, "Logs accepted attack state, role detection, and lifecycle cleanup events.");
			DebugCounterDamage = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugCounterDamage", false, "Logs counter-vulnerability and counter-damage accept/reject reasons.");
			DebugHitStop = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugHitStop", false, "Logs direct Character.FreezeFrame hit-stop suppression/scaling decisions.");
			DebugMovement = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugMovement", false, "Logs attack movement-speed multiplier application/restoration.");
			DebugHyperArmorLifecycle = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugHyperArmorLifecycle", false, "Logs hyperarmor start/end lifecycle events. Damage-specific mitigation logs still use DebugHyperArmorDamageMitigation.");
			EnableCounterDamage = ((BaseUnityPlugin)this).Config.Bind<bool>("Counter Damage", "EnableCounterDamage", true, "Master switch for counter-damage. Category settings decide which weapon types and which attack buttons can use it.");
			OnlyPlayersCanDealCounterDamage = ((BaseUnityPlugin)this).Config.Bind<bool>("Counter Damage", "OnlyPlayersCanDealCounterDamage", true, "If true, only player attacks can trigger counter-damage. If false, mobs using eligible melee attacks can also trigger it.");
			EnableCounterDamageDing = ((BaseUnityPlugin)this).Config.Bind<bool>("Counter Damage", "EnableCounterDamageDing", true, "If true, plays a short parry/stagger-style ding when counter-damage is successfully applied.");
			HyperArmorMitigationMode = ((BaseUnityPlugin)this).Config.Bind<HyperArmorDamageMitigationMode>("Hyperarmor", "HyperArmorDamageMitigationMode", HyperArmorDamageMitigationMode.RawHitData, "How hyperarmor damage reduction is applied. RawHitData is reliable but interacts with armor nonlinearly; FinalHealthRestore tries to correct final HP loss; RawHitDataAndFinalHealthRestore does both for reliability.");
			MitigateUnknownAttackerDamageDuringHyperArmor = ((BaseUnityPlugin)this).Config.Bind<bool>("Hyperarmor", "MitigateUnknownAttackerDamageDuringHyperArmor", true, "If true, hyperarmor can mitigate incoming damage even when HitData.GetAttacker() fails. This fixes some enemy hits but may also affect non-environmental unknown-source damage.");
			DebugHyperArmorDamageMitigation = ((BaseUnityPlugin)this).Config.Bind<bool>("Hyperarmor", "DebugHyperArmorDamageMitigation", false, "Logs why hyperarmor damage mitigation does or does not run. Useful when KB/stagger resistance works but damage reduction does not.");
			EnablePvpDamageModifiers = ((BaseUnityPlugin)this).Config.Bind<bool>("PvP Damage", "EnablePvpDamageModifiers", true, "Master switch for per-category PvP damage multipliers. Defaults reduce bows, crossbows, and magic to 0.5x against players; all other categories remain 1x.");
			HitStopDurationMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Hit-Stop / Hitlag", "HitStopDurationMultiplier", 1f, "Global multiplier for Valheim's hardcoded melee hit FreezeFrame duration on categories where EnableHitStopOnHit=true. 1 keeps vanilla 0.15s; 0 disables it; 0.5 halves it. Categories with EnableHitStopOnHit=false skip FreezeFrame entirely.");
			HitStopDurationOverrideSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Hit-Stop / Hitlag", "HitStopDurationOverrideSeconds", -1f, "Optional absolute hit-stop duration in seconds for categories where EnableHitStopOnHit=true. -1 uses HitStopDurationMultiplier. 0 disables hit-stop. This only applies to player melee hit FreezeFrame calls during tracked attacks.");
			AttackMovementSpeedActiveWindowSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Movement", "AttackMovementSpeedActiveWindowSeconds", 0.25f, "Seconds that AttackMovementSpeedMultiplier remains applied after the real melee hitbox/damage-scan event. This prevents movement-speed changes from activating during failed/no-stamina attack attempts or the entire attack animation.");
			ActiveHitboxEndsOnDoMeleeAttackPostfix = ((BaseUnityPlugin)this).Config.Bind<bool>("Hyperarmor", "ActiveHitboxEndsOnDoMeleeAttackPostfix", false, "If true, ActiveHitboxFrames hyperarmor ends immediately at DoMeleeAttack postfix. False is recommended: DoMeleeAttack is an instant damage scan, so ending at postfix makes the hyperarmor window too tiny to matter.");
			AddCategory(WeaponCategory.TwoHandedSword, "Two-Handed Swords", 2f, 2f, HyperArmorMode.Balanced, HyperArmorMode.Balanced, enableHitStopOnHit: false, 2.46f, 2.46f, primaryCounter: false, secondaryCounter: true, StaggerApplicationMode.MultiplyVanilla, StaggerApplicationMode.MultiplyVanilla, 0.7f, 0.7f, 0f, 0f, 0f, 0f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, primaryLockRotationAfterAttackTrigger: true, secondaryLockRotationAfterAttackTrigger: true, 1f, 6f);
			AddCategory(WeaponCategory.TwoHandedAxe, "Two-Handed Axes", 2f, 1.5f, HyperArmorMode.Balanced, HyperArmorMode.Balanced, enableHitStopOnHit: false, 3.44f, 3.44f, primaryCounter: false, secondaryCounter: false, StaggerApplicationMode.MultiplyVanilla, StaggerApplicationMode.MultiplyVanilla, 0.7f, 0.7f, 0f, 0f, 0f, 0f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, primaryLockRotationAfterAttackTrigger: true, secondaryLockRotationAfterAttackTrigger: true);
			AddCategory(WeaponCategory.Sledge, "Sledges", 3f, 3f, HyperArmorMode.Balanced, HyperArmorMode.Balanced, enableHitStopOnHit: true, 2.23f, 2.23f, primaryCounter: false, secondaryCounter: false, StaggerApplicationMode.MultiplyVanilla, StaggerApplicationMode.MultiplyVanilla, 0.7f, 0.7f, 0f, 0f, 0f, 0f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, primaryLockRotationAfterAttackTrigger: true, secondaryLockRotationAfterAttackTrigger: true);
			AddCategory(WeaponCategory.DualAxes, "Dual Axes", 1f, 2f, HyperArmorMode.Off, HyperArmorMode.Off, enableHitStopOnHit: false, 2.4f, 2.4f, primaryCounter: false, secondaryCounter: false, StaggerApplicationMode.MultiplyVanilla, StaggerApplicationMode.MultiplyVanilla, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 2f, 2f, 1f, 1.5f, 1f, -1f, primaryLockRotationAfterAttackTrigger: true);
			AddCategory(WeaponCategory.Atgeir, "Atgeirs", 1f, 1f, HyperArmorMode.Off, HyperArmorMode.Balanced, enableHitStopOnHit: true, 2.98f, 2.98f, primaryCounter: true, secondaryCounter: false, StaggerApplicationMode.MultiplyVanilla, StaggerApplicationMode.MultiplyVanilla, 1f, 0.7f, 1f, 0f, 1f, 0f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, -1f, primaryLockRotationAfterAttackTrigger: true, secondaryLockRotationAfterAttackTrigger: false, 1f, 6f);
			AddCategory(WeaponCategory.OneHandedSword, "One-Handed Swords", 1f, 1f, HyperArmorMode.Off, HyperArmorMode.Balanced, enableHitStopOnHit: false, 2.46f, 2.46f, primaryCounter: false, secondaryCounter: true, StaggerApplicationMode.MultiplyVanilla, StaggerApplicationMode.MultiplyVanilla, 1f, 1f, 1f, 1f, 1f, 0f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, primaryLockRotationAfterAttackTrigger: true, secondaryLockRotationAfterAttackTrigger: true);
			AddCategory(WeaponCategory.OneHandedAxe, "One-Handed Axes", 1f, 3f, HyperArmorMode.Off, HyperArmorMode.Balanced, enableHitStopOnHit: true, 2.74f, 2.74f, primaryCounter: false, secondaryCounter: false, StaggerApplicationMode.MultiplyVanilla, StaggerApplicationMode.MultiplyVanilla, 1f, 1f, 1f, 1f, 1f, 0f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1.25f, 1f, 1f, primaryLockRotationAfterAttackTrigger: true, secondaryLockRotationAfterAttackTrigger: true);
			AddCategory(WeaponCategory.Club, "Clubs and Maces", 1.5f, 1.5f, HyperArmorMode.Off, HyperArmorMode.Balanced, enableHitStopOnHit: true, 2.46f, 2.46f, primaryCounter: false, secondaryCounter: false, StaggerApplicationMode.MultiplyVanilla, StaggerApplicationMode.MultiplyVanilla, 1f, 1f, 1f, 1f, 1f, 0f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, primaryLockRotationAfterAttackTrigger: true, secondaryLockRotationAfterAttackTrigger: true);
			AddCategory(WeaponCategory.Knife, "Knives", 1f, 1f, HyperArmorMode.Off, HyperArmorMode.Off, enableHitStopOnHit: true, 2.04f, 2.04f, primaryCounter: false, secondaryCounter: false, StaggerApplicationMode.MultiplyVanilla, StaggerApplicationMode.MultiplyVanilla, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, primaryLockRotationAfterAttackTrigger: true, secondaryLockRotationAfterAttackTrigger: true);
			AddCategory(WeaponCategory.Spear, "Spears", 1f, 2f, HyperArmorMode.Off, HyperArmorMode.Off, enableHitStopOnHit: true, 0.68f, 0.68f, primaryCounter: true, secondaryCounter: false, StaggerApplicationMode.MultiplyVanilla, StaggerApplicationMode.MultiplyVanilla, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, primaryLockRotationAfterAttackTrigger: true, secondaryLockRotationAfterAttackTrigger: true);
			AddCategory(WeaponCategory.Bow, "Bows", 1f, 1f, HyperArmorMode.Off, HyperArmorMode.Off, enableHitStopOnHit: true, 1.15f, 1.15f, primaryCounter: false, secondaryCounter: false, StaggerApplicationMode.MultiplyVanilla, StaggerApplicationMode.MultiplyVanilla, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, -1f, -1f, primaryLockRotationAfterAttackTrigger: false, secondaryLockRotationAfterAttackTrigger: false, 1f, 1f, 0.5f, 0.5f);
			AddCategory(WeaponCategory.Crossbow, "Crossbows", 1f, 1f, HyperArmorMode.Off, HyperArmorMode.Off, enableHitStopOnHit: true, 1.15f, 1.15f, primaryCounter: false, secondaryCounter: false, StaggerApplicationMode.MultiplyVanilla, StaggerApplicationMode.MultiplyVanilla, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, -1f, -1f, primaryLockRotationAfterAttackTrigger: false, secondaryLockRotationAfterAttackTrigger: false, 1f, 1f, 0.5f, 0.5f);
			AddCategory(WeaponCategory.Magic, "Magic", 1f, 1f, HyperArmorMode.Off, HyperArmorMode.Off, enableHitStopOnHit: true, 1.15f, 1.15f, primaryCounter: false, secondaryCounter: false, StaggerApplicationMode.MultiplyVanilla, StaggerApplicationMode.MultiplyVanilla, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, -1f, -1f, primaryLockRotationAfterAttackTrigger: false, secondaryLockRotationAfterAttackTrigger: false, 1f, 1f, 0.5f, 0.5f);
			AddCategory(WeaponCategory.Unarmed, "Unarmed", 1f, 1f, HyperArmorMode.Off, HyperArmorMode.Off, enableHitStopOnHit: true, 1.15f, 1.15f, primaryCounter: false, secondaryCounter: false, StaggerApplicationMode.MultiplyVanilla, StaggerApplicationMode.MultiplyVanilla, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, primaryLockRotationAfterAttackTrigger: true, secondaryLockRotationAfterAttackTrigger: true);
			AddCategory(WeaponCategory.Other, "Other Weapons", 1f, 1f, HyperArmorMode.Off, HyperArmorMode.Off, enableHitStopOnHit: true, 1.15f, 1.15f);
			ProcessGeneralPresetToggles();
		}

		private void AddCategory(WeaponCategory category, string section, float primaryStagger, float secondaryStagger, HyperArmorMode primaryHyper, HyperArmorMode secondaryHyper, bool enableHitStopOnHit, float primaryFullSeconds, float secondaryFullSeconds, bool primaryCounter = false, bool secondaryCounter = false, StaggerApplicationMode primaryStaggerMode = StaggerApplicationMode.MultiplyVanilla, StaggerApplicationMode secondaryStaggerMode = StaggerApplicationMode.MultiplyVanilla, float primaryDamageTakenMultiplierDuringHyperArmor = 1f, float secondaryDamageTakenMultiplierDuringHyperArmor = 1f, float primaryStaggerTakenMultiplierDuringHyperArmor = 1f, float secondaryStaggerTakenMultiplierDuringHyperArmor = 1f, float primaryKnockbackTakenMultiplierDuringHyperArmor = 1f, float secondaryKnockbackTakenMultiplierDuringHyperArmor = 1f, float primaryDamageRateMultiplier = 1f, float secondaryDamageRateMultiplier = 1f, float primaryStaminaRateMultiplier = 1f, float secondaryStaminaRateMultiplier = 1f, float primaryAttackMovementSpeedMultiplier = 1f, float secondaryAttackMovementSpeedMultiplier = 1f, float primaryAttackAnimationSpeedMultiplier = 1f, float secondaryAttackAnimationSpeedMultiplier = 1f, float primaryAttackRotationFactor = -1f, float secondaryAttackRotationFactor = -1f, bool primaryLockRotationAfterAttackTrigger = false, bool secondaryLockRotationAfterAttackTrigger = false, float primaryAdrenalineMultiplier = 1f, float secondaryAdrenalineMultiplier = 1f, float primaryPvpDamageMultiplier = 1f, float secondaryPvpDamageMultiplier = 1f)
		{
			CategoryConfigs[category] = new CategoryConfig(((BaseUnityPlugin)this).Config.Bind<StaggerApplicationMode>(section, "PrimaryStaggerMode", primaryStaggerMode, "How PrimaryStaggerPowerValue is applied. MultiplyVanilla multiplies Valheim's current stagger multiplier. SetFinalMultiplier replaces the final HitData stagger multiplier with this value."), ((BaseUnityPlugin)this).Config.Bind<float>(section, "PrimaryStaggerPowerValue", primaryStagger, "Light/primary attack stagger calibration. In MultiplyVanilla mode, 2 means 2x vanilla. In SetFinalMultiplier mode, 4 means final stagger multiplier becomes 4."), ((BaseUnityPlugin)this).Config.Bind<StaggerApplicationMode>(section, "SecondaryStaggerMode", secondaryStaggerMode, "How SecondaryStaggerPowerValue is applied. MultiplyVanilla multiplies Valheim's current stagger multiplier. SetFinalMultiplier replaces the final HitData stagger multiplier with this value."), ((BaseUnityPlugin)this).Config.Bind<float>(section, "SecondaryStaggerPowerValue", secondaryStagger, "Heavy/secondary attack stagger calibration. For dual axes, default 2x is intended to turn a vanilla 2x finishing hit into 4x final stagger."), ((BaseUnityPlugin)this).Config.Bind<HyperArmorMode>(section, "PrimaryHyperArmorMode", primaryHyper, "Hyperarmor mode for light/primary attacks. Balanced starts when Valheim enters the real attack animation and ends immediately after Valheim's attack-trigger/hitbox event; FullAttackAnimation lasts through recovery; ActiveHitboxFrames starts at the hitbox event; WhileHoldingWeaponType is always on while the weapon type is held."), ((BaseUnityPlugin)this).Config.Bind<HyperArmorMode>(section, "SecondaryHyperArmorMode", secondaryHyper, "Hyperarmor mode for heavy/secondary attacks. Balanced starts when Valheim enters the real attack animation and ends immediately after Valheim's attack-trigger/hitbox event; FullAttackAnimation lasts through recovery; ActiveHitboxFrames starts at the hitbox event; WhileHoldingWeaponType is always on while the weapon type is held."), ((BaseUnityPlugin)this).Config.Bind<float>(section, "PrimaryDamageTakenMultiplierDuringHyperArmor", primaryDamageTakenMultiplierDuringHyperArmor, "Final HP-loss multiplier while primary-attack hyperarmor is active. 0.70 means 30% less final damage after armor/block/resistance; 1.00 means no damage reduction. Environmental/lava/fall damage is ignored by requiring a real attacker."), ((BaseUnityPlugin)this).Config.Bind<float>(section, "SecondaryDamageTakenMultiplierDuringHyperArmor", secondaryDamageTakenMultiplierDuringHyperArmor, "Final HP-loss multiplier while secondary-attack hyperarmor is active. 0.70 means 30% less final damage after armor/block/resistance; 1.00 means no damage reduction. Environmental/lava/fall damage is ignored by requiring a real attacker."), ((BaseUnityPlugin)this).Config.Bind<float>(section, "PrimaryStaggerTakenMultiplierDuringHyperArmor", primaryStaggerTakenMultiplierDuringHyperArmor, "Incoming stagger multiplier during primary-attack hyperarmor. 0 means stagger immunity; 1 means vanilla."), ((BaseUnityPlugin)this).Config.Bind<float>(section, "SecondaryStaggerTakenMultiplierDuringHyperArmor", secondaryStaggerTakenMultiplierDuringHyperArmor, "Incoming stagger multiplier during secondary-attack hyperarmor. 0 means stagger immunity; 1 means vanilla."), ((BaseUnityPlugin)this).Config.Bind<float>(section, "PrimaryKnockbackTakenMultiplierDuringHyperArmor", primaryKnockbackTakenMultiplierDuringHyperArmor, "Incoming push/knockback multiplier during primary-attack hyperarmor. 0 means knockback immunity; 1 means vanilla."), ((BaseUnityPlugin)this).Config.Bind<float>(section, "SecondaryKnockbackTakenMultiplierDuringHyperArmor", secondaryKnockbackTakenMultiplierDuringHyperArmor, "Incoming push/knockback multiplier during secondary-attack hyperarmor. 0 means knockback immunity; 1 means vanilla."), ((BaseUnityPlugin)this).Config.Bind<bool>(section, "EnableHitStopOnHit", enableHitStopOnHit, "Valheim calls Character.FreezeFrame(0.15s) after successful melee hits. True keeps/modulates that hit-stop through global HitStop settings; false suppresses it for this weapon category."), ((BaseUnityPlugin)this).Config.Bind<bool>(section, "PrimaryCounterDamageEnabled", primaryCounter, "If true, eligible melee primary attacks can gain counter-damage against targets during Valheim's real attack animation/combat state."), ((BaseUnityPlugin)this).Config.Bind<float>(section, "PrimaryCounterDamageMultiplier", 1.3f, "Counter-damage multiplier for eligible primary attacks. 1.30 means 30% additional damage."), ((BaseUnityPlugin)this).Config.Bind<bool>(section, "SecondaryCounterDamageEnabled", secondaryCounter, "If true, eligible melee secondary attacks can gain counter-damage against targets during Valheim's real attack animation/combat state."), ((BaseUnityPlugin)this).Config.Bind<float>(section, "SecondaryCounterDamageMultiplier", 1.3f, "Counter-damage multiplier for eligible secondary attacks. 1.30 means 30% additional damage."), ((BaseUnityPlugin)this).Config.Bind<float>(section, "PrimaryDamageRateMultiplier", primaryDamageRateMultiplier, "Outgoing primary/light attack damage multiplier. 1 leaves vanilla damage unchanged."), ((BaseUnityPlugin)this).Config.Bind<float>(section, "SecondaryDamageRateMultiplier", secondaryDamageRateMultiplier, "Outgoing secondary/heavy attack damage multiplier. 1 leaves vanilla damage unchanged."), ((BaseUnityPlugin)this).Config.Bind<float>(section, "PrimaryStaminaRateMultiplier", primaryStaminaRateMultiplier, "Primary/light attack stamina-cost multiplier where the Valheim stamina method is available. 1 leaves vanilla stamina unchanged."), ((BaseUnityPlugin)this).Config.Bind<float>(section, "SecondaryStaminaRateMultiplier", secondaryStaminaRateMultiplier, "Secondary/heavy attack stamina-cost multiplier where the Valheim stamina method is available. 1 leaves vanilla stamina unchanged."), ((BaseUnityPlugin)this).Config.Bind<float>(section, "PrimaryAttackMovementSpeedMultiplier", primaryAttackMovementSpeedMultiplier, "Primary/light attack movement-speed multiplier applied only for the short active window after DoMeleeAttack/hitbox startup. 1 leaves movement unchanged."), ((BaseUnityPlugin)this).Config.Bind<float>(section, "SecondaryAttackMovementSpeedMultiplier", secondaryAttackMovementSpeedMultiplier, "Secondary/heavy attack movement-speed multiplier applied only for the short active window after DoMeleeAttack/hitbox startup. 1 leaves movement unchanged."), ((BaseUnityPlugin)this).Config.Bind<float>(section, "PrimaryAttackAnimationSpeedMultiplier", primaryAttackAnimationSpeedMultiplier, "Primary/light actual swing animation-speed multiplier using Animator.speed during the attack. 1 leaves vanilla animation timing unchanged."), ((BaseUnityPlugin)this).Config.Bind<float>(section, "SecondaryAttackAnimationSpeedMultiplier", secondaryAttackAnimationSpeedMultiplier, "Secondary/heavy actual swing animation-speed multiplier using Animator.speed during the attack. 1 leaves vanilla animation timing unchanged."), ((BaseUnityPlugin)this).Config.Bind<float>(section, "PrimaryAttackRotationFactor", primaryAttackRotationFactor, "Primary/light attack rotation factor override for the live Attack.m_speedFactorRotation field. -1 leaves vanilla; 0 prevents turning; 1 allows full 100% turn speed during the attack animation."), ((BaseUnityPlugin)this).Config.Bind<float>(section, "SecondaryAttackRotationFactor", secondaryAttackRotationFactor, "Secondary/heavy attack rotation factor override for the live Attack.m_speedFactorRotation field. -1 leaves vanilla; 0 prevents turning; 1 allows full 100% turn speed during the attack animation."), ((BaseUnityPlugin)this).Config.Bind<bool>(section, "PrimaryLockRotationAfterAttackTrigger", primaryLockRotationAfterAttackTrigger, "If true, primary/light attack rotation is forced to 0 from Valheim's OnAttackTrigger/hitbox event until the attack recovers/stops."), ((BaseUnityPlugin)this).Config.Bind<bool>(section, "SecondaryLockRotationAfterAttackTrigger", secondaryLockRotationAfterAttackTrigger, "If true, secondary/heavy attack rotation is forced to 0 from Valheim's OnAttackTrigger/hitbox event until the attack recovers/stops."), ((BaseUnityPlugin)this).Config.Bind<float>(section, "PrimaryAdrenalineMultiplier", primaryAdrenalineMultiplier, "Multiplier over vanilla adrenaline gained by primary/light hits. 1 leaves vanilla adrenaline unchanged; 6 attempts to add 5 extra points assuming vanilla gives 1."), ((BaseUnityPlugin)this).Config.Bind<float>(section, "SecondaryAdrenalineMultiplier", secondaryAdrenalineMultiplier, "Multiplier over vanilla adrenaline gained by secondary/heavy hits. 1 leaves vanilla adrenaline unchanged; 6 attempts to add 5 extra points assuming vanilla gives 1."), ((BaseUnityPlugin)this).Config.Bind<float>(section, "PrimaryPvpDamageMultiplier", primaryPvpDamageMultiplier, "PvP primary/light damage multiplier when attacker and victim are both players. 1 leaves PvP unchanged."), ((BaseUnityPlugin)this).Config.Bind<float>(section, "SecondaryPvpDamageMultiplier", secondaryPvpDamageMultiplier, "PvP secondary/heavy damage multiplier when attacker and victim are both players. 1 leaves PvP unchanged."), ((BaseUnityPlugin)this).Config.Bind<float>(section, "PrimaryFullAttackAnimationDurationSeconds", primaryFullSeconds, "Fallback safety cap for FullAttackAnimation primary hyperarmor/counter windows. Attack.Stop/Abort/done hooks clear the real window earlier."), ((BaseUnityPlugin)this).Config.Bind<float>(section, "SecondaryFullAttackAnimationDurationSeconds", secondaryFullSeconds, "Fallback safety cap for FullAttackAnimation secondary hyperarmor/counter windows. Attack.Stop/Abort/done hooks clear the real window earlier."), ((BaseUnityPlugin)this).Config.Bind<float>(section, "PrimaryActiveHitboxDurationSeconds", 0.25f, "Fallback maximum for ActiveHitboxFrames primary hyperarmor if the DoMeleeAttack postfix/end hook fails. Normally prefix starts and postfix ends the active window."), ((BaseUnityPlugin)this).Config.Bind<float>(section, "SecondaryActiveHitboxDurationSeconds", 0.25f, "Fallback maximum for ActiveHitboxFrames secondary hyperarmor if the DoMeleeAttack postfix/end hook fails. Normally prefix starts and postfix ends the active window."));
		}

		private void ProcessGeneralPresetToggles()
		{
			if (_processingPresetToggle || ApplyGoosSettings == null || ResetToVanillaSettings == null || (!ApplyGoosSettings.Value && !ResetToVanillaSettings.Value))
			{
				return;
			}
			_processingPresetToggle = true;
			try
			{
				if (ResetToVanillaSettings.Value)
				{
					ApplyVanillaPresetValues();
					ResetToVanillaSettings.Value = false;
					ApplyGoosSettings.Value = false;
					((BaseUnityPlugin)this).Config.Save();
					((BaseUnityPlugin)this).Logger.LogInfo((object)"Reset Goo's Combat Overhaul combat/balance config entries to vanilla/no-op Valheim values.");
				}
				else if (ApplyGoosSettings.Value)
				{
					ApplyGooPresetValues();
					ApplyGoosSettings.Value = false;
					((BaseUnityPlugin)this).Config.Save();
					((BaseUnityPlugin)this).Logger.LogInfo((object)"Applied Goo's curated combat-overhaul preset to the config. ApplyGoosSettings was reset to false; edit individual entries freely.");
				}
			}
			finally
			{
				_processingPresetToggle = false;
			}
		}

		private static void ApplyVanillaPresetValues()
		{
			AffectPlayersOnlyForOutgoingStagger.Value = true;
			EnableCounterDamage.Value = true;
			OnlyPlayersCanDealCounterDamage.Value = true;
			EnableCounterDamageDing.Value = true;
			HyperArmorMitigationMode.Value = HyperArmorDamageMitigationMode.RawHitData;
			MitigateUnknownAttackerDamageDuringHyperArmor.Value = true;
			EnablePvpDamageModifiers.Value = true;
			HitStopDurationMultiplier.Value = 1f;
			HitStopDurationOverrideSeconds.Value = -1f;
			AttackMovementSpeedActiveWindowSeconds.Value = 0.25f;
			ActiveHitboxEndsOnDoMeleeAttackPostfix.Value = false;
			foreach (CategoryConfig value in CategoryConfigs.Values)
			{
				SetCategoryToVanilla(value);
			}
		}

		private static void SetCategoryToVanilla(CategoryConfig cfg)
		{
			cfg.PrimaryStaggerMode.Value = StaggerApplicationMode.MultiplyVanilla;
			cfg.SecondaryStaggerMode.Value = StaggerApplicationMode.MultiplyVanilla;
			cfg.PrimaryStaggerPowerValue.Value = 1f;
			cfg.SecondaryStaggerPowerValue.Value = 1f;
			cfg.PrimaryHyperArmorMode.Value = HyperArmorMode.Off;
			cfg.SecondaryHyperArmorMode.Value = HyperArmorMode.Off;
			cfg.PrimaryDamageTakenMultiplier.Value = 1f;
			cfg.SecondaryDamageTakenMultiplier.Value = 1f;
			cfg.PrimaryStaggerTakenMultiplier.Value = 1f;
			cfg.SecondaryStaggerTakenMultiplier.Value = 1f;
			cfg.PrimaryKnockbackTakenMultiplier.Value = 1f;
			cfg.SecondaryKnockbackTakenMultiplier.Value = 1f;
			cfg.EnableHitStopOnHit.Value = true;
			cfg.PrimaryCounterDamageEnabled.Value = false;
			cfg.SecondaryCounterDamageEnabled.Value = false;
			cfg.PrimaryCounterDamageMultiplier.Value = 1.3f;
			cfg.SecondaryCounterDamageMultiplier.Value = 1.3f;
			cfg.PrimaryDamageRateMultiplier.Value = 1f;
			cfg.SecondaryDamageRateMultiplier.Value = 1f;
			cfg.PrimaryStaminaRateMultiplier.Value = 1f;
			cfg.SecondaryStaminaRateMultiplier.Value = 1f;
			cfg.PrimaryAttackMovementSpeedMultiplier.Value = 1f;
			cfg.SecondaryAttackMovementSpeedMultiplier.Value = 1f;
			cfg.PrimaryAttackAnimationSpeedMultiplier.Value = 1f;
			cfg.SecondaryAttackAnimationSpeedMultiplier.Value = 1f;
			cfg.PrimaryAttackRotationFactor.Value = -1f;
			cfg.SecondaryAttackRotationFactor.Value = -1f;
			cfg.PrimaryLockRotationAfterAttackTrigger.Value = false;
			cfg.SecondaryLockRotationAfterAttackTrigger.Value = false;
			cfg.PrimaryAdrenalineMultiplier.Value = 1f;
			cfg.SecondaryAdrenalineMultiplier.Value = 1f;
			cfg.PrimaryPvpDamageMultiplier.Value = 1f;
			cfg.SecondaryPvpDamageMultiplier.Value = 1f;
			cfg.PrimaryActiveHitboxDuration.Value = 0.25f;
			cfg.SecondaryActiveHitboxDuration.Value = 0.25f;
		}

		private static void ApplyGooPresetValues()
		{
			ApplyVanillaPresetValues();
			AffectPlayersOnlyForOutgoingStagger.Value = true;
			EnableCounterDamage.Value = true;
			OnlyPlayersCanDealCounterDamage.Value = true;
			EnableCounterDamageDing.Value = true;
			HyperArmorMitigationMode.Value = HyperArmorDamageMitigationMode.RawHitData;
			MitigateUnknownAttackerDamageDuringHyperArmor.Value = true;
			EnablePvpDamageModifiers.Value = true;
			HitStopDurationMultiplier.Value = 1f;
			HitStopDurationOverrideSeconds.Value = -1f;
			AttackMovementSpeedActiveWindowSeconds.Value = 0.25f;
			ActiveHitboxEndsOnDoMeleeAttackPostfix.Value = false;
			WeaponCategory[] array = new WeaponCategory[11]
			{
				WeaponCategory.TwoHandedSword,
				WeaponCategory.TwoHandedAxe,
				WeaponCategory.Sledge,
				WeaponCategory.DualAxes,
				WeaponCategory.Atgeir,
				WeaponCategory.OneHandedSword,
				WeaponCategory.OneHandedAxe,
				WeaponCategory.Club,
				WeaponCategory.Knife,
				WeaponCategory.Spear,
				WeaponCategory.Unarmed
			};
			for (int i = 0; i < array.Length; i++)
			{
				SetRotationPreset(GetConfig(array[i]), 1f, 1f, primaryLock: true, secondaryLock: true);
			}
			SetSecondaryRotationPreset(GetConfig(WeaponCategory.DualAxes), -1f, lockAfterTrigger: false);
			SetSecondaryRotationPreset(GetConfig(WeaponCategory.Atgeir), -1f, lockAfterTrigger: false);
			CategoryConfig config = GetConfig(WeaponCategory.TwoHandedSword);
			SetStagger(config, 2f, 2f);
			SetHyper(config, HyperArmorMode.Balanced, HyperArmorMode.Balanced);
			SetHyperArmorProtection(config, 0.7f, 0.7f, 0f, 0f, 0f, 0f);
			config.EnableHitStopOnHit.Value = false;
			config.SecondaryCounterDamageEnabled.Value = true;
			config.SecondaryAdrenalineMultiplier.Value = 6f;
			SetDurations(config, 2.46f, 2.46f);
			CategoryConfig config2 = GetConfig(WeaponCategory.TwoHandedAxe);
			SetStagger(config2, 2f, 1.5f);
			SetHyper(config2, HyperArmorMode.Balanced, HyperArmorMode.Balanced);
			SetHyperArmorProtection(config2, 0.7f, 0.7f, 0f, 0f, 0f, 0f);
			config2.EnableHitStopOnHit.Value = false;
			SetDurations(config2, 3.44f, 3.44f);
			CategoryConfig config3 = GetConfig(WeaponCategory.Sledge);
			SetStagger(config3, 3f, 3f);
			SetHyper(config3, HyperArmorMode.Balanced, HyperArmorMode.Balanced);
			SetHyperArmorProtection(config3, 0.7f, 0.7f, 0f, 0f, 0f, 0f);
			config3.EnableHitStopOnHit.Value = true;
			SetDurations(config3, 2.23f, 2.23f);
			CategoryConfig config4 = GetConfig(WeaponCategory.DualAxes);
			SetStagger(config4, 1f, 2f);
			SetHyper(config4, HyperArmorMode.Off, HyperArmorMode.Off);
			config4.EnableHitStopOnHit.Value = false;
			config4.PrimaryAttackMovementSpeedMultiplier.Value = 2f;
			config4.SecondaryAttackMovementSpeedMultiplier.Value = 2f;
			config4.SecondaryAttackAnimationSpeedMultiplier.Value = 1.5f;
			SetDurations(config4, 2.4f, 2.4f);
			CategoryConfig config5 = GetConfig(WeaponCategory.Atgeir);
			SetStagger(config5, 1f, 1f);
			SetHyper(config5, HyperArmorMode.Off, HyperArmorMode.Balanced);
			SetHyperArmorProtection(config5, 1f, 0.7f, 1f, 0f, 1f, 0f);
			config5.EnableHitStopOnHit.Value = true;
			config5.PrimaryCounterDamageEnabled.Value = true;
			config5.SecondaryAdrenalineMultiplier.Value = 6f;
			SetDurations(config5, 2.98f, 2.98f);
			CategoryConfig config6 = GetConfig(WeaponCategory.OneHandedSword);
			SetStagger(config6, 1f, 1f);
			SetHyper(config6, HyperArmorMode.Off, HyperArmorMode.Balanced);
			SetHyperArmorProtection(config6, 1f, 1f, 1f, 1f, 1f, 0f);
			config6.EnableHitStopOnHit.Value = false;
			config6.SecondaryCounterDamageEnabled.Value = true;
			SetDurations(config6, 2.46f, 2.46f);
			CategoryConfig config7 = GetConfig(WeaponCategory.OneHandedAxe);
			SetStagger(config7, 1f, 3f);
			SetHyper(config7, HyperArmorMode.Off, HyperArmorMode.Balanced);
			SetHyperArmorProtection(config7, 1f, 1f, 1f, 1f, 1f, 0f);
			config7.EnableHitStopOnHit.Value = true;
			config7.SecondaryAttackAnimationSpeedMultiplier.Value = 1.25f;
			SetDurations(config7, 2.74f, 2.74f);
			CategoryConfig config8 = GetConfig(WeaponCategory.Club);
			SetStagger(config8, 1.5f, 1.5f);
			SetHyper(config8, HyperArmorMode.Off, HyperArmorMode.Balanced);
			SetHyperArmorProtection(config8, 1f, 1f, 1f, 1f, 1f, 0f);
			config8.EnableHitStopOnHit.Value = true;
			SetDurations(config8, 2.46f, 2.46f);
			CategoryConfig config9 = GetConfig(WeaponCategory.Spear);
			SetStagger(config9, 1f, 2f);
			config9.PrimaryCounterDamageEnabled.Value = true;
			config9.EnableHitStopOnHit.Value = true;
			SetDurations(config9, 0.68f, 0.68f);
			CategoryConfig config10 = GetConfig(WeaponCategory.Bow);
			config10.PrimaryPvpDamageMultiplier.Value = 0.5f;
			config10.SecondaryPvpDamageMultiplier.Value = 0.5f;
			CategoryConfig config11 = GetConfig(WeaponCategory.Crossbow);
			config11.PrimaryPvpDamageMultiplier.Value = 0.5f;
			config11.SecondaryPvpDamageMultiplier.Value = 0.5f;
			CategoryConfig config12 = GetConfig(WeaponCategory.Magic);
			config12.PrimaryPvpDamageMultiplier.Value = 0.5f;
			config12.SecondaryPvpDamageMultiplier.Value = 0.5f;
		}

		private static void SetStagger(CategoryConfig cfg, float primary, float secondary)
		{
			cfg.PrimaryStaggerMode.Value = StaggerApplicationMode.MultiplyVanilla;
			cfg.SecondaryStaggerMode.Value = StaggerApplicationMode.MultiplyVanilla;
			cfg.PrimaryStaggerPowerValue.Value = primary;
			cfg.SecondaryStaggerPowerValue.Value = secondary;
		}

		private static void SetHyper(CategoryConfig cfg, HyperArmorMode primary, HyperArmorMode secondary)
		{
			cfg.PrimaryHyperArmorMode.Value = primary;
			cfg.SecondaryHyperArmorMode.Value = secondary;
		}

		private static void SetHyperArmorProtection(CategoryConfig cfg, float primaryDamage, float secondaryDamage, float primaryStagger, float secondaryStagger, float primaryKnockback, float secondaryKnockback)
		{
			cfg.PrimaryDamageTakenMultiplier.Value = primaryDamage;
			cfg.SecondaryDamageTakenMultiplier.Value = secondaryDamage;
			cfg.PrimaryStaggerTakenMultiplier.Value = primaryStagger;
			cfg.SecondaryStaggerTakenMultiplier.Value = secondaryStagger;
			cfg.PrimaryKnockbackTakenMultiplier.Value = primaryKnockback;
			cfg.SecondaryKnockbackTakenMultiplier.Value = secondaryKnockback;
		}

		private static void SetRotationPreset(CategoryConfig cfg, float primaryFactor, float secondaryFactor, bool primaryLock, bool secondaryLock)
		{
			cfg.PrimaryAttackRotationFactor.Value = primaryFactor;
			cfg.SecondaryAttackRotationFactor.Value = secondaryFactor;
			cfg.PrimaryLockRotationAfterAttackTrigger.Value = primaryLock;
			cfg.SecondaryLockRotationAfterAttackTrigger.Value = secondaryLock;
		}

		private static void SetSecondaryRotationPreset(CategoryConfig cfg, float factor, bool lockAfterTrigger)
		{
			cfg.SecondaryAttackRotationFactor.Value = factor;
			cfg.SecondaryLockRotationAfterAttackTrigger.Value = lockAfterTrigger;
		}

		private static void SetDurations(CategoryConfig cfg, float primary, float secondary)
		{
			cfg.PrimaryFullAttackDuration.Value = primary;
			cfg.SecondaryFullAttackDuration.Value = secondary;
		}

		private void PatchCoreDamageAndStagger()
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Expected O, but got Unknown
			//IL_0060: Expected O, but got Unknown
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Expected O, but got Unknown
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Expected O, but got Unknown
			_harmony.Patch((MethodBase)AccessTools.Method(typeof(Character), "Damage", new Type[1] { typeof(HitData) }, (Type[])null), new HarmonyMethod(typeof(GooCombatOverhaulPlugin), "CharacterDamagePrefix", (Type[])null), new HarmonyMethod(typeof(GooCombatOverhaulPlugin), "CharacterDamagePostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Patched Character.Damage for stagger/damage/PvP/counter/adrenaline calibration and hyperarmor damage mitigation.");
			MethodInfo methodInfo = AccessTools.Method(typeof(Character), "FreezeFrame", new Type[1] { typeof(float) }, (Type[])null);
			if (methodInfo != null)
			{
				_harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(GooCombatOverhaulPlugin), "CharacterFreezeFramePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Patched Character.FreezeFrame(float) for direct melee hit-stop control.");
			}
			else
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Could not find Character.FreezeFrame(float). Hit-stop control is unavailable on this Valheim build.");
			}
			int num = 0;
			foreach (MethodInfo item in from m in AccessTools.GetDeclaredMethods(typeof(Character))
				where m.Name == "Stagger"
				select m)
			{
				ParameterInfo[] parameters = item.GetParameters();
				if (parameters.Length != 0 && parameters[0].ParameterType == typeof(Vector3))
				{
					_harmony.Patch((MethodBase)item, new HarmonyMethod(typeof(GooCombatOverhaulPlugin), "CharacterStaggerPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					num++;
				}
			}
			if (num > 0)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Patched {num} Character.Stagger overload(s) for hyperarmor stagger immunity.");
			}
			else
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Could not find a Character.Stagger(Vector3...) overload. Hyperarmor stagger-immunity hooks may be unavailable on this Valheim build.");
			}
		}

		private void PatchAttackLifecycle()
		{
			Type type = AccessTools.TypeByName("Attack");
			if (type == null)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Could not find Attack type. Hyperarmor/counter timing patches are unavailable.");
				return;
			}
			PatchAttackStartMethods(type);
			PatchAttackMethod(type, "Update", "AttackUpdatePostfix", postfix: true);
			PatchAttackMethodOptional(type, "OnAttackTrigger", "AttackHitboxEventPrefix");
			PatchAttackMethodOptional(type, "OnAttackTrigger", "AttackHitboxEventPostfix", postfix: true);
			PatchAttackMethod(type, "Stop", "AttackStopPostfix", postfix: true);
			PatchAttackMethod(type, "Abort", "AttackStopPostfix", postfix: true);
			PatchAttackMethodOptional(type, "OnAttackDone", "AttackStopPostfix", postfix: true);
			PatchAttackMethodOptional(type, "AttackDone", "AttackStopPostfix", postfix: true);
			PatchAttackMethodOptional(type, "Done", "AttackStopPostfix", postfix: true);
			PatchAttackMethod(type, "DoMeleeAttack", "AttackDoMeleeAttackPrefix");
			PatchAttackMethod(type, "DoMeleeAttack", "AttackDoMeleeAttackPostfix", postfix: true);
			PatchHumanoidStartAttack();
			PatchAttackStaminaMethods(type);
		}

		private void PatchAttackStartMethods(Type attackType)
		{
			List<MethodInfo> list = (from m in AccessTools.GetDeclaredMethods(attackType)
				where m.Name == "Start" && m.ReturnType == typeof(bool)
				select m).ToList();
			if (list.Count == 0)
			{
				int num = AccessTools.GetDeclaredMethods(attackType).Count((MethodInfo m) => m.Name == "Start");
				((BaseUnityPlugin)this).Logger.LogWarning((object)$"Could not find a bool-returning Attack.Start overload. Found {num} Attack.Start overload(s), but successful-attack gating is unavailable on this Valheim build.");
			}
			else
			{
				PatchAttackMethods(list, "AttackStartPostfix", postfix: true);
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Patched {list.Count} successful Attack.Start overload(s) for valid-attack gated attack state, animation speed, and FullAttackAnimation hyperarmor. Balanced hyperarmor and counter vulnerability are gated by Attack.Update/InAttack; movement-speed tweaks are gated by DoMeleeAttack hitbox startup and a short active window.");
			}
		}

		private void PatchAttackMethod(Type attackType, string methodName, string patchName, bool postfix = false)
		{
			string methodName2 = methodName;
			List<MethodInfo> list = (from m in AccessTools.GetDeclaredMethods(attackType)
				where m.Name == methodName2
				select m).ToList();
			if (list.Count == 0)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not find Attack." + methodName2 + ". Some timing modes may use fallback behavior only."));
				return;
			}
			PatchAttackMethods(list, patchName, postfix);
			((BaseUnityPlugin)this).Logger.LogInfo((object)$"Patched {list.Count} Attack.{methodName2} overload(s).");
		}

		private void PatchAttackMethodOptional(Type attackType, string methodName, string patchName, bool postfix = false)
		{
			string methodName2 = methodName;
			List<MethodInfo> list = (from m in AccessTools.GetDeclaredMethods(attackType)
				where m.Name == methodName2
				select m).ToList();
			if (list.Count != 0)
			{
				PatchAttackMethods(list, patchName, postfix);
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Patched optional {list.Count} Attack.{methodName2} overload(s).");
			}
		}

		private void PatchAttackMethods(List<MethodInfo> methods, string patchName, bool postfix)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Expected O, but got Unknown
			foreach (MethodInfo method in methods)
			{
				HarmonyMethod val = new HarmonyMethod(typeof(GooCombatOverhaulPlugin), patchName, (Type[])null);
				if (postfix)
				{
					_harmony.Patch((MethodBase)method, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
				else
				{
					_harmony.Patch((MethodBase)method, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
			}
		}

		private void PatchHumanoidStartAttack()
		{
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Expected O, but got Unknown
			//IL_008f: Expected O, but got Unknown
			Type type = AccessTools.TypeByName("Humanoid");
			if (type == null)
			{
				return;
			}
			List<MethodInfo> list = (from m in AccessTools.GetDeclaredMethods(type)
				where m.Name == "StartAttack" && m.ReturnType == typeof(bool)
				select m).ToList();
			foreach (MethodInfo item in list)
			{
				_harmony.Patch((MethodBase)item, new HarmonyMethod(typeof(GooCombatOverhaulPlugin), "HumanoidStartAttackPrefix", (Type[])null), new HarmonyMethod(typeof(GooCombatOverhaulPlugin), "HumanoidStartAttackPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			if (list.Count > 0)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Patched {list.Count} bool-returning Humanoid.StartAttack overload(s) for exact primary/secondary attack-role detection and failed-start cleanup.");
			}
		}

		private void PatchAttackStaminaMethods(Type attackType)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Expected O, but got Unknown
			int num = 0;
			foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(attackType))
			{
				if (SafeLower(declaredMethod.Name).Contains("stamina") && !(declaredMethod.ReturnType != typeof(float)))
				{
					_harmony.Patch((MethodBase)declaredMethod, (HarmonyMethod)null, new HarmonyMethod(typeof(GooCombatOverhaulPlugin), "AttackStaminaCostPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					num++;
				}
			}
			if (num > 0)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Patched {num} Attack stamina-return method(s) for stamina-rate multipliers.");
			}
			else
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Could not find an Attack stamina-return method. StaminaRateMultiplier config may be unavailable on this Valheim build.");
			}
		}

		public static void HumanoidStartAttackPrefix(object __instance, object[] __args)
		{
			if (!ModActive)
			{
				return;
			}
			Character val = (Character)((__instance is Character) ? __instance : null);
			if (val == null)
			{
				return;
			}
			AttackRole role = AttackRole.Primary;
			bool flag = default(bool);
			foreach (object obj in __args)
			{
				int num;
				if (obj is bool)
				{
					flag = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
				if (((uint)num & (flag ? 1u : 0u)) != 0)
				{
					role = AttackRole.Secondary;
					break;
				}
			}
			PendingAttackRoles[val] = new PendingAttackRoleState(role, Time.time + 0.75f);
		}

		public static void HumanoidStartAttackPostfix(object __instance, object[] __args, bool __result)
		{
			if (ModActive)
			{
				Character val = (Character)((__instance is Character) ? __instance : null);
				if (val != null && !__result)
				{
					PendingAttackRoles.Remove(val);
				}
			}
		}

		public static void AttackStaminaCostPostfix(object __instance, object[] __args, ref float __result)
		{
			if (ModActive && !Mathf.Approximately(__result, 0f) && TryGetAttackContext(__instance, __args, out Character character, out ItemData weapon) && weapon != null)
			{
				WeaponCategory category = ClassifyWeapon(weapon);
				AttackRole role = DetermineAttackRole(character, __instance, weapon);
				float num = Mathf.Clamp(GetConfig(category).GetStaminaRateMultiplier(role), 0f, 10f);
				if (!Mathf.Approximately(num, 1f))
				{
					__result *= num;
				}
			}
		}

		public static void AttackStartPostfix(object __instance, object[] __args, bool __result)
		{
			if (!ModActive || !__result || !TryGetAttackContext(__instance, __args, out Character character, out ItemData weapon))
			{
				return;
			}
			WeaponCategory category = ((weapon != null) ? ClassifyWeapon(weapon) : WeaponCategory.Unarmed);
			AttackRole role = DetermineAttackRole(character, __instance, weapon);
			SetAttackState(character, category, role, __instance);
			ClearBalancedHyperArmorCompleted(__instance);
			if (character is Player && weapon != null)
			{
				ApplyAttackAnimationSpeedIfConfigured(__instance, character, weapon, category, role);
				ApplyAttackRotationOverrideIfConfigured(__instance, weapon, category, role);
			}
			if (character is Player && weapon != null)
			{
				CategoryConfig config = GetConfig(category);
				if (config.GetHyperArmorMode(role) == HyperArmorMode.FullAttackAnimation)
				{
					float num = Mathf.Max(0.01f, config.GetFullAttackDuration(role));
					SetHyperArmor(character, category, role, HyperArmorMode.FullAttackAnimation, Time.time + num, __instance);
				}
			}
		}

		public static void AttackUpdatePostfix(object __instance, object[] __args)
		{
			if (!ModActive || !TryGetAttackContext(__instance, __args, out Character character, out ItemData weapon))
			{
				return;
			}
			if (!CharacterReportsInAttack(character))
			{
				EndCounterVulnerableForAttack(character, __instance);
				if (character is Player)
				{
					EndHyperArmorForAttack(character, __instance, HyperArmorMode.Balanced);
				}
				return;
			}
			WeaponCategory category = ((weapon != null) ? ClassifyWeapon(weapon) : WeaponCategory.Unarmed);
			AttackRole role = DetermineAttackRole(character, __instance, weapon);
			SetAttackStateIfChanged(character, category, role, __instance);
			MarkCounterVulnerableDuringAttackAnimation(character, category, role, __instance);
			if (character is Player && weapon != null)
			{
				CategoryConfig config = GetConfig(category);
				if (config.GetHyperArmorMode(role) == HyperArmorMode.Balanced && !IsBalancedHyperArmorCompleted(__instance))
				{
					float num = Mathf.Max(0.05f, config.GetFullAttackDuration(role));
					SetHyperArmorIfChanged(character, category, role, HyperArmorMode.Balanced, Time.time + num, __instance);
				}
			}
		}

		public static void AttackDoMeleeAttackPrefix(object __instance, object[] __args)
		{
			if (!ModActive || !TryGetAttackContext(__instance, __args, out Character character, out ItemData weapon))
			{
				return;
			}
			WeaponCategory category = ((weapon != null) ? ClassifyWeapon(weapon) : WeaponCategory.Unarmed);
			AttackRole role = DetermineAttackRole(character, __instance, weapon);
			SetAttackState(character, category, role, __instance);
			if (character is Player && weapon != null)
			{
				BeginMeleeHitStopContext(character, category, role, __instance);
				ApplyAttackMovementSpeedIfConfigured(__instance, weapon, category, role);
			}
			if (character is Player && weapon != null)
			{
				CategoryConfig config = GetConfig(category);
				if (config.GetHyperArmorMode(role) == HyperArmorMode.ActiveHitboxFrames)
				{
					float num = Mathf.Max(0.01f, config.GetActiveHitboxDuration(role));
					SetHyperArmor(character, category, role, HyperArmorMode.ActiveHitboxFrames, Time.time + num, __instance);
				}
			}
		}

		public static void AttackHitboxEventPrefix(object __instance, object[] __args)
		{
			if (ModActive && TryGetAttackContext(__instance, __args, out Character character, out ItemData weapon) && character is Player && weapon != null)
			{
				WeaponCategory category = ClassifyWeapon(weapon);
				AttackRole role = DetermineAttackRole(character, __instance, weapon);
				ApplyAttackRotationLockAfterTriggerIfConfigured(__instance, weapon, category, role);
			}
		}

		public static void AttackHitboxEventPostfix(object __instance, object[] __args)
		{
			EndBalancedHyperArmorAfterAttackEvent(__instance, __args, "OnAttackTrigger");
		}

		public static void AttackDoMeleeAttackPostfix(object __instance, object[] __args)
		{
			EndBalancedHyperArmorAfterAttackEvent(__instance, __args, "DoMeleeAttack");
			EndMeleeHitStopContext(__instance, __args);
		}

		private static void EndBalancedHyperArmorAfterAttackEvent(object __instance, object[] __args, string source)
		{
			if (ModActive && TryGetAttackContext(__instance, __args, out Character character, out ItemData _) && character is Player)
			{
				MarkBalancedHyperArmorCompleted(__instance);
				EndHyperArmorForAttack(character, __instance, HyperArmorMode.Balanced);
				if (source == "DoMeleeAttack" && ActiveHitboxEndsOnDoMeleeAttackPostfix.Value)
				{
					EndHyperArmorForAttack(character, __instance, HyperArmorMode.ActiveHitboxFrames);
				}
			}
		}

		public static void AttackStopPostfix(object __instance, object[] __args)
		{
			if (ModActive && TryGetAttackContext(__instance, __args, out Character character, out ItemData _))
			{
				EndCounterVulnerableForAttack(character, __instance);
				EndAttackStateForAttack(character, __instance);
				EndMeleeHitStopContext(__instance, __args);
				AttackInstanceOwners.Remove(__instance);
				AttackInstanceWeapons.Remove(__instance);
				AttackInstanceRoles.Remove(__instance);
				ClearBalancedHyperArmorCompleted(__instance);
				RestoreAttackMovementSpeedTweaks(__instance);
				RestoreAttackRotationTweaks(__instance);
				RestoreAttackAnimationSpeedTweaks(__instance);
				if (character is Player)
				{
					EndHyperArmorForAttack(character, __instance, null);
				}
			}
		}

		public static void CharacterDamagePrefix(Character __instance, HitData hit, ref DamagePatchState __state)
		{
			__state = null;
			if (ModActive && hit != null && !((Object)(object)__instance == (Object)null))
			{
				Character attacker = GetAttacker(hit);
				ApplyOutgoingDamageAndPvpCalibration(__instance, attacker, hit);
				ApplyCounterDamageIfEligible(__instance, attacker, hit);
				ApplyOutgoingStaggerCalibration(attacker, hit);
				ApplyAdrenalineMultiplierIfEligible(attacker, hit);
				__state = ApplyHyperArmorIfActive(__instance, attacker, hit);
			}
		}

		public static void CharacterDamagePostfix(Character __instance, DamagePatchState __state)
		{
			if ((Object)(object)__instance != (Object)null && __state != null)
			{
				ApplyHyperArmorFinalDamageReduction(__state);
			}
		}

		public static bool CharacterStaggerPrefix(Character __instance, ref Vector3 __0)
		{
			if (!ModActive || (Object)(object)__instance == (Object)null)
			{
				return true;
			}
			if (__instance is Player && TryGetActiveHyperArmor(__instance, out var category, out var role))
			{
				CategoryConfig config = GetConfig(category);
				if (Mathf.Max(0f, config.GetStaggerTakenMultiplier(role)) <= 0.0001f)
				{
					if (DebugLogging.Value)
					{
						Log.LogInfo((object)("Blocked stagger during hyperarmor for " + SafeName(__instance) + "."));
					}
					return false;
				}
			}
			return true;
		}

		private static void BeginMeleeHitStopContext(Character character, WeaponCategory category, AttackRole role, object? attackInstance)
		{
			if (!((Object)(object)character == (Object)null))
			{
				MeleeHitStopContexts[character] = new AttackRuntimeState(category, role, Time.time + 0.5f, attackInstance);
			}
		}

		private static void EndMeleeHitStopContext(object? attackInstance, object[]? args)
		{
			Character character = null;
			Character value;
			if (attackInstance != null && args != null && TryGetAttackContext(attackInstance, args, out character, out ItemData _))
			{
				if ((Object)(object)character != (Object)null)
				{
					MeleeHitStopContexts.Remove(character);
				}
			}
			else if (attackInstance != null && AttackInstanceOwners.TryGetValue(attackInstance, out value))
			{
				MeleeHitStopContexts.Remove(value);
			}
		}

		private static bool TryGetMeleeHitStopContext(Character character, out AttackRuntimeState state)
		{
			state = default(AttackRuntimeState);
			if ((Object)(object)character == (Object)null)
			{
				return false;
			}
			if (!MeleeHitStopContexts.TryGetValue(character, out state))
			{
				return false;
			}
			if (Time.time > state.EndTime || IsCharacterDead(character))
			{
				MeleeHitStopContexts.Remove(character);
				return false;
			}
			return true;
		}

		public static bool CharacterFreezeFramePrefix(Character __instance, ref float __0)
		{
			if (ModActive)
			{
				Player val = (Player)(object)((__instance is Player) ? __instance : null);
				if (val != null)
				{
					if (__0 <= 0f || __0 > 0.5f)
					{
						return true;
					}
					if (!TryGetMeleeHitStopContext((Character)(object)val, out var state))
					{
						return true;
					}
					if (!GetConfig(state.Category).EnableHitStopOnHit.Value)
					{
						if (Debug(DebugHitStop))
						{
							Log.LogInfo((object)$"Suppressed melee FreezeFrame({__0.ToString(CultureInfo.InvariantCulture)}s) for {SafeName((Character)(object)val)} using {state.Category} {state.Role}.");
						}
						return false;
					}
					float value = HitStopDurationOverrideSeconds.Value;
					if (value >= 0f)
					{
						__0 = Mathf.Max(0f, value);
					}
					else
					{
						__0 *= Mathf.Clamp(HitStopDurationMultiplier.Value, 0f, 5f);
					}
					if (__0 <= 0.0001f)
					{
						if (Debug(DebugHitStop))
						{
							Log.LogInfo((object)$"Suppressed melee FreezeFrame through global hit-stop duration settings for {SafeName((Character)(object)val)} using {state.Category} {state.Role}.");
						}
						return false;
					}
					if (Debug(DebugHitStop))
					{
						Log.LogInfo((object)$"Allowed melee FreezeFrame duration {__0.ToString(CultureInfo.InvariantCulture)}s for {SafeName((Character)(object)val)} using {state.Category} {state.Role}.");
					}
					return true;
				}
			}
			return true;
		}

		private static bool IsBalancedHyperArmorCompleted(object? attackInstance)
		{
			if (attackInstance != null)
			{
				return BalancedHyperArmorCompletedAttacks.Contains(attackInstance);
			}
			return false;
		}

		private static void MarkBalancedHyperArmorCompleted(object? attackInstance)
		{
			if (attackInstance != null)
			{
				BalancedHyperArmorCompletedAttacks.Add(attackInstance);
				if (Debug(DebugHyperArmorLifecycle))
				{
					Log.LogInfo((object)"Balanced hyperarmor marked complete for current attack; later recovery-frame Attack.Update calls will not restart it.");
				}
			}
		}

		private static void ClearBalancedHyperArmorCompleted(object? attackInstance)
		{
			if (attackInstance != null)
			{
				BalancedHyperArmorCompletedAttacks.Remove(attackInstance);
			}
		}

		private static void SetAttackStateIfChanged(Character character, WeaponCategory category, AttackRole role, object? attackInstance)
		{
			if (!((Object)(object)character == (Object)null) && (!AttackStates.TryGetValue(character, out var value) || value.Category != category || value.Role != role || value.AttackInstance != attackInstance || !(Time.time <= value.EndTime)))
			{
				SetAttackState(character, category, role, attackInstance);
			}
		}

		private static void SetAttackState(Character character, WeaponCategory category, AttackRole role, object? attackInstance)
		{
			if ((Object)(object)character == (Object)null)
			{
				return;
			}
			float num = Mathf.Max(0.01f, GetConfig(category).GetFullAttackDuration(role));
			AttackStates[character] = new AttackRuntimeState(category, role, Time.time + num, attackInstance);
			if (attackInstance != null)
			{
				AttackInstanceOwners[attackInstance] = character;
				AttackInstanceRoles[attackInstance] = role;
				ItemData currentWeapon = GetCurrentWeapon(character);
				if (currentWeapon != null)
				{
					AttackInstanceWeapons[attackInstance] = currentWeapon;
				}
			}
			if (Debug(DebugAttackLifecycle))
			{
				Log.LogInfo((object)$"Attack state: {SafeName(character)} {category} {role}; fallback end {(Time.time + num).ToString(CultureInfo.InvariantCulture)}.");
			}
		}

		private static void EndAttackStateForAttack(Character character, object? attackInstance)
		{
			if (!((Object)(object)character == (Object)null) && AttackStates.TryGetValue(character, out var value) && (value.AttackInstance == null || attackInstance == null || value.AttackInstance == attackInstance))
			{
				AttackStates.Remove(character);
			}
		}

		private static AttackRole GetCurrentAttackRole(Character attacker, WeaponCategory category)
		{
			if ((Object)(object)attacker == (Object)null)
			{
				return AttackRole.Primary;
			}
			if (!AttackStates.TryGetValue(attacker, out var value))
			{
				return AttackRole.Primary;
			}
			if (Time.time > value.EndTime || IsCharacterDead(attacker))
			{
				AttackStates.Remove(attacker);
				return AttackRole.Primary;
			}
			if (value.Category != category)
			{
				return AttackRole.Primary;
			}
			return value.Role;
		}

		private static bool AttackObjectsLookSimilar(object attackInstance, object candidate)
		{
			if (attackInstance == candidate)
			{
				return true;
			}
			if (attackInstance == null || candidate == null)
			{
				return false;
			}
			string[] obj = new string[8] { "m_attackAnimation", "m_attackChainLevels", "m_attackStamina", "m_damageMultiplier", "m_staggerMultiplier", "m_attackType", "m_attackRandomAnimations", "m_attackProjectile" };
			int num = 0;
			int num2 = 0;
			string[] array = obj;
			foreach (string fieldName in array)
			{
				object fieldValue = GetFieldValue<object>(attackInstance, fieldName);
				object fieldValue2 = GetFieldValue<object>(candidate, fieldName);
				if (fieldValue != null && fieldValue2 != null)
				{
					num++;
					if (object.Equals(fieldValue, fieldValue2) || string.Equals(fieldValue.ToString(), fieldValue2.ToString(), StringComparison.OrdinalIgnoreCase))
					{
						num2++;
					}
				}
			}
			if (num >= 2)
			{
				return num2 >= 2;
			}
			return false;
		}

		private static bool IsInAttackState(Character character)
		{
			if ((Object)(object)character == (Object)null || !AttackStates.TryGetValue(character, out var value))
			{
				return false;
			}
			if (Time.time > value.EndTime || IsCharacterDead(character))
			{
				AttackStates.Remove(character);
				return false;
			}
			return true;
		}

		private static AttackRole DetermineAttackRole(Character? character, object? attackInstance, ItemData? weapon)
		{
			if (attackInstance != null && AttackInstanceRoles.TryGetValue(attackInstance, out var value))
			{
				return value;
			}
			if ((Object)(object)character != (Object)null && AttackStates.TryGetValue(character, out var value2) && (value2.AttackInstance == null || attackInstance == null || value2.AttackInstance == attackInstance))
			{
				return value2.Role;
			}
			if ((Object)(object)character != (Object)null && PendingAttackRoles.TryGetValue(character, out var value3))
			{
				if (Time.time <= value3.EndTime)
				{
					return value3.Role;
				}
				PendingAttackRoles.Remove(character);
			}
			if ((Object)(object)character != (Object)null)
			{
				object fieldValueRaw = GetFieldValueRaw(character, "m_currentAttack");
				if (GetFieldValueRaw(character, "m_currentAttackIsSecondary") is bool flag && (attackInstance == null || fieldValueRaw == null || fieldValueRaw == attackInstance))
				{
					if (!flag)
					{
						return AttackRole.Primary;
					}
					return AttackRole.Secondary;
				}
			}
			if (attackInstance != null && weapon?.m_shared != null)
			{
				object fieldValue = GetFieldValue<object>(weapon.m_shared, "m_attack");
				object fieldValue2 = GetFieldValue<object>(weapon.m_shared, "m_secondaryAttack");
				if (fieldValue2 != null && AttackObjectsLookSimilar(attackInstance, fieldValue2))
				{
					return AttackRole.Secondary;
				}
				if (fieldValue != null && AttackObjectsLookSimilar(attackInstance, fieldValue))
				{
					return AttackRole.Primary;
				}
			}
			if (attackInstance != null)
			{
				FieldInfo[] fields = attackInstance.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				bool flag2 = default(bool);
				foreach (FieldInfo fieldInfo in fields)
				{
					string text = SafeLower(fieldInfo.Name);
					object value4;
					try
					{
						value4 = fieldInfo.GetValue(attackInstance);
					}
					catch
					{
						continue;
					}
					int num;
					if (value4 is bool)
					{
						flag2 = (bool)value4;
						num = 1;
					}
					else
					{
						num = 0;
					}
					if (((uint)num & (flag2 ? 1u : 0u)) != 0 && (text.Contains("secondary") || text.Contains("alternate") || text.Contains("alt") || text.Contains("special") || text.Contains("heavy")))
					{
						return AttackRole.Secondary;
					}
					string text2 = SafeLower(value4?.ToString());
					if ((text.Contains("secondary") || text.Contains("alternate") || text.Contains("special") || text.Contains("heavy")) && text2 != "false" && text2 != "0" && !string.IsNullOrEmpty(text2))
					{
						return AttackRole.Secondary;
					}
					if (text2.Contains("secondary") || text2.Contains("alternate") || text2.Contains("altattack") || text2.Contains("special") || text2.Contains("heavy"))
					{
						return AttackRole.Secondary;
					}
				}
			}
			return AttackRole.Primary;
		}

		private static void MarkCounterVulnerableDuringAttackAnimation(Character character, WeaponCategory category, AttackRole role, object? attackInstance)
		{
			if (!((Object)(object)character == (Object)null) && (!CounterVulnerableStates.TryGetValue(character, out var value) || value.AttackInstance != attackInstance))
			{
				CounterVulnerableStates[character] = new CounterVulnerableState(attackInstance);
				if (Debug(DebugCounterDamage))
				{
					Log.LogInfo((object)$"Counter vulnerability began for {SafeName(character)} using {category} {role}; ends when Valheim's real InAttack() state ends.");
				}
			}
		}

		private static void EndCounterVulnerableForAttack(Character character, object? attackInstance)
		{
			if (!((Object)(object)character == (Object)null) && CounterVulnerableStates.TryGetValue(character, out var value) && (value.AttackInstance == null || attackInstance == null || value.AttackInstance == attackInstance))
			{
				CounterVulnerableStates.Remove(character);
				if (Debug(DebugCounterDamage))
				{
					Log.LogInfo((object)("Counter vulnerability ended for " + SafeName(character) + "."));
				}
			}
		}

		private static bool IsCounterVulnerable(Character target)
		{
			string rejectReason;
			return TryGetCounterVulnerable(target, out rejectReason);
		}

		private static bool TryGetCounterVulnerable(Character target, out string rejectReason)
		{
			rejectReason = string.Empty;
			if ((Object)(object)target == (Object)null)
			{
				rejectReason = "victim is null";
				return false;
			}
			if (!CounterVulnerableStates.TryGetValue(target, out var value))
			{
				rejectReason = "victim is not in a tracked attack animation";
				return false;
			}
			if (!AttackStates.TryGetValue(target, out var value2))
			{
				CounterVulnerableStates.Remove(target);
				rejectReason = "victim has counter state but no tracked Attack runtime state";
				return false;
			}
			if (value.AttackInstance != null && value2.AttackInstance != null && value.AttackInstance != value2.AttackInstance)
			{
				CounterVulnerableStates.Remove(target);
				rejectReason = "victim counter state does not match current tracked attack instance";
				return false;
			}
			if (IsCharacterDead(target))
			{
				CounterVulnerableStates.Remove(target);
				rejectReason = "victim is dead";
				return false;
			}
			if (!CharacterReportsInAttack(target))
			{
				CounterVulnerableStates.Remove(target);
				rejectReason = "victim's real Valheim InAttack() state has ended";
				return false;
			}
			return true;
		}

		private static bool CharacterReportsInAttack(Character target)
		{
			if ((Object)(object)target == (Object)null)
			{
				return false;
			}
			try
			{
				MethodInfo methodInfo = GetCachedMethod(((object)target).GetType(), "InAttack");
				if ((object)CharacterInAttackMethod == null)
				{
					CharacterInAttackMethod = AccessTools.Method(typeof(Character), "InAttack", (Type[])null, (Type[])null);
				}
				if ((object)methodInfo == null)
				{
					methodInfo = CharacterInAttackMethod;
				}
				if (methodInfo == null)
				{
					return true;
				}
				return !(methodInfo.Invoke(target, null) is bool flag) || flag;
			}
			catch
			{
				return true;
			}
		}

		private static void ApplyCounterDamageIfEligible(Character victim, Character? attacker, HitData hit)
		{
			if (!EnableCounterDamage.Value || (Object)(object)victim == (Object)null || (Object)(object)attacker == (Object)null || (Object)(object)attacker == (Object)(object)victim || hit == null)
			{
				return;
			}
			bool flag = attacker is Player;
			if (OnlyPlayersCanDealCounterDamage.Value && !flag)
			{
				return;
			}
			string rejectReason;
			bool flag2 = TryGetCounterVulnerable(victim, out rejectReason);
			bool flag3 = IsInAttackState(victim);
			if (!flag2)
			{
				DebugCounterDamageReject(attacker, victim, $"{rejectReason}; victimInAttackState={flag3}");
				return;
			}
			ItemData currentWeapon = GetCurrentWeapon(attacker);
			if (currentWeapon == null)
			{
				DebugCounterDamageReject(attacker, victim, "attacker has no current weapon");
				return;
			}
			WeaponCategory weaponCategory = ClassifyWeapon(currentWeapon);
			if (!IsMeleeWeaponCategory(weaponCategory))
			{
				DebugCounterDamageReject(attacker, victim, $"attacker weapon category {weaponCategory} is not melee");
				return;
			}
			AttackRole currentAttackRole = GetCurrentAttackRole(attacker, weaponCategory);
			CategoryConfig config = GetConfig(weaponCategory);
			if (!config.GetCounterDamageEnabled(currentAttackRole))
			{
				DebugCounterDamageReject(attacker, victim, $"counter damage disabled for {weaponCategory} {currentAttackRole}; victimCounterVulnerable={flag2}, victimInAttackState={flag3}");
				return;
			}
			float num = Mathf.Max(0f, config.GetCounterDamageMultiplier(currentAttackRole));
			if (Mathf.Approximately(num, 1f))
			{
				DebugCounterDamageReject(attacker, victim, $"counter multiplier for {weaponCategory} {currentAttackRole} is 1");
				return;
			}
			((DamageTypes)(ref hit.m_damage)).Modify(num);
			if (EnableCounterDamageDing.Value)
			{
				TryPlayCounterDamageDing(victim, attacker);
			}
			if (Debug(DebugCounterDamage))
			{
				Log.LogInfo((object)$"Applied counter-damage x{num.ToString(CultureInfo.InvariantCulture)}: {SafeName(attacker)} -> {SafeName(victim)} with {weaponCategory} {currentAttackRole}.");
			}
		}

		private static void DebugCounterDamageReject(Character attacker, Character victim, string reason)
		{
			if (Debug(DebugCounterDamage) && attacker is Player)
			{
				Log.LogInfo((object)("Counter-damage rejected: " + reason + ". attacker=" + SafeName(attacker) + ", victim=" + SafeName(victim) + "."));
			}
		}

		private static void TryPlayCounterDamageDing(Character victim, Character attacker)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: 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)
			Vector3 counterDingPosition = GetCounterDingPosition(victim, attacker);
			if (TryPlayCounterDamageDingPrefab(counterDingPosition))
			{
				return;
			}
			object obj = FindBestCounterDingEffect(victim) ?? FindBestCounterDingEffect(attacker);
			if (obj == null)
			{
				if (DebugLogging.Value)
				{
					Log.LogInfo((object)"Counter-damage ding requested, but no prefab or parry/stagger/hit EffectList field was found.");
				}
			}
			else if (!TryCreateEffectList(obj, counterDingPosition, Quaternion.identity, null))
			{
				if (DebugLogging.Value)
				{
					Log.LogInfo((object)("Counter-damage ding requested, but EffectList.Create invocation failed on " + obj.GetType().FullName + "."));
				}
			}
			else if (DebugLogging.Value)
			{
				Log.LogInfo((object)("Counter-damage ding played through reflected EffectList " + obj.GetType().FullName + "."));
			}
		}

		private static Vector3 GetCounterDingPosition(Character? victim, Character? attacker)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: 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_0018: 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_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)victim != (Object)null && (Object)(object)attacker != (Object)null)
			{
				return Vector3.Lerp(((Component)victim).transform.position, ((Component)attacker).transform.position, 0.5f) + Vector3.up;
			}
			if ((Object)(object)victim != (Object)null)
			{
				return ((Component)victim).transform.position + Vector3.up;
			}
			if ((Object)(object)attacker != (Object)null)
			{
				return ((Component)attacker).transform.position + Vector3.up;
			}
			return Vector3.zero;
		}

		private static bool TryPlayCounterDamageDingPrefab(Vector3 pos)
		{
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			ZNetScene instance = ZNetScene.instance;
			if ((Object)(object)instance == (Object)null)
			{
				if (DebugLogging.Value)
				{
					Log.LogInfo((object)"Counter-damage ding prefab playback skipped: ZNetScene.instance is null.");
				}
				return false;
			}
			string[] array = new string[5] { "sfx_perfectblock", "sfx_parry", "sfx_blocked", "sfx_stagger", "sfx_hit" };
			foreach (string text in array)
			{
				GameObject prefab;
				try
				{
					prefab = instance.GetPrefab(text);
				}
				catch
				{
					continue;
				}
				if ((Object)(object)prefab == (Object)null)
				{
					continue;
				}
				try
				{
					Object.Destroy((Object)(object)Object.Instantiate<GameObject>(prefab, pos, Quaternion.identity), 4f);
					if (DebugLogging.Value)
					{
						Log.LogInfo((object)("Counter-damage ding played using prefab '" + text + "'."));
					}
					return true;
				}
				catch (Exception ex)
				{
					if (DebugLogging.Value)
					{
						Log.LogInfo((object)("Counter-damage ding prefab '" + text + "' failed: " + ex.GetType().Name + ": " + ex.Message));
					}
				}
			}
			if (DebugLogging.Value)
			{
				Log.LogInfo((object)"Counter-damage ding prefab playback failed: no configured SFX prefab was found.");
			}
			return false;
		}

		private static object? FindBestCounterDingEffect(Character? character)
		{
			if ((Object)(object)character == (Object)null)
			{
				return null;
			}
			object obj = null;
			foreach (FieldInfo allInstanceField in GetAllInstanceFields(((object)character).GetType()))
			{
				if (allInstanceField.FieldType.Name.IndexOf("EffectList", StringComparison.OrdinalIgnoreCase) < 0)
				{
					continue;
				}
				string text = SafeLower(allInstanceField.Name);
				object value;
				try
				{
					value = allInstanceField.GetValue(character);
				}
				catch
				{
					continue;
				}
				if (value != null)
				{
					if (text.Contains("perfect") || text.Contains("parry") || text.Contains("stagger"))
					{
						return value;
					}
					if (obj == null && (text.Contains("block") || text.Contains("hit") || text.Contains("damage")))
					{
						obj = value;
					}
				}
			}
			return obj;
		}

		[IteratorStateMachine(typeof(<GetAllInstanceFields>d__114))]
		private static IEnumerable<FieldInfo> GetAllInstanceFields(Type type)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <GetAllInstanceFields>d__114(-2)
			{
				<>3__type = type
			};
		}

		private static bool TryCreateEffectList(object effectList, Vector3 pos, Quaternion rot, Transform? parent)
		{
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				MethodInfo methodInfo = null;
				MethodInfo[] methods = effectList.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (MethodInfo methodInfo2 in methods)
				{
					if (!(methodInfo2.Name != "Create"))
					{
						ParameterInfo[] parameters = methodInfo2.GetParameters();
						if (parameters.Length >= 2 && parameters[0].ParameterType == typeof(Vector3) && parameters[1].ParameterType == typeof(Quaternion))
						{
							methodInfo = methodInfo2;
							break;
						}
					}
				}
				if (methodInfo == null)
				{
					return false;
				}
				ParameterInfo[] parameters2 = methodInfo.GetParameters();
				object[] array = new object[parameters2.Length];
				for (int j = 0; j < parameters2.Length; j++)
				{
					Type parameterType = parameters2[j].ParameterType;