Decompiled source of ImprovedCrossbows v1.0.2

ImprovedCrossbows.dll

Decompiled a week ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.UI;

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ImprovedCrossbows
{
	[HarmonyPatch(typeof(Attack), "GetProjectileSpawnPoint")]
	internal static class AimAtCrosshair
	{
		private const float MaxAimDistance = 500f;

		private const float MaxCorrectionDegrees = 15f;

		private static int rayMask;

		private static readonly RaycastHit[] Hits = (RaycastHit[])(object)new RaycastHit[32];

		internal static int RayMask
		{
			get
			{
				if (rayMask == 0)
				{
					rayMask = LayerMask.GetMask(new string[12]
					{
						"Default", "static_solid", "Default_small", "piece", "piece_nonsolid", "terrain", "character", "character_net", "character_ghost", "hitbox",
						"character_noenv", "vehicle"
					});
				}
				return rayMask;
			}
		}

		private static void Postfix(Attack __instance, Humanoid ___m_character, ItemData ___m_weapon, Vector3 spawnPoint, ref Vector3 aimDir)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)___m_character != (Object)(object)Player.m_localPlayer) && Plugin.IsCrossbow(___m_weapon) && TryGetCorrectedDir(___m_character, spawnPoint, aimDir, out var corrected))
			{
				float launchAngle = __instance.m_launchAngle;
				aimDir = ((launchAngle != 0f) ? (Quaternion.AngleAxis(0f - launchAngle, Vector3.Cross(Vector3.up, corrected)) * corrected) : corrected);
			}
		}

		internal static bool TryGetCrosshairTarget(Humanoid shooter, Vector3 spawnPoint, out Vector3 target)
		{
			//IL_0001: 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_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			target = default(Vector3);
			Camera mainCamera = Utils.GetMainCamera();
			if ((Object)(object)mainCamera == (Object)null)
			{
				return false;
			}
			target = FindCrosshairTarget(((Component)mainCamera).transform, shooter, spawnPoint);
			return true;
		}

		private static bool TryGetCorrectedDir(Humanoid shooter, Vector3 spawnPoint, Vector3 vanillaDir, out Vector3 corrected)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: 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_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_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			corrected = vanillaDir;
			if (!Plugin.AimAtCrosshair || !TryGetCrosshairTarget(shooter, spawnPoint, out var target))
			{
				return false;
			}
			Vector3 val = target - spawnPoint;
			Vector3 normalized = ((Vector3)(ref val)).normalized;
			if (Vector3.Angle(vanillaDir, normalized) > 15f)
			{
				return false;
			}
			corrected = normalized;
			return true;
		}

		private static Vector3 FindCrosshairTarget(Transform camera, Humanoid shooter, Vector3 spawnPoint)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: 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_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			Vector3 position = camera.position;
			Vector3 forward = camera.forward;
			float num = Vector3.Dot(spawnPoint - position, forward);
			int num2 = Physics.RaycastNonAlloc(position, forward, Hits, 500f, RayMask, (QueryTriggerInteraction)1);
			float num3 = 500f;
			for (int i = 0; i < num2; i++)
			{
				RaycastHit val = Hits[i];
				if (!(((RaycastHit)(ref val)).distance <= num) && !(((RaycastHit)(ref val)).distance >= num3) && !((Object)(object)((Component)((RaycastHit)(ref val)).collider).GetComponentInParent<Character>() == (Object)(object)shooter))
				{
					num3 = ((RaycastHit)(ref val)).distance;
				}
			}
			return position + forward * num3;
		}
	}
	[HarmonyPatch(typeof(GameCamera), "UpdateCamera")]
	[HarmonyPriority(200)]
	internal static class ImpactMarker
	{
		private const float MaxObstacleDistance = 30f;

		private const float TargetTolerance = 0.5f;

		private const float Size = 12f;

		private const int TextureSize = 64;

		private static readonly Color MarkerColor = new Color(1f, 0.3f, 0.25f, 0.9f);

		private static readonly RaycastHit[] Hits = (RaycastHit[])(object)new RaycastHit[16];

		private static Image marker;

		private static Sprite sprite;

		private static string originJointName;

		private static Transform originJoint;

		private static bool wasShown;

		private static void Postfix(Camera ___m_camera)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Expected O, but got Unknown
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: 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_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_018b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			bool flag = false;
			Vector3 val = default(Vector3);
			Player localPlayer = Player.m_localPlayer;
			if (Plugin.ShowImpactMarker && Zoom.IsZoomingIn && (Object)(object)localPlayer != (Object)null && (Object)(object)Hud.instance != (Object)null && FindObstacle(localPlayer, out var obstacle))
			{
				val = ___m_camera.WorldToScreenPoint(((RaycastHit)(ref obstacle)).point);
				flag = val.z > 0f;
				if (flag && !wasShown && Plugin.DebugLogging)
				{
					Plugin.Log.LogInfo((object)("Impact marker: bolt would hit " + ((Object)((RaycastHit)(ref obstacle)).collider).name + " at " + $"{Vector3.Distance(((Component)localPlayer).transform.position, ((RaycastHit)(ref obstacle)).point):0.0} m"));
				}
			}
			if (!flag && wasShown && Plugin.DebugLogging)
			{
				Plugin.Log.LogInfo((object)"Impact marker: hidden");
			}
			wasShown = flag;
			if ((Object)(object)marker == (Object)null)
			{
				if (!flag)
				{
					return;
				}
				Create(((Component)Hud.instance.m_crosshair).transform);
			}
			((Component)marker).gameObject.SetActive(flag);
			if (flag)
			{
				RectTransform val2 = (RectTransform)((Component)Hud.instance.m_crosshair).transform;
				Canvas canvas = ((Graphic)Hud.instance.m_crosshair).canvas;
				Camera val3 = (((int)canvas.renderMode == 0) ? null : canvas.worldCamera);
				Vector2 val4 = default(Vector2);
				RectTransformUtility.ScreenPointToLocalPointInRectangle(val2, Vector2.op_Implicit(val), val3, ref val4);
				RectTransform val5 = (RectTransform)((Component)marker).transform;
				Vector2 val6 = val4;
				Rect rect = val2.rect;
				val5.anchoredPosition = val6 - ((Rect)(ref rect)).center;
			}
		}

		private static bool FindObstacle(Player player, out RaycastHit obstacle)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: 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_00fa: 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_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: 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_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: 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_01ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f3: 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_01d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: 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_01c9: Unknown result type (might be due to invalid IL or missing references)
			obstacle = default(RaycastHit);
			ItemData currentWeapon = ((Humanoid)player).GetCurrentWeapon();
			if (!Plugin.IsCrossbow(currentWeapon))
			{
				return false;
			}
			Attack attack = currentWeapon.m_shared.m_attack;
			ItemData ammoItem = ((Humanoid)player).GetAmmoItem();
			GameObject val = ((ammoItem != null && (Object)(object)ammoItem.m_shared.m_attack.m_attackProjectile != (Object)null) ? ammoItem.m_shared.m_attack.m_attackProjectile : attack.m_attackProjectile);
			Projectile val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<Projectile>() : null);
			float num = (((Object)(object)val2 != (Object)null) ? val2.m_rayRadius : 0f);
			Transform attackOrigin = GetAttackOrigin(player, attack);
			Transform transform = ((Component)player).transform;
			Vector3 val3 = attackOrigin.position + transform.up * attack.m_attackHeight + transform.forward * attack.m_attackRange + transform.right * attack.m_attackOffset;
			if (!AimAtCrosshair.TryGetCrosshairTarget((Humanoid)(object)player, val3, out var target))
			{
				return false;
			}
			Vector3 val4 = target - val3;
			float num2 = Mathf.Min(((Vector3)(ref val4)).magnitude - 0.5f, 32f);
			if (num2 <= 0f)
			{
				return false;
			}
			Vector3 normalized = ((Vector3)(ref val4)).normalized;
			int num3 = ((num > 0f) ? Physics.SphereCastNonAlloc(val3, num, normalized, Hits, num2, AimAtCrosshair.RayMask, (QueryTriggerInteraction)1) : Physics.RaycastNonAlloc(val3, normalized, Hits, num2, AimAtCrosshair.RayMask, (QueryTriggerInteraction)1));
			bool flag = false;
			for (int i = 0; i < num3; i++)
			{
				RaycastHit val5 = Hits[i];
				if ((!flag || !(((RaycastHit)(ref val5)).distance >= ((RaycastHit)(ref obstacle)).distance)) && !((Object)(object)((Component)((RaycastHit)(ref val5)).collider).GetComponentInParent<Character>() == (Object)(object)player))
				{
					if (((RaycastHit)(ref val5)).distance == 0f && ((RaycastHit)(ref val5)).point == Vector3.zero)
					{
						((RaycastHit)(ref val5)).point = val3;
					}
					obstacle = val5;
					flag = true;
				}
			}
			if (flag)
			{
				return Vector3.Distance(transform.position, ((RaycastHit)(ref obstacle)).point) <= 30f;
			}
			return false;
		}

		private static Transform GetAttackOrigin(Player player, Attack attack)
		{
			if (string.IsNullOrEmpty(attack.m_attackOriginJoint))
			{
				return ((Component)player).transform;
			}
			if ((Object)(object)originJoint == (Object)null || originJointName != attack.m_attackOriginJoint || !originJoint.IsChildOf(((Component)player).transform))
			{
				originJointName = attack.m_attackOriginJoint;
				originJoint = Utils.FindChild(((Character)player).GetVisual().transform, originJointName, (IterativeSearchType)0);
			}
			if (!((Object)(object)originJoint != (Object)null))
			{
				return ((Component)player).transform;
			}
			return originJoint;
		}

		private static void Create(Transform crosshair)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: 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_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: 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_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)sprite == (Object)null)
			{
				sprite = CreateCrossSprite();
			}
			GameObject val = new GameObject("ImprovedCrossbows_ImpactMarker", new Type[2]
			{
				typeof(RectTransform),
				typeof(Image)
			});
			RectTransform val2 = (RectTransform)val.transform;
			((Transform)val2).SetParent(crosshair, false);
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor(0.5f, 0.5f);
			val2.pivot = val3;
			Vector2 anchorMin = (val2.anchorMax = val3);
			val2.anchorMin = anchorMin;
			val2.sizeDelta = new Vector2(12f, 12f);
			marker = val.GetComponent<Image>();
			marker.sprite = sprite;
			((Graphic)marker).color = MarkerColor;
			((Graphic)marker).raycastTarget = false;
		}

		private static Sprite CreateCrossSprite()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: 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_0102: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = new Texture2D(64, 64, (TextureFormat)4, true)
			{
				wrapMode = (TextureWrapMode)1,
				filterMode = (FilterMode)2
			};
			float num = 5.12f;
			float num2 = 6.4f;
			Color32[] array = (Color32[])(object)new Color32[4096];
			for (int i = 0; i < 64; i++)
			{
				for (int j = 0; j < 64; j++)
				{
					float num3 = (float)j + 0.5f;
					float num4 = (float)i + 0.5f;
					float num5 = Mathf.Abs(num3 - num4) / Mathf.Sqrt(2f);
					float num6 = Mathf.Abs(num3 + num4 - 64f) / Mathf.Sqrt(2f);
					float num7 = Mathf.Clamp01(num - Mathf.Min(num5, num6) + 0.5f);
					float num8 = Mathf.Min(Mathf.Min(num3, 64f - num3), Mathf.Min(num4, 64f - num4));
					num7 *= Mathf.Clamp01(num8 - num2 + 0.5f);
					array[i * 64 + j] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, (byte)(num7 * 255f));
				}
			}
			val.SetPixels32(array);
			val.Apply();
			return Sprite.Create(val, new Rect(0f, 0f, 64f, 64f), new Vector2(0.5f, 0.5f));
		}
	}
	[HarmonyPatch(typeof(Humanoid), "UnequipItem")]
	internal static class LoadedStateSave
	{
		internal const string LoadedKey = "Iteravio.ImprovedCrossbows.Loaded";

		private static void Prefix(Humanoid __instance, ItemData item)
		{
			if (!Plugin.KeepLoadedWhenUnequipped || !Plugin.IsCrossbow(item))
			{
				return;
			}
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if (val == null || (Object)(object)val != (Object)(object)Player.m_localPlayer || LoadedStateRestore.WeaponLoaded.Invoke(val) != item)
			{
				return;
			}
			foreach (ItemData allItem in ((Humanoid)val).GetInventory().GetAllItems())
			{
				allItem.m_customData.Remove("Iteravio.ImprovedCrossbows.Loaded");
			}
			item.m_customData["Iteravio.ImprovedCrossbows.Loaded"] = "1";
		}
	}
	[HarmonyPatch(typeof(Player), "UpdateWeaponLoading")]
	internal static class LoadedStateRestore
	{
		internal static readonly FieldRef<Player, ItemData> WeaponLoaded = AccessTools.FieldRefAccess<Player, ItemData>("m_weaponLoaded");

		private static readonly Action<Player, ItemData> SetWeaponLoaded = AccessTools.MethodDelegate<Action<Player, ItemData>>(AccessTools.Method(typeof(Player), "SetWeaponLoaded", (Type[])null, (Type[])null), (object)null, true);

		private static void Prefix(Player __instance, ItemData weapon)
		{
			if (weapon != null && weapon != WeaponLoaded.Invoke(__instance) && weapon.m_customData.Remove("Iteravio.ImprovedCrossbows.Loaded") && Plugin.KeepLoadedWhenUnequipped && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
			{
				SetWeaponLoaded(__instance, weapon);
				if (Plugin.DebugLogging)
				{
					Plugin.Log.LogInfo((object)("Restored loaded state of " + weapon.m_shared.m_name));
				}
			}
		}
	}
	[BepInPlugin("Iteravio.ImprovedCrossbows", "ImprovedCrossbows", "1.0.2")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	[SynchronizationMode(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "Iteravio.ImprovedCrossbows";

		public const string PluginName = "ImprovedCrossbows";

		public const string PluginVersion = "1.0.2";

		private const string MainSection = "Improved Crossbows";

		internal static ManualLogSource Log;

		private static ConfigEntry<bool> pauseReloadWhileRunning;

		private static ConfigEntry<bool> keepLoadedWhenUnequipped;

		private static ConfigEntry<bool> aimAtCrosshair;

		private static ConfigEntry<bool> showImpactMarker;

		private static ConfigEntry<int> recoilPercent;

		private static ConfigEntry<bool> zoomWithBlockButton;

		private static ConfigEntry<float> zoomMagnification;

		private static ConfigEntry<int> zoomMoveSpeedPercent;

		private static ConfigEntry<bool> showReloadAroundCrosshair;

		private static ConfigEntry<int> reloadRingOpacityPercent;

		private static ConfigEntry<bool> bigSneakAttackNumbers;

		private static ConfigEntry<bool> muffleSoundWhileZooming;

		private static ConfigEntry<bool> debugLogging;

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

		internal static bool PauseReloadWhileRunning => pauseReloadWhileRunning.Value;

		internal static bool KeepLoadedWhenUnequipped => keepLoadedWhenUnequipped.Value;

		internal static bool AimAtCrosshair => aimAtCrosshair.Value;

		internal static bool ShowImpactMarker => showImpactMarker.Value;

		internal static float RecoilFactor => (float)recoilPercent.Value / 100f;

		internal static bool ZoomWithBlockButton => zoomWithBlockButton.Value;

		internal static float ZoomMagnification => zoomMagnification.Value;

		internal static float MoveSpeedFactorWhileZooming => (float)zoomMoveSpeedPercent.Value / 100f;

		internal static bool ShowReloadAroundCrosshair => showReloadAroundCrosshair.Value;

		internal static float ReloadRingOpacity => (float)reloadRingOpacityPercent.Value / 100f;

		internal static bool BigSneakAttackNumbers => bigSneakAttackNumbers.Value;

		internal static bool MuffleSoundWhileZooming => muffleSoundWhileZooming.Value;

		internal static bool DebugLogging => debugLogging.Value;

		internal static bool IsReloadPaused(Player player)
		{
			return ActionQueuePause.Invoke(player) > 0f;
		}

		private void Awake()
		{
			//IL_023b: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			pauseReloadWhileRunning = ((BaseUnityPlugin)this).Config.Bind<bool>("Improved Crossbows", "PauseReloadWhileRunning", true, Synced("Running pauses a crossbow reload instead of cancelling it. Jumping and dodging still cancel it. Vanilla: cancels.", null, 12));
			keepLoadedWhenUnequipped = ((BaseUnityPlugin)this).Config.Bind<bool>("Improved Crossbows", "KeepLoadedWhenUnequipped", true, Synced("The last crossbow you put away stays loaded. Vanilla: unloads.", null, 11));
			aimAtCrosshair = ((BaseUnityPlugin)this).Config.Bind<bool>("Improved Crossbows", "AimAtCrosshair", true, Synced("Bolts fly toward your crosshair. Vanilla: parallel to it, so they land low.", null, 10));
			showImpactMarker = ((BaseUnityPlugin)this).Config.Bind<bool>("Improved Crossbows", "ShowImpactMarker", true, Synced("While zoomed, a red X marks an obstacle within 30 m that would block your shot. Vanilla: none.", null, 9));
			recoilPercent = ((BaseUnityPlugin)this).Config.Bind<int>("Improved Crossbows", "PercentRecoil", 50, Synced("Crossbow recoil as a percentage of vanilla. Vanilla: 100%.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), 8));
			zoomWithBlockButton = ((BaseUnityPlugin)this).Config.Bind<bool>("Improved Crossbows", "ZoomWithBlockButton", true, Synced("Holding Block with a crossbow zooms in instead of blocking. Vanilla: blocks.", null, 7));
			zoomMagnification = ((BaseUnityPlugin)this).Config.Bind<float>("Improved Crossbows", "ZoomMagnification", 2f, Synced("Zoom strength. Higher Crossbows skill zooms in faster. Vanilla: no zoom.", (AcceptableValueBase)(object)new AcceptableValueList<float>(new float[5] { 1.5f, 2f, 2.5f, 3f, 4f }), 6));
			zoomMoveSpeedPercent = ((BaseUnityPlugin)this).Config.Bind<int>("Improved Crossbows", "PercentMoveSpeedWhileZooming", 50, Synced("Jogging and running speed while zooming, as a percentage of normal. Vanilla: 100% (as it has no zoom).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 100), 5));
			muffleSoundWhileZooming = ((BaseUnityPlugin)this).Config.Bind<bool>("Improved Crossbows", "MuffleSoundWhileZooming", true, Local("Sounds get slightly muffled while zooming. Local, not synced. Vanilla: no muffle (as it has no zoom : D).", null, 4));
			showReloadAroundCrosshair = ((BaseUnityPlugin)this).Config.Bind<bool>("Improved Crossbows", "ShowReloadAroundCrosshair", true, Local("Reload progress shows as a ring around the crosshair. Local, not synced. Vanilla: bar at the bottom.", null, 3));
			reloadRingOpacityPercent = ((BaseUnityPlugin)this).Config.Bind<int>("Improved Crossbows", "PercentReloadRingOpacity", 25, Local("Opacity of the reload ring and its yellow flash. 100 = solid, lower = more see-through. Local, not synced. Vanilla: no ring.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 100), 2));
			bigSneakAttackNumbers = ((BaseUnityPlugin)this).Config.Bind<bool>("Improved Crossbows", "BigSneakAttackNumbers", true, Local("Your sneak attacks show you a big orange damage number with the multiplier. Local, not synced. Vanilla: normal number.", null, 1));
			debugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "LogCrossbowState", false, "Log your crossbow's loaded, reload and zoom state when it changes. Local, not synced.");
			new Harmony("Iteravio.ImprovedCrossbows").PatchAll();
		}

		private void Update()
		{
			if (debugLogging.Value)
			{
				DebugStatus.Update();
			}
		}

		private static ConfigDescription Synced(string description, AcceptableValueBase range, int order)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Expected O, but got Unknown
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			return new ConfigDescription(description, range, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true,
				Order = order
			} });
		}

		private static ConfigDescription Local(string description, AcceptableValueBase range, int order)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			return new ConfigDescription(description, range, new object[1] { (object)new ConfigurationManagerAttributes
			{
				Order = order
			} });
		}

		internal static bool IsCrossbow(ItemData item)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Invalid comparison between Unknown and I4
			if (item != null)
			{
				return (int)item.m_shared.m_skillType == 14;
			}
			return false;
		}
	}
	internal static class DebugStatus
	{
		private static string lastKey;

		internal static void Update()
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Invalid comparison between Unknown and I4
			Player localPlayer = Player.m_localPlayer;
			ItemData val = (((Object)(object)localPlayer != (Object)null) ? ((Humanoid)localPlayer).GetCurrentWeapon() : null);
			if (!Plugin.IsCrossbow(val))
			{
				lastKey = null;
				return;
			}
			string text = default(string);
			float num = default(float);
			MinorActionData val2 = default(MinorActionData);
			localPlayer.GetActionProgress(ref text, ref num, ref val2);
			bool flag = val2 != null && (int)val2.m_type == 2;
			string text2 = ((!flag) ? "none" : (Plugin.IsReloadPaused(localPlayer) ? "paused" : "running"));
			string text3 = ((Zoom.Progress <= 0f) ? "out" : ((Zoom.Progress >= 1f) ? "full" : (Zoom.IsZoomingIn ? "zooming in" : "zooming out")));
			string text4 = $"{val.m_shared.m_name}|{((Humanoid)localPlayer).IsWeaponLoaded()}|{text2}|{text3}";
			if (!(text4 == lastKey))
			{
				lastKey = text4;
				string arg = (flag ? $"{text2} at {num:0%}" : text2);
				Plugin.Log.LogInfo((object)("Crossbow " + val.m_shared.m_name + ": " + $"loaded {((Humanoid)localPlayer).IsWeaponLoaded()}, reload {arg}, zoom {text3}"));
			}
		}
	}
	[HarmonyPatch(typeof(Attack), "OnAttackTrigger")]
	internal static class Recoil
	{
		private static void Prefix(Attack __instance, Humanoid ___m_character, ItemData ___m_weapon)
		{
			if (!((Object)(object)___m_character != (Object)(object)Player.m_localPlayer) && Plugin.IsCrossbow(___m_weapon))
			{
				__instance.m_recoilPushback *= Plugin.RecoilFactor;
			}
		}
	}
	[HarmonyPatch(typeof(Player), "CheckRun")]
	internal static class ReloadPause
	{
		internal static bool InCheckRun;

		private static void Prefix(Player __instance)
		{
			InCheckRun = (Object)(object)__instance == (Object)(object)Player.m_localPlayer && Plugin.PauseReloadWhileRunning;
		}

		private static void Finalizer()
		{
			InCheckRun = false;
		}
	}
	[HarmonyPatch(typeof(Player), "ClearActionQueue")]
	internal static class ReloadPauseClearActionQueue
	{
		private const float ResumeDelaySeconds = 0.2f;

		private static bool Prefix(List<MinorActionData> ___m_actionQueue, ref float ___m_actionQueuePause)
		{
			if (!ReloadPause.InCheckRun)
			{
				return true;
			}
			MinorActionData val = ___m_actionQueue.Find((MinorActionData a) => (int)a.m_type == 2);
			if (val == null || !Plugin.IsCrossbow(val.m_item))
			{
				return true;
			}
			___m_actionQueue.Clear();
			___m_actionQueue.Add(val);
			___m_actionQueuePause = Mathf.Max(___m_actionQueuePause, 0.2f);
			return false;
		}
	}
	[HarmonyPatch(typeof(Hud), "UpdateActionProgress")]
	internal static class ReloadRing
	{
		private const float Diameter = 26f;

		private const float Thickness = 1.6f;

		private const int TextureSize = 256;

		private const float FlashSeconds = 0.2f;

		private const float FlashMaxScale = 1.25f;

		private const float MaxFinishSeconds = 2f;

		private const float DefaultFinishSeconds = 1.16f;

		private const float SweepFillPerSecond = 4f;

		private const float TrackAlpha = 0.15f;

		private const float FillAlpha = 0.8f;

		private const float PausedAlpha = 0.45f;

		private static readonly Color FlashColor = Color.yellow;

		private static Sprite ringSprite;

		private static Image track;

		private static Image fill;

		private static Image flash;

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

		private static bool wasReloading;

		private static float reloadSeconds;

		private static string reloadAnimation;

		private static float finishTime = -1f;

		private static float flashTime = 0.2f;

		private static float lastFill;

		private static bool sweeping;

		private static void Postfix(Hud __instance, Player player)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Invalid comparison between Unknown and I4
			//IL_0295: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ac: Unknown result type (might be due to invalid IL or missing references)
			string text = default(string);
			float num = default(float);
			MinorActionData val = default(MinorActionData);
			player.GetActionProgress(ref text, ref num, ref val);
			bool flag = val != null && (int)val.m_type == 2 && Plugin.IsCrossbow(val.m_item);
			bool showReloadAroundCrosshair = Plugin.ShowReloadAroundCrosshair;
			bool flag2 = ((Humanoid)player).IsWeaponLoaded();
			if (flag)
			{
				reloadSeconds = val.m_duration;
				reloadAnimation = val.m_item.m_shared.m_attack.m_reloadAnimation;
			}
			float value;
			float num2 = (finishSeconds.TryGetValue(reloadAnimation ?? "", out value) ? value : 1.16f);
			float num3 = Mathf.Max(0.01f, reloadSeconds + num2);
			if (wasReloading && !flag && flag2)
			{
				finishTime = 0f;
			}
			wasReloading = flag;
			bool flag3 = false;
			if (finishTime >= 0f)
			{
				finishTime += Time.deltaTime;
				if (!flag2 || !Plugin.IsCrossbow(((Humanoid)player).GetCurrentWeapon()))
				{
					finishTime = -1f;
				}
				else if (!((Character)player).InMinorAction() || finishTime > 2f)
				{
					if (finishTime <= 2f)
					{
						finishSeconds[reloadAnimation ?? ""] = finishTime;
					}
					if (Plugin.DebugLogging)
					{
						Plugin.Log.LogInfo((object)$"Ready to fire {finishTime:0.00} s after reload (expected {num2:0.00} s)");
					}
					finishTime = -1f;
					sweeping = showReloadAroundCrosshair;
				}
				else
				{
					flag3 = true;
				}
			}
			if (sweeping)
			{
				if (!flag2 || !Plugin.IsCrossbow(((Humanoid)player).GetCurrentWeapon()))
				{
					sweeping = false;
				}
				else if ((lastFill += Time.deltaTime * 4f) >= 1f)
				{
					sweeping = false;
					flashTime = 0f;
				}
			}
			bool flag4 = showReloadAroundCrosshair && (flag || flag3 || sweeping);
			if (flag4)
			{
				__instance.m_actionBarRoot.SetActive(false);
			}
			if ((Object)(object)fill == (Object)null)
			{
				if (!flag4)
				{
					return;
				}
				Create(((Component)__instance.m_crosshair).transform);
			}
			((Component)track).gameObject.SetActive(flag4);
			((Component)fill).gameObject.SetActive(flag4);
			float num4 = Plugin.ReloadRingOpacity / 0.8f;
			if (flag4)
			{
				if (!sweeping)
				{
					lastFill = Mathf.Clamp01((flag ? val.m_time : (reloadSeconds + finishTime)) / num3);
				}
				fill.fillAmount = lastFill;
				((Graphic)fill).color = White(((flag && Plugin.IsReloadPaused(player)) ? 0.45f : 0.8f) * num4);
				((Graphic)track).color = White(0.15f * num4);
			}
			UpdateFlash(Mathf.Min(1f, num4));
			if (flag4 || ((Component)flash).gameObject.activeSelf)
			{
				HideStealth(__instance);
			}
		}

		private static void HideStealth(Hud hud)
		{
			((Component)hud.m_stealthBar).gameObject.SetActive(false);
			hud.m_hidden.SetActive(false);
			hud.m_targeted.SetActive(false);
			hud.m_targetedAlert.SetActive(false);
		}

		private static void UpdateFlash(float maxAlpha)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0086: Unknown result type (might be due to invalid IL or missing references)
			bool flag = flashTime < 0.2f;
			((Component)flash).gameObject.SetActive(flag);
			if (flag)
			{
				flashTime += Time.deltaTime;
				float num = Mathf.Clamp01(flashTime / 0.2f);
				((Component)flash).transform.localScale = Vector3.one * Mathf.Lerp(1f, 1.25f, num);
				Color flashColor = FlashColor;
				flashColor.a = (1f - num) * maxAlpha;
				((Graphic)flash).color = flashColor;
			}
		}

		private static Color White(float alpha)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			return new Color(1f, 1f, 1f, alpha);
		}

		private static void Create(Transform crosshair)
		{
			if ((Object)(object)ringSprite == (Object)null)
			{
				ringSprite = CreateRingSprite();
			}
			track = CreateImage("ImprovedCrossbows_ReloadTrack", crosshair);
			fill = CreateImage("ImprovedCrossbows_ReloadFill", crosshair);
			fill.type = (Type)3;
			fill.fillMethod = (FillMethod)4;
			fill.fillOrigin = 2;
			fill.fillClockwise = true;
			flash = CreateImage("ImprovedCrossbows_ReloadFlash", crosshair);
			((Component)flash).gameObject.SetActive(false);
		}

		private static Image CreateImage(string name, Transform parent)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: 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_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: 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_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name, new Type[2]
			{
				typeof(RectTransform),
				typeof(Image)
			});
			RectTransform val2 = (RectTransform)val.transform;
			((Transform)val2).SetParent(parent, false);
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor(0.5f, 0.5f);
			val2.pivot = val3;
			Vector2 anchorMin = (val2.anchorMax = val3);
			val2.anchorMin = anchorMin;
			val2.anchoredPosition = Vector2.zero;
			val2.sizeDelta = new Vector2(26f, 26f);
			Image component = val.GetComponent<Image>();
			component.sprite = ringSprite;
			((Graphic)component).raycastTarget = false;
			return component;
		}

		private static Sprite CreateRingSprite()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: 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_0020: Expected O, but got Unknown
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: 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_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = new Texture2D(256, 256, (TextureFormat)4, true)
			{
				wrapMode = (TextureWrapMode)1,
				filterMode = (FilterMode)2
			};
			float num = 127.5f;
			float num2 = 127f;
			float num3 = num2 - 15.753846f;
			Color32[] array = (Color32[])(object)new Color32[65536];
			for (int i = 0; i < 256; i++)
			{
				for (int j = 0; j < 256; j++)
				{
					float num4 = Vector2.Distance(new Vector2((float)j, (float)i), new Vector2(num, num));
					float num5 = Mathf.Clamp01(num2 - num4 + 0.5f) * Mathf.Clamp01(num4 - num3 + 0.5f);
					array[i * 256 + j] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, (byte)(num5 * 255f));
				}
			}
			val.SetPixels32(array);
			val.Apply();
			return Sprite.Create(val, new Rect(0f, 0f, 256f, 256f), new Vector2(0.5f, 0.5f));
		}
	}
	internal static class SneakAttackText
	{
		private struct HitState
		{
			public Character Victim;

			public HitData Hit;

			public float BackstabTimeBefore;
		}

		[HarmonyPatch(typeof(Character), "RPC_Damage")]
		private static class RpcDamagePatch
		{
			private static void Prefix(Character __instance, HitData hit, out HitState __state)
			{
				__state = new HitState
				{
					Victim = victim,
					Hit = currentHit,
					BackstabTimeBefore = backstabTimeBefore
				};
				victim = __instance;
				currentHit = hit;
				backstabTimeBefore = BackstabTime.Invoke(__instance);
			}

			private static void Finalizer(HitState __state)
			{
				victim = __state.Victim;
				currentHit = __state.Hit;
				backstabTimeBefore = __state.BackstabTimeBefore;
			}
		}

		[HarmonyPatch(typeof(DamageText), "ShowText", new Type[]
		{
			typeof(DamageModifier),
			typeof(Vector3),
			typeof(float),
			typeof(bool)
		})]
		private static class ShowTextPatch
		{
			private static void Prefix(Vector3 pos, float dmg)
			{
				//IL_006d: Unknown result type (might be due to invalid IL or missing references)
				if (!((Object)(object)victim == (Object)null) && BackstabTime.Invoke(victim) != backstabTimeBefore)
				{
					backstabTimeBefore = BackstabTime.Invoke(victim);
					if (currentHit.GetAttacker() is Player)
					{
						ZRoutedRpc.instance.InvokeRoutedRPC(((ZDOID)(ref currentHit.m_attacker)).UserID, "Iteravio.ImprovedCrossbows.SneakAttackText", new object[3] { pos, dmg, currentHit.m_backstabBonus });
					}
				}
			}
		}

		[HarmonyPatch(typeof(DamageText), "Awake")]
		private static class RegisterPatch
		{
			private static void Postfix()
			{
				ZRoutedRpc.instance.Register<Vector3, float, float>("Iteravio.ImprovedCrossbows.SneakAttackText", (Action<long, Vector3, float, float>)OnSneakAttackText);
			}
		}

		[HarmonyPatch(typeof(DamageText), "RPC_DamageText")]
		private static class SuppressPatch
		{
			private static bool Prefix(ZPackage pkg)
			{
				//IL_0015: Unknown result type (might be due to invalid IL or missing references)
				//IL_001a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0025: Unknown result type (might be due to invalid IL or missing references)
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				if (Time.time > suppressUntil)
				{
					return true;
				}
				int num = pkg.ReadInt();
				Vector3 val = pkg.ReadVector3();
				pkg.SetPos(0);
				if (num == 7 || val != suppressPos)
				{
					return true;
				}
				suppressUntil = -1f;
				return false;
			}
		}

		private const string RpcName = "Iteravio.ImprovedCrossbows.SneakAttackText";

		private const float SuppressWindowSeconds = 2f;

		private static readonly FieldRef<Character, float> BackstabTime = AccessTools.FieldRefAccess<Character, float>("m_backstabTime");

		private static readonly Action<DamageText, long, ZPackage> ShowDamageTextLocally = AccessTools.MethodDelegate<Action<DamageText, long, ZPackage>>(AccessTools.Method(typeof(DamageText), "RPC_DamageText", (Type[])null, (Type[])null), (object)null, true);

		private static Character victim;

		private static HitData currentHit;

		private static float backstabTimeBefore;

		private static Vector3 suppressPos;

		private static float suppressUntil = -1f;

		private static void OnSneakAttackText(long sender, Vector3 pos, float dmg, float bonus)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected O, but got Unknown
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			if (Plugin.BigSneakAttackNumbers && !((Object)(object)DamageText.instance == (Object)null))
			{
				suppressPos = pos;
				suppressUntil = Time.time + 2f;
				string text = dmg.ToString("0.#", CultureInfo.InvariantCulture) + " (x" + bonus.ToString("0.#", CultureInfo.InvariantCulture) + ")";
				ZPackage val = new ZPackage();
				val.Write(7);
				val.Write(pos);
				val.Write(text);
				val.Write(false);
				val.SetPos(0);
				ShowDamageTextLocally(DamageText.instance, ZNet.GetUID(), val);
			}
		}
	}
	internal static class Zoom
	{
		[HarmonyPatch(typeof(Player), "SetControls")]
		[HarmonyPriority(200)]
		private static class SetControlsPatch
		{
			private static void Prefix(Player __instance, ref bool block, ref bool blockHold, bool jump, bool dodge)
			{
				held = blockHold && Plugin.ZoomWithBlockButton && (Object)(object)__instance == (Object)(object)Player.m_localPlayer && __instance.GetDoodadController() == null && Plugin.IsCrossbow(((Humanoid)__instance).GetCurrentWeapon());
				if (!(!held || jump || dodge))
				{
					block = false;
					blockHold = false;
				}
			}
		}

		[HarmonyPatch]
		private static class MoveSpeedPatch
		{
			private static IEnumerable<MethodBase> TargetMethods()
			{
				yield return AccessTools.Method(typeof(Player), "GetJogSpeedFactor", (Type[])null, (Type[])null);
				yield return AccessTools.Method(typeof(Player), "GetRunSpeedFactor", (Type[])null, (Type[])null);
			}

			private static void Postfix(Player __instance, ref float __result)
			{
				if (IsZoomingIn && (Object)(object)__instance == (Object)(object)Player.m_localPlayer)
				{
					__result *= Plugin.MoveSpeedFactorWhileZooming;
				}
			}
		}

		[HarmonyPatch(typeof(GameCamera), "UpdateCamera")]
		private static class UpdateCameraPatch
		{
			private static void Postfix(GameCamera __instance, Camera ___m_camera, bool ___m_freeFly, AudioListener ___m_listner)
			{
				Player localPlayer = Player.m_localPlayer;
				IsZoomingIn = held && !___m_freeFly && (Object)(object)localPlayer != (Object)null && !((Character)localPlayer).IsDead() && Plugin.ZoomWithBlockButton && Plugin.IsCrossbow(((Humanoid)localPlayer).GetCurrentWeapon()) && ((Humanoid)localPlayer).IsWeaponLoaded() && !((Character)localPlayer).InMinorAction() && !((Character)localPlayer).InAttack();
				if (IsZoomingIn)
				{
					float num = Mathf.Max(0.1f, 2f * (1f - ((Character)localPlayer).GetSkillFactor((SkillType)14)));
					Progress = Mathf.MoveTowards(Progress, 1f, Time.deltaTime / num);
				}
				else
				{
					float num2 = (shotZoomOut ? 0.1f : 0.3f);
					Progress = Mathf.MoveTowards(Progress, 0f, Time.deltaTime / num2);
					if (Progress <= 0f)
					{
						shotZoomOut = false;
					}
				}
				UpdateMuffle(___m_listner);
				UpdatePlayerBounds(localPlayer);
				if (!(Progress <= 0f || ___m_freeFly))
				{
					float fieldOfView = (___m_camera.fieldOfView = Mathf.Lerp(__instance.m_fov, __instance.m_fov / Plugin.ZoomMagnification, Progress));
					__instance.m_skyCamera.fieldOfView = fieldOfView;
				}
			}
		}

		[HarmonyPatch(typeof(Attack), "Start")]
		private static class AttackStartPatch
		{
			private static void Postfix(bool __result, Humanoid character, ItemData weapon)
			{
				if (__result && !((Object)(object)character != (Object)(object)Player.m_localPlayer) && Plugin.IsCrossbow(weapon))
				{
					shotZoomOut = Progress > 0f;
					IsZoomingIn = false;
					if ((Object)(object)muffle != (Object)null)
					{
						((Behaviour)muffle).enabled = false;
					}
				}
			}
		}

		private const float MinZoomInSeconds = 0.1f;

		private const float ZoomOutSeconds = 0.3f;

		private const float ShotZoomOutSeconds = 0.1f;

		private static bool held;

		private static bool shotZoomOut;

		private const float OpenCutoffHz = 22000f;

		private const float MuffledCutoffHz = 8000f;

		private static AudioLowPassFilter muffle;

		private const float BoundsRescanSeconds = 0.5f;

		private static readonly List<SkinnedMeshRenderer> boundsChanged = new List<SkinnedMeshRenderer>();

		private static float nextBoundsScan;

		internal static float Progress { get; private set; }

		internal static bool IsZoomingIn { get; private set; }

		private static void UpdateMuffle(AudioListener listener)
		{
			if ((Object)(object)listener == (Object)null)
			{
				return;
			}
			if ((Object)(object)muffle == (Object)null)
			{
				muffle = ((Component)listener).GetComponent<AudioLowPassFilter>();
				if ((Object)(object)muffle == (Object)null)
				{
					muffle = ((Component)listener).gameObject.AddComponent<AudioLowPassFilter>();
				}
				((Behaviour)muffle).enabled = false;
			}
			bool flag = Progress > 0f && !shotZoomOut && Plugin.MuffleSoundWhileZooming;
			((Behaviour)muffle).enabled = flag;
			if (flag)
			{
				muffle.cutoffFrequency = Mathf.Exp(Mathf.Lerp(Mathf.Log(22000f), Mathf.Log(8000f), Progress));
			}
		}

		private static void UpdatePlayerBounds(Player player)
		{
			if (Progress > 0f && (Object)(object)player != (Object)null)
			{
				if (Time.time < nextBoundsScan)
				{
					return;
				}
				nextBoundsScan = Time.time + 0.5f;
				SkinnedMeshRenderer[] componentsInChildren = ((Component)player).GetComponentsInChildren<SkinnedMeshRenderer>();
				foreach (SkinnedMeshRenderer val in componentsInChildren)
				{
					if (!val.updateWhenOffscreen)
					{
						val.updateWhenOffscreen = true;
						boundsChanged.Add(val);
					}
				}
			}
			else
			{
				if (boundsChanged.Count == 0)
				{
					return;
				}
				foreach (SkinnedMeshRenderer item in boundsChanged)
				{
					if ((Object)(object)item != (Object)null)
					{
						item.updateWhenOffscreen = false;
					}
				}
				boundsChanged.Clear();
				nextBoundsScan = 0f;
			}
		}
	}
}