Decompiled source of ShieldBash v1.6.0

BepInEx/plugins/ShieldBash/ShieldBash.dll

Decompiled 3 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Managers;
using Jotunn.Utils;
using UnityEngine;
using UnityEngine.Animations;
using UnityEngine.Playables;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("ShieldBash")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ShieldBash")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("be1580d5-2dda-4cf4-b5c8-d4f902314e67")]
[assembly: AssemblyFileVersion("1.6.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace ShieldBashMod;

[BepInPlugin("mexanik.shieldbash", "ShieldBash", "1.6.0")]
[BepInDependency("com.jotunn.jotunn", "2.30.2")]
[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
public sealed class ShieldBashPlugin : BaseUnityPlugin
{
	private sealed class BashVisual
	{
		internal Player Player;

		internal Animator Animator;

		internal bool RootMotion;

		internal bool Local;

		internal PlayableGraph Graph;
	}

	[HarmonyPatch(typeof(Player), "Awake")]
	private static class PlayerAwakePatch
	{
		private static void Postfix(Player __instance)
		{
			ShieldBashPlugin instance = Instance;
			if (!((Object)(object)instance == (Object)null))
			{
				instance.RegisterPlayerRpc(__instance);
			}
		}
	}

	[HarmonyPatch]
	private static class ShieldTooltipPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			MethodInfo[] methods = typeof(ItemData).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo in methods)
			{
				if (methodInfo != null && methodInfo.Name == "GetTooltip" && methodInfo.ReturnType == typeof(string))
				{
					yield return methodInfo;
				}
			}
		}

		private static void Postfix(ItemData __instance, ref string __result)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Invalid comparison between Unknown and I4
			try
			{
				ShieldBashPlugin instance = Instance;
				if ((Object)(object)instance == (Object)null || !instance.ShowShieldBashInTooltip.Value || __instance == null || __instance.m_shared == null || (int)__instance.m_shared.m_itemType != 5)
				{
					return;
				}
				string text = __result ?? string.Empty;
				if (text.IndexOf("Shield Bash:", StringComparison.Ordinal) < 0 && text.IndexOf("Удар щитом:", StringComparison.Ordinal) < 0)
				{
					string text2 = instance.BuildTooltipLineForShield(__instance);
					if (!string.IsNullOrEmpty(text2))
					{
						__result = text + text2;
					}
				}
			}
			catch (Exception ex)
			{
				ShieldBashPlugin instance2 = Instance;
				if ((Object)(object)instance2 != (Object)null && instance2.DebugLog != null && instance2.DebugLog.Value)
				{
					((BaseUnityPlugin)instance2).Logger.LogWarning((object)("[ShieldBash] Tooltip patch error: " + ex.Message));
				}
			}
		}
	}

	private sealed class AttackPhase
	{
		internal bool Triggered;

		internal bool Interrupted;
	}

	[HarmonyPatch(typeof(Character), "Awake")]
	private static class InterruptCharacterAwakePatch
	{
		private static void Postfix(Character __instance)
		{
			if (Instance != null)
			{
				Instance.RegisterInterruptRpc(__instance);
			}
		}
	}

	[HarmonyPatch(typeof(Attack), "OnAttackTrigger")]
	private static class InterruptAttackTriggerPatch
	{
		private static bool Prefix(Attack __instance)
		{
			return ConsumeAttackTrigger(__instance);
		}
	}

	public const string PluginGUID = "mexanik.shieldbash";

	public const string PluginName = "ShieldBash";

	public const string PluginVersion = "1.6.0";

	private const string InputName = "ShieldBash_Bash";

	private const string RpcPlayBashVisual = "ShieldBash_PlayVisual";

	internal static ShieldBashPlugin Instance;

	internal ConfigEntry<KeyCode> BashKey;

	internal ConfigEntry<float> Cooldown;

	internal ConfigEntry<bool> RequireShield;

	internal ConfigEntry<float> BaseStaminaCost;

	internal ConfigEntry<float> BlockPowerStaminaScale;

	internal ConfigEntry<float> SkillStaminaReduction;

	internal ConfigEntry<float> MinStaminaCost;

	internal ConfigEntry<float> MaxStaminaCost;

	internal ConfigEntry<float> AntiSlideTime;

	internal ConfigEntry<float> BaseDamage;

	internal ConfigEntry<float> BlockPowerScale;

	internal ConfigEntry<float> QualityBonusPerLevel;

	internal ConfigEntry<float> MinDamage;

	internal ConfigEntry<float> MaxDamage;

	internal ConfigEntry<float> BlockingSkillMultiplier;

	internal ConfigEntry<float> DamageRandomMin;

	internal ConfigEntry<float> DamageRandomMax;

	internal ConfigEntry<float> HitDelay;

	internal ConfigEntry<bool> DamageWorldObjects;

	internal ConfigEntry<float> WorldDamageScale;

	internal ConfigEntry<bool> WorldDamageUseRandom;

	internal ConfigEntry<float> Range;

	internal ConfigEntry<float> Radius;

	internal ConfigEntry<float> PushForce;

	internal ConfigEntry<float> Angle;

	internal ConfigEntry<bool> LineOfSightCheck;

	internal ConfigEntry<int> MaxLoSRaycastsPerBash;

	internal ConfigEntry<bool> HitTamed;

	internal ConfigEntry<bool> HitPlayers;

	internal ConfigEntry<int> OverlapBufferSize;

	internal ConfigEntry<bool> ShowShieldBashInTooltip;

	internal ConfigEntry<bool> ShowCooldownHint;

	internal ConfigEntry<bool> ShowStaminaHint;

	internal ConfigEntry<bool> DebugLog;

	internal ConfigEntry<bool> DebugLogDamageAfterArmor;

	internal ConfigEntry<string> HitClipName;

	internal ConfigEntry<string> MissClipName;

	internal ConfigEntry<bool> PlayHitSfxPerTarget;

	internal ConfigEntry<float> HitSfxVolumeMin;

	internal ConfigEntry<float> HitSfxVolumeMax;

	internal ConfigEntry<float> MissSfxVolume;

	internal ConfigEntry<float> RandomVolJitterMin;

	internal ConfigEntry<float> RandomVolJitterMax;

	internal ConfigEntry<float> RandomPitchJitterMin;

	internal ConfigEntry<float> RandomPitchJitterMax;

	internal ConfigEntry<float> MetalVolumeMul;

	internal ConfigEntry<float> WoodVolumeMul;

	internal ConfigEntry<float> MetalPitchMul;

	internal ConfigEntry<float> WoodPitchMul;

	internal ConfigEntry<float> MultiHitBonusMin;

	internal ConfigEntry<float> MultiHitBonusMax;

	internal ConfigEntry<float> LocalClipVolumeMul;

	private ButtonConfig _bashButton;

	private Harmony _harmony;

	private AnimationClip _bashClip;

	private AudioClip _hitClip;

	private AudioClip _missClip;

	private float _nextUseTime;

	private bool _isBashing;

	private float _nextCooldownHintTime;

	private readonly HashSet<int> _remoteBashes = new HashSet<int>();

	private readonly Dictionary<int, BashVisual> _visuals = new Dictionary<int, BashVisual>();

	private readonly List<int> _expiredVisuals = new List<int>();

	private float _nextStaminaHintTime;

	private Collider[] _overlapCols;

	private int[] _hitOnceIds;

	private int _hitOnceCount;

	private int _overlapMask = -1;

	private int _losWorldMask = -1;

	private float _cachedAngle = -999f;

	private float _cachedCosHalf = -2f;

	private AudioSource _localSfxSource;

	private static bool _locRefReady;

	private static Type _locType;

	private static PropertyInfo _locInstanceProp;

	private static MethodInfo _locGetSelectedLanguage;

	private static readonly FieldInfo CharacterAnimatorField = AccessTools.Field(typeof(Character), "m_animator");

	private static readonly FieldInfo CharacterBodyField = AccessTools.Field(typeof(Character), "m_body");

	private static readonly FieldInfo CharacterMoveDirField = AccessTools.Field(typeof(Character), "m_moveDir");

	private static readonly FieldInfo HumanoidLeftItemField = AccessTools.Field(typeof(Humanoid), "m_leftItem");

	private static readonly FieldInfo CharacterNViewField = AccessTools.Field(typeof(Character), "m_nview");

	private const string InterruptRpc = "mexanik.shieldbash.InterruptV1";

	private const string InterruptUntilKey = "mexanik.shieldbash.interruptUntilV1";

	private const long InterruptProtectionTicks = 40000000L;

	private ConfigEntry<bool> InterruptEnabled;

	private static readonly ConditionalWeakTable<Attack, AttackPhase> AttackPhases = new ConditionalWeakTable<Attack, AttackPhase>();

	private static readonly FieldInfo CurrentAttackField = AccessTools.Field(typeof(Humanoid), "m_currentAttack");

	private static readonly FieldInfo AttackDoneField = AccessTools.Field(typeof(Attack), "m_attackDone");

	private static readonly FieldInfo AttackAbortField = AccessTools.Field(typeof(Attack), "m_abortAttack");

	private static readonly FieldInfo StaggerFactorField = AccessTools.Field(typeof(Character), "m_staggerDamageFactor");

	private void Awake()
	{
		//IL_0757: Unknown result type (might be due to invalid IL or missing references)
		//IL_0761: Expected O, but got Unknown
		//IL_07cb: Unknown result type (might be due to invalid IL or missing references)
		//IL_07d5: Expected O, but got Unknown
		Instance = this;
		ConfigureInterrupt();
		BashKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Input", "BashKey", (KeyCode)103, "Key for Shield Bash");
		Cooldown = ((BaseUnityPlugin)this).Config.Bind<float>("Balance", "Cooldown", 0.9f, "Cooldown seconds");
		RequireShield = ((BaseUnityPlugin)this).Config.Bind<bool>("Balance", "RequireShield", true, "Require shield in left hand");
		BaseStaminaCost = ((BaseUnityPlugin)this).Config.Bind<float>("Stamina", "BaseStaminaCost", 10f, "Base stamina cost of Shield Bash");
		BlockPowerStaminaScale = ((BaseUnityPlugin)this).Config.Bind<float>("Stamina", "BlockPowerStaminaScale", 0.08f, "Additional stamina cost per point of shield block power");
		SkillStaminaReduction = ((BaseUnityPlugin)this).Config.Bind<float>("Stamina", "SkillStaminaReduction", 0.25f, "Maximum stamina cost reduction at Blocking skill 100");
		MinStaminaCost = ((BaseUnityPlugin)this).Config.Bind<float>("Stamina", "MinStaminaCost", 8f, "Minimum stamina cost of Shield Bash");
		MaxStaminaCost = ((BaseUnityPlugin)this).Config.Bind<float>("Stamina", "MaxStaminaCost", 18f, "Maximum stamina cost of Shield Bash");
		AntiSlideTime = ((BaseUnityPlugin)this).Config.Bind<float>("Feel", "AntiSlideTime", 0.2f, "Anti-slide duration seconds (0 = off)");
		BaseDamage = ((BaseUnityPlugin)this).Config.Bind<float>("Damage", "BaseDamage", 4f, "Flat blunt damage added to bash");
		BlockPowerScale = ((BaseUnityPlugin)this).Config.Bind<float>("Damage", "BlockPowerScale", 0.16f, "Damage per 1 shield block power");
		QualityBonusPerLevel = ((BaseUnityPlugin)this).Config.Bind<float>("Damage", "QualityBonusPerLevel", 0.4f, "Bonus damage per shield upgrade level (quality-1)");
		MinDamage = ((BaseUnityPlugin)this).Config.Bind<float>("Damage", "MinDamage", 3f, "Minimum final damage");
		MaxDamage = ((BaseUnityPlugin)this).Config.Bind<float>("Damage", "MaxDamage", 20f, "Maximum final damage cap");
		BlockingSkillMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Damage", "BlockingSkillMultiplier", 0.3f, "At Blocking skill 100, damage is increased by 30 percent");
		DamageRandomMin = ((BaseUnityPlugin)this).Config.Bind<float>("Damage", "DamageRandomMin", 0.92f, "Per-bash random damage multiplier min.");
		DamageRandomMax = ((BaseUnityPlugin)this).Config.Bind<float>("Damage", "DamageRandomMax", 1.05f, "Per-bash random damage multiplier max.");
		HitDelay = ((BaseUnityPlugin)this).Config.Bind<float>("Damage", "HitDelay", 0.12f, "Seconds after animation start when damage is applied");
		DamageWorldObjects = ((BaseUnityPlugin)this).Config.Bind<bool>("WorldDamage", "DamageWorldObjects", true, "Allow Shield Bash to damage trees/rocks/buildings.");
		WorldDamageScale = ((BaseUnityPlugin)this).Config.Bind<float>("WorldDamage", "WorldDamageScale", 0.85f, "Multiplier for world-object damage.");
		WorldDamageUseRandom = ((BaseUnityPlugin)this).Config.Bind<bool>("WorldDamage", "WorldDamageUseRandom", false, "If true: world damage uses per-bash random multiplier.");
		Range = ((BaseUnityPlugin)this).Config.Bind<float>("Hitbox", "Range", 1.6f, "Forward distance of hit center");
		Radius = ((BaseUnityPlugin)this).Config.Bind<float>("Hitbox", "Radius", 0.9f, "Hit radius");
		PushForce = ((BaseUnityPlugin)this).Config.Bind<float>("Hitbox", "PushForce", 20f, "Push force applied to Characters");
		Angle = ((BaseUnityPlugin)this).Config.Bind<float>("Hitbox", "AngleDegrees", 110f, "Cone angle filter (degrees). 180 = no cone.");
		LineOfSightCheck = ((BaseUnityPlugin)this).Config.Bind<bool>("Hitbox", "LineOfSightCheck", true, "Raycast visibility check (avoid hitting through walls)");
		MaxLoSRaycastsPerBash = ((BaseUnityPlugin)this).Config.Bind<int>("Hitbox", "MaxLoSRaycastsPerBash", 6, "Max LoS raycasts per bash. Unchecked targets are skipped; 0 blocks all hits while LineOfSightCheck is enabled.");
		HitTamed = ((BaseUnityPlugin)this).Config.Bind<bool>("Targets", "HitTamed", false, "Hit tamed creatures");
		HitPlayers = ((BaseUnityPlugin)this).Config.Bind<bool>("Targets", "HitPlayers", false, "Hit other players (PvP)");
		OverlapBufferSize = ((BaseUnityPlugin)this).Config.Bind<int>("Performance", "OverlapBufferSize", 64, "NonAlloc overlap collider buffer size (64/96/128)");
		ShowShieldBashInTooltip = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "ShowShieldBashInTooltip", true, "Add Shield Bash damage line to shield tooltip (RU/EN)");
		ShowCooldownHint = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "ShowCooldownHint", true, "Show remaining cooldown on an early key press (local only, RU/EN)");
		ShowStaminaHint = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "ShowStaminaHint", true, "Show insufficient stamina on key press (local only, RU/EN)");
		DebugLog = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Log", false, "General debug logging");
		DebugLogDamageAfterArmor = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "LogDamageAfterArmor", false, "Log dealt damage per hit target");
		HitClipName = ((BaseUnityPlugin)this).Config.Bind<string>("SFX", "HitClipName", "shield_bash_hit", "AudioClip.name inside AssetBundle for HIT sound.");
		MissClipName = ((BaseUnityPlugin)this).Config.Bind<string>("SFX", "MissClipName", "shield_bash_miss", "AudioClip.name inside AssetBundle for MISS sound.");
		PlayHitSfxPerTarget = ((BaseUnityPlugin)this).Config.Bind<bool>("SFX", "PlayHitSfxPerTarget", false, "If true: play hit sound for each target.");
		HitSfxVolumeMin = ((BaseUnityPlugin)this).Config.Bind<float>("SFX", "HitSfxVolumeMin", 0.6f, "Min hit volume at MinDamage.");
		HitSfxVolumeMax = ((BaseUnityPlugin)this).Config.Bind<float>("SFX", "HitSfxVolumeMax", 1.1f, "Max hit volume at MaxDamage.");
		MissSfxVolume = ((BaseUnityPlugin)this).Config.Bind<float>("SFX", "MissSfxVolume", 0.7f, "Base miss whoosh volume.");
		RandomVolJitterMin = ((BaseUnityPlugin)this).Config.Bind<float>("SFX", "RandomVolJitterMin", 0.95f, "Random volume multiplier min.");
		RandomVolJitterMax = ((BaseUnityPlugin)this).Config.Bind<float>("SFX", "RandomVolJitterMax", 1.05f, "Random volume multiplier max.");
		RandomPitchJitterMin = ((BaseUnityPlugin)this).Config.Bind<float>("SFX", "RandomPitchJitterMin", 0.97f, "Random pitch multiplier min.");
		RandomPitchJitterMax = ((BaseUnityPlugin)this).Config.Bind<float>("SFX", "RandomPitchJitterMax", 1.03f, "Random pitch multiplier max.");
		MetalVolumeMul = ((BaseUnityPlugin)this).Config.Bind<float>("SFX", "MetalVolumeMul", 1.07f, "Volume multiplier for metal shields.");
		WoodVolumeMul = ((BaseUnityPlugin)this).Config.Bind<float>("SFX", "WoodVolumeMul", 0.92f, "Volume multiplier for wood shields.");
		MetalPitchMul = ((BaseUnityPlugin)this).Config.Bind<float>("SFX", "MetalPitchMul", 1.02f, "Pitch multiplier for metal shields.");
		WoodPitchMul = ((BaseUnityPlugin)this).Config.Bind<float>("SFX", "WoodPitchMul", 0.96f, "Pitch multiplier for wood shields.");
		MultiHitBonusMin = ((BaseUnityPlugin)this).Config.Bind<float>("SFX", "MultiHitBonusMin", 0.05f, "Bonus volume add when hits>1, min.");
		MultiHitBonusMax = ((BaseUnityPlugin)this).Config.Bind<float>("SFX", "MultiHitBonusMax", 0.1f, "Bonus volume add when hits>1, max.");
		LocalClipVolumeMul = ((BaseUnityPlugin)this).Config.Bind<float>("SFX", "LocalClipVolumeMul", 1f, "Multiplier for local AudioClips.");
		_bashButton = new ButtonConfig();
		_bashButton.Name = "ShieldBash_Bash";
		_bashButton.Config = BashKey;
		_bashButton.HintToken = "Shield Bash";
		_bashButton.ActiveInGUI = false;
		_bashButton.ActiveInCustomGUI = false;
		InputManager.Instance.AddButton("mexanik.shieldbash", _bashButton);
		EnsureBuffers();
		_harmony = new Harmony("mexanik.shieldbash");
		_harmony.PatchAll();
		bool flag = LoadAssetsFromBundle();
		((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("[{0}] Assets loaded: {1} (anim={2}, hit={3}, miss={4})", "ShieldBash", flag, ((Object)(object)_bashClip != (Object)null) ? ((Object)_bashClip).name : "null", ((Object)(object)_hitClip != (Object)null) ? ((Object)_hitClip).name : "null", ((Object)(object)_missClip != (Object)null) ? ((Object)_missClip).name : "null"));
	}

	private void OnDestroy()
	{
		OnDisable();
		if ((Object)(object)Instance == (Object)(object)this)
		{
			Instance = null;
		}
		try
		{
			if (_harmony != null)
			{
				_harmony.UnpatchSelf();
			}
		}
		catch
		{
		}
		try
		{
			if ((Object)(object)_localSfxSource != (Object)null && (Object)(object)((Component)_localSfxSource).gameObject != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_localSfxSource).gameObject);
			}
		}
		catch
		{
		}
	}

	private void EnsureBuffers()
	{
		int num = Mathf.Clamp(OverlapBufferSize.Value, 16, 256);
		if (_overlapCols == null)
		{
			_overlapCols = (Collider[])(object)new Collider[num];
		}
		else if (_overlapCols.Length != num)
		{
			_overlapCols = (Collider[])(object)new Collider[num];
		}
		int num2 = Mathf.Clamp(num * 2, 32, 1024);
		if (_hitOnceIds == null)
		{
			_hitOnceIds = new int[num2];
		}
		else if (_hitOnceIds.Length != num2)
		{
			_hitOnceIds = new int[num2];
		}
		_hitOnceCount = 0;
	}

	private void Update()
	{
		//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ea: Expected O, but got Unknown
		//IL_011d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0127: Expected O, but got Unknown
		//IL_0109: Unknown result type (might be due to invalid IL or missing references)
		//IL_010f: Invalid comparison between Unknown and I4
		CleanupExpiredVisuals();
		if (ZInput.instance == null || !ZInput.GetButtonDown(_bashButton.Name))
		{
			return;
		}
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null || !CanBash(localPlayer))
		{
			return;
		}
		if (_isBashing || Time.time < _nextUseTime)
		{
			if (ShowCooldownHint.Value && Time.time < _nextUseTime && Time.unscaledTime >= _nextCooldownHintTime)
			{
				float num = _nextUseTime - Time.time;
				((Character)localPlayer).Message((MessageType)1, string.Format(IsRussianUi() ? "Удар щитом: ещё {0:0.0} с" : "Shield Bash: {0:0.0}s remaining", num), 0, (Sprite)null, false);
				_nextCooldownHintTime = Time.unscaledTime + 1f;
			}
			return;
		}
		if ((Object)(object)_bashClip == (Object)null)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)"[ShieldBash] Bash clip is null. Bundle/clip not loaded.");
			return;
		}
		ItemData leftItem = GetLeftItem((Humanoid)localPlayer);
		if (!RequireShield.Value || (leftItem != null && leftItem.m_shared != null && (int)leftItem.m_shared.m_itemType == 5))
		{
			float cost = ComputeStaminaCost(localPlayer, leftItem);
			Animator animator = GetAnimator((Character)localPlayer);
			if (!((Object)(object)animator == (Object)null) && TryUseStaminaNoMessage(localPlayer, cost))
			{
				EnsureBuffers();
				_nextUseTime = Time.time + Cooldown.Value;
				((MonoBehaviour)this).StartCoroutine(DoBash(localPlayer, animator, _bashClip, applyDamage: true, (leftItem != null) ? leftItem.Clone() : null, GetBlockingSkillFactor(localPlayer), ComputeShieldBaseDamage(leftItem)));
				BroadcastBashVisual(localPlayer);
			}
		}
	}

	private static bool CanBash(Player player)
	{
		if ((Object)(object)player != (Object)null && !((Character)player).IsDead() && !((Character)player).IsTeleporting() && !((Character)player).InDodge() && !((Character)player).IsStaggering() && !((Character)player).InAttack())
		{
			return !((Character)player).InCutscene();
		}
		return false;
	}

	private void ReleaseVisual(int id, BashVisual visual)
	{
		if (!_visuals.TryGetValue(id, out var value) || value != visual)
		{
			return;
		}
		_visuals.Remove(id);
		try
		{
			if (((PlayableGraph)(ref visual.Graph)).IsValid())
			{
				((PlayableGraph)(ref visual.Graph)).Destroy();
			}
		}
		finally
		{
			_remoteBashes.Remove(id);
			if (visual.Local)
			{
				_isBashing = false;
			}
			if ((Object)(object)visual.Animator != (Object)null)
			{
				visual.Animator.applyRootMotion = visual.RootMotion;
			}
		}
	}

	private void CleanupExpiredVisuals()
	{
		_expiredVisuals.Clear();
		foreach (KeyValuePair<int, BashVisual> visual in _visuals)
		{
			BashVisual value = visual.Value;
			if ((Object)(object)value.Player == (Object)null || (Object)(object)value.Animator == (Object)null || ((Character)value.Player).IsDead() || (value.Local && (Object)(object)value.Player != (Object)(object)Player.m_localPlayer))
			{
				_expiredVisuals.Add(visual.Key);
			}
		}
		foreach (int expiredVisual in _expiredVisuals)
		{
			ReleaseVisual(expiredVisual, _visuals[expiredVisual]);
		}
	}

	private void OnDisable()
	{
		((MonoBehaviour)this).StopAllCoroutines();
		foreach (int item in new List<int>(_visuals.Keys))
		{
			ReleaseVisual(item, _visuals[item]);
		}
		_remoteBashes.Clear();
		_isBashing = false;
		_nextUseTime = 0f;
	}

	private IEnumerator DoBash(Player player, Animator animator, AnimationClip clip, bool applyDamage, ItemData attackShield = null, float attackSkill = 0f, float attackBaseDamage = 0f)
	{
		int playerId = ((Object)player).GetInstanceID();
		if (!applyDamage && !_remoteBashes.Add(playerId))
		{
			yield break;
		}
		if (applyDamage)
		{
			_isBashing = true;
		}
		bool applyRootMotion = animator.applyRootMotion;
		PlayableGraph val = default(PlayableGraph);
		BashVisual visual = new BashVisual
		{
			Player = player,
			Animator = animator,
			RootMotion = applyRootMotion,
			Local = applyDamage
		};
		_visuals[playerId] = visual;
		try
		{
			if (applyDamage && AntiSlideTime.Value > 0.0001f)
			{
				((MonoBehaviour)this).StartCoroutine(AntiSlideRoutine(player, AntiSlideTime.Value));
			}
			animator.applyRootMotion = false;
			val = (visual.Graph = PlayableGraph.Create("ShieldBashGraph"));
			AnimatorControllerPlayable val2 = AnimatorControllerPlayable.Create(val, animator.runtimeAnimatorController);
			AnimationClipPlayable val3 = AnimationClipPlayable.Create(val, clip);
			((AnimationClipPlayable)(ref val3)).SetApplyFootIK(false);
			((AnimationClipPlayable)(ref val3)).SetApplyPlayableIK(false);
			AnimationLayerMixerPlayable val4 = AnimationLayerMixerPlayable.Create(val, 2);
			((PlayableGraph)(ref val)).Connect<AnimatorControllerPlayable, AnimationLayerMixerPlayable>(val2, 0, val4, 0);
			((PlayableGraph)(ref val)).Connect<AnimationClipPlayable, AnimationLayerMixerPlayable>(val3, 0, val4, 1);
			PlayableExtensions.SetInputWeight<AnimationLayerMixerPlayable>(val4, 0, 0f);
			PlayableExtensions.SetInputWeight<AnimationLayerMixerPlayable>(val4, 1, 1f);
			PlayableOutputExtensions.SetSourcePlayable<AnimationPlayableOutput, AnimationLayerMixerPlayable>(AnimationPlayableOutput.Create(val, "ShieldBashOutput", animator), val4);
			((PlayableGraph)(ref val)).Play();
			float hitAt = Mathf.Clamp(HitDelay.Value, 0.01f, Mathf.Max(0.01f, clip.length));
			yield return (object)new WaitForSeconds(hitAt);
			if (!_visuals.TryGetValue(playerId, out var value) || value != visual)
			{
				yield break;
			}
			if ((Object)(object)player != (Object)null && !((Character)player).IsDead())
			{
				if (applyDamage)
				{
					ZNetView zNetView = GetZNetView((Character)(object)player);
					if (CanBash(player) && (Object)(object)zNetView != (Object)null && zNetView.IsValid() && zNetView.IsOwner())
					{
						ApplyBashDamageAndSfx(player, attackShield, attackSkill, attackBaseDamage);
					}
				}
				else
				{
					PlayRemoteBashSfx(player);
				}
			}
			float num = Mathf.Max(0.01f, clip.length - hitAt);
			yield return (object)new WaitForSeconds(num);
		}
		finally
		{
			ReleaseVisual(playerId, visual);
		}
	}

	private void BroadcastBashVisual(Player player)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Expected O, but got Unknown
		ZNetView zNetView = GetZNetView((Character)player);
		if ((Object)(object)zNetView == (Object)null || !zNetView.IsValid() || !zNetView.IsOwner())
		{
			return;
		}
		try
		{
			zNetView.InvokeRPC(ZNetView.Everybody, "ShieldBash_PlayVisual", Array.Empty<object>());
		}
		catch (Exception ex)
		{
			if (DebugLog != null && DebugLog.Value)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("[ShieldBash] RPC send failed: " + ex.Message));
			}
		}
	}

	internal void RegisterPlayerRpc(Player player)
	{
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: Expected O, but got Unknown
		if ((Object)(object)player == (Object)null)
		{
			return;
		}
		ZNetView zNetView = GetZNetView((Character)player);
		if ((Object)(object)zNetView == (Object)null || !zNetView.IsValid())
		{
			return;
		}
		try
		{
			zNetView.Register("ShieldBash_PlayVisual", (Action<long>)delegate(long sender)
			{
				RpcPlayRemoteBashVisual(player, sender);
			});
		}
		catch
		{
		}
	}

	private void RpcPlayRemoteBashVisual(Player player, long sender)
	{
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: Expected O, but got Unknown
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		//IL_006d: Expected O, but got Unknown
		if (!((Behaviour)this).isActiveAndEnabled || (Object)(object)player == (Object)null)
		{
			return;
		}
		ZNetView zNetView = GetZNetView((Character)player);
		if (!((Object)(object)zNetView == (Object)null) && zNetView.IsValid() && !zNetView.IsOwner() && zNetView.GetZDO() != null && sender == zNetView.GetZDO().GetOwner() && !((Object)(object)_bashClip == (Object)null))
		{
			Animator animator = GetAnimator((Character)player);
			if (!((Object)(object)animator == (Object)null))
			{
				((MonoBehaviour)this).StartCoroutine(DoBash(player, animator, _bashClip, applyDamage: false));
			}
		}
	}

	private void PlayRemoteBashSfx(Player player)
	{
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)player == (Object)null))
		{
			Vector3 pos = ((Component)player).transform.position + ((Component)player).transform.forward * 0.6f + Vector3.up * 1f;
			PlayLocalMissAt(player, pos, Mathf.Max(0f, MissSfxVolume.Value), 1f);
		}
	}

	private IEnumerator AntiSlideRoutine(Player player, float duration)
	{
		if ((Object)(object)player == (Object)null)
		{
			yield break;
		}
		Rigidbody body;
		try
		{
			body = GetCharacterBody((Character)(object)player);
		}
		catch
		{
			body = null;
		}
		if (!((Object)(object)body == (Object)null) && !body.isKinematic)
		{
			float t = 0f;
			SetMoveDirZero(player);
			body.linearVelocity = Vector3.zero;
			while (t < duration && CanBash(player) && _isBashing && !((Object)(object)body == (Object)null))
			{
				Vector3 linearVelocity = body.linearVelocity;
				linearVelocity.x = 0f;
				linearVelocity.z = 0f;
				body.linearVelocity = linearVelocity;
				SetMoveDirZero(player);
				t += Time.deltaTime;
				yield return null;
			}
		}
	}

	private static void SetMoveDirZero(Player player)
	{
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)player == (Object)null || CharacterMoveDirField == null)
		{
			return;
		}
		try
		{
			CharacterMoveDirField.SetValue(player, Vector3.zero);
		}
		catch
		{
		}
	}

	private float ComputeStaminaCost(Player player, ItemData shield)
	{
		float blockPower = 0f;
		if (shield != null && shield.m_shared != null)
		{
			int num = Mathf.Max(1, shield.m_quality);
			blockPower = shield.m_shared.m_blockPower + shield.m_shared.m_blockPowerPerLevel * (float)(num - 1);
		}
		float blockingSkillFactor = GetBlockingSkillFactor(player);
		return ComputeStaminaCostFromBlock(blockPower, blockingSkillFactor);
	}

	private float ComputeStaminaCostFromBlock(float blockPower, float skillFactor)
	{
		float num = Mathf.Max(0f, BaseStaminaCost.Value) + blockPower * Mathf.Max(0f, BlockPowerStaminaScale.Value);
		float num2 = skillFactor * Mathf.Clamp01(SkillStaminaReduction.Value);
		float num3 = num * (1f - num2);
		float num4 = Mathf.Max(0f, MinStaminaCost.Value);
		float num5 = Mathf.Max(num4, MaxStaminaCost.Value);
		return Mathf.Clamp(num3, num4, num5);
	}

	private bool TryUseStaminaNoMessage(Player player, float cost)
	{
		if (cost <= 0f)
		{
			return true;
		}
		if (player.GetStamina() + 0.001f < cost)
		{
			if (ShowStaminaHint.Value && Time.unscaledTime >= _nextStaminaHintTime)
			{
				((Character)player).Message((MessageType)1, IsRussianUi() ? "Удар щитом: недостаточно выносливости" : "Shield Bash: not enough stamina", 0, (Sprite)null, false);
				_nextStaminaHintTime = Time.unscaledTime + 1f;
			}
			return false;
		}
		((Character)player).UseStamina(cost);
		return true;
	}

	internal float ComputeShieldBaseDamage(ItemData shield)
	{
		int num = 1;
		float num2 = 0f;
		if (shield != null && shield.m_shared != null)
		{
			num = Mathf.Max(1, shield.m_quality);
			num2 = shield.m_shared.m_blockPower + shield.m_shared.m_blockPowerPerLevel * (float)(num - 1);
		}
		return BaseDamage.Value + num2 * BlockPowerScale.Value + (float)Mathf.Max(0, num - 1) * Mathf.Max(0f, QualityBonusPerLevel.Value);
	}

	private float GetBlockingSkillFactor(Player player)
	{
		if ((Object)(object)player == (Object)null)
		{
			return 0f;
		}
		try
		{
			return Mathf.Clamp01(((Character)player).GetSkillFactor((SkillType)6));
		}
		catch
		{
			return 0f;
		}
	}

	private float ApplySkillAndRandom(float baseDamage, float skillFactor, float randMul)
	{
		float num = Mathf.Max(0f, BlockingSkillMultiplier.Value);
		return Mathf.Clamp(baseDamage * (1f + skillFactor * num) * randMul, MinDamage.Value, MaxDamage.Value);
	}

	private int GetOverlapMask()
	{
		if (_overlapMask != -1)
		{
			return _overlapMask;
		}
		_overlapMask = LayerMask.GetMask(new string[7] { "character", "character_net", "Default", "static_solid", "piece", "Hitbox", "hitbox" });
		if (_overlapMask == 0)
		{
			_overlapMask = -1;
		}
		return _overlapMask;
	}

	private int GetWorldLosMask()
	{
		if (_losWorldMask != -1)
		{
			return _losWorldMask;
		}
		_losWorldMask = LayerMask.GetMask(new string[7] { "Default", "static_solid", "piece", "Hitbox", "hitbox", "character", "character_net" });
		if (_losWorldMask == 0)
		{
			_losWorldMask = -1;
		}
		return _losWorldMask;
	}

	private float GetCosHalfAngle()
	{
		float num = Mathf.Clamp(Angle.Value, 1f, 180f);
		if (Mathf.Abs(num - _cachedAngle) < 0.001f && _cachedCosHalf > -1f)
		{
			return _cachedCosHalf;
		}
		_cachedAngle = num;
		_cachedCosHalf = Mathf.Cos(num * ((float)Math.PI / 180f) * 0.5f);
		return _cachedCosHalf;
	}

	private void HitOnceReset()
	{
		_hitOnceCount = 0;
	}

	private bool HitOnceTryAdd(int id)
	{
		for (int i = 0; i < _hitOnceCount; i++)
		{
			if (_hitOnceIds[i] == id)
			{
				return false;
			}
		}
		if (_hitOnceCount >= _hitOnceIds.Length)
		{
			return false;
		}
		_hitOnceIds[_hitOnceCount] = id;
		_hitOnceCount++;
		return true;
	}

	private static Vector3 GetSafeHitPoint(Collider col, Vector3 nearPoint, Vector3 fallback)
	{
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_0009: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)col == (Object)null)
		{
			return fallback;
		}
		Vector3 val = col.ClosestPointOnBounds(nearPoint);
		if (float.IsNaN(val.x))
		{
			return fallback;
		}
		if (float.IsInfinity(val.x))
		{
			return fallback;
		}
		return val;
	}

	private void ApplyBashDamageAndSfx(Player player, ItemData shield, float attackSkill, float attackBaseDamage)
	{
		//IL_0170: Unknown result type (might be due to invalid IL or missing references)
		//IL_0175: Unknown result type (might be due to invalid IL or missing references)
		//IL_017d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0182: Unknown result type (might be due to invalid IL or missing references)
		//IL_018c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0191: Unknown result type (might be due to invalid IL or missing references)
		//IL_0196: Unknown result type (might be due to invalid IL or missing references)
		//IL_019e: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
		//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01db: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0223: Unknown result type (might be due to invalid IL or missing references)
		//IL_0225: Unknown result type (might be due to invalid IL or missing references)
		//IL_0245: Unknown result type (might be due to invalid IL or missing references)
		//IL_024a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0251: Unknown result type (might be due to invalid IL or missing references)
		//IL_0256: Unknown result type (might be due to invalid IL or missing references)
		//IL_025b: Unknown result type (might be due to invalid IL or missing references)
		//IL_06c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_06d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_06db: Unknown result type (might be due to invalid IL or missing references)
		//IL_06e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_06e5: Unknown result type (might be due to invalid IL or missing references)
		//IL_06ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_06f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_06f9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0728: Unknown result type (might be due to invalid IL or missing references)
		//IL_05b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_05bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_05c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_05c3: Unknown result type (might be due to invalid IL or missing references)
		//IL_05cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_06b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_05e3: Unknown result type (might be due to invalid IL or missing references)
		//IL_05e5: Unknown result type (might be due to invalid IL or missing references)
		//IL_061b: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f9: Unknown result type (might be due to invalid IL or missing references)
		//IL_02fe: Unknown result type (might be due to invalid IL or missing references)
		//IL_0308: Unknown result type (might be due to invalid IL or missing references)
		//IL_030d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0312: Unknown result type (might be due to invalid IL or missing references)
		//IL_0314: Unknown result type (might be due to invalid IL or missing references)
		//IL_0316: Unknown result type (might be due to invalid IL or missing references)
		//IL_0318: Unknown result type (might be due to invalid IL or missing references)
		//IL_031d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0334: Unknown result type (might be due to invalid IL or missing references)
		//IL_0338: Unknown result type (might be due to invalid IL or missing references)
		//IL_033d: Unknown result type (might be due to invalid IL or missing references)
		//IL_033f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0341: Unknown result type (might be due to invalid IL or missing references)
		//IL_03f9: Unknown result type (might be due to invalid IL or missing references)
		//IL_03fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_03fd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0402: Unknown result type (might be due to invalid IL or missing references)
		//IL_0421: Unknown result type (might be due to invalid IL or missing references)
		//IL_0428: Expected O, but got Unknown
		//IL_042b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0430: Unknown result type (might be due to invalid IL or missing references)
		//IL_0437: Unknown result type (might be due to invalid IL or missing references)
		//IL_0439: Unknown result type (might be due to invalid IL or missing references)
		//IL_0447: Unknown result type (might be due to invalid IL or missing references)
		//IL_0452: Unknown result type (might be due to invalid IL or missing references)
		//IL_0457: Unknown result type (might be due to invalid IL or missing references)
		//IL_045c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0460: Unknown result type (might be due to invalid IL or missing references)
		//IL_0465: Unknown result type (might be due to invalid IL or missing references)
		//IL_04af: Unknown result type (might be due to invalid IL or missing references)
		//IL_04ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_04bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_04c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0380: Unknown result type (might be due to invalid IL or missing references)
		//IL_0386: Unknown result type (might be due to invalid IL or missing references)
		//IL_0388: Unknown result type (might be due to invalid IL or missing references)
		//IL_0564: Unknown result type (might be due to invalid IL or missing references)
		//IL_0566: Unknown result type (might be due to invalid IL or missing references)
		//IL_059f: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)player == (Object)null)
		{
			return;
		}
		HitOnceReset();
		float num = DamageRandomMin.Value;
		float num2 = DamageRandomMax.Value;
		if (num > num2)
		{
			float num3 = num;
			num = num2;
			num2 = num3;
		}
		float num4 = Random.Range(num, num2);
		float num5 = ApplySkillAndRandom(attackBaseDamage, attackSkill, num4);
		float randMul = ((!WorldDamageUseRandom.Value) ? 1f : num4);
		float num6 = ApplySkillAndRandom(attackBaseDamage, attackSkill, randMul) * Mathf.Clamp(WorldDamageScale.Value, 0f, 5f);
		float num7 = Mathf.Clamp01(Mathf.InverseLerp(MinDamage.Value, MaxDamage.Value, num5));
		float num8 = Mathf.Lerp(HitSfxVolumeMin.Value, HitSfxVolumeMax.Value, num7);
		float num9 = Mathf.Max(0f, MissSfxVolume.Value);
		GetMaterialMix(shield, out var volMul, out var pitchMul);
		float num10 = Random.Range(Mathf.Min(RandomVolJitterMin.Value, RandomVolJitterMax.Value), Mathf.Max(RandomVolJitterMin.Value, RandomVolJitterMax.Value));
		float num11 = Random.Range(Mathf.Min(RandomPitchJitterMin.Value, RandomPitchJitterMax.Value), Mathf.Max(RandomPitchJitterMin.Value, RandomPitchJitterMax.Value));
		Vector3 forward = ((Component)player).transform.forward;
		Vector3 val = ((Component)player).transform.position + Vector3.up * 1.3f;
		Vector3 val2 = ((Component)player).transform.position + forward * Range.Value;
		Vector3 val3 = val2 + Vector3.up * 0.6f;
		Vector3 val4 = val2 + Vector3.up * 1.6f;
		int overlapMask = GetOverlapMask();
		float cosHalfAngle = GetCosHalfAngle();
		int num12 = Physics.OverlapCapsuleNonAlloc(val3, val4, Radius.Value, _overlapCols, overlapMask, (QueryTriggerInteraction)2);
		int num13 = 0;
		Character val5 = null;
		float num14 = float.MaxValue;
		Vector3 pos = val;
		int losChecks = 0;
		int num15 = Mathf.Clamp(MaxLoSRaycastsPerBash.Value, 0, 64);
		Vector3 nearPoint = ((Component)player).transform.position + forward * 0.5f;
		for (int i = 0; i < num12; i++)
		{
			Collider val6 = _overlapCols[i];
			if ((Object)(object)val6 == (Object)null)
			{
				continue;
			}
			Character componentInParent = ((Component)val6).GetComponentInParent<Character>();
			if ((Object)(object)componentInParent != (Object)null)
			{
				if ((object)componentInParent == player || componentInParent.IsDead())
				{
					continue;
				}
				if (!HitTamed.Value)
				{
					Tameable component = ((Component)componentInParent).GetComponent<Tameable>();
					if ((Object)(object)component != (Object)null && component.IsTamed())
					{
						continue;
					}
				}
				if (!HitPlayers.Value && (Object)(object)((componentInParent is Player) ? componentInParent : null) != (Object)null)
				{
					continue;
				}
				Vector3 val7 = ((Component)componentInParent).transform.position + Vector3.up * 1.1f;
				Vector3 val8 = val7 - val;
				float magnitude = ((Vector3)(ref val8)).magnitude;
				if (magnitude < 0.001f)
				{
					continue;
				}
				Vector3 val9 = val8 / magnitude;
				if (Vector3.Dot(forward, val9) < cosHalfAngle)
				{
					continue;
				}
				if (LineOfSightCheck.Value)
				{
					if (losChecks >= num15)
					{
						continue;
					}
					if (num15 > 0 && losChecks < num15)
					{
						losChecks++;
						int worldLosMask = GetWorldLosMask();
						RaycastHit val10 = default(RaycastHit);
						if (Physics.Raycast(val, val9, ref val10, magnitude, worldLosMask, (QueryTriggerInteraction)1))
						{
							Character val11 = null;
							if ((Object)(object)((RaycastHit)(ref val10)).collider != (Object)null)
							{
								val11 = ((Component)((RaycastHit)(ref val10)).collider).GetComponentInParent<Character>();
							}
							if (val11 != componentInParent)
							{
								continue;
							}
						}
					}
				}
				if (!HitOnceTryAdd(((Object)((Component)componentInParent).gameObject).GetInstanceID()))
				{
					continue;
				}
				float num16 = Mathf.Clamp(num5, MinDamage.Value, MaxDamage.Value);
				Vector3 safeHitPoint = GetSafeHitPoint(val6, nearPoint, val7);
				float num17 = 0f;
				if (DebugLogDamageAfterArmor.Value)
				{
					num17 = componentInParent.GetHealth();
				}
				HitData val12 = new HitData();
				val12.m_attacker = ((Character)player).GetZDOID();
				val12.m_point = safeHitPoint;
				Vector3 val13 = ((Component)componentInParent).transform.position - ((Component)player).transform.position;
				val12.m_dir = ((Vector3)(ref val13)).normalized;
				val12.m_pushForce = PushForce.Value;
				val12.m_backstabBonus = 1f;
				val12.m_damage.m_blunt = num16;
				componentInParent.Damage(val12);
				if (IsInterruptCandidate(componentInParent))
				{
					val13 = ((Component)componentInParent).transform.position - ((Component)player).transform.position;
					float sqrMagnitude = ((Vector3)(ref val13)).sqrMagnitude;
					if (sqrMagnitude < num14)
					{
						num14 = sqrMagnitude;
						val5 = componentInParent;
					}
				}
				if (DebugLogDamageAfterArmor.Value)
				{
					float health = componentInParent.GetHealth();
					float num18 = Mathf.Max(0f, num17 - health);
					((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("[{0}] DEALT '{1}': {2:0.0} (damage={3:0.0}) hp {4:0.0}->{5:0.0}", "ShieldBash", ((Object)componentInParent).name, num18, num16, num17, health));
				}
				num13++;
				if (num13 == 1)
				{
					pos = safeHitPoint;
				}
				if (PlayHitSfxPerTarget.Value)
				{
					float volumeMul = Mathf.Max(0f, num8 * volMul * num10);
					float pitchMul2 = Mathf.Max(0.01f, pitchMul * num11);
					PlayLocalHitAt(player, safeHitPoint, volumeMul, pitchMul2);
				}
			}
			else
			{
				if (!DamageWorldObjects.Value)
				{
					continue;
				}
				Vector3 hitPoint = val;
				if (TryDamageWorldObject(val6, player, val, forward, cosHalfAngle, ref losChecks, num15, num6, nearPoint, out hitPoint))
				{
					num13++;
					if (num13 == 1)
					{
						pos = hitPoint;
					}
					if (PlayHitSfxPerTarget.Value)
					{
						float volumeMul2 = Mathf.Max(0f, num8 * volMul * num10);
						float pitchMul3 = Mathf.Max(0.01f, pitchMul * num11);
						PlayLocalHitAt(player, hitPoint, volumeMul2, pitchMul3);
					}
				}
			}
		}
		if (!PlayHitSfxPerTarget.Value)
		{
			if (num13 > 0)
			{
				float num19 = 0f;
				if (num13 > 1)
				{
					float num20 = MultiHitBonusMin.Value;
					float num21 = MultiHitBonusMax.Value;
					if (num20 > num21)
					{
						float num22 = num20;
						num20 = num21;
						num21 = num22;
					}
					num19 = Random.Range(num20, num21);
				}
				float volumeMul3 = Mathf.Max(0f, (num8 + num19) * volMul * num10);
				float pitchMul4 = Mathf.Max(0.01f, pitchMul * num11);
				PlayLocalHitAt(player, pos, volumeMul3, pitchMul4);
			}
			else
			{
				Vector3 pos2 = ((Component)player).transform.position + ((Component)player).transform.forward * 0.6f + Vector3.up * 1f;
				float volumeMul4 = Mathf.Max(0f, num9 * volMul * num10);
				float pitchMul5 = Mathf.Max(0.01f, 1.02f * pitchMul * num11);
				PlayLocalMissAt(player, pos2, volumeMul4, pitchMul5);
			}
		}
		if ((Object)(object)val5 != (Object)null)
		{
			RequestInterrupt(player, val5);
		}
		if (DebugLog.Value)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("[{0}] Bash: shieldBase={1:0.0}, sf={2:0.00}, rand={3:0.00}, char={4:0.0}, world={5:0.0}, hits={6}, LoS={7}/{8}, overlap={9}", "ShieldBash", attackBaseDamage, attackSkill, num4, num5, num6, num13, losChecks, num15, num12));
		}
	}

	private bool TryDamageWorldObject(Collider col, Player player, Vector3 origin, Vector3 forward, float cosHalf, ref int losChecks, int maxLos, float worldDamage, Vector3 nearPoint, out Vector3 hitPoint)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0003: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00de: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
		//IL_0101: Unknown result type (might be due to invalid IL or missing references)
		//IL_023a: Unknown result type (might be due to invalid IL or missing references)
		//IL_023c: Unknown result type (might be due to invalid IL or missing references)
		//IL_023e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0243: Unknown result type (might be due to invalid IL or missing references)
		//IL_014b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0151: Unknown result type (might be due to invalid IL or missing references)
		//IL_0152: Unknown result type (might be due to invalid IL or missing references)
		//IL_027e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0285: Expected O, but got Unknown
		//IL_0288: Unknown result type (might be due to invalid IL or missing references)
		//IL_028d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0296: Unknown result type (might be due to invalid IL or missing references)
		//IL_029b: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a4: Unknown result type (might be due to invalid IL or missing references)
		hitPoint = origin;
		if ((Object)(object)col == (Object)null)
		{
			return false;
		}
		if ((Object)(object)player == (Object)null)
		{
			return false;
		}
		TreeBase componentInParent = ((Component)col).GetComponentInParent<TreeBase>();
		TreeLog componentInParent2 = ((Component)col).GetComponentInParent<TreeLog>();
		MineRock componentInParent3 = ((Component)col).GetComponentInParent<MineRock>();
		MineRock5 componentInParent4 = ((Component)col).GetComponentInParent<MineRock5>();
		Destructible componentInParent5 = ((Component)col).GetComponentInParent<Destructible>();
		WearNTear componentInParent6 = ((Component)col).GetComponentInParent<WearNTear>();
		Component val = null;
		if ((Object)(object)componentInParent != (Object)null)
		{
			val = (Component)(object)componentInParent;
		}
		else if ((Object)(object)componentInParent2 != (Object)null)
		{
			val = (Component)(object)componentInParent2;
		}
		else if ((Object)(object)componentInParent3 != (Object)null)
		{
			val = (Component)(object)componentInParent3;
		}
		else if ((Object)(object)componentInParent4 != (Object)null)
		{
			val = (Component)(object)componentInParent4;
		}
		else if ((Object)(object)componentInParent5 != (Object)null)
		{
			val = (Component)(object)componentInParent5;
		}
		else if ((Object)(object)componentInParent6 != (Object)null)
		{
			val = (Component)(object)componentInParent6;
		}
		if ((Object)(object)val == (Object)null)
		{
			return false;
		}
		if (!HitOnceTryAdd(((Object)val.gameObject).GetInstanceID()))
		{
			return false;
		}
		Bounds bounds = col.bounds;
		Vector3 center = ((Bounds)(ref bounds)).center;
		Vector3 val2 = center - origin;
		float magnitude = ((Vector3)(ref val2)).magnitude;
		if (magnitude < 0.001f)
		{
			return false;
		}
		Vector3 val3 = val2 / magnitude;
		if (Vector3.Dot(forward, val3) < cosHalf)
		{
			return false;
		}
		if (LineOfSightCheck.Value)
		{
			if (losChecks >= maxLos)
			{
				return false;
			}
			if (maxLos > 0 && losChecks < maxLos)
			{
				losChecks++;
				int worldLosMask = GetWorldLosMask();
				RaycastHit val4 = default(RaycastHit);
				if (Physics.Raycast(origin, val3, ref val4, magnitude, worldLosMask, (QueryTriggerInteraction)1))
				{
					Component val5 = null;
					if ((Object)(object)((RaycastHit)(ref val4)).collider != (Object)null)
					{
						TreeBase componentInParent7 = ((Component)((RaycastHit)(ref val4)).collider).GetComponentInParent<TreeBase>();
						if ((Object)(object)componentInParent7 != (Object)null)
						{
							val5 = (Component)(object)componentInParent7;
						}
						else
						{
							TreeLog componentInParent8 = ((Component)((RaycastHit)(ref val4)).collider).GetComponentInParent<TreeLog>();
							if ((Object)(object)componentInParent8 != (Object)null)
							{
								val5 = (Component)(object)componentInParent8;
							}
							else
							{
								MineRock componentInParent9 = ((Component)((RaycastHit)(ref val4)).collider).GetComponentInParent<MineRock>();
								if ((Object)(object)componentInParent9 != (Object)null)
								{
									val5 = (Component)(object)componentInParent9;
								}
								else
								{
									MineRock5 componentInParent10 = ((Component)((RaycastHit)(ref val4)).collider).GetComponentInParent<MineRock5>();
									if ((Object)(object)componentInParent10 != (Object)null)
									{
										val5 = (Component)(object)componentInParent10;
									}
									else
									{
										Destructible componentInParent11 = ((Component)((RaycastHit)(ref val4)).collider).GetComponentInParent<Destructible>();
										if ((Object)(object)componentInParent11 != (Object)null)
										{
											val5 = (Component)(object)componentInParent11;
										}
										else
										{
											WearNTear componentInParent12 = ((Component)((RaycastHit)(ref val4)).collider).GetComponentInParent<WearNTear>();
											if ((Object)(object)componentInParent12 != (Object)null)
											{
												val5 = (Component)(object)componentInParent12;
											}
										}
									}
								}
							}
						}
					}
					if (val5 != val)
					{
						return false;
					}
				}
			}
		}
		hitPoint = GetSafeHitPoint(col, nearPoint, center);
		bool flag = false;
		if ((Object)(object)componentInParent != (Object)null)
		{
			flag = true;
		}
		if ((Object)(object)componentInParent2 != (Object)null)
		{
			flag = true;
		}
		bool flag2 = false;
		if ((Object)(object)componentInParent3 != (Object)null)
		{
			flag2 = true;
		}
		if ((Object)(object)componentInParent4 != (Object)null)
		{
			flag2 = true;
		}
		HitData val6 = new HitData();
		val6.m_attacker = ((Character)player).GetZDOID();
		val6.m_point = hitPoint;
		val6.m_dir = val3;
		val6.m_pushForce = 0f;
		float num = Mathf.Max(0f, worldDamage);
		if (flag)
		{
			val6.m_damage.m_chop = num;
		}
		else if (flag2)
		{
			val6.m_damage.m_pickaxe = num;
		}
		else
		{
			val6.m_damage.m_blunt = num;
		}
		if ((Object)(object)componentInParent != (Object)null)
		{
			componentInParent.Damage(val6);
		}
		else if ((Object)(object)componentInParent2 != (Object)null)
		{
			componentInParent2.Damage(val6);
		}
		else if ((Object)(object)componentInParent3 != (Object)null)
		{
			componentInParent3.Damage(val6);
		}
		else if ((Object)(object)componentInParent4 != (Object)null)
		{
			componentInParent4.Damage(val6);
		}
		else if ((Object)(object)componentInParent5 != (Object)null)
		{
			componentInParent5.Damage(val6);
		}
		else
		{
			if (!((Object)(object)componentInParent6 != (Object)null))
			{
				return false;
			}
			componentInParent6.Damage(val6);
		}
		return true;
	}

	private void GetMaterialMix(ItemData shield, out float volMul, out float pitchMul)
	{
		volMul = 1f;
		pitchMul = 1f;
		string text = "";
		text = ((shield == null) ? "" : ((shield.m_shared == null) ? "" : ((shield.m_shared.m_name != null) ? shield.m_shared.m_name : "")));
		string text2 = text.ToLowerInvariant();
		bool num = text2.Contains("iron") || text2.Contains("blackmetal") || text2.Contains("silver") || text2.Contains("bronze") || text2.Contains("metal");
		bool flag = false;
		if (!num)
		{
			flag = text2.Contains("wood") || text2.Contains("tower");
		}
		if (num)
		{
			volMul *= Mathf.Max(0.01f, MetalVolumeMul.Value);
			pitchMul *= Mathf.Max(0.01f, MetalPitchMul.Value);
		}
		else if (flag)
		{
			volMul *= Mathf.Max(0.01f, WoodVolumeMul.Value);
			pitchMul *= Mathf.Max(0.01f, WoodPitchMul.Value);
		}
	}

	private AudioSource GetOrCreateLocalSfxSource(Player player)
	{
		//IL_0092: Unknown result type (might be due to invalid IL or missing references)
		//IL_0097: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)player == (Object)null)
		{
			return null;
		}
		if ((Object)(object)_localSfxSource != (Object)null && (Object)(object)((Component)_localSfxSource).gameObject != (Object)null)
		{
			Transform transform = ((Component)_localSfxSource).transform;
			if ((Object)(object)transform != (Object)null && transform.IsChildOf(((Component)player).transform))
			{
				return _localSfxSource;
			}
		}
		try
		{
			if ((Object)(object)_localSfxSource != (Object)null && (Object)(object)((Component)_localSfxSource).gameObject != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_localSfxSource).gameObject);
			}
		}
		catch
		{
		}
		GameObject val = new GameObject("ShieldBash_LocalSFX_Source");
		val.transform.SetParent(((Component)player).transform, false);
		AudioSource val2 = val.AddComponent<AudioSource>();
		val2.spatialBlend = 1f;
		val2.rolloffMode = (AudioRolloffMode)0;
		val2.minDistance = 1f;
		val2.maxDistance = 35f;
		val2.playOnAwake = false;
		val2.loop = false;
		val2.dopplerLevel = 0f;
		_localSfxSource = val2;
		return _localSfxSource;
	}

	private void PlayLocalHitAt(Player player, Vector3 pos, float volumeMul, float pitchMul)
	{
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		PlayLocalAt(player, _hitClip, pos, volumeMul, pitchMul);
	}

	private void PlayLocalMissAt(Player player, Vector3 pos, float volumeMul, float pitchMul)
	{
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		PlayLocalAt(player, _missClip, pos, volumeMul, pitchMul);
	}

	private void PlayLocalAt(Player player, AudioClip clip, Vector3 pos, float volumeMul, float pitchMul)
	{
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)player == (Object)null) && !((Object)(object)clip == (Object)null))
		{
			AudioSource orCreateLocalSfxSource = GetOrCreateLocalSfxSource(player);
			if (!((Object)(object)orCreateLocalSfxSource == (Object)null))
			{
				((Component)orCreateLocalSfxSource).transform.position = pos;
				orCreateLocalSfxSource.pitch = Mathf.Clamp(pitchMul, 0.5f, 2f);
				float num = Mathf.Clamp01(volumeMul) * Mathf.Clamp(LocalClipVolumeMul.Value, 0f, 3f);
				orCreateLocalSfxSource.PlayOneShot(clip, num);
			}
		}
	}

	private bool LoadAssetsFromBundle()
	{
		string text = null;
		string text2 = null;
		try
		{
			text = Assembly.GetExecutingAssembly().Location;
			text2 = Path.GetDirectoryName(text);
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("[ShieldBash] Could not resolve DLL location: " + ex.Message));
		}
		List<string> list = new List<string>();
		AddBundleCandidates(list, text2);
		AddBundleCandidates(list, Path.Combine(text2 ?? string.Empty, "Assets"));
		AddBundleCandidates(list, Path.Combine(Paths.PluginPath, "ShieldBash"));
		AddBundleCandidates(list, Path.Combine(Paths.PluginPath, "ShieldBash", "Assets"));
		AddBundleCandidates(list, Paths.PluginPath);
		string text3 = null;
		for (int i = 0; i < list.Count; i++)
		{
			string text4 = list[i];
			if (!string.IsNullOrEmpty(text4) && File.Exists(text4))
			{
				text3 = text4;
				break;
			}
		}
		if (text3 == null && !string.IsNullOrEmpty(text2) && Directory.Exists(text2))
		{
			try
			{
				string[] files = Directory.GetFiles(text2, "*", SearchOption.AllDirectories);
				for (int i = 0; i < files.Length; i++)
				{
					string fileName = Path.GetFileName(files[i]);
					if (fileName.Equals("shieldbashbundle", StringComparison.OrdinalIgnoreCase) || fileName.Equals("shieldbashbundle.bundle", StringComparison.OrdinalIgnoreCase))
					{
						text3 = files[i];
						break;
					}
				}
			}
			catch (Exception ex2)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("[ShieldBash] Bundle fallback search failed: " + ex2.Message));
			}
		}
		((BaseUnityPlugin)this).Logger.LogInfo((object)("[ShieldBash] DLL: " + (text ?? "unknown")));
		((BaseUnityPlugin)this).Logger.LogInfo((object)("[ShieldBash] AssetBundle: " + (text3 ?? "NOT FOUND")));
		if (text3 == null)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)"[ShieldBash] Put 'shieldbashbundle' next to ShieldBash.dll or into an Assets subfolder.");
			return false;
		}
		AssetBundle val = null;
		try
		{
			val = AssetBundle.LoadFromFile(text3);
			if ((Object)(object)val == (Object)null)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"[ShieldBash] Unity returned null while loading the AssetBundle. Rebuild it with Unity 6000.0.61f1 for StandaloneWindows64.");
				return false;
			}
			string[] allAssetNames = val.GetAllAssetNames();
			((BaseUnityPlugin)this).Logger.LogInfo((object)("[ShieldBash] Bundle contains " + allAssetNames.Length + " assets."));
			_bashClip = FindAnimationClip(val, "AttackShield01");
			_hitClip = FindAudioClip(val, HitClipName.Value);
			_missClip = FindAudioClip(val, MissClipName.Value);
			if ((Object)(object)_bashClip == (Object)null)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"[ShieldBash] AnimationClip 'AttackShield01' was not found in the AssetBundle.");
				((BaseUnityPlugin)this).Logger.LogError((object)("[ShieldBash] Assets:\n - " + string.Join("\n - ", allAssetNames)));
				return false;
			}
			if ((Object)(object)_hitClip == (Object)null)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("[ShieldBash] Hit AudioClip was not found: " + HitClipName.Value));
			}
			if ((Object)(object)_missClip == (Object)null)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("[ShieldBash] Miss AudioClip was not found: " + MissClipName.Value));
			}
			return true;
		}
		catch (Exception ex3)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("[ShieldBash] AssetBundle load exception: " + ex3));
			return false;
		}
		finally
		{
			if ((Object)(object)val != (Object)null)
			{
				val.Unload(false);
			}
		}
	}

	private static void AddBundleCandidates(List<string> candidates, string directory)
	{
		if (!string.IsNullOrEmpty(directory))
		{
			candidates.Add(Path.Combine(directory, "shieldbashbundle"));
			candidates.Add(Path.Combine(directory, "shieldbashbundle.bundle"));
		}
	}

	private static AnimationClip FindAnimationClip(AssetBundle bundle, string preferredName)
	{
		AnimationClip[] array = bundle.LoadAllAssets<AnimationClip>();
		if (array == null || array.Length == 0)
		{
			return null;
		}
		foreach (AnimationClip val in array)
		{
			if ((Object)(object)val != (Object)null && ((Object)val).name.Equals(preferredName, StringComparison.OrdinalIgnoreCase))
			{
				return val;
			}
		}
		return null;
	}

	private static AudioClip FindAudioClip(AssetBundle bundle, string preferredName)
	{
		AudioClip[] array = bundle.LoadAllAssets<AudioClip>();
		if (array == null || array.Length == 0)
		{
			return null;
		}
		string value = (preferredName ?? string.Empty).Trim();
		foreach (AudioClip val in array)
		{
			if ((Object)(object)val != (Object)null && ((Object)val).name.Equals(value, StringComparison.OrdinalIgnoreCase))
			{
				return val;
			}
		}
		return null;
	}

	private ZNetView GetZNetView(Character character)
	{
		if ((Object)(object)character == (Object)null || CharacterNViewField == null)
		{
			return null;
		}
		try
		{
			object? value = CharacterNViewField.GetValue(character);
			return (ZNetView)((value is ZNetView) ? value : null);
		}
		catch
		{
			return null;
		}
	}

	private Animator GetAnimator(Character character)
	{
		if ((Object)(object)character == (Object)null || CharacterAnimatorField == null)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)"[ShieldBash] Character.m_animator was not found.");
			return null;
		}
		try
		{
			object? value = CharacterAnimatorField.GetValue(character);
			return (Animator)((value is Animator) ? value : null);
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("[ShieldBash] Cannot read Character.m_animator: " + ex.Message));
			return null;
		}
	}

	private Rigidbody GetCharacterBody(Character character)
	{
		if ((Object)(object)character == (Object)null || CharacterBodyField == null)
		{
			return null;
		}
		try
		{
			object? value = CharacterBodyField.GetValue(character);
			return (Rigidbody)((value is Rigidbody) ? value : null);
		}
		catch
		{
			return null;
		}
	}

	private ItemData GetLeftItem(Humanoid humanoid)
	{
		if ((Object)(object)humanoid == (Object)null || HumanoidLeftItemField == null)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)"[ShieldBash] Humanoid.m_leftItem was not found.");
			return null;
		}
		try
		{
			object? value = HumanoidLeftItemField.GetValue(humanoid);
			return (ItemData)((value is ItemData) ? value : null);
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("[ShieldBash] Cannot read Humanoid.m_leftItem: " + ex.Message));
			return null;
		}
	}

	private static void EnsureLocalizationReflection()
	{
		if (_locRefReady)
		{
			return;
		}
		_locRefReady = true;
		try
		{
			_locType = AccessTools.TypeByName("Localization");
			if (!(_locType == null))
			{
				_locInstanceProp = _locType.GetProperty("instance", BindingFlags.Static | BindingFlags.Public);
				_locGetSelectedLanguage = _locType.GetMethod("GetSelectedLanguage", BindingFlags.Instance | BindingFlags.Public);
			}
		}
		catch
		{
			_locType = null;
			_locInstanceProp = null;
			_locGetSelectedLanguage = null;
		}
	}

	private static string GetGameLanguage()
	{
		EnsureLocalizationReflection();
		if (_locInstanceProp == null || _locGetSelectedLanguage == null)
		{
			return null;
		}
		try
		{
			object value = _locInstanceProp.GetValue(null, null);
			if (value == null)
			{
				return null;
			}
			return _locGetSelectedLanguage.Invoke(value, null) as string;
		}
		catch
		{
			return null;
		}
	}

	private static bool IsRussianUi()
	{
		string gameLanguage = GetGameLanguage();
		if (string.IsNullOrEmpty(gameLanguage))
		{
			return false;
		}
		gameLanguage = gameLanguage.ToLowerInvariant();
		if (!gameLanguage.Contains("russian"))
		{
			return gameLanguage.StartsWith("ru");
		}
		return true;
	}

	internal string BuildTooltipLineForShield(ItemData item)
	{
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_0019: Invalid comparison between Unknown and I4
		//IL_0105: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
		if (item == null || item.m_shared == null)
		{
			return null;
		}
		if ((int)item.m_shared.m_itemType != 5)
		{
			return null;
		}
		Player localPlayer = Player.m_localPlayer;
		float blockingSkillFactor = GetBlockingSkillFactor(localPlayer);
		float baseDamage = ComputeShieldBaseDamage(item);
		float num = DamageRandomMin.Value;
		float num2 = DamageRandomMax.Value;
		if (num > num2)
		{
			float num3 = num;
			num = num2;
			num2 = num3;
		}
		float num4 = ApplySkillAndRandom(baseDamage, blockingSkillFactor, num);
		float num5 = ApplySkillAndRandom(baseDamage, blockingSkillFactor, num2);
		float num6 = ComputeStaminaCost(localPlayer, item);
		if (IsRussianUi())
		{
			return $"\nУдар щитом: {num4:0}–{num5:0} (дробящий)\nВыносливость: {num6:0.#}\nКлавиша: {BashKey.Value}; перезарядка: {Cooldown.Value:0.#} с\n";
		}
		return $"\nShield Bash: {num4:0}–{num5:0} (blunt)\nStamina: {num6:0.#}\nKey: {BashKey.Value}; cooldown: {Cooldown.Value:0.#}s\n";
	}

	private static AttackPhase Phase(Attack attack)
	{
		return AttackPhases.GetValue(attack, (Attack key) => new AttackPhase());
	}

	private void ConfigureInterrupt()
	{
		InterruptEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Combat", "InterruptEnabled", true, "Interrupt one ordinary enemy's melee windup. Creature owner decides; 4s shared protection. Install 1.6.x on server and all clients.");
	}

	private static bool IsInterruptCandidate(Character target)
	{
		if ((Object)(object)target != (Object)null && !(target is Player) && target is Humanoid && target.GetBaseAI() is MonsterAI && !target.IsBoss() && !target.IsTamed() && !target.IsDead() && !target.IsStaggering())
		{
			return target.InAttack();
		}
		return false;
	}

	private static bool IsMeleeWindup(Attack attack)
	{
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: Invalid comparison between Unknown and I4
		if (attack == null || AttackDoneField == null || AttackAbortField == null)
		{
			return false;
		}
		if ((bool)AttackDoneField.GetValue(attack) || (bool)AttackAbortField.GetValue(attack))
		{
			return false;
		}
		if ((int)attack.m_attackType != 0 && (int)attack.m_attackType != 1)
		{
			return false;
		}
		if (attack.m_loopingAttack || attack.m_attackKillsSelf)
		{
			return false;
		}
		AttackPhase attackPhase = Phase(attack);
		if (!attackPhase.Triggered)
		{
			return !attackPhase.Interrupted;
		}
		return false;
	}

	private static bool ConsumeAttackTrigger(Attack attack)
	{
		AttackPhase attackPhase = Phase(attack);
		if (attackPhase.Interrupted)
		{
			return false;
		}
		attackPhase.Triggered = true;
		return true;
	}

	private static bool HasInterruptProtection(long now, long until)
	{
		return until > now;
	}

	private static bool IsRecentInterruptRequest(long now, long sent)
	{
		if (sent >= now - 7500000)
		{
			return sent <= now + 2500000;
		}
		return false;
	}

	private void RegisterInterruptRpc(Character target)
	{
		if ((Object)(object)target == (Object)null || target is Player)
		{
			return;
		}
		ZNetView zNetView = GetZNetView(target);
		if (!((Object)(object)zNetView == (Object)null) && zNetView.IsValid())
		{
			zNetView.Register<ZDOID, long>("mexanik.shieldbash.InterruptV1", (Action<long, ZDOID, long>)delegate(long sender, ZDOID attackerId, long sent)
			{
				//IL_000d: Unknown result type (might be due to invalid IL or missing references)
				ReceiveInterrupt(target, sender, attackerId, sent);
			});
		}
	}

	private void RequestInterrupt(Player attacker, Character target)
	{
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		if (InterruptEnabled.Value && !((Object)(object)ZNet.instance == (Object)null))
		{
			ZNetView zNetView = GetZNetView(target);
			if (!((Object)(object)zNetView == (Object)null) && zNetView.IsValid())
			{
				zNetView.InvokeRPC("mexanik.shieldbash.InterruptV1", new object[2]
				{
					((Character)attacker).GetZDOID(),
					ZNet.instance.GetTime().Ticks
				});
			}
		}
	}

	private void ReceiveInterrupt(Character target, long sender, ZDOID attackerId, long sent)
	{
		//IL_007e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f9: Invalid comparison between Unknown and I4
		//IL_0102: Unknown result type (might be due to invalid IL or missing references)
		//IL_0107: Unknown result type (might be due to invalid IL or missing references)
		//IL_0111: Unknown result type (might be due to invalid IL or missing references)
		//IL_0116: Unknown result type (might be due to invalid IL or missing references)
		//IL_011b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0123: Unknown result type (might be due to invalid IL or missing references)
		//IL_0128: Unknown result type (might be due to invalid IL or missing references)
		//IL_0132: Unknown result type (might be due to invalid IL or missing references)
		//IL_0137: Unknown result type (might be due to invalid IL or missing references)
		//IL_013c: Unknown result type (might be due to invalid IL or missing references)
		//IL_013e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0143: Unknown result type (might be due to invalid IL or missing references)
		//IL_019d: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_0287: Unknown result type (might be due to invalid IL or missing references)
		//IL_0292: Unknown result type (might be due to invalid IL or missing references)
		//IL_0297: Unknown result type (might be due to invalid IL or missing references)
		//IL_029c: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a0: Unknown result type (might be due to invalid IL or missing references)
		if (!((Behaviour)this).isActiveAndEnabled || !InterruptEnabled.Value || (Object)(object)ZNet.instance == (Object)null || (Object)(object)ZNetScene.instance == (Object)null || !IsInterruptCandidate(target))
		{
			return;
		}
		long ticks = ZNet.instance.GetTime().Ticks;
		if (!IsRecentInterruptRequest(ticks, sent))
		{
			return;
		}
		ZNetView zNetView = GetZNetView(target);
		if ((Object)(object)zNetView == (Object)null || !zNetView.IsValid() || !zNetView.IsOwner())
		{
			return;
		}
		GameObject val = ZNetScene.instance.FindInstance(attackerId);
		Player val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<Player>() : null);
		if ((Object)(object)val2 == (Object)null || ((Character)val2).IsDead())
		{
			return;
		}
		ZNetView zNetView2 = GetZNetView((Character)(object)val2);
		if ((Object)(object)zNetView2 == (Object)null || !zNetView2.IsValid() || zNetView2.GetZDO().GetOwner() != sender)
		{
			return;
		}
		ItemData leftItem = GetLeftItem((Humanoid)(object)val2);
		if (leftItem == null || leftItem.m_shared == null || (int)leftItem.m_shared.m_itemType != 5)
		{
			return;
		}
		Vector3 val3 = ((Component)val2).transform.position + Vector3.up * 1.3f;
		Vector3 val4 = ((Component)target).transform.position + Vector3.up * 1.1f - val3;
		float magnitude = ((Vector3)(ref val4)).magnitude;
		RaycastHit val5 = default(RaycastHit);
		if (magnitude < 0.001f || magnitude > Mathf.Min(4f, Mathf.Max(0f, Range.Value) + Mathf.Max(0f, Radius.Value) + 0.5f) || Vector3.Dot(((Component)val2).transform.forward, val4 / magnitude) < GetCosHalfAngle() || (Physics.Raycast(val3, val4 / magnitude, ref val5, magnitude, GetWorldLosMask(), (QueryTriggerInteraction)1) && (Object)(object)((Component)((RaycastHit)(ref val5)).collider).GetComponentInParent<Character>() != (Object)(object)target) || CurrentAttackField == null || StaggerFactorField == null || (float)StaggerFactorField.GetValue(target) <= 0f)
		{
			return;
		}
		object? value = CurrentAttackField.GetValue(target);
		Attack val6 = (Attack)((value is Attack) ? value : null);
		if (IsMeleeWindup(val6))
		{
			ZDO zDO = zNetView.GetZDO();
			if (!HasInterruptProtection(ticks, zDO.GetLong("mexanik.shieldbash.interruptUntilV1", 0L)))
			{
				zDO.Set("mexanik.shieldbash.interruptUntilV1", ticks + 40000000);
				Phase(val6).Interrupted = true;
				val6.Abort();
				Vector3 val7 = ((Component)target).transform.position - ((Component)val2).transform.position;
				target.Stagger(((Vector3)(ref val7)).normalized);
			}
		}
	}
}