Decompiled source of AntiMaterialRifle v1.2.0

BepInEx/plugins/AntiMaterialRifle.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Pigeon.Math;
using Pigeon.Movement;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("AntiMaterialRifle")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("AntiMaterialRifle")]
[assembly: AssemblyTitle("AntiMaterialRifle")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[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;
		}
	}
}
public sealed class BallisticSniperBehaviour : MonoBehaviour
{
	[Serializable]
	public struct Data
	{
		public bool singleRoundReload;

		public float shellDamageMult;

		public float fullHpDamageMult;

		public int bonusBounces;

		public float postBounceHoming;

		public bool overpressure;

		public float overpressureChargeDuration;

		public float overpressureDamageMult;

		public float overpressureMoveMult;

		public int overpressureMagPenalty;

		public int extraBulletsPerShot;

		public float twinLinkFenceDamage;

		public float twinLinkFenceRadius;

		public float exhaustionCooldown;

		public float exhaustionSlowDuration;

		public float exhaustionSlowStrength;

		public float longwatchAimSeconds;

		public float longwatchRangeBonus;

		public float longwatchGravityMult;

		public float longwatchSpeedMult;

		public float scouterInterval;

		public float scouterRadius;

		public int pierceTargets;

		public float pierceFalloff;

		public float spotterRadius;

		public float spotterDuration;

		public float deadboltDuration;

		public bool deathMark;

		public float deathMarkFuse;

		public float deathMarkRadius;

		public float deathMarkDamageScale;

		public float deathMarkHeadshotFuseMult;

		public float deathMarkStackScale;

		public bool autoTrigger;

		public float autoTriggerFireInterval;

		public float autoTriggerDamageMult;

		public int autoTriggerMagBonus;

		public float autoTriggerAdsSpread;

		public bool highExplosive;

		public float c4Damage;

		public float c4Radius;

		public float c4ThrowForce;

		public float c4ArmTime;

		public bool clipped;

		public float clippedReloadMult;

		public bool anchor;

		public float anchorSpeedThreshold;

		public bool oneInTheChamber;

		public float disruptDuration;

		public float heavyGrainDamageMult;

		public float reserveAmmoMult;

		public float rotAmount;

		public float rotSplashRadius;

		public float waterAmount;

		public float repositionMoveBonus;

		public float repositionAdsMoveMult;

		public float echoDelay;

		public float echoDamageScale;

		public float echoShockAmount;

		public float transferRelayScale;

		public float syncWindow;

		public float syncDamageMult;

		public bool overkill;
	}

	public struct DeathMarkEntry
	{
		public ITarget target;

		public float detonateAt;

		public float stacks;

		public Vector3 lastPos;

		public bool fromHeadshot;
	}

	public struct PendingEcho
	{
		public float fireAt;

		public Vector3 position;

		public Quaternion rotation;

		public float damage;

		public float speed;

		public float gravity;
	}

	[SerializeField]
	private Data data = CreateDefaultData();

	private Data prefabSnapshot = CreateDefaultData();

	private string description = "Anti-Material Rifle";

	public readonly List<DeathMarkEntry> DeathMarks = new List<DeathMarkEntry>(8);

	public readonly List<PendingEcho> PendingEchoes = new List<PendingEcho>(4);

	public readonly List<ITarget> TwinShotHits = new List<ITarget>(4);

	private Gun boundGun;

	private bool hooksBound;

	private bool moveHookBound;

	private GameObject c4Visual;

	private readonly List<ISelectable> activeHighlights = new List<ISelectable>(16);

	private float highlightClearAt = -999f;

	private BulletData lastFiredBullet;

	private bool hasLastFiredBullet;

	private bool hasPendingOverkill;

	private float pendingOverkill;

	private ITarget pendingOverkillParent;

	private Vector3 pendingOverkillPos;

	private Gun pendingOverkillGun;

	private EffectType pendingOverkillEffect;

	private float pendingOverkillEffectAmt;

	public bool IsTubeReloading { get; set; }

	public float AimHoldTime { get; private set; }

	public float LastKillTime { get; set; } = -999f;

	public float ExhaustionReadyAt { get; set; }

	public bool ExhaustionShotArmed { get; set; }

	public float LastScouterPulse { get; set; } = -999f;

	public bool ChamberBonusReady { get; set; }

	public bool LastReloadWasFullTopOff { get; set; }

	public bool BlockAutoReloadForChamber { get; set; }

	public bool C4Deployed { get; set; }

	public Vector3 C4Position { get; set; }

	public float C4DeployedAt { get; set; }

	public float ReloadHoldTime { get; set; }

	public bool ReloadHeld { get; set; }

	public ref Data WeaponData => ref data;

	public string Description => description;

	public bool SingleRoundReload
	{
		get
		{
			if (data.singleRoundReload)
			{
				return !data.clipped;
			}
			return false;
		}
	}

	public Data GetPrefabSnapshot()
	{
		return prefabSnapshot;
	}

	public static Data CreateDefaultData()
	{
		return new Data
		{
			singleRoundReload = true,
			shellDamageMult = 1f,
			fullHpDamageMult = 1f,
			bonusBounces = 0,
			postBounceHoming = 0f,
			overpressure = false,
			overpressureChargeDuration = 0f,
			overpressureDamageMult = 1f,
			overpressureMoveMult = 1f,
			overpressureMagPenalty = 0,
			extraBulletsPerShot = 0,
			twinLinkFenceDamage = 0f,
			twinLinkFenceRadius = 0f,
			exhaustionCooldown = 0f,
			exhaustionSlowDuration = 0f,
			exhaustionSlowStrength = 0f,
			longwatchAimSeconds = 0f,
			longwatchRangeBonus = 0f,
			longwatchGravityMult = 1f,
			longwatchSpeedMult = 1f,
			scouterInterval = 0f,
			scouterRadius = 0f,
			pierceTargets = 0,
			pierceFalloff = 0f,
			spotterRadius = 0f,
			spotterDuration = 0f,
			deadboltDuration = 0f,
			deathMark = false,
			deathMarkFuse = 2f,
			deathMarkRadius = 4f,
			deathMarkDamageScale = 1.2f,
			deathMarkHeadshotFuseMult = 0.6f,
			deathMarkStackScale = 0.35f,
			autoTrigger = false,
			autoTriggerFireInterval = 0.35f,
			autoTriggerDamageMult = 0.5f,
			autoTriggerMagBonus = 5,
			autoTriggerAdsSpread = 0.8f,
			highExplosive = false,
			c4Damage = 220f,
			c4Radius = 8f,
			c4ThrowForce = 18f,
			c4ArmTime = 0.45f,
			clipped = false,
			clippedReloadMult = 1f,
			anchor = false,
			anchorSpeedThreshold = 0.15f,
			oneInTheChamber = false,
			disruptDuration = 0f,
			heavyGrainDamageMult = 1f,
			reserveAmmoMult = 1f,
			rotAmount = 0f,
			rotSplashRadius = 0f,
			waterAmount = 0f,
			repositionMoveBonus = 0f,
			repositionAdsMoveMult = 1f,
			echoDelay = 0f,
			echoDamageScale = 0f,
			echoShockAmount = 0f,
			transferRelayScale = 0f,
			syncWindow = 0f,
			syncDamageMult = 1f,
			overkill = false
		};
	}

	public void InitializeAsPrefab(string desc)
	{
		description = desc ?? "Anti-Material Rifle";
		data = CreateDefaultData();
		prefabSnapshot = data;
		ResetRuntime();
	}

	public void RestoreFromPrefab()
	{
		data = prefabSnapshot;
	}

	public void CapturePrefabSnapshot()
	{
		prefabSnapshot = data;
	}

	public void CopyFrom(BallisticSniperBehaviour template)
	{
		if (!((Object)(object)template == (Object)null))
		{
			description = template.description;
			prefabSnapshot = template.prefabSnapshot;
			data = prefabSnapshot;
			ResetRuntime();
		}
	}

	public void ResetRuntime()
	{
		IsTubeReloading = false;
		AimHoldTime = 0f;
		LastKillTime = -999f;
		ExhaustionReadyAt = 0f;
		ExhaustionShotArmed = false;
		LastScouterPulse = -999f;
		ChamberBonusReady = false;
		LastReloadWasFullTopOff = false;
		BlockAutoReloadForChamber = false;
		C4Deployed = false;
		ReloadHoldTime = 0f;
		ReloadHeld = false;
		DeathMarks.Clear();
		PendingEchoes.Clear();
		TwinShotHits.Clear();
		hasLastFiredBullet = false;
		hasPendingOverkill = false;
		pendingOverkill = 0f;
		pendingOverkillParent = null;
		pendingOverkillGun = null;
		ClearHighlights();
		DestroyC4Visual();
	}

	public void InterruptTubeReload(Gun gun)
	{
		IsTubeReloading = false;
		if ((Object)(object)gun == (Object)null)
		{
			return;
		}
		try
		{
			if (gun.Reloading)
			{
				gun.CancelReload();
			}
		}
		catch (Exception ex)
		{
			ManualLogSource logger = SparrohPlugin.Logger;
			if (logger != null)
			{
				logger.LogDebug((object)("[BallisticSniper] CancelReload: " + ex.Message));
			}
		}
	}

	public void OnUpgradesApplied(Gun gun)
	{
		boundGun = gun;
		BindHooks(gun, bind: true);
		ApplyGunDataMutations(gun);
		WeaponRegistration.EnsureProjectileBullet(gun);
	}

	public void OnUpgradesCleared(Gun gun)
	{
		BindHooks(gun, bind: false);
		data = prefabSnapshot;
		ResetRuntime();
	}

	private void BindHooks(Gun gun, bool bind)
	{
		//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ea: Expected O, but got Unknown
		//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f4: Expected O, but got Unknown
		//IL_0102: Unknown result type (might be due to invalid IL or missing references)
		//IL_010c: Expected O, but got Unknown
		//IL_010c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0116: Expected O, but got Unknown
		//IL_0124: Unknown result type (might be due to invalid IL or missing references)
		//IL_012e: Expected O, but got Unknown
		//IL_012e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0138: Expected O, but got Unknown
		//IL_0037: Unknown result type (might be due to invalid IL or missing references)
		//IL_0041: Expected O, but got Unknown
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_004b: Expected O, but got Unknown
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_0063: 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
		//IL_007b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0085: Expected O, but got Unknown
		//IL_0085: Unknown result type (might be due to invalid IL or missing references)
		//IL_008f: Expected O, but got Unknown
		//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b5: 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_0155: Unknown result type (might be due to invalid IL or missing references)
		//IL_0166: Unknown result type (might be due to invalid IL or missing references)
		//IL_016b: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)gun == (Object)null || (bind && hooksBound) || (!bind && !hooksBound))
		{
			return;
		}
		try
		{
			if (bind)
			{
				gun.OnDamageTarget = (DamageCallback)Delegate.Combine((Delegate?)(object)gun.OnDamageTarget, (Delegate?)new DamageCallback(OnDamageTarget));
				gun.OnBeforeDamage = (MutableDamageCallback)Delegate.Combine((Delegate?)(object)gun.OnBeforeDamage, (Delegate?)new MutableDamageCallback(OnBeforeDamage));
				gun.OnKillTarget = (KillCallback)Delegate.Combine((Delegate?)(object)gun.OnKillTarget, (Delegate?)new KillCallback(OnKillTarget));
				if ((Object)(object)gun.Player != (Object)null)
				{
					Player player = gun.Player;
					player.OnSetMovementSpeed += (RefAction<float>)HandleSetMoveSpeed;
					moveHookBound = true;
				}
				hooksBound = true;
			}
			else
			{
				gun.OnDamageTarget = (DamageCallback)Delegate.Remove((Delegate?)(object)gun.OnDamageTarget, (Delegate?)new DamageCallback(OnDamageTarget));
				gun.OnBeforeDamage = (MutableDamageCallback)Delegate.Remove((Delegate?)(object)gun.OnBeforeDamage, (Delegate?)new MutableDamageCallback(OnBeforeDamage));
				gun.OnKillTarget = (KillCallback)Delegate.Remove((Delegate?)(object)gun.OnKillTarget, (Delegate?)new KillCallback(OnKillTarget));
				if ((Object)(object)gun.Player != (Object)null && moveHookBound)
				{
					Player player2 = gun.Player;
					player2.OnSetMovementSpeed -= (RefAction<float>)HandleSetMoveSpeed;
					moveHookBound = false;
				}
				hooksBound = false;
			}
		}
		catch (Exception ex)
		{
			ManualLogSource logger = SparrohPlugin.Logger;
			if (logger != null)
			{
				logger.LogDebug((object)$"[BallisticSniper] BindHooks({bind}): {ex.Message}");
			}
		}
	}

	private void HandleSetMoveSpeed(ref float speed)
	{
		if (data.repositionMoveBonus > 0f)
		{
			speed *= 1f + data.repositionMoveBonus;
		}
		bool flag = false;
		try
		{
			flag = (Object)(object)boundGun != (Object)null && boundGun.IsAiming;
		}
		catch
		{
		}
		if (flag && data.repositionAdsMoveMult < 0.99f)
		{
			speed *= data.repositionAdsMoveMult;
		}
		if (!data.overpressure || !((Object)(object)boundGun != (Object)null))
		{
			return;
		}
		try
		{
			if (boundGun.GunData.chargeData.duration > 0.01f && boundGun.GunData.chargeData.time > 0.01f)
			{
				speed *= data.overpressureMoveMult;
			}
		}
		catch
		{
		}
	}

	public void ApplyGunDataMutations(Gun gun)
	{
		//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
		//IL_022a: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)gun == (Object)null))
		{
			ref GunData gunData = ref gun.GunData;
			if (data.heavyGrainDamageMult > 1f)
			{
				gunData.damage *= data.heavyGrainDamageMult;
			}
			if (data.reserveAmmoMult > 1f)
			{
				gunData.ammoCapacity = Mathf.Max(1, Mathf.RoundToInt((float)gunData.ammoCapacity * data.reserveAmmoMult));
			}
			if (data.bonusBounces > 0)
			{
				gunData.maxBounces = Mathf.Max(gunData.maxBounces, data.bonusBounces);
			}
			if (data.extraBulletsPerShot > 0)
			{
				gunData.bulletsPerShot = Mathf.Max(1, gunData.bulletsPerShot + data.extraBulletsPerShot);
			}
			if (data.clipped)
			{
				gunData.refillAmmoOnReload = true;
				gunData.reloadDuration = Mathf.Max(0.25f, gunData.reloadDuration * data.clippedReloadMult);
				data.singleRoundReload = false;
			}
			if (data.autoTrigger)
			{
				gunData.automatic = 1;
				gunData.fireInterval = data.autoTriggerFireInterval;
				gunData.damage *= data.autoTriggerDamageMult;
				gunData.magazineSize += data.autoTriggerMagBonus;
			}
			if (data.overpressure)
			{
				gunData.chargeData.duration = data.overpressureChargeDuration;
				gunData.chargeData.fireWhenFullyCharged = true;
				gunData.chargeData.fireOnRelease = false;
				gunData.chargeData.canFireWhileCharging = false;
				gunData.chargeData.coolDownSpeed = 2.5f;
				gunData.damage *= data.overpressureDamageMult;
				gunData.magazineSize = Mathf.Max(1, gunData.magazineSize - data.overpressureMagPenalty);
			}
			if (data.rotAmount > 0f)
			{
				gunData.damageEffect = (EffectType)7;
				gunData.damageEffectAmount = Mathf.Max(gunData.damageEffectAmount, data.rotAmount);
			}
			else if (data.waterAmount > 0f)
			{
				gunData.damageEffect = (EffectType)9;
				gunData.damageEffectAmount = Mathf.Max(gunData.damageEffectAmount, data.waterAmount);
			}
		}
	}

	public void Tick(float dt, Gun gun)
	{
		if (!((Object)(object)gun == (Object)null) && ((NetworkBehaviour)gun).IsOwner)
		{
			if (gun.IsAiming)
			{
				AimHoldTime += dt;
			}
			else
			{
				AimHoldTime = 0f;
			}
			if (data.exhaustionCooldown > 0f && Time.time >= ExhaustionReadyAt)
			{
				ExhaustionShotArmed = true;
			}
			if (data.oneInTheChamber && ChamberBonusReady && gun.RemainingAmmoCount <= 0)
			{
				OnMagazineEmptied(gun);
			}
			TickDeathMarks(gun);
			TickEchoes(gun);
			TickScouter(gun);
		}
	}

	private void TickDeathMarks(Gun gun)
	{
		//IL_011e: Unknown result type (might be due to invalid IL or missing references)
		//IL_012e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ef: 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_0065: Unknown result type (might be due to invalid IL or missing references)
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		if (!data.deathMark || DeathMarks.Count == 0)
		{
			return;
		}
		for (int num = DeathMarks.Count - 1; num >= 0; num--)
		{
			DeathMarkEntry value = DeathMarks[num];
			if (value.target != null && ((ISelectable)value.target).Exists() && value.target.IsAlive)
			{
				value.lastPos = ((IFollowable)value.target).GetHealthbarPosition();
			}
			DeathMarks[num] = value;
			if (!(Time.time < value.detonateAt))
			{
				float num2 = gun.GunData.damage * data.deathMarkDamageScale * (1f + (value.stacks - 1f) * data.deathMarkStackScale);
				float num3 = data.deathMarkRadius * (1f + (value.stacks - 1f) * 0.15f);
				try
				{
					GameManager.Instance.SpawnExplosionObserverSeeThrough((IDamageSource)(object)gun, value.lastPos, num3, (TargetType)5, new DamageData(num2, (EffectType)0, 0f, (DamageFlags)16), ((NetworkBehaviour)gun).OwnerClientId, 12f);
				}
				catch
				{
					try
					{
						GameManager.Instance.SpawnExplosionFirstPerson((IDamageSource)(object)gun, value.lastPos, num3, (TargetType)5, new DamageData(num2, (EffectType)0, 0f, (DamageFlags)16), 2f);
					}
					catch
					{
					}
				}
				DeathMarks.RemoveAt(num);
			}
		}
	}

	private void TickEchoes(Gun gun)
	{
		if (data.echoDelay <= 0f || PendingEchoes.Count == 0)
		{
			return;
		}
		for (int num = PendingEchoes.Count - 1; num >= 0; num--)
		{
			if (!(Time.time < PendingEchoes[num].fireAt))
			{
				PendingEcho echo = PendingEchoes[num];
				PendingEchoes.RemoveAt(num);
				FireEchoBullet(gun, echo);
			}
		}
	}

	private void FireEchoBullet(Gun gun, PendingEcho echo)
	{
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_0048: Unknown result type (might be due to invalid IL or missing references)
		//IL_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_005b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_0009: Unknown result type (might be due to invalid IL or missing references)
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_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_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		//IL_008b: 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)
		try
		{
			BulletData bulletData;
			if (hasLastFiredBullet)
			{
				bulletData = lastFiredBullet;
				bulletData.position = echo.position;
				bulletData.rotation = echo.rotation;
				bulletData.direction = echo.rotation * Vector3.forward;
			}
			else
			{
				Vector3 position = echo.position;
				Quaternion rotation = echo.rotation;
				bulletData = ((GunData)(ref gun.GunData)).GetBulletData(ref position, ref rotation);
			}
			bulletData.damage = echo.damage;
			bulletData.speed = echo.speed;
			bulletData.gravity = echo.gravity;
			bulletData.damageEffect = (EffectType)2;
			bulletData.damageEffectAmount = Mathf.Max(data.echoShockAmount, 10f);
			IBullet bullet = gun.GetBullet();
			if (bullet != null)
			{
				Action<IBullet> action = ResolveBulletRelease(gun);
				gun.ModifyBulletData(ref bulletData, (BulletFlags)11);
				bullet.Initialize(bulletData, (IDamageSource)(object)gun, action, (BulletFlags)11);
			}
		}
		catch (Exception ex)
		{
			ManualLogSource logger = SparrohPlugin.Logger;
			if (logger != null)
			{
				logger.LogDebug((object)("[BallisticSniper] Echo fire failed: " + ex.Message));
			}
		}
	}

	private static Action<IBullet> ResolveBulletRelease(Gun gun)
	{
		try
		{
			FieldInfo fieldInfo = AccessTools.Field(typeof(Gun), "releaseBulletToPool");
			if (fieldInfo != null && fieldInfo.GetValue(gun) is Action<IBullet> result)
			{
				return result;
			}
		}
		catch
		{
		}
		return delegate
		{
		};
	}

	private void TickScouter(Gun gun)
	{
		//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
		//IL_0104: 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_010e: 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_0113: Unknown result type (might be due to invalid IL or missing references)
		//IL_0114: Unknown result type (might be due to invalid IL or missing references)
		//IL_011a: Unknown result type (might be due to invalid IL or missing references)
		//IL_011f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0124: Unknown result type (might be due to invalid IL or missing references)
		//IL_0171: Unknown result type (might be due to invalid IL or missing references)
		//IL_0176: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b4: 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_01ba: 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_00a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00af: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c0: 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_01d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00df: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
		if (highlightClearAt > 0f && Time.time >= highlightClearAt)
		{
			ClearHighlights();
		}
		if (data.scouterInterval <= 0f || !gun.IsAiming || Time.time - LastScouterPulse < data.scouterInterval)
		{
			return;
		}
		LastScouterPulse = Time.time;
		try
		{
			Player player = gun.Player;
			if ((Object)(object)player == (Object)null)
			{
				return;
			}
			float num = ((data.scouterRadius > 0f) ? data.scouterRadius : 50f);
			Vector3 val = ((Component)player).transform.position + Vector3.up * 1.4f;
			Vector3 forward = ((Component)player).transform.forward;
			try
			{
				if ((Object)(object)gun.playerLook != (Object)null)
				{
					val = ((Component)gun.playerLook).transform.position;
					forward = ((Component)gun.playerLook).transform.forward;
				}
			}
			catch
			{
			}
			Vector3 val2 = val + forward * (num * 0.5f);
			int num2 = -1;
			try
			{
				RaycastHit val3 = default(RaycastHit);
				if (Physics.Raycast(val + forward * 0.35f, forward, ref val3, num * 2f, num2, (QueryTriggerInteraction)1))
				{
					bool flag = false;
					try
					{
						if ((Object)(object)((RaycastHit)(ref val3)).collider != (Object)null && ((Component)((RaycastHit)(ref val3)).collider).transform.IsChildOf(((Component)player).transform))
						{
							flag = true;
						}
					}
					catch
					{
					}
					if (!flag)
					{
						val2 = ((RaycastHit)(ref val3)).point;
					}
				}
			}
			catch
			{
			}
			Collider[] array = Physics.OverlapSphere(val2, num, -1, (QueryTriggerInteraction)1);
			int num3 = 0;
			float num4 = 0.15f;
			for (int i = 0; i < array.Length && num3 < 32; i++)
			{
				ITarget target = IDamageSource.GetTarget((Component)(object)array[i]);
				if (target == null || !target.IsAlive)
				{
					continue;
				}
				try
				{
					Vector3 val4 = ((IFollowable)target).GetHealthbarPosition() - val;
					if (((Vector3)(ref val4)).sqrMagnitude > 0.01f && Vector3.Dot(((Vector3)(ref val4)).normalized, forward) < num4)
					{
						continue;
					}
				}
				catch
				{
				}
				ISelectable val5 = (ISelectable)(object)target;
				if (val5 != null)
				{
					HighlightTarget(val5, 2.5f);
					num3++;
				}
			}
		}
		catch (Exception ex)
		{
			ManualLogSource logger = SparrohPlugin.Logger;
			if (logger != null)
			{
				logger.LogDebug((object)("[BallisticSniper] Scouter: " + ex.Message));
			}
		}
	}

	private void HighlightTarget(ISelectable selectable, float duration)
	{
		if (selectable == null)
		{
			return;
		}
		try
		{
			Highlighter.RegisterObjectForHighlight(selectable);
			if (!activeHighlights.Contains(selectable))
			{
				activeHighlights.Add(selectable);
			}
			highlightClearAt = Mathf.Max(highlightClearAt, Time.time + duration);
		}
		catch
		{
		}
	}

	private void ClearHighlights()
	{
		for (int i = 0; i < activeHighlights.Count; i++)
		{
			try
			{
				Highlighter.RemoveObjectFromHighlighting(activeHighlights[i]);
			}
			catch
			{
			}
		}
		activeHighlights.Clear();
		highlightClearAt = -999f;
	}

	private void DestroyC4Visual()
	{
		if ((Object)(object)c4Visual != (Object)null)
		{
			try
			{
				Object.Destroy((Object)(object)c4Visual);
			}
			catch
			{
			}
			c4Visual = null;
		}
	}

	public void ModifyBulletData(ref BulletData bullet, Gun gun)
	{
		//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
		//IL_011a: Unknown result type (might be due to invalid IL or missing references)
		if (data.longwatchAimSeconds > 0f && AimHoldTime >= data.longwatchAimSeconds)
		{
			bullet.speed *= data.longwatchSpeedMult;
			bullet.gravity *= data.longwatchGravityMult;
			bullet.range.falloffStartDistance += data.longwatchRangeBonus;
			bullet.range.falloffEndDistance += data.longwatchRangeBonus;
			bullet.range.maxDamageRange += data.longwatchRangeBonus;
		}
		if (data.bonusBounces > 0)
		{
			bullet.maxBounces = Mathf.Max(bullet.maxBounces, data.bonusBounces);
		}
		if (data.rotAmount > 0f)
		{
			bullet.damageEffect = (EffectType)7;
			bullet.damageEffectAmount = Mathf.Max(bullet.damageEffectAmount, data.rotAmount);
		}
		else if (data.waterAmount > 0f)
		{
			bullet.damageEffect = (EffectType)9;
			bullet.damageEffectAmount = Mathf.Max(bullet.damageEffectAmount, data.waterAmount);
		}
	}

	public void OnBulletFired(IBullet bullet, ref BulletData bulletData)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		lastFiredBullet = bulletData;
		hasLastFiredBullet = true;
		if (data.pierceTargets > 0 && bullet != null)
		{
			SniperProjectilePierceHook.ResetBullet(bullet);
		}
	}

	public void OnShotFired(Gun gun)
	{
		//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a6: 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)
		//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fe: 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_0128: Unknown result type (might be due to invalid IL or missing references)
		//IL_012f: 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_00ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
		TwinShotHits.Clear();
		if (BlockAutoReloadForChamber)
		{
			BlockAutoReloadForChamber = false;
			try
			{
				IUpgradable prefab = gun.Prefab;
				IWeapon val = (IWeapon)(object)((prefab is IWeapon) ? prefab : null);
				if (val != null)
				{
					gun.GunData.autoReloadWhenEmpty = val.GunData.autoReloadWhenEmpty;
				}
				else
				{
					gun.GunData.autoReloadWhenEmpty = true;
				}
			}
			catch
			{
			}
		}
		if (data.echoDelay > 0f && (Object)(object)gun != (Object)null)
		{
			try
			{
				Vector3 position = (hasLastFiredBullet ? lastFiredBullet.position : (((Object)(object)gun.GunData.firePoint != (Object)null) ? gun.GunData.firePoint.position : ((Component)gun).transform.position));
				Quaternion rotation = (hasLastFiredBullet ? lastFiredBullet.rotation : (((Object)(object)gun.Player != (Object)null) ? ((Component)gun.Player.playerLook).transform.rotation : ((Component)gun).transform.rotation));
				PendingEchoes.Add(new PendingEcho
				{
					fireAt = Time.time + data.echoDelay,
					position = position,
					rotation = rotation,
					damage = gun.GunData.damage * ((data.echoDamageScale > 0f) ? data.echoDamageScale : 0.85f),
					speed = (hasLastFiredBullet ? lastFiredBullet.speed : gun.GunData.bulletSpeed),
					gravity = (hasLastFiredBullet ? lastFiredBullet.gravity : gun.GunData.bulletGravity)
				});
			}
			catch
			{
			}
		}
	}

	public void OnMagazineEmptied(Gun gun)
	{
		if (!data.oneInTheChamber || !ChamberBonusReady || (Object)(object)gun == (Object)null)
		{
			return;
		}
		gun.RemainingAmmo = 1f;
		ChamberBonusReady = false;
		BlockAutoReloadForChamber = true;
		try
		{
			gun.GunData.autoReloadWhenEmpty = false;
		}
		catch
		{
		}
	}

	public void OnFullReloadCompleted(Gun gun)
	{
		if (data.oneInTheChamber)
		{
			LastReloadWasFullTopOff = true;
			ChamberBonusReady = true;
			BlockAutoReloadForChamber = false;
		}
	}

	private void OnBeforeDamage(ref DamageCallbackData callback)
	{
		//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
		//IL_016f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0174: 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_018c: Unknown result type (might be due to invalid IL or missing references)
		IDamageSource source = callback.source;
		object obj = ((source is Gun) ? source : null);
		if (obj == null)
		{
			IDamageSource source2 = callback.source;
			IDamageSource obj2 = ((source2 != null) ? source2.ParentSource : null);
			obj = ((obj2 is Gun) ? obj2 : null);
		}
		Gun val = (Gun)obj;
		if ((Object)(object)val == (Object)null || !IsOurGear((IGear)(object)val))
		{
			return;
		}
		ITarget target = callback.target;
		if (target == null)
		{
			return;
		}
		if (data.shellDamageMult > 1.01f && IsShellTarget(target))
		{
			callback.damageData.damage *= data.shellDamageMult;
		}
		if (data.fullHpDamageMult > 1.01f && GetHealthFraction(target) >= 0.98f)
		{
			callback.damageData.damage *= data.fullHpDamageMult;
		}
		if (data.syncWindow > 0f && data.syncDamageMult > 1.01f && SniperAllyDamageTracker.WasDamagedByAlly(target, val, data.syncWindow))
		{
			callback.damageData.damage *= data.syncDamageMult;
		}
		if (data.overkill && IsShellTarget(target))
		{
			EnemyPart val2 = (EnemyPart)(object)((target is EnemyPart) ? target : null);
			if (val2 != null)
			{
				try
				{
					float health = val2.Health;
					float damage = callback.damageData.damage;
					if (health > 0f && damage > health + 0.01f)
					{
						pendingOverkill = damage - health;
						ref ITarget reference = ref pendingOverkillParent;
						IEnemyComponent parent = val2.Parent;
						reference = (ITarget)(object)((parent is ITarget) ? parent : null);
						pendingOverkillPos = callback.position;
						pendingOverkillGun = val;
						pendingOverkillEffect = callback.damageData.effect;
						pendingOverkillEffectAmt = callback.damageData.effectAmount;
						hasPendingOverkill = true;
					}
				}
				catch
				{
				}
			}
		}
		if (!(data.transferRelayScale > 0f) || !(callback.damageData.damage <= 0.01f))
		{
			return;
		}
		try
		{
			float num = val.GunData.damage * data.transferRelayScale;
			IDamageSource.DamageTarget((IDamageSource)(object)val, target, new DamageData(num, (EffectType)(-1), 0f, (DamageFlags)0), callback.position, (Collider)null);
		}
		catch
		{
		}
	}

	private void ApplyPendingOverkill()
	{
		//IL_0018: 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_0026: 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_0076: Unknown result type (might be due to invalid IL or missing references)
		//IL_0081: 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)
		if (!hasPendingOverkill)
		{
			return;
		}
		float num = pendingOverkill;
		ITarget val = pendingOverkillParent;
		Vector3 val2 = pendingOverkillPos;
		Gun val3 = pendingOverkillGun;
		EffectType val4 = pendingOverkillEffect;
		float num2 = pendingOverkillEffectAmt;
		hasPendingOverkill = false;
		pendingOverkill = 0f;
		pendingOverkillParent = null;
		pendingOverkillGun = null;
		if (num <= 0.01f || val == null || !val.IsAlive || (Object)(object)val3 == (Object)null)
		{
			return;
		}
		try
		{
			IDamageSource.DamageTarget((IDamageSource)(object)val3, val, new DamageData(num, val4, num2 * 0.5f, (DamageFlags)0), val2, (Collider)null);
		}
		catch (Exception ex)
		{
			ManualLogSource logger = SparrohPlugin.Logger;
			if (logger != null)
			{
				logger.LogDebug((object)("[BallisticSniper] Overkill carry: " + ex.Message));
			}
		}
	}

	private static bool IsShellTarget(ITarget target)
	{
		//IL_000b: 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_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Invalid comparison between Unknown and I4
		try
		{
			EnemyPart val = (EnemyPart)(object)((target is EnemyPart) ? target : null);
			if (val != null)
			{
				if ((val.ComponentType & 4) != 0)
				{
					return true;
				}
				if ((val.ComponentType & 2) == 0 && (int)val.ComponentType != 1)
				{
					return true;
				}
			}
		}
		catch
		{
		}
		return (((object)((target is Component) ? target : null))?.GetType().Name ?? ((object)target).GetType().Name).IndexOf("Shell", StringComparison.OrdinalIgnoreCase) >= 0;
	}

	private static float GetHealthFraction(ITarget target)
	{
		try
		{
			EnemyPart val = (EnemyPart)(object)((target is EnemyPart) ? target : null);
			if (val != null)
			{
				return val.NormalizedHealth;
			}
		}
		catch
		{
		}
		try
		{
			float maxHealth = target.MaxHealth;
			if (maxHealth > 0f)
			{
				EnemyPart val2 = (EnemyPart)(object)((target is EnemyPart) ? target : null);
				if (val2 != null)
				{
					return val2.Health / maxHealth;
				}
			}
		}
		catch
		{
		}
		return 0.5f;
	}

	private void OnDamageTarget(in DamageCallbackData callback)
	{
		//IL_0166: Unknown result type (might be due to invalid IL or missing references)
		//IL_018a: Unknown result type (might be due to invalid IL or missing references)
		//IL_024e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0254: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d3: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0081: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
		if (callback.source == null)
		{
			return;
		}
		IDamageSource source = callback.source;
		Gun val = (Gun)(((object)((source is Gun) ? source : null)) ?? ((object)/*isinst with value type is only supported in some contexts*/));
		if ((Object)(object)val == (Object)null || !IsOurGear((IGear)(object)val) || callback.damageData.damage <= 0f)
		{
			return;
		}
		ITarget target = callback.target;
		if (target == null)
		{
			return;
		}
		if (BlockAutoReloadForChamber && val.RemainingAmmoCount <= 0)
		{
			BlockAutoReloadForChamber = false;
		}
		if (data.deathMark)
		{
			ApplyDeathMark(target, callback.position, headshot: false);
		}
		if (data.spotterRadius > 0f)
		{
			float duration = ((data.spotterDuration > 0f) ? data.spotterDuration : 6f);
			ISelectable val2 = (ISelectable)(object)target;
			if (val2 != null)
			{
				HighlightTarget(val2, duration);
			}
			try
			{
				Collider[] array = Physics.OverlapSphere(callback.position, data.spotterRadius, -1, (QueryTriggerInteraction)1);
				for (int i = 0; i < array.Length && i < 32; i++)
				{
					ITarget target2 = IDamageSource.GetTarget((Component)(object)array[i]);
					if (target2 != null && target2.IsAlive && target2 != target)
					{
						ISelectable val3 = (ISelectable)(object)target2;
						if (val3 != null)
						{
							HighlightTarget(val3, duration);
						}
					}
				}
			}
			catch
			{
			}
		}
		if (data.rotSplashRadius > 0f && data.rotAmount > 0f)
		{
			try
			{
				GameManager.Instance.SpawnExplosionFirstPerson((IDamageSource)(object)val, callback.position, data.rotSplashRadius, (TargetType)5, new DamageData(0f, (EffectType)7, data.rotAmount, (DamageFlags)16), 1.5f);
			}
			catch
			{
			}
		}
		if (data.twinLinkFenceDamage > 0f && !TwinShotHits.Contains(target))
		{
			TwinShotHits.Add(target);
			if (TwinShotHits.Count >= 2)
			{
				TryTwinFence(val);
			}
		}
		if (ExhaustionShotArmed && data.exhaustionSlowDuration > 0f)
		{
			ExhaustionShotArmed = false;
			ExhaustionReadyAt = Time.time + data.exhaustionCooldown;
			try
			{
				target.SlowTargetThisTick(Mathf.Clamp01(1f - data.exhaustionSlowStrength * 0.05f));
				IDamageSource.DamageTarget((IDamageSource)(object)val, target, new DamageData(0f, (EffectType)10, data.exhaustionSlowStrength, (DamageFlags)0), callback.position, (Collider)null);
			}
			catch
			{
			}
		}
		if (!(data.disruptDuration > 0f))
		{
			return;
		}
		try
		{
			string text = ((object)((target is Component) ? target : null))?.GetType().Name ?? "";
			if (text.IndexOf("Shield", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("Bubble", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				IDamageSource.DamageTarget((IDamageSource)(object)val, target, new DamageData(val.GunData.damage * 3.5f, (EffectType)(-1), 0f, (DamageFlags)0), callback.position, (Collider)null);
			}
		}
		catch
		{
		}
	}

	private void ApplyDeathMark(ITarget target, Vector3 pos, bool headshot)
	{
		//IL_00ca: 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_0072: Unknown result type (might be due to invalid IL or missing references)
		//IL_0073: Unknown result type (might be due to invalid IL or missing references)
		float num = data.deathMarkFuse * (headshot ? data.deathMarkHeadshotFuseMult : 1f);
		for (int i = 0; i < DeathMarks.Count; i++)
		{
			if (DeathMarks[i].target == target)
			{
				DeathMarkEntry value = DeathMarks[i];
				value.stacks += 1f;
				value.detonateAt = Mathf.Min(value.detonateAt, Time.time + num);
				value.lastPos = pos;
				DeathMarks[i] = value;
				return;
			}
		}
		DeathMarks.Add(new DeathMarkEntry
		{
			target = target,
			detonateAt = Time.time + num,
			stacks = 1f,
			lastPos = pos,
			fromHeadshot = headshot
		});
	}

	private void TryTwinFence(Gun gun)
	{
		//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cc: 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_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_0037: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: 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_00d4: 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_008e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0093: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a9: 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_015a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0161: Unknown result type (might be due to invalid IL or missing references)
		if (TwinShotHits.Count < 2)
		{
			return;
		}
		try
		{
			ITarget val = TwinShotHits[0];
			ITarget val2 = TwinShotHits[1];
			Vector3 healthbarPosition = ((IFollowable)val).GetHealthbarPosition();
			Vector3 healthbarPosition2 = ((IFollowable)val2).GetHealthbarPosition();
			if (Vector3.Distance(healthbarPosition, healthbarPosition2) < 0.05f)
			{
				TwinShotHits.Clear();
				return;
			}
			float num = Mathf.Max(14f, data.twinLinkFenceDamage * 0.15f);
			float twinLinkFenceDamage = data.twinLinkFenceDamage;
			try
			{
				IDamageSource.DamageTarget((IDamageSource)(object)gun, val, new DamageData(twinLinkFenceDamage * 0.5f, (EffectType)2, num, (DamageFlags)0), healthbarPosition, (Collider)null);
				IDamageSource.DamageTarget((IDamageSource)(object)gun, val2, new DamageData(twinLinkFenceDamage * 0.5f, (EffectType)2, num, (DamageFlags)0), healthbarPosition2, (Collider)null);
			}
			catch
			{
			}
			Vector3 val3 = (healthbarPosition + healthbarPosition2) * 0.5f;
			try
			{
				GameManager.Instance.SpawnExplosionFirstPerson((IDamageSource)(object)gun, val3, Mathf.Max(1.2f, data.twinLinkFenceRadius * 0.5f), (TargetType)5, new DamageData(twinLinkFenceDamage * 0.35f, (EffectType)2, num, (DamageFlags)16), 1.2f);
			}
			catch
			{
			}
			for (int i = 0; i < 2; i++)
			{
				ITarget obj3 = TwinShotHits[i];
				EnemyPart val4 = (EnemyPart)(object)((obj3 is EnemyPart) ? obj3 : null);
				if (val4 == null)
				{
					continue;
				}
				IEnemyComponent parent = val4.Parent;
				ITarget val5 = (ITarget)(object)((parent is ITarget) ? parent : null);
				if (val5 != null && val5.IsAlive)
				{
					try
					{
						IDamageSource.DamageTarget((IDamageSource)(object)gun, val5, new DamageData(twinLinkFenceDamage * 0.4f, (EffectType)2, num, (DamageFlags)0), ((IFollowable)val5).GetHealthbarPosition(), (Collider)null);
					}
					catch
					{
					}
				}
			}
		}
		catch
		{
		}
		TwinShotHits.Clear();
	}

	private void OnKillTarget(in KillCallbackData callback)
	{
		if (callback.source == null)
		{
			return;
		}
		IDamageSource source = callback.source;
		Gun val = (Gun)(((object)((source is Gun) ? source : null)) ?? ((object)/*isinst with value type is only supported in some contexts*/));
		if (!((Object)(object)val == (Object)null) && IsOurGear((IGear)(object)val))
		{
			LastKillTime = Time.time;
			if (data.overkill)
			{
				ApplyPendingOverkill();
			}
		}
	}

	public bool IsDeadboltActive()
	{
		if (data.deadboltDuration > 0f)
		{
			return Time.time - LastKillTime <= data.deadboltDuration;
		}
		return false;
	}

	public bool IsLongwatchActive()
	{
		if (data.longwatchAimSeconds > 0f)
		{
			return AimHoldTime >= data.longwatchAimSeconds;
		}
		return false;
	}

	public bool IsAnchored(Gun gun)
	{
		//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)
		if (!data.anchor || (Object)(object)((gun != null) ? gun.Player : null) == (Object)null)
		{
			return false;
		}
		try
		{
			Vector3 velocity = gun.Player.Velocity;
			velocity.y = 0f;
			return ((Vector3)(ref velocity)).sqrMagnitude <= data.anchorSpeedThreshold * data.anchorSpeedThreshold && !gun.Player.IsSprinting && !gun.Player.Sliding;
		}
		catch
		{
			return false;
		}
	}

	public void TryThrowOrDetonateC4(Gun gun)
	{
		//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01da: Unknown result type (might be due to invalid IL or missing references)
		//IL_01df: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fd: Unknown result type (might be due to invalid IL or missing references)
		//IL_006e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0092: 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_0055: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_00df: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_010e: 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_0113: Unknown result type (might be due to invalid IL or missing references)
		//IL_0115: 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_0122: 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_0131: Unknown result type (might be due to invalid IL or missing references)
		//IL_0136: Unknown result type (might be due to invalid IL or missing references)
		//IL_0140: Unknown result type (might be due to invalid IL or missing references)
		//IL_0145: Unknown result type (might be due to invalid IL or missing references)
		//IL_0150: Unknown result type (might be due to invalid IL or missing references)
		//IL_0155: 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_0164: Unknown result type (might be due to invalid IL or missing references)
		//IL_0169: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_017f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0184: Unknown result type (might be due to invalid IL or missing references)
		//IL_018e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0193: Unknown result type (might be due to invalid IL or missing references)
		if (!data.highExplosive || (Object)(object)gun == (Object)null || !((NetworkBehaviour)gun).IsOwner)
		{
			return;
		}
		if (C4Deployed)
		{
			try
			{
				GameManager.Instance.SpawnExplosionFirstPerson((IDamageSource)(object)gun, C4Position, data.c4Radius, (TargetType)5, new DamageData(data.c4Damage, (EffectType)0, 0f, (DamageFlags)16), 3f);
			}
			catch
			{
				try
				{
					GameManager.Instance.SpawnExplosionObserverSeeThrough((IDamageSource)(object)gun, C4Position, data.c4Radius, (TargetType)5, new DamageData(data.c4Damage, (EffectType)0, 0f, (DamageFlags)16), ((NetworkBehaviour)gun).OwnerClientId, 12f);
				}
				catch
				{
				}
			}
			DestroyC4Visual();
			C4Deployed = false;
			return;
		}
		try
		{
			PlayerLook val = gun.Player?.playerLook;
			Vector3 val2 = (((Object)(object)val != (Object)null) ? ((Component)val).transform.position : ((Component)gun).transform.position);
			Vector3 val3 = (((Object)(object)val != (Object)null) ? ((Component)val).transform.forward : ((Component)gun).transform.forward);
			C4Position = val2 + val3 * data.c4ThrowForce * 0.65f + Vector3.up * 0.5f;
			RaycastHit val4 = default(RaycastHit);
			if (Physics.Raycast(C4Position + Vector3.up * 2f, Vector3.down, ref val4, 40f))
			{
				C4Position = ((RaycastHit)(ref val4)).point + Vector3.up * 0.15f;
			}
			C4Deployed = true;
			C4DeployedAt = Time.time;
			SpawnC4Visual(C4Position);
		}
		catch
		{
			C4Position = ((Component)gun).transform.position + ((Component)gun).transform.forward * 10f;
			C4Deployed = true;
			C4DeployedAt = Time.time;
			SpawnC4Visual(C4Position);
		}
	}

	private void SpawnC4Visual(Vector3 pos)
	{
		//IL_002d: 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_0048: 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)
		DestroyC4Visual();
		try
		{
			c4Visual = GameObject.CreatePrimitive((PrimitiveType)3);
			((Object)c4Visual).name = "BallisticSniper_C4";
			c4Visual.transform.position = pos;
			c4Visual.transform.localScale = Vector3.one * 0.35f;
			Collider component = c4Visual.GetComponent<Collider>();
			if ((Object)(object)component != (Object)null)
			{
				Object.Destroy((Object)(object)component);
			}
			Renderer component2 = c4Visual.GetComponent<Renderer>();
			if ((Object)(object)component2 != (Object)null)
			{
				component2.material.color = new Color(0.15f, 0.15f, 0.15f, 1f);
			}
		}
		catch (Exception ex)
		{
			ManualLogSource logger = SparrohPlugin.Logger;
			if (logger != null)
			{
				logger.LogDebug((object)("[BallisticSniper] C4 visual: " + ex.Message));
			}
		}
	}

	private void OnDestroy()
	{
		if ((Object)(object)boundGun != (Object)null)
		{
			BindHooks(boundGun, bind: false);
		}
		ClearHighlights();
		DestroyC4Visual();
	}

	public static bool TryGet(IGear gear, out BallisticSniperBehaviour behaviour)
	{
		behaviour = null;
		if ((Object)(object)((gear != null) ? gear.gameObject : null) == (Object)null)
		{
			return false;
		}
		behaviour = gear.gameObject.GetComponent<BallisticSniperBehaviour>();
		if ((Object)(object)behaviour != (Object)null)
		{
			return true;
		}
		bool num = (Object)(object)((IUpgradable)gear).Info != (Object)null && (((IUpgradable)gear).Info.APIName == "ballistic_sniper" || ((IUpgradable)gear).Info.ID == 87421);
		BallisticSniperBehaviour ballisticSniperBehaviour = null;
		IUpgradable prefab = ((IUpgradable)gear).Prefab;
		Component val = (Component)(object)((prefab is Component) ? prefab : null);
		if (val != null)
		{
			ballisticSniperBehaviour = val.GetComponent<BallisticSniperBehaviour>();
		}
		if (!num && (Object)(object)ballisticSniperBehaviour == (Object)null)
		{
			return false;
		}
		string desc = (((Object)(object)ballisticSniperBehaviour != (Object)null) ? ballisticSniperBehaviour.Description : "Heavy kinetic bolt-action rifle. High single-shot damage, low capacity, and a deliberate single-round reload. Built for long-range elimination.");
		behaviour = gear.gameObject.AddComponent<BallisticSniperBehaviour>();
		behaviour.InitializeAsPrefab(desc);
		if ((Object)(object)ballisticSniperBehaviour != (Object)null)
		{
			behaviour.CopyFrom(ballisticSniperBehaviour);
		}
		return true;
	}

	public static bool IsOurGear(IGear gear)
	{
		if ((Object)(object)((gear != null) ? ((IUpgradable)gear).Info : null) == (Object)null)
		{
			return false;
		}
		if (!(((IUpgradable)gear).Info.APIName == "ballistic_sniper"))
		{
			return ((IUpgradable)gear).Info.ID == 87421;
		}
		return true;
	}

	public static bool IsOurGear(IUpgradable gear)
	{
		if ((Object)(object)((gear != null) ? gear.Info : null) == (Object)null)
		{
			return false;
		}
		if (!(gear.Info.APIName == "ballistic_sniper"))
		{
			return gear.Info.ID == 87421;
		}
		return true;
	}
}
internal static class BallisticSniperCombatHooks
{
	public static void Apply(Harmony harmony)
	{
		TryPatch(harmony, typeof(SniperGunUpdateHook));
		TryPatch(harmony, typeof(SniperModifyBulletHook));
		TryPatch(harmony, typeof(SniperCartridgeModifyBulletHook));
		TryPatch(harmony, typeof(SniperOnFireHook));
		TryPatch(harmony, typeof(SniperCartridgeOnFireHook));
		TryPatch(harmony, typeof(SniperOnFiredBulletHook));
		TryPatch(harmony, typeof(SniperRecoilHook));
		TryPatch(harmony, typeof(SniperUpgradesEnabledHook));
		TryPatch(harmony, typeof(SniperUpgradesDisabledHook));
		TryPatch(harmony, typeof(SniperReloadHoldC4Hook));
		TryPatch(harmony, typeof(SniperCanReloadHook));
		TryPatch(harmony, typeof(SniperProjectileBounceHomingHook));
		TryPatch(harmony, typeof(SniperProjectilePierceHook));
		TryPatch(harmony, typeof(SniperAllyDamageTracker));
	}

	private static void TryPatch(Harmony harmony, Type patchClass)
	{
		try
		{
			harmony.PatchAll(patchClass);
		}
		catch (Exception ex)
		{
			ManualLogSource logger = SparrohPlugin.Logger;
			if (logger != null)
			{
				logger.LogWarning((object)("[BallisticSniper] Skipped patch " + patchClass.Name + ": " + ex.Message));
			}
		}
	}
}
[HarmonyPatch(typeof(Gun), "Update")]
internal static class SniperGunUpdateHook
{
	[HarmonyPostfix]
	private static void Postfix(Gun __instance)
	{
		try
		{
			if (!((Object)(object)__instance == (Object)null) && ((NetworkBehaviour)__instance).IsOwner && BallisticSniperBehaviour.TryGet((IGear)(object)__instance, out var behaviour))
			{
				behaviour.Tick(Time.deltaTime, __instance);
			}
		}
		catch (Exception ex)
		{
			ManualLogSource logger = SparrohPlugin.Logger;
			if (logger != null)
			{
				logger.LogDebug((object)("[BallisticSniper] Update: " + ex.Message));
			}
		}
	}
}
[HarmonyPatch(typeof(Gun), "ModifyBulletData")]
internal static class SniperModifyBulletHook
{
	[HarmonyPostfix]
	private static void Postfix(Gun __instance, ref BulletData data)
	{
		try
		{
			if (!((Object)(object)__instance == (Object)null) && !(__instance is CartridgeSMG) && BallisticSniperBehaviour.TryGet((IGear)(object)__instance, out var behaviour))
			{
				behaviour.ModifyBulletData(ref data, __instance);
			}
		}
		catch (Exception ex)
		{
			ManualLogSource logger = SparrohPlugin.Logger;
			if (logger != null)
			{
				logger.LogDebug((object)("[BallisticSniper] ModifyBullet: " + ex.Message));
			}
		}
	}
}
[HarmonyPatch(typeof(CartridgeSMG), "ModifyBulletData")]
internal static class SniperCartridgeModifyBulletHook
{
	[HarmonyPostfix]
	private static void Postfix(CartridgeSMG __instance, ref BulletData data, BulletFlags flags)
	{
		try
		{
			if (!((Object)(object)__instance == (Object)null) && BallisticSniperBehaviour.TryGet((IGear)(object)__instance, out var behaviour))
			{
				behaviour.ModifyBulletData(ref data, (Gun)(object)__instance);
			}
		}
		catch (Exception ex)
		{
			ManualLogSource logger = SparrohPlugin.Logger;
			if (logger != null)
			{
				logger.LogDebug((object)("[BallisticSniper] SMG ModifyBullet: " + ex.Message));
			}
		}
	}
}
[HarmonyPatch(typeof(Gun), "OnFire")]
internal static class SniperOnFireHook
{
	[HarmonyPostfix]
	private static void Postfix(Gun __instance)
	{
		try
		{
			if (!((Object)(object)__instance == (Object)null) && ((NetworkBehaviour)__instance).IsOwner && BallisticSniperBehaviour.TryGet((IGear)(object)__instance, out var behaviour))
			{
				behaviour.OnShotFired(__instance);
			}
		}
		catch
		{
		}
	}
}
[HarmonyPatch(typeof(CartridgeSMG), "OnFire")]
internal static class SniperCartridgeOnFireHook
{
	[HarmonyPostfix]
	private static void Postfix(CartridgeSMG __instance, int numBullets)
	{
		try
		{
			if (!((Object)(object)__instance == (Object)null) && ((NetworkBehaviour)__instance).IsOwner && BallisticSniperBehaviour.TryGet((IGear)(object)__instance, out var behaviour))
			{
				behaviour.OnShotFired((Gun)(object)__instance);
			}
		}
		catch (Exception ex)
		{
			ManualLogSource logger = SparrohPlugin.Logger;
			if (logger != null)
			{
				logger.LogDebug((object)("[BallisticSniper] Cartridge OnFire: " + ex.Message));
			}
		}
	}
}
[HarmonyPatch(typeof(Gun), "OnFiredBullet")]
internal static class SniperOnFiredBulletHook
{
	[HarmonyPostfix]
	private static void Postfix(Gun __instance, IBullet bullet, BulletFlags flags, int shotIndex, ref BulletData bulletData)
	{
		try
		{
			if (!((Object)(object)__instance == (Object)null) && bullet != null && BallisticSniperBehaviour.TryGet((IGear)(object)__instance, out var behaviour))
			{
				behaviour.OnBulletFired(bullet, ref bulletData);
			}
		}
		catch (Exception ex)
		{
			ManualLogSource logger = SparrohPlugin.Logger;
			if (logger != null)
			{
				logger.LogDebug((object)("[BallisticSniper] OnFiredBullet: " + ex.Message));
			}
		}
	}
}
[HarmonyPatch(typeof(Gun), "AddRecoil")]
internal static class SniperRecoilHook
{
	[HarmonyPrefix]
	private static void Prefix(Gun __instance, ref float multiplier)
	{
		try
		{
			if (!((Object)(object)__instance == (Object)null) && BallisticSniperBehaviour.TryGet((IGear)(object)__instance, out var behaviour) && behaviour.IsAnchored(__instance))
			{
				multiplier = 0f;
			}
		}
		catch
		{
		}
	}
}
[HarmonyPatch(typeof(Gun), "OnUpgradesEnabled")]
internal static class SniperUpgradesEnabledHook
{
	[HarmonyPostfix]
	private static void Postfix(Gun __instance)
	{
		try
		{
			if (!((Object)(object)__instance == (Object)null) && BallisticSniperBehaviour.TryGet((IGear)(object)__instance, out var behaviour))
			{
				behaviour.OnUpgradesApplied(__instance);
			}
		}
		catch (Exception ex)
		{
			ManualLogSource logger = SparrohPlugin.Logger;
			if (logger != null)
			{
				logger.LogDebug((object)("[BallisticSniper] OnUpgradesEnabled: " + ex.Message));
			}
		}
	}
}
[HarmonyPatch(typeof(Gun), "OnUpgradesDisabled")]
internal static class SniperUpgradesDisabledHook
{
	[HarmonyPrefix]
	private static void Prefix(Gun __instance)
	{
		try
		{
			if (!((Object)(object)__instance == (Object)null) && BallisticSniperBehaviour.TryGet((IGear)(object)__instance, out var behaviour))
			{
				behaviour.OnUpgradesCleared(__instance);
			}
		}
		catch
		{
		}
	}
}
[HarmonyPatch(typeof(Gun), "OnReloadPerformed")]
internal static class SniperReloadHoldC4Hook
{
	[HarmonyPrefix]
	private static bool Prefix(Gun __instance)
	{
		try
		{
			if ((Object)(object)__instance == (Object)null || !((NetworkBehaviour)__instance).IsOwner)
			{
				return true;
			}
			if (!BallisticSniperBehaviour.TryGet((IGear)(object)__instance, out var behaviour))
			{
				return true;
			}
			if (!behaviour.WeaponData.highExplosive)
			{
				return true;
			}
			if (behaviour.C4Deployed)
			{
				behaviour.TryThrowOrDetonateC4(__instance);
				return false;
			}
			if (__instance.RemainingAmmoCount >= __instance.GunData.magazineSize)
			{
				behaviour.TryThrowOrDetonateC4(__instance);
				return false;
			}
		}
		catch (Exception ex)
		{
			ManualLogSource logger = SparrohPlugin.Logger;
			if (logger != null)
			{
				logger.LogDebug((object)("[BallisticSniper] C4 reload: " + ex.Message));
			}
		}
		return true;
	}
}
[HarmonyPatch(typeof(Gun), "CanReload")]
internal static class SniperCanReloadHook
{
	[HarmonyPrefix]
	private static bool Prefix(Gun __instance, ref bool __result)
	{
		try
		{
			if ((Object)(object)__instance == (Object)null || !((NetworkBehaviour)__instance).IsOwner)
			{
				return true;
			}
			if (!BallisticSniperBehaviour.TryGet((IGear)(object)__instance, out var behaviour))
			{
				return true;
			}
			if (!behaviour.WeaponData.oneInTheChamber)
			{
				return true;
			}
			if (behaviour.BlockAutoReloadForChamber && __instance.RemainingAmmoCount > 0)
			{
				__result = false;
				return false;
			}
		}
		catch
		{
		}
		return true;
	}
}
[HarmonyPatch(typeof(SimpleProjectileBullet), "Bounce")]
internal static class SniperProjectileBounceHomingHook
{
	[HarmonyPostfix]
	private static void Postfix(SimpleProjectileBullet __instance, ref RaycastHit hit)
	{
		//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a7: 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)
		//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00df: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_016c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0171: Unknown result type (might be due to invalid IL or missing references)
		//IL_0173: 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_017c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0181: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b2: 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_01bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ec: 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_0135: 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_0220: Unknown result type (might be due to invalid IL or missing references)
		//IL_0222: Unknown result type (might be due to invalid IL or missing references)
		//IL_0229: Unknown result type (might be due to invalid IL or missing references)
		//IL_0200: Unknown result type (might be due to invalid IL or missing references)
		//IL_0215: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return;
			}
			IDamageSource parentSource = __instance.ParentSource;
			Gun val = (Gun)(object)((parentSource is Gun) ? parentSource : null);
			if (val == null || !BallisticSniperBehaviour.TryGet((IGear)(object)val, out var behaviour) || (behaviour.WeaponData.postBounceHoming <= 0f && behaviour.WeaponData.bonusBounces <= 0))
			{
				return;
			}
			FieldInfo fieldInfo = AccessTools.Field(typeof(SimpleProjectileBullet), "data");
			FieldInfo fieldInfo2 = AccessTools.Field(typeof(SimpleProjectileBullet), "velocity");
			if (fieldInfo == null || !(fieldInfo.GetValue(__instance) is BulletData val2))
			{
				return;
			}
			Vector3 point = ((RaycastHit)(ref hit)).point;
			float num = Mathf.Max(25f, behaviour.WeaponData.postBounceHoming * 8f);
			ITarget val3 = null;
			float num2 = float.MaxValue;
			int num3 = val2.targetCollisionMask;
			if (num3 == 0)
			{
				num3 = -1;
			}
			Collider[] array = Physics.OverlapSphere(point, num, num3, (QueryTriggerInteraction)1);
			ITarget target = IDamageSource.GetTarget((Component)(object)((RaycastHit)(ref hit)).collider);
			Vector3 val4;
			for (int i = 0; i < array.Length; i++)
			{
				ITarget target2 = IDamageSource.GetTarget((Component)(object)array[i]);
				if (target2 != null && target2.IsAlive && target2 != target)
				{
					val4 = ((IFollowable)target2).GetHealthbarPosition() - point;
					float sqrMagnitude = ((Vector3)(ref val4)).sqrMagnitude;
					if (sqrMagnitude < num2)
					{
						num2 = sqrMagnitude;
						val3 = target2;
					}
				}
			}
			if (val3 == null)
			{
				return;
			}
			val4 = ((IFollowable)val3).GetHealthbarPosition() - point;
			Vector3 normalized = ((Vector3)(ref val4)).normalized;
			float num4 = Mathf.Clamp01(behaviour.WeaponData.postBounceHoming / 8f);
			num4 = Mathf.Max(num4, 0.75f);
			val4 = Vector3.Slerp(val2.direction, normalized, num4);
			val2.direction = ((Vector3)(ref val4)).normalized;
			fieldInfo.SetValue(__instance, val2);
			if (fieldInfo2 != null)
			{
				val4 = (Vector3)fieldInfo2.GetValue(__instance);
				float num5 = ((Vector3)(ref val4)).magnitude;
				if (num5 < 1f)
				{
					num5 = ((val2.speed > 0f) ? val2.speed : 220f);
				}
				fieldInfo2.SetValue(__instance, val2.direction * num5);
			}
		}
		catch (Exception ex)
		{
			ManualLogSource logger = SparrohPlugin.Logger;
			if (logger != null)
			{
				logger.LogDebug((object)("[BallisticSniper] Projectile bounce homing: " + ex.Message));
			}
		}
	}
}
[HarmonyPatch(typeof(SimpleProjectileBullet), "OnHit")]
internal static class SniperProjectilePierceHook
{
	private static readonly Dictionary<int, int> HitCounts = new Dictionary<int, int>(32);

	private static readonly FieldInfo BouncesField = AccessTools.Field(typeof(SimpleProjectileBullet), "bounces");

	private static readonly FieldInfo DataField = AccessTools.Field(typeof(SimpleProjectileBullet), "data");

	private static readonly FieldInfo IsAliveField = AccessTools.Field(typeof(SimpleProjectileBullet), "isAlive");

	private static readonly FieldInfo VelocityField = AccessTools.Field(typeof(SimpleProjectileBullet), "velocity");

	private static readonly FieldInfo PositionNextField = AccessTools.Field(typeof(SimpleProjectileBullet), "positionNext");

	public static void ResetBullet(IBullet bullet)
	{
		if (bullet != null)
		{
			HitCounts[((object)bullet).GetHashCode()] = 0;
		}
	}

	[HarmonyPrefix]
	private static bool Prefix(SimpleProjectileBullet __instance, ref RaycastHit hit, ref Vector3 direction, ITarget target)
	{
		//IL_015c: Unknown result type (might be due to invalid IL or missing references)
		//IL_016b: 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_0180: 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_0189: Unknown result type (might be due to invalid IL or missing references)
		//IL_018e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0194: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c6: 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_01d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01db: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_0201: Unknown result type (might be due to invalid IL or missing references)
		//IL_0232: Unknown result type (might be due to invalid IL or missing references)
		//IL_0237: Unknown result type (might be due to invalid IL or missing references)
		//IL_023b: Unknown result type (might be due to invalid IL or missing references)
		//IL_023d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0248: Unknown result type (might be due to invalid IL or missing references)
		//IL_00da: Unknown result type (might be due to invalid IL or missing references)
		//IL_0119: Unknown result type (might be due to invalid IL or missing references)
		//IL_011f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0125: Unknown result type (might be due to invalid IL or missing references)
		//IL_010e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0113: Unknown result type (might be due to invalid IL or missing references)
		//IL_0136: Unknown result type (might be due to invalid IL or missing references)
		//IL_013b: Unknown result type (might be due to invalid IL or missing references)
		//IL_013d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0142: Unknown result type (might be due to invalid IL or missing references)
		//IL_0147: 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)
		try
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return true;
			}
			IDamageSource parentSource = __instance.ParentSource;
			Gun val = (Gun)(object)((parentSource is Gun) ? parentSource : null);
			if (val == null)
			{
				return true;
			}
			if (!BallisticSniperBehaviour.TryGet((IGear)(object)val, out var behaviour))
			{
				return true;
			}
			if (behaviour.WeaponData.pierceTargets <= 0)
			{
				return true;
			}
			if (target == null || !IBullet.CanDamageTarget(target, (IBullet)(object)__instance))
			{
				return true;
			}
			int hashCode = ((object)__instance).GetHashCode();
			HitCounts.TryGetValue(hashCode, out var value);
			int pierceTargets = behaviour.WeaponData.pierceTargets;
			if (value >= pierceTargets)
			{
				return true;
			}
			float num = ((behaviour.WeaponData.pierceFalloff > 0f) ? behaviour.WeaponData.pierceFalloff : 0.25f);
			float num2 = ((value == 0) ? 1f : Mathf.Pow(Mathf.Clamp01(1f - num), (float)value));
			if (BulletFlagsExtensions.IsOwner(__instance.Flags))
			{
				BulletData val2 = (BulletData)((DataField != null && DataField.GetValue(__instance) is BulletData val3) ? val3 : default(BulletData));
				float num3 = 1f;
				try
				{
					ref RangeData range = ref val2.range;
					Vector3 val4 = ((RaycastHit)(ref hit)).point - val2.position;
					num3 = ((RangeData)(ref range)).GetDamageMultiplier(((Vector3)(ref val4)).magnitude);
				}
				catch
				{
				}
				float num4 = val2.damage * num3 * num2;
				float num5 = val2.damageEffectAmount * num3 * num2;
				IDamageSource.DamageTarget((IDamageSource)(object)__instance, target, new DamageData(num4, val2.damageEffect, num5, val2.damageFlags), ((RaycastHit)(ref hit)).point, ((RaycastHit)(ref hit)).collider);
			}
			HitCounts[hashCode] = value + 1;
			Vector3 val5 = ((((Vector3)(ref direction)).sqrMagnitude > 0.001f) ? ((Vector3)(ref direction)).normalized : ((Component)__instance).transform.forward);
			Vector3 val6 = ((RaycastHit)(ref hit)).point + val5 * 0.35f;
			if (PositionNextField != null)
			{
				PositionNextField.SetValue(__instance, val6);
			}
			if (DataField != null && DataField.GetValue(__instance) is BulletData val7)
			{
				val7.position = val6;
				DataField.SetValue(__instance, val7);
			}
			if (IsAliveField != null)
			{
				IsAliveField.SetValue(__instance, true);
			}
			return false;
		}
		catch (Exception ex)
		{
			ManualLogSource logger = SparrohPlugin.Logger;
			if (logger != null)
			{
				logger.LogDebug((object)("[BallisticSniper] Projectile pierce: " + ex.Message));
			}
			return true;
		}
	}
}
[HarmonyPatch(typeof(EnemyPart), "Damage")]
internal static class SniperAllyDamageTracker
{
	private struct HitRecord
	{
		public ulong ownerId;

		public float time;
	}

	private static readonly Dictionary<int, List<HitRecord>> Recent = new Dictionary<int, List<HitRecord>>(64);

	[HarmonyPostfix]
	private static void Postfix(EnemyPart __instance, DamageData data, IDamageSource source, Vector3 position)
	{
		try
		{
			if ((Object)(object)__instance == (Object)null || source == null)
			{
				return;
			}
			ulong num = ResolveOwnerId(source);
			if (num == ulong.MaxValue)
			{
				return;
			}
			int instanceID = ((Object)__instance).GetInstanceID();
			if (!Recent.TryGetValue(instanceID, out var value))
			{
				value = new List<HitRecord>(4);
				Recent[instanceID] = value;
			}
			float time = Time.time;
			for (int num2 = value.Count - 1; num2 >= 0; num2--)
			{
				if (time - value[num2].time > 6f)
				{
					value.RemoveAt(num2);
				}
			}
			value.Add(new HitRecord
			{
				ownerId = num,
				time = time
			});
			if (value.Count > 8)
			{
				value.RemoveAt(0);
			}
		}
		catch
		{
		}
	}

	public static bool WasDamagedByAlly(ITarget target, Gun selfGun, float windowSeconds)
	{
		if (target == null || (Object)(object)selfGun == (Object)null || windowSeconds <= 0f)
		{
			return false;
		}
		try
		{
			EnemyPart val = (EnemyPart)(object)((target is EnemyPart) ? target : null);
			int instanceID;
			if (val != null)
			{
				instanceID = ((Object)val).GetInstanceID();
			}
			else
			{
				Component val2 = (Component)(object)((target is Component) ? target : null);
				if (val2 == null)
				{
					return false;
				}
				instanceID = ((Object)val2).GetInstanceID();
			}
			if (!Recent.TryGetValue(instanceID, out var value) || value.Count == 0)
			{
				return false;
			}
			ulong ownerClientId = ((NetworkBehaviour)selfGun).OwnerClientId;
			float time = Time.time;
			for (int num = value.Count - 1; num >= 0; num--)
			{
				if (!(time - value[num].time > windowSeconds) && value[num].ownerId != ownerClientId && value[num].ownerId != ulong.MaxValue)
				{
					return true;
				}
			}
		}
		catch
		{
		}
		return false;
	}

	private static ulong ResolveOwnerId(IDamageSource source)
	{
		try
		{
			IDamageSource val = source.GetBase();
			Gun val2 = (Gun)(object)((val is Gun) ? val : null);
			if (val2 != null)
			{
				return ((NetworkBehaviour)val2).OwnerClientId;
			}
			Player val3 = (Player)(object)((val is Player) ? val : null);
			if (val3 != null)
			{
				return ((NetworkBehaviour)val3).OwnerClientId;
			}
			Gun val4 = (Gun)(object)((source is Gun) ? source : null);
			if (val4 != null)
			{
				return ((NetworkBehaviour)val4).OwnerClientId;
			}
		}
		catch
		{
		}
		return ulong.MaxValue;
	}
}
[HarmonyPatch(typeof(Gun), "OnAmmoLoaded")]
internal static class BallisticSniperReloadHook
{
	[HarmonyPostfix]
	private static void Postfix(Gun __instance)
	{
		try
		{
			if ((Object)(object)__instance == (Object)null || !((NetworkBehaviour)__instance).IsOwner || !BallisticSniperBehaviour.TryGet((IGear)(object)__instance, out var behaviour))
			{
				return;
			}
			if (!behaviour.SingleRoundReload)
			{
				if (__instance.RemainingAmmoCount >= __instance.GunData.magazineSize)
				{
					behaviour.OnFullReloadCompleted(__instance);
				}
			}
			else
			{
				LoadOneRound(__instance, behaviour);
			}
		}
		catch (Exception arg)
		{
			ManualLogSource logger = SparrohPlugin.Logger;
			if (logger != null)
			{
				logger.LogError((object)$"[BallisticSniper] OnAmmoLoaded hook failed: {arg}");
			}
		}
	}

	private static void LoadOneRound(Gun gun, BallisticSniperBehaviour behaviour)
	{
		ref GunData gunData = ref gun.GunData;
		int num = Mathf.Max(gunData.magazineSize, 1);
		int remainingAmmoCount = gun.RemainingAmmoCount;
		if (remainingAmmoCount >= num)
		{
			behaviour.IsTubeReloading = false;
			behaviour.OnFullReloadCompleted(gun);
			return;
		}
		bool flag = false;
		if (gunData.hasLimitedAmmo)
		{
			if (gun.StoredAmmo >= 1f)
			{
				gun.StoredAmmo -= 1f;
				gun.RemainingAmmo = remainingAmmoCount + 1;
				flag = true;
			}
		}
		else
		{
			gun.RemainingAmmo = remainingAmmoCount + 1;
			flag = true;
		}
		if (!flag)
		{
			behaviour.IsTubeReloading = false;
			return;
		}
		if (gun.RemainingAmmoCount < num && (!gunData.hasLimitedAmmo || gun.StoredAmmo >= 1f))
		{
			behaviour.IsTubeReloading = true;
			((MonoBehaviour)gun).StartCoroutine(ContinueReloadAfterCurrent(gun));
			return;
		}
		behaviour.IsTubeReloading = false;
		if (gun.RemainingAmmoCount >= num)
		{
			behaviour.OnFullReloadCompleted(gun);
		}
	}

	private static IEnumerator ContinueReloadAfterCurrent(Gun gun)
	{
		float timeout = 5f;
		while ((Object)(object)gun != (Object)null && gun.Reloading && timeout > 0f)
		{
			timeout -= Time.unscaledDeltaTime;
			yield return null;
		}
		yield return null;
		if ((Object)(object)gun == (Object)null || !((NetworkBehaviour)gun).IsOwner || !gun.Active || !BallisticSniperBehaviour.TryGet((IGear)(object)gun, out var behaviour) || !behaviour.IsTubeReloading || !behaviour.SingleRoundReload || gun.Reloading)
		{
			yield break;
		}
		ref GunData gunData = ref gun.GunData;
		if (gun.RemainingAmmoCount >= gunData.magazineSize)
		{
			behaviour.IsTubeReloading = false;
			yield break;
		}
		if (gunData.hasLimitedAmmo && gun.StoredAmmo < 1f)
		{
			behaviour.IsTubeReloading = false;
			yield break;
		}
		try
		{
			gun.Reload();
		}
		catch (Exception ex)
		{
			ManualLogSource logger = SparrohPlugin.Logger;
			if (logger != null)
			{
				logger.LogDebug((object)("[BallisticSniper] Continue reload: " + ex.Message));
			}
			behaviour.IsTubeReloading = false;
		}
	}
}
[HarmonyPatch(typeof(Gun), "Update")]
internal static class BallisticSniperReloadInterruptHook
{
	[HarmonyPrefix]
	private static void Prefix(Gun __instance)
	{
		try
		{
			if (!((Object)(object)__instance == (Object)null) && ((NetworkBehaviour)__instance).IsOwner && __instance.Active && BallisticSniperBehaviour.TryGet((IGear)(object)__instance, out var behaviour) && behaviour.SingleRoundReload && (__instance.Reloading || behaviour.IsTubeReloading) && __instance.RemainingAmmoCount >= 1 && IsFirePressedOrHeld())
			{
				behaviour.InterruptTubeReload(__instance);
			}
		}
		catch (Exception ex)
		{
			ManualLogSource logger = SparrohPlugin.Logger;
			if (logger != null)
			{
				logger.LogDebug((object)("[BallisticSniper] Reload interrupt: " + ex.Message));
			}
		}
	}

	private static bool IsFirePressedOrHeld()
	{
		//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)
		try
		{
			if (PlayerInput.Controls == null)
			{
				return false;
			}
			PlayerActions player = PlayerInput.Controls.Player;
			InputAction fire = ((PlayerActions)(ref player)).Fire;
			return fire.IsPressed() || fire.WasPressedThisFrame();
		}
		catch
		{
			return false;
		}
	}
}
[HarmonyPatch(typeof(Gun), "GetReloadDuration")]
internal static class BallisticSniperReloadDurationHook
{
	[HarmonyPostfix]
	private static void Postfix(Gun __instance, ref float __result)
	{
		try
		{
			if (!((Object)(object)__instance == (Object)null) && BallisticSniperBehaviour.TryGet((IGear)(object)__instance, out var behaviour) && behaviour.SingleRoundReload)
			{
				int num = Mathf.Max(__instance.GunData.magazineSize, 1);
				__result = Mathf.Max(__result / (float)num, 0.12f);
			}
		}
		catch (Exception ex)
		{
			ManualLogSource logger = SparrohPlugin.Logger;
			if (logger != null)
			{
				logger.LogDebug((object)("[BallisticSniper] GetReloadDuration: " + ex.Message));
			}
		}
	}
}
[HarmonyPatch(typeof(Gun), "GetSpread")]
internal static class BallisticSniperSpreadHook
{
	private const float AdsSpread = 0.04f;

	private const float AdsMovingSpread = 1.15f;

	[HarmonyPostfix]
	private static void Postfix(Gun __instance, ref Vector2 __result)
	{
		//IL_0026: 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_008c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0091: 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)
		//IL_00c1: 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)
		try
		{
			if ((Object)(object)__instance == (Object)null || !BallisticSniperBehaviour.TryGet((IGear)(object)__instance, out var behaviour))
			{
				return;
			}
			if (behaviour.IsDeadboltActive())
			{
				__result = Vector2.zero;
				return;
			}
			bool isAiming = __instance.IsAiming;
			if (behaviour.WeaponData.autoTrigger && isAiming)
			{
				float autoTriggerAdsSpread = behaviour.WeaponData.autoTriggerAdsSpread;
				float num = Random.Range(0f, MathF.PI * 2f);
				float num2 = Random.Range(0f, autoTriggerAdsSpread);
				__result = new Vector2(Mathf.Cos(num) * num2, Mathf.Sin(num) * num2);
			}
			else if (isAiming)
			{
				float num3 = (IsMovingWhileScoped(__instance) ? 1.15f : 0.04f);
				if (num3 <= 0.001f)
				{
					__result = Vector2.zero;
					return;
				}
				float num4 = Random.Range(0f, MathF.PI * 2f);
				float num5 = Random.Range(0f, num3);
				__result = new Vector2(Mathf.Cos(num4) * num5, Mathf.Sin(num4) * num5);
			}
		}
		catch (Exception ex)
		{
			ManualLogSource logger = SparrohPlugin.Logger;
			if (logger != null)
			{
				logger.LogDebug((object)("[BallisticSniper] GetSpread: " + ex.Message));
			}
		}
	}

	private static bool IsMovingWhileScoped(Gun gun)
	{
		//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)
		try
		{
			Player player = gun.Player;
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			if (player.IsSprinting || player.Sliding)
			{
				return true;
			}
			try
			{
				Vector3 velocity = player.Velocity;
				velocity.y = 0f;
				if (((Vector3)(ref velocity)).sqrMagnitude > 0.122499995f)
				{
					return true;
				}
			}
			catch
			{
			}
		}
		catch
		{
		}
		return false;
	}
}
[Serializable]
public class MatchGradeRoundsProperty : UpgradeProperty
{
	public Range<float> damageBonus = new Range<float>(0.12f, 0.22f);

	public override IEnumerator<StatData> GetStatData(Random rand, IUpgradable gear, UpgradeInstance upgrade)
	{
		//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)
		yield return StatData.Create("Damage:", damageBonus, upgrade, ref rand, (OverrideType)2, (LabelType)1, default(BoostParams), (Range<float>?)null);
	}

	public override void Apply(IGear gear, UpgradeInstance upgrade, ref Random rand)
	{
		//IL_0015: 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)
		IWeapon val = (IWeapon)(object)((gear is IWeapon) ? gear : null);
		if (val != null)
		{
			float value = damageBonus.GetValue(ref rand, upgrade, default(BoostParams));
			val.GunData.damage *= 1f + value;
		}
	}

	public override void Remove(IGear gear, IGear prefab, UpgradeInstance upgrade, ref Random rand)
	{
		IWeapon val = (IWeapon)(object)((gear is IWeapon) ? gear : null);
		if (val != null)
		{
			IWeapon val2 = (IWeapon)(object)((prefab is IWeapon) ? prefab : null);
			if (val2 != null)
			{
				val.GunData.damage = val2.GunData.damage;
			}
		}
	}
}
[BepInPlugin("sparroh.antimaterialrifle", "AntiMaterialRifle", "1.2.0")]
[MycoMod(/*Could not decode attribute arguments.*/)]
public class SparrohPlugin : BaseUnityPlugin
{
	public const string PluginGUID = "sparroh.antimaterialrifle";

	public const string PluginName = "AntiMaterialRifle";

	public const string PluginVersion = "1.2.0";

	public const int GearId = 87421;

	public const string GearApiName = "ballistic_sniper";

	public const string GearDisplayName = "Anti-Material Rifle";

	public const string GearDescription = "Heavy kinetic bolt-action rifle. High single-shot damage, low capacity, and a deliberate single-round reload. Built for long-range elimination.";

	public const string BaseTypeName = "CartridgeSMG";

	internal static ManualLogSource Logger;

	internal static SparrohPlugin Instance;

	internal static ConfigEntry<bool> GrantAllUpgrades;

	public static IUpgradable CustomWeaponPrefab;

	private Harmony _harmony;

	private bool _gearRegistered;

	private void Awake()
	{
		//IL_0037: Unknown result type (might be due to invalid IL or missing references)
		//IL_0041: Expected O, but got Unknown
		Instance = this;
		Logger = ((BaseUnityPlugin)this).Logger;
		GrantAllUpgrades = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "GrantAllUpgrades", true, "Grant one unlocked inventory instance of each Anti-Material Rifle upgrade on load. Idempotent (tops up to 1). Disable before shipping if players should earn drops normally.");
		_harmony = new Harmony("sparroh.antimaterialrifle");
		_harmony.PatchAll(typeof(GlobalLoadHook));
		_harmony.PatchAll(typeof(PlayerDataPersistenceHooks));
		_harmony.PatchAll(typeof(BallisticSniperReloadHook));
		_harmony.PatchAll(typeof(BallisticSniperReloadInterruptHook));
		_harmony.PatchAll(typeof(BallisticSniperReloadDurationHook));
		_harmony.PatchAll(typeof(BallisticSniperSpreadHook));
		_harmony.PatchAll(typeof(GearSelectionWindowHooks));
		PlayerData.AddRegisterUpgradesCallback((Action)RegisterUpgrades);
		BallisticSniperCombatHooks.Apply(_harmony);
		SpawnGearHooks.Apply(_harmony);
		TryRegisterGear("Awake");
		if (PlayerData.Instance != null)
		{
			RegisterUpgrades();
		}
		Logger.LogInfo((object)"AntiMaterialRifle v1.2.0 loaded.");
	}

	private void OnDestroy()
	{
		Harmony harmony = _harmony;
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
		_harmony = null;
		Instance = null;
	}

	internal void TryRegisterGear(string reason)
	{
		if (_gearRegistered)
		{
			return;
		}
		if ((Object)(object)Global.Instance == (Object)null || Global.Instance.AllGear == null || Global.Instance.AllGear.Length == 0)
		{
			Logger.LogDebug((object)("[BallisticSniper] Global.AllGear not ready yet (" + reason + ")."));
			return;
		}
		try
		{
			if (WeaponRegistration.TryCreateAndRegister("sparroh.antimaterialrifle", 87421, "ballistic_sniper", "Anti-Material Rifle", "Heavy kinetic bolt-action rifle. High single-shot damage, low capacity, and a deliberate single-round reload. Built for long-range elimination.", "CartridgeSMG", autoUnlock: true, Logger, out CustomWeaponPrefab))
			{
				_gearRegistered = true;
				Logger.LogInfo((object)("[BallisticSniper] Registered gear 'Anti-Material Rifle' " + string.Format("(api={0}, id={1}) via {2}.", "ballistic_sniper", 87421, reason)));
				if (PlayerData.Instance != null)
				{
					RegisterUpgrades();
				}
			}
		}
		catch (Exception arg)
		{
			Logger.LogError((object)$"[BallisticSniper] Gear registration failed: {arg}");
		}
	}

	internal void RegisterUpgrades()
	{
		try
		{
			if (PlayerData.Instance == null)
			{
				Logger.LogDebug((object)"[BallisticSniper] Deferring upgrades — PlayerData.Instance null.");
				return;
			}
			IUpgradable val = ResolveRegisteredGear();
			if (val == null)
			{
				Logger.LogDebug((object)"[BallisticSniper] Deferring upgrades until gear is registered.");
				return;
			}
			WeaponRegistration.EnsureGearData(val, autoUnlock: true, Logger);
			if (PlayerData.GetGearData(val) == null && PlayerData.GetGearData(val.Info.ID) == null)
			{
				Logger.LogDebug((object)"[BallisticSniper] Deferring upgrades — GearData not bound yet.");
				return;
			}
			CustomWeaponPrefab = val;
			SniperUpgrades.RegisterAll(Logger);
		}
		catch (Exception arg)
		{
			Logger.LogError((object)$"[BallisticSniper] Upgrade registration failed: {arg}");
		}
	}

	internal static IUpgradable ResolveRegisteredGear()
	{
		if (CustomWeaponPrefab != null)
		{
			return CustomWeaponPrefab;
		}
		if (WeaponRegistration.CatalogGear != null)
		{
			return WeaponRegistration.CatalogGear;
		}
		return WeaponRegistration.FindGearSafe("ballistic_sniper", 87421);
	}
}
[HarmonyPatch(typeof(Global), "LoadInstance")]
internal static class GlobalLoadHook
{
	[HarmonyPostfix]
	private static void Postfix()
	{
		SparrohPlugin.Instance?.TryRegisterGear("Global.LoadInstance");
	}
}
[HarmonyPatch(typeof(PlayerData), "OnAwake")]
internal static class PlayerDataPersistenceHooks
{
	[HarmonyPrefix]
	private static void Prefix()
	{
		SparrohPlugin.Instance?.TryRegisterGear("PlayerData.OnAwake.Prefix");
	}

	[HarmonyPostfix]
	private static void Postfix()
	{
		try
		{
			SparrohPlugin.Instance?.TryRegisterGear("PlayerData.OnAwake.Postfix");
			SparrohPlugin.Instance?.RegisterUpgrades();
			IUpgradable val = SparrohPlugin.ResolveRegisteredGear();
			if (val == null)
			{
				ManualLogSource logger = SparrohPlugin.Logger;
				if (logger != null)
				{
					logger.LogWarning((object)"[BallisticSniper] Persistence: gear missing after OnAwake.");
				}
				return;
			}
			GearData gearData = PlayerData.GetGearData(val);
			if (gearData == null)
			{
				WeaponRegistration.EnsureGearData(val, autoUnlock: true, SparrohPlugin.Logger);
				gearData = PlayerData.GetGearData(val);
			}
			else
			{
				gearData.Gear = val;
			}
			if (gearData != null)
			{
				if (!gearData.IsUnlocked)
				{
					gearData.Unlock();
				}
				ManualLogSource logger2 = SparrohPlugin.Logger;
				if (logger2 != null)
				{
					string text = $"[BallisticSniper] Persistence OK: level={gearData.Level} unlocked={gearData.IsUnlocked} ";
					string text2 = $"equipped={gearData.EquippedUpgradeCount} xp={gearData.LevelXP} ";
					object arg = PlayerData.HasUpgrades(val);
					GearInfo info = val.Info;
					logger2.LogInfo((object)(text + text2 + $"HasUpgrades={arg} HasGrid={((info != null) ? new bool?(info.HasUpgradeGrid) : ((bool?)null))}."));
				}
			}
			SniperUpgrades.GrantAllInstances(SparrohPlugin.Logger);
		}
		catch (Exception arg2)
		{
			ManualLogSource logger3 = SparrohPlugin.Logger;
			if (logger3 != null)
			{
				logger3.LogError((object)$"[BallisticSniper] Persistence postfix failed: {arg2}");
			}
		}
	}
}
public static class SniperUpgradePatterns
{
	public static HexMap Small()
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Expected O, but got Unknown
		//IL_0011: 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_0026: Expected O, but got Unknown
		//IL_0027: Expected O, but got Unknown
		HexMap val = new HexMap(2, 2);
		Set(val, 0, 0, enabled: true, 4);
		Set(val, 1, 0, enabled: true, 16);
		Set(val, 0, 1, enabled: true, 1);
		return val;
	}

	public static HexMap Line()
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Expected O, but got Unknown
		//IL_0011: 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_0027: Expected O, but got Unknown
		//IL_0028: Expected O, but got Unknown
		HexMap val = new HexMap(3, 1);
		Set(val, 0, 0, enabled: true, 4);
		Set(val, 1, 0, enabled: true, 20);
		Set(val, 2, 0, enabled: true, 16);
		return val;
	}

	public static HexMap Medium()
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Expected O, but got Unknown
		//IL_0011: 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_0026: Expected O, but got Unknown
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Expected O, but got Unknown
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_003a: Expected O, but got Unknown
		//IL_003b: Expected O, but got Unknown
		HexMap val = new HexMap(3, 2);
		Set(val, 0, 0, enabled: true, 8);
		Set(val, 1, 0, enabled: true, 16);
		Set(val, 2, 0, enabled: true, 0);
		Set(val, 0, 1, enabled: true, 4);
		Set(val, 1, 1, enabled: true, 2);
		return val;
	}

	public static HexMap Large()
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Expected O, but got Unknown
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Expected O, but got Unknown
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: Expected O, but got Unknown
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_002f: Expected O, but got Unknown
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Expected O, but got Unknown
		//IL_003a: Expected O, but got Unknown
		HexMap val = new HexMap(3, 3);
		Set(val, 1, 0, enabled: true, 4);
		Set(val, 0, 1, enabled: true, 2);
		Set(val, 1, 1, enabled: true, 0);
		Set(val, 2, 1, enabled: true, 0);
		Set(val, 1, 2, enabled: true, 1);
		return val;
	}

	public static HexMap Wide()
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Expected O, but got Unknown
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Expected O, but got Unknown
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Expected O, but got Unknown
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Expected O, but got Unknown
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_003a: Expected O, but got Unknown
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0044: Expected O, but got Unknown
		//IL_0045: Expected O, but got Unknown
		HexMap val = new HexMap(4, 2);
		Set(val, 0, 0, enabled: true, 4);
		Set(val, 1, 0, enabled: true, 8);
		Set(val, 2, 0, enabled: true, 16);
		Set(val, 1, 1, enabled: true, 1);
		Set(val, 2, 1, enabled: true, 1);
		Set(val, 3, 1, enabled: true, 0);
		return val;
	}

	public static HexMap Exotic()
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Expected O, but got Unknown
		//IL_0011: 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_0026: Expected O, but got Unknown
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Expected O, but got Unknown
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_003a: Expected O, but got Unknown
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0044: Expected O, but got Unknown
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: Expected O, but got Unknown
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Expected O, but got Unknown
		//IL_0059: Expected O, but got Unknown
		HexMap val = new HexMap(4, 3);
		Set(val, 1, 0, enabled: true, 4);
		Set(val, 2, 0, enabled: true, 16);
		Set(val, 0, 1, enabled: true, 2);
		Set(val, 1, 1, enabled: true, 0);
		Set(val, 2, 1, enabled: true, 0);
		Set(val, 3, 1, enabled: true, 0);
		Set(val, 1, 2, enabled: true, 1);
		Set(val, 2, 2, enabled: true, 1);
		return val;
	}

	public static HexMap BoundaryIncursion()
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Expected O, but got Unknown
		//IL_0012: Expected O, but got Unknown
		HexMap val = new HexMap(1, 1);
		Set(val, 0, 0, enabled: true, 0);
		return val;
	}

	private static void Set(HexMap map, int x, int y, bool enabled, byte connections)
	{
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		ref Node reference = ref map[x, y];
		reference.enabled = enabled;
		reference.connections = (Direction)connections;
	}
}
[Serializable]
public class SniperProp_Hullbreaker : UpgradeProperty
{
	public Range<float> mult = new Range<float>(1.9f, 2.3f);

	public override IEnumerator<StatData> GetStatData(Random rand, IUpgradable gear, UpgradeInstance upgrade)
	{
		//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)
		yield return StatData.Create("Shell damage:", mult, upgrade, ref rand, (OverrideType)2, (LabelType)1, default(BoostParams), (Range<float>?)null);
	}

	public override void Apply(IGear gear, UpgradeInstance upgrade, ref Random rand)
	{
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		if (BallisticSniperBehaviour.TryGet(gear, out var behaviour))
		{
			behaviour.WeaponData.shellDamageMult *= mult.GetValue(ref rand, upgrade, default(BoostParams));
		}
	}

	public override void Remove(IGear gear, IGear prefab, UpgradeInstance upgrade, ref Random rand)
	{
		if (BallisticSniperBehaviour.TryGet(gear, out var behaviour))
		{
			behaviour.WeaponData.shellDamageMult = behaviour.GetPrefabSnapshot().shellDamageMult;
		}
	}
}
[Serializable]
public class SniperProp_Subsonic : UpgradeProperty
{
	public Range<float> mult = new Range<float>(2.1f, 2.6f);

	public override IEnumerator<StatData> GetStatData(Random rand, IUpgradable gear, UpgradeInstance upgrade)
	{
		//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)
		yield return StatData.Create("Full-HP damage:", mult, upgrade, ref rand, (OverrideType)2, (LabelType)1, default(BoostParams), (Range<float>?)null);
	}

	public override void Apply(IGear gear, UpgradeInstance upgrade, ref Random rand)
	{
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		if (BallisticSniperBehaviour.TryGet(gear, out var behaviour))
		{
			behaviour.WeaponData.fullHpDamageMult *= mult.GetValue(ref rand, upgrade, default(BoostParams));
		}
	}

	public override void Remove(IGear gear, IGear prefab, UpgradeInstance upgrade, ref Random rand)
	{
		if (BallisticSniperBehaviour.TryGet(gear, out var behaviour))
		{
			behaviour.WeaponData.fullHpDamageMult = behaviour.GetPrefabSnapshot().fullHpDamageMult;
		}
	}
}
[Serializable]
public class SniperProp_RicochetProtocol : UpgradeProperty
{
	public Rang