Decompiled source of Survivors v0.6.5

plugins/MuckSurvivors.dll

Decompiled 9 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("MuckSurvivors")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Pick a Risk of Rain 2 survivor in the Muck lobby.")]
[assembly: AssemblyFileVersion("0.6.5.0")]
[assembly: AssemblyInformationalVersion("0.6.5+1911ecd07d605294f346388086fc942f4c18d4fa")]
[assembly: AssemblyProduct("Survivors")]
[assembly: AssemblyTitle("MuckSurvivors")]
[assembly: AssemblyVersion("0.6.5.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class IsReadOnlyAttribute : Attribute
	{
	}
}
namespace MuckSurvivors
{
	internal sealed class AbilityController : MonoBehaviour
	{
		private readonly struct MobTarget
		{
			public readonly HitableMob Mob;

			public readonly Collider Collider;

			public MobTarget(HitableMob mob, Collider collider)
			{
				Mob = mob;
				Collider = collider;
			}
		}

		private readonly struct PhaseTarget
		{
			public readonly HitableMob Mob;

			public readonly Vector3 Point;

			public PhaseTarget(HitableMob mob, Vector3 point)
			{
				//IL_0008: 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)
				Mob = mob;
				Point = point;
			}
		}

		private readonly struct Strike
		{
			public readonly int BaseDamage;

			public readonly float Strength;

			public readonly List<MobTarget> Targets;

			private Strike(int baseDamage, float strength, List<MobTarget> targets)
			{
				BaseDamage = baseDamage;
				Strength = strength;
				Targets = targets;
			}

			public static Strike Take()
			{
				return new Strike(KitDamageReader.Read(), PowerupInventory.Instance.GetStrengthMultiplier((int[])null), SnapshotMobs());
			}
		}

		private const float DashExitSpeed = 6.5f;

		private const float DoubleTapRadius = 0.15f;

		private const float MuzzleSpread = 0.18f;

		private const float CanisterCastRadius = 0.3f;

		private readonly Cooldown doubleTap = new Cooldown();

		private readonly HudRow doubleTapRow = new HudRow();

		private bool rightMuzzle;

		private readonly Cooldown dive = new Cooldown();

		private readonly Cooldown phase = new Cooldown();

		private readonly Cooldown assault = new Cooldown();

		private readonly Cooldown parryCd = new Cooldown();

		private readonly ParryWindow parry = new ParryWindow();

		private float parryCooldown;

		private readonly Cooldown transport = new Cooldown();

		private readonly Cooldown canister = new Cooldown();

		private readonly HitLedger ledger = new HitLedger();

		private readonly HitLedger canisterLedger = new HitLedger();

		private readonly HudRow diveRow = new HudRow();

		private readonly HudRow phaseRow = new HudRow();

		private readonly HudRow assaultRow = new HudRow();

		private readonly HudRow parryRow = new HudRow();

		private readonly HudRow transportRow = new HudRow();

		private readonly HudRow canisterRow = new HudRow();

		private readonly List<HudRow> hud = new List<HudRow>();

		private DashChain chain;

		private bool busy;

		private static SurvivorTuning T => SurvivorState.Tuning;

		private static WeaponTuning W => SurvivorState.Tuning.Weapons;

		private static SurvivorsConfig Cfg => SurvivorsPlugin.Cfg;

		private void Awake()
		{
			chain = NewChain();
		}

		public void StopAll()
		{
			((MonoBehaviour)this).StopAllCoroutines();
			doubleTap.Reset();
			dive.Reset();
			phase.Reset();
			assault.Reset();
			parryCd.Reset();
			parry.Reset();
			parryCooldown = 0f;
			SurvivorState.ParryActive = false;
			ParryPose.Clear();
			ParryFov.Release();
			ParryCue.Reset();
			transport.Reset();
			canister.Reset();
			ledger.Reset();
			canisterLedger.Reset();
			chain = NewChain();
			busy = false;
		}

		private static DashChain NewChain()
		{
			return new DashChain(T.AssaultMaxDashes, T.AssaultRedashWindow);
		}

		private static bool PlayerAlive()
		{
			if ((Object)(object)PlayerStatus.Instance != (Object)null && (Object)(object)PlayerMovement.Instance != (Object)null)
			{
				return !PlayerStatus.Instance.IsPlayerDead();
			}
			return false;
		}

		private void Update()
		{
			if (!SurvivorState.Active)
			{
				return;
			}
			try
			{
				float time = Time.time;
				SurvivorId locked = SurvivorState.Session.Locked;
				if (locked == SurvivorId.Mercenary && (Object)(object)PlayerStatus.Instance != (Object)null && PlayerStatus.Instance.IsPlayerDead())
				{
					EndParryOnDeath(time);
				}
				if (!PlayerAlive())
				{
					return;
				}
				if (locked == SurvivorId.Mercenary)
				{
					if (chain.Tick(time))
					{
						assault.Start(time, AssaultCooldownNow());
					}
					if (parry.Tick(time))
					{
						FinishParry(time);
					}
				}
				if (!InputBlocked())
				{
					switch (locked)
					{
					case SurvivorId.Commando:
						UpdateCommando(time);
						break;
					case SurvivorId.Mercenary:
						UpdateMercenary(time);
						break;
					case SurvivorId.MulT:
						UpdateMulT(time);
						break;
					}
				}
			}
			catch (Exception arg)
			{
				SurvivorsPlugin.LogOnce("AbilityController", $"Survivors ability update failed: {arg}");
			}
		}

		internal static bool InputBlocked()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Invalid comparison between Unknown and I4
			if ((int)GameManager.state == 1)
			{
				if ((Object)(object)OtherInput.Instance != (Object)null)
				{
					if (!OtherInput.Instance.OtherUiActive())
					{
						return OtherInput.Instance.IsAnyMenuOpen();
					}
					return true;
				}
				return false;
			}
			return true;
		}

		private void UpdateCommando(float now)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			if (Keys.PrimaryHeld(SurvivorId.Commando) && doubleTap.Ready(now))
			{
				FireDoubleTap(now);
			}
			if (Keys.Use(SkillKeys.Utility, "Tactical Dive", dive, now, busy))
			{
				dive.Start(now, T.DiveCooldown);
				SurvivorsPlugin.Verbose("Tactical Dive");
				((MonoBehaviour)this).StartCoroutine(Dash(DashStyle.Dive, T.DiveSpeed, T.DiveDuration, 0f, 0f, null));
			}
			if (Keys.Use(SkillKeys.Secondary, "Phase Round", phase, now))
			{
				phase.Start(now, T.PhaseCooldown);
				SurvivorsPlugin.Verbose("Phase Round");
				FirePhaseRound();
			}
			Keys.LogUnused("Commando", hasSecondary: true);
		}

		private void UpdateMercenary(float now)
		{
			Keys.LogUnused("Mercenary", hasSecondary: true);
			TryAssault(now);
			TryParry(now);
		}

		private void TryAssault(float now)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			if (Keys.Pressed(SkillKeys.Utility))
			{
				DashDecision dashDecision = ((!busy) ? chain.OnKeyPressed(now, assault.Ready(now)) : DashDecision.None);
				if (dashDecision == DashDecision.None)
				{
					SurvivorsPlugin.Verbose(SkillLog.Refusal("Blinding Assault", busy, assault.Remaining(now)) ?? SkillLog.NotCast("Blinding Assault", "the re-dash window has closed or the chain is spent"));
					return;
				}
				float moveMultiplier = MoveSpeed();
				float duration = MercenaryMath.DashDuration(T.AssaultDuration, moveMultiplier);
				string[] obj = new string[9]
				{
					"Blinding Assault ",
					dashDecision.ToString(),
					" (dash ",
					chain.DashesUsed.ToString(),
					", ",
					null,
					null,
					null,
					null
				};
				float assaultSpeed = T.AssaultSpeed;
				obj[5] = assaultSpeed.ToString("0.0", CultureInfo.InvariantCulture);
				obj[6] = " m/s for ";
				obj[7] = duration.ToString("0.00", CultureInfo.InvariantCulture);
				obj[8] = " s)";
				SurvivorsPlugin.Verbose(string.Concat(obj));
				((MonoBehaviour)this).StartCoroutine(Dash(DashStyle.Assault, T.AssaultSpeed, duration, T.AssaultDamage, T.AssaultHitRadius, OnAssaultEnded));
			}
		}

		private void OnAssaultEnded(bool hit)
		{
			if (chain.OnDashEnded(hit, Time.time))
			{
				assault.Start(Time.time, AssaultCooldownNow());
			}
		}

		private static float AssaultCooldownNow()
		{
			return MercenaryMath.Cooldown(T.AssaultCooldown, AttackSpeed());
		}

		private void TryParry(float now)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			if (!Keys.Pressed(SkillKeys.Secondary))
			{
				return;
			}
			if (parry.Active)
			{
				SurvivorsPlugin.Verbose(SkillLog.NotCast("Parry", "already up"));
				return;
			}
			if (!parryCd.Ready(now))
			{
				SurvivorsPlugin.Verbose(SkillLog.Refusal("Parry", busy: false, parryCd.Remaining(now)));
				return;
			}
			float attackSpeed = AttackSpeed();
			float num = MercenaryMath.ParryDuration(T.ParryDuration, attackSpeed);
			if (!parry.TryStart(now, num))
			{
				SurvivorsPlugin.Verbose(SkillLog.NotCast("Parry", "its duration is not positive"));
				return;
			}
			parryCooldown = MercenaryMath.Cooldown(T.ParryCooldown, attackSpeed);
			SurvivorState.ParryActive = true;
			ParryPose.Copy(parry);
			SurvivorsPlugin.Verbose("Parry");
			SurvivorsPlugin.Verbose("Parry: up for " + num.ToString("0.00", CultureInfo.InvariantCulture) + " s");
			ParryCue.Cast();
			float num2 = MercenaryMath.WireSeconds(num);
			if (num2 > 0f)
			{
				SkillFxNet.Send(SkillFxEvent.Parry(num2));
			}
		}

		private void EndParryOnDeath(float now)
		{
			if (parry.End(now))
			{
				FinishParry(now);
			}
		}

		private void FinishParry(float now)
		{
			SurvivorState.ParryActive = false;
			parryCd.Start(now, parryCooldown);
			ParryPose.Copy(parry);
		}

		private void UpdateMulT(float now)
		{
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			Keys.LogUnused("MUL-T", T.BlastCanister.Enabled);
			if (T.BlastCanister.Enabled && Keys.Use(SkillKeys.Secondary, "Blast Canister", canister, now))
			{
				canister.Start(now, T.BlastCanister.Cooldown);
				SurvivorsPlugin.Verbose("Blast Canister");
				((MonoBehaviour)this).StartCoroutine(BlastCanister());
			}
			if (Keys.Use(SkillKeys.Utility, "Transport Mode", transport, now, busy))
			{
				SurvivorsPlugin.Verbose("Transport Mode");
				((MonoBehaviour)this).StartCoroutine(Transport());
			}
		}

		private IEnumerator Dash(DashStyle style, float speed, float duration, float coefficient, float radius, Action<bool> onEnd)
		{
			busy = true;
			SurvivorState.DashActive = true;
			ledger.Reset();
			int hits = 0;
			Color color = ((style == DashStyle.Dive) ? FxColors.Commando : FxColors.Mercenary);
			if (TryBeginDash(coefficient, out var dir, out var exitSpeed, out var strike))
			{
				Vector3 start = LocalFx.Feet();
				SkillFx.SpeedLines(dir, color, duration);
				float end = Time.time + duration;
				while (Time.time < end && DashStep(dir, speed, coefficient, radius, strike, color, ref hits))
				{
					yield return (object)new WaitForFixedUpdate();
				}
				ExitDash(dir, exitSpeed);
				DashEnded(style, start, dir, hits > 0);
			}
			if (style == DashStyle.Assault)
			{
				SurvivorsPlugin.Verbose(SkillLog.Outcome($"Blinding Assault dash {chain.DashesUsed}", hits));
			}
			SurvivorState.DashActive = false;
			busy = false;
			onEnd?.Invoke(hits > 0);
		}

		private static void DashEnded(DashStyle style, Vector3 start, Vector3 dir, bool hit)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: 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_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_0047: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Vector3 val = LocalFx.Feet();
				LocalFx.Cast(SkillFxEvent.Dash(style, SkillFxPlayer.V(start), SkillFxPlayer.V(val)));
				if (style == DashStyle.Assault && hit)
				{
					SkillFx.Arc(val + Vector3.up, dir, Vector3.Cross(Vector3.up, dir), 2f, 140f, FxColors.Mercenary);
				}
			}
			catch (Exception arg)
			{
				SurvivorsPlugin.LogOnce("dash-fx", $"Survivors: drawing the dash failed: {arg}");
			}
		}

		private static bool TryBeginDash(float coefficient, out Vector3 dir, out float exitSpeed, out Strike strike)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			dir = Vector3.zero;
			exitSpeed = 6.5f;
			strike = default(Strike);
			try
			{
				PlayerMovement instance = PlayerMovement.Instance;
				dir = instance.orientation.forward;
				dir.y = 0f;
				if (((Vector3)(ref dir)).sqrMagnitude < 0.0001f)
				{
					dir = ((Component)instance).transform.forward;
				}
				((Vector3)(ref dir)).Normalize();
				Rigidbody rb = instance.GetRb();
				if ((Object)(object)rb != (Object)null)
				{
					Vector3 val = new Vector3(rb.velocity.x, 0f, rb.velocity.z);
					exitSpeed = Mathf.Max(6.5f, ((Vector3)(ref val)).magnitude);
				}
				if (coefficient > 0f)
				{
					strike = Strike.Take();
				}
				return true;
			}
			catch (Exception arg)
			{
				SurvivorsPlugin.LogOnce("dash-begin", $"Survivors dash failed to start: {arg}");
				return false;
			}
		}

		private bool DashStep(Vector3 dir, float speed, float coefficient, float radius, Strike strike, Color color, ref int hits)
		{
			//IL_002e: 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_0035: 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_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!PlayerAlive())
				{
					return false;
				}
				Rigidbody rb = PlayerMovement.Instance.GetRb();
				if ((Object)(object)rb == (Object)null)
				{
					return false;
				}
				rb.isKinematic = false;
				rb.velocity = dir * speed + Vector3.up * rb.velocity.y;
				if (coefficient > 0f)
				{
					hits += SweepHits(strike, ((Component)PlayerMovement.Instance).transform.position, radius, coefficient, color, ledger);
				}
				return true;
			}
			catch (Exception arg)
			{
				SurvivorsPlugin.LogOnce("dash-step", $"Survivors dash step failed: {arg}");
				return false;
			}
		}

		private static void ExitDash(Vector3 dir, float exitSpeed)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!((Object)(object)PlayerMovement.Instance == (Object)null))
				{
					Rigidbody rb = PlayerMovement.Instance.GetRb();
					if (!((Object)(object)rb == (Object)null))
					{
						rb.velocity = dir * exitSpeed + Vector3.up * rb.velocity.y;
					}
				}
			}
			catch (Exception arg)
			{
				SurvivorsPlugin.LogOnce("dash-exit", $"Survivors dash exit failed: {arg}");
			}
		}

		private IEnumerator Transport()
		{
			busy = true;
			SurvivorState.TransportActive = true;
			ledger.Reset();
			int hits = 0;
			if (TryBeginTransport(out var strike))
			{
				LocalFx.Cast(SkillFxEvent.Transport(T.TransportDuration));
				SkillFx.SpeedLines(PlayerMovement.Instance.orientation.forward, FxColors.MulT, T.TransportDuration);
				float end = Time.time + T.TransportDuration;
				while (Time.time < end && TransportStep(strike, ref hits))
				{
					yield return (object)new WaitForFixedUpdate();
				}
			}
			SurvivorsPlugin.Verbose(SkillLog.Outcome("Transport Mode", hits));
			SurvivorState.TransportActive = false;
			busy = false;
			transport.Start(Time.time, T.TransportCooldown);
		}

		private static bool TryBeginTransport(out Strike strike)
		{
			strike = default(Strike);
			try
			{
				strike = Strike.Take();
				return true;
			}
			catch (Exception arg)
			{
				SurvivorsPlugin.LogOnce("transport-begin", $"Survivors Transport Mode failed to start: {arg}");
				return false;
			}
		}

		private bool TransportStep(Strike strike, ref int hits)
		{
			//IL_0019: 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)
			try
			{
				if (!PlayerAlive())
				{
					return false;
				}
				hits += SweepHits(strike, ((Component)PlayerMovement.Instance).transform.position, T.TransportHitRadius, T.TransportDamage, FxColors.MulT, ledger);
				return true;
			}
			catch (Exception arg)
			{
				SurvivorsPlugin.LogOnce("transport-step", $"Survivors Transport Mode step failed: {arg}");
				return false;
			}
		}

		private IEnumerator BlastCanister()
		{
			int hits = 0;
			if (TryFireCanister(out var strike, out var burst, out var forward, ref hits))
			{
				float start = Time.time;
				for (int i = 0; i < T.BlastCanister.Bomblets; i++)
				{
					float due = start + T.BlastCanister.BombletTime(i);
					while (Time.time < due)
					{
						yield return null;
					}
					if (!Bomblet(strike, burst, forward, i, ref hits))
					{
						break;
					}
				}
			}
			SurvivorsPlugin.Verbose(SkillLog.Outcome("Blast Canister", hits));
		}

		private bool TryFireCanister(out Strike strike, out Vector3 burst, out Vector3 forward, ref int hits)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: 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_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: 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_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_00c2: 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_00f3: 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_0129: 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_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: 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_0191: Unknown result type (might be due to invalid IL or missing references)
			strike = default(Strike);
			burst = Vector3.zero;
			forward = Vector3.forward;
			try
			{
				if (!Hitscan.TryAim(out var origin, out var forward2))
				{
					return false;
				}
				strike = Strike.Take();
				HitscanShot hitscanShot = Hitscan.Cast(origin, forward2, T.BlastCanister.Range, 0.3f);
				burst = (hitscanShot.Hit ? hitscanShot.Point : (origin + forward2 * hitscanShot.Stop));
				forward = new Vector3(forward2.x, 0f, forward2.z);
				forward = ((((Vector3)(ref forward)).sqrMagnitude < 0.0001f) ? Vector3.forward : ((Vector3)(ref forward)).normalized);
				SkillFx.Segment(Tracer.Muzzle(origin, forward2), burst, FxColors.MulT, 0.12f, 0.15f);
				canisterLedger.Reset();
				hits += SweepHits(strike, burst, T.BlastCanister.Radius, T.BlastCanister.Damage, FxColors.MulT, canisterLedger);
				SkillFx.Flash(burst, FxColors.MulT, 1.2f, 0.12f);
				SkillFx.Ring(burst, 0.3f, T.BlastCanister.Radius, FxColors.MulT, 0.15f, 0.3f);
				SkillFx.Puffs(burst, 0.3f, T.BlastCanister.Radius * 0.6f, 5, FxColors.Smoke, 0.6f, 0.6f);
				return true;
			}
			catch (Exception arg)
			{
				SurvivorsPlugin.LogOnce("canister", $"Survivors Blast Canister failed: {arg}");
				return false;
			}
		}

		private bool Bomblet(Strike strike, Vector3 burst, Vector3 forward, int i, ref int hits)
		{
			//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_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_001e: 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_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_0030: 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_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: 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_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: 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)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Vec3 vec = T.BlastCanister.BombletOffset(i);
				Vector3 val = Vector3.Cross(Vector3.up, forward);
				Vector3 val2 = burst + val * vec.X + forward * vec.Z;
				RaycastHit val3 = default(RaycastHit);
				if (Physics.Raycast(val2 + Vector3.up * 2f, Vector3.down, ref val3, 8f, Layers.Walls, (QueryTriggerInteraction)1))
				{
					val2 = ((RaycastHit)(ref val3)).point + Vector3.up * 0.3f;
				}
				canisterLedger.Reset();
				hits += SweepHits(strike, val2, T.BlastCanister.BombletRadius, T.BlastCanister.BombletDamage, FxColors.MulT, canisterLedger);
				SkillFx.Flash(val2, FxColors.MulT, 0.6f, 0.1f);
				SkillFx.Ring(val2, 0.2f, T.BlastCanister.BombletRadius, FxColors.MulT, 0.1f, 0.25f);
				return true;
			}
			catch (Exception arg)
			{
				SurvivorsPlugin.LogOnce("canister-bomblet", $"Survivors Blast Canister bomblet failed: {arg}");
				return false;
			}
		}

		private static List<MobTarget> SnapshotMobs()
		{
			List<MobTarget> list = new List<MobTarget>();
			if ((Object)(object)MobManager.Instance == (Object)null || MobManager.Instance.mobs == null)
			{
				return list;
			}
			foreach (Mob value in MobManager.Instance.mobs.Values)
			{
				if (!((Object)(object)value == (Object)null))
				{
					HitableMob component = ((Component)value).GetComponent<HitableMob>();
					if (!((Object)(object)component == (Object)null) && ((Hitable)component).hp > 0)
					{
						list.Add(new MobTarget(component, ((Component)component).GetComponentInChildren<Collider>()));
					}
				}
			}
			return list;
		}

		private static int SweepHits(Strike strike, Vector3 center, float radius, float coefficient, Color spark, HitLedger claims)
		{
			//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_0064: 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_004e: 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)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: 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_00b6: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			foreach (MobTarget target in strike.Targets)
			{
				HitableMob mob = target.Mob;
				if ((Object)(object)mob == (Object)null || ((Hitable)mob).hp <= 0)
				{
					continue;
				}
				Vector3 val;
				if (!((Object)(object)target.Collider != (Object)null))
				{
					val = ((Component)mob).transform.position;
				}
				else
				{
					Bounds bounds = target.Collider.bounds;
					val = ((Bounds)(ref bounds)).ClosestPoint(center);
				}
				Vector3 val2 = val;
				Vector3 val3 = val2 - center;
				if (!(((Vector3)(ref val3)).sqrMagnitude > radius * radius) && claims.TryClaim(((Hitable)mob).GetId()))
				{
					int num2 = AbilityDamage.Hit(strike.BaseDamage, coefficient, strike.Strength);
					if (num2 > 0)
					{
						HitMob(mob, num2, val2);
						SkillFx.Spark(val2, spark);
						num++;
					}
				}
			}
			return num;
		}

		private static void FirePhaseRound()
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0253: 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_0255: Unknown result type (might be due to invalid IL or missing references)
			//IL_025a: Unknown result type (might be due to invalid IL or missing references)
			//IL_025d: Unknown result type (might be due to invalid IL or missing references)
			//IL_025e: Unknown result type (might be due to invalid IL or missing references)
			//IL_025f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0261: Unknown result type (might be due to invalid IL or missing references)
			//IL_0266: Unknown result type (might be due to invalid IL or missing references)
			//IL_0268: Unknown result type (might be due to invalid IL or missing references)
			//IL_0269: Unknown result type (might be due to invalid IL or missing references)
			//IL_0271: Unknown result type (might be due to invalid IL or missing references)
			//IL_0276: Unknown result type (might be due to invalid IL or missing references)
			//IL_027b: Unknown result type (might be due to invalid IL or missing references)
			//IL_027e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0285: Unknown result type (might be due to invalid IL or missing references)
			//IL_0292: Unknown result type (might be due to invalid IL or missing references)
			//IL_0299: Unknown result type (might be due to invalid IL or missing references)
			//IL_020b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0217: Unknown result type (might be due to invalid IL or missing references)
			//IL_021c: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_02be: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: 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)
			try
			{
				PlayerMovement instance = PlayerMovement.Instance;
				Vector3 position = instance.playerCam.position;
				Vector3 forward = instance.playerCam.forward;
				RaycastHit[] array = Physics.SphereCastAll(position, T.PhaseRadius, forward, T.PhaseRange, -1, (QueryTriggerInteraction)1);
				Transform root = ((Component)instance).transform.root;
				int mask = LayerMask.GetMask(new string[2] { "Ground", "Object" });
				List<PierceCandidate> list = new List<PierceCandidate>(array.Length);
				Dictionary<int, PhaseTarget> dictionary = new Dictionary<int, PhaseTarget>();
				RaycastHit[] array2 = array;
				for (int i = 0; i < array2.Length; i++)
				{
					RaycastHit val = array2[i];
					Transform root2 = ((Component)((RaycastHit)(ref val)).collider).transform.root;
					int instanceID = ((Object)root2).GetInstanceID();
					if ((Object)(object)root2 == (Object)(object)root)
					{
						list.Add(new PierceCandidate(instanceID, ((RaycastHit)(ref val)).distance, CastKind.Ignore));
						continue;
					}
					HitableMob component = ((Component)root2).GetComponent<HitableMob>();
					if ((Object)(object)component != (Object)null && ((Hitable)component).hp > 0)
					{
						if (!dictionary.ContainsKey(instanceID))
						{
							dictionary[instanceID] = new PhaseTarget(component, (((RaycastHit)(ref val)).distance > 0f) ? ((RaycastHit)(ref val)).point : ((Component)component).transform.position);
						}
						list.Add(new PierceCandidate(instanceID, ((RaycastHit)(ref val)).distance, CastKind.Mob));
					}
					else
					{
						bool flag = (mask & (1 << ((Component)((RaycastHit)(ref val)).collider).gameObject.layer)) != 0;
						list.Add(new PierceCandidate(instanceID, ((RaycastHit)(ref val)).distance, flag ? CastKind.Blocker : CastKind.Ignore));
					}
				}
				PiercePlan piercePlan = PierceOrder.Plan(list, T.PhaseMaxTargets, T.PhaseRange);
				int baseDamage = KitDamageReader.Read();
				float strengthMultiplier = PowerupInventory.Instance.GetStrengthMultiplier((int[])null);
				int num = 0;
				for (int j = 0; j < piercePlan.Keys.Count; j++)
				{
					PhaseTarget phaseTarget = dictionary[piercePlan.Keys[j]];
					int num2 = AbilityDamage.Pierce(baseDamage, T.PhaseDamage, T.PhasePierceBonus, j, strengthMultiplier);
					if (num2 > 0)
					{
						HitMob(phaseTarget.Mob, num2, phaseTarget.Point);
						SkillFx.Spark(phaseTarget.Point, Color.white);
						num++;
					}
				}
				SurvivorsPlugin.Verbose(SkillLog.Outcome("Phase Round", num));
				Vector3 val2 = Tracer.Muzzle(position, forward);
				Vector3 val3 = Tracer.HeldMuzzle(SurvivorId.Commando, position, forward, val2);
				Vector3 val4 = position + forward * piercePlan.StopDistance;
				LocalFx.Cast(SkillFxEvent.Shot(ShotStyle.PhaseRound, SkillFxPlayer.V(val3), SkillFxPlayer.V(val4)), SkillFxEvent.Shot(ShotStyle.PhaseRound, SkillFxPlayer.V(val2), SkillFxPlayer.V(val4)));
				if (Cfg.LogPhaseRound.Value)
				{
					LogPhaseLook(val3, val4);
				}
			}
			catch (Exception arg)
			{
				SurvivorsPlugin.LogOnce("phase-round", $"Survivors Phase Round failed: {arg}");
			}
		}

		private static void LogPhaseLook(Vector3 muzzle, Vector3 end)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			Camera main = Camera.main;
			if (!((Object)(object)main == (Object)null))
			{
				Vector3 position = ((Component)main).transform.position;
				float num = Mathf.Max(2f, Vector3.Distance(position, muzzle));
				Vector3 val = muzzle - position;
				Vector3 val2 = position + ((Vector3)(ref val)).normalized * num;
				float num2 = Vector3.Distance(position, end);
				float num3 = Vector3.Distance(val2, end);
				float fieldOfView = main.fieldOfView;
				int height = Screen.height;
				Vector3 val3 = main.WorldToScreenPoint(val2);
				SurvivorsPlugin.Log.LogInfo((object)string.Format(CultureInfo.InvariantCulture, "Survivors Phase Round fx: flash {0:0.00} m at {1:0.0} m = {2:0} px tall for {3:0.00} s; trail {4:0.00}->{5:0.00} m wide over {6:0.0} m, {7:0} px at its start ({1:0.0} m) and {8:0} px at the end ({9:0.0} m); starts {14:+0;-0} px right and {15:+0;-0} px up of screen centre; bolt flies {10:0.00} s, then the whole trail stays and fades out over {11:0.00} s (screen {12} px, FOV {13:0})", 0.45f, num, PhaseRoundLook.Pixels(0.45f, num, fieldOfView, height), 0.12f, 0.45f, 0.25f, num3, PhaseRoundLook.Pixels(0.45f, num, fieldOfView, height), PhaseRoundLook.Pixels(0.25f, num2, fieldOfView, height), num2, PhaseRoundLook.Travel(num3), 0.25f, height, fieldOfView, val3.x - (float)Screen.width / 2f, val3.y - (float)height / 2f));
			}
		}

		private void FireDoubleTap(float now)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: 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_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: 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_00aa: 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_011f: 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)
			if (!Hitscan.TryAim(out var origin, out var forward))
			{
				return;
			}
			doubleTap.Repeat(now, WeaponTuning.Interval(W.DoubleTapInterval, AttackSpeed()), Time.deltaTime);
			SurvivorsPlugin.Verbose("Double Tap");
			try
			{
				HitscanShot hitscanShot = Hitscan.Cast(origin, forward, W.DoubleTapRange, 0.15f);
				rightMuzzle = !rightMuzzle;
				Vector3 val = PlayerMovement.Instance.playerCam.right * (rightMuzzle ? 0.18f : (-0.18f));
				LocalFx.Cast(SkillFxEvent.Shot(ShotStyle.DoubleTap, SkillFxPlayer.V(Tracer.Muzzle(origin, forward) + val), SkillFxPlayer.V(origin + forward * hitscanShot.Stop)));
				Recoil();
				int num = (hitscanShot.Hit ? AbilityDamage.Hit(KitDamageReader.Read(), W.DoubleTapDamage, PowerupInventory.Instance.GetStrengthMultiplier((int[])null)) : 0);
				SurvivorsPlugin.Verbose(SkillLog.Outcome("Double Tap", (num > 0) ? 1 : 0));
				if (num > 0)
				{
					HitMob(hitscanShot.Mob, num, hitscanShot.Point);
					SkillFx.Spark(hitscanShot.Point, Cfg.TracerColor.Value);
				}
			}
			catch (Exception arg)
			{
				SurvivorsPlugin.LogOnce("double-tap", $"Survivors Double Tap failed: {arg}");
			}
		}

		internal static void Recoil()
		{
			try
			{
				UseInventory instance = UseInventory.Instance;
				if ((Object)(object)instance != (Object)null && (Object)(object)instance.animator != (Object)null)
				{
					instance.animator.Play("Shoot", -1, 0f);
				}
			}
			catch (Exception arg)
			{
				SurvivorsPlugin.LogOnce("recoil", $"Survivors: the gun recoil animation failed: {arg}");
			}
		}

		internal static float MoveSpeed()
		{
			try
			{
				return ((Object)(object)PowerupInventory.Instance != (Object)null) ? PowerupInventory.Instance.GetSpeedMultiplier((int[])null) : 1f;
			}
			catch (Exception arg)
			{
				SurvivorsPlugin.LogOnce("move-speed", $"Survivors: reading move speed failed; the dash uses its base length: {arg}");
				return 1f;
			}
		}

		internal static float AttackSpeed()
		{
			try
			{
				return ((Object)(object)PowerupInventory.Instance != (Object)null) ? PowerupInventory.Instance.GetAttackSpeedMultiplier((int[])null) : 1f;
			}
			catch (Exception arg)
			{
				SurvivorsPlugin.LogOnce("attack-speed", $"Survivors: reading attack speed failed; primaries fire at their base rate: {arg}");
				return 1f;
			}
		}

		private static void HitMob(HitableMob mob, int damage, Vector3 point)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			SurvivorHits.Deal(mob, damage, 0, point, HitSource.Skill);
			SurvivorsPlugin.Verbose($"Ability hit mob {((Hitable)mob).GetId()} for {damage}");
		}

		private void OnGUI()
		{
			DrawParryVignette();
			if (!SkillHud.Visible || !PlayerAlive())
			{
				return;
			}
			float time = Time.time;
			int version = SkillKeys.Version;
			hud.Clear();
			switch (SurvivorState.Session.Locked)
			{
			default:
				return;
			case SurvivorId.Commando:
				SkillHud.PrimaryRow(doubleTapRow, SurvivorId.Commando, "Double Tap: ", HudClock.Key(doubleTap, time));
				hud.Add(doubleTapRow);
				SkillHud.CooldownRow(diveRow, SkillKeys.UtilityLabel, "Tactical Dive: ", HudClock.Key(dive, time));
				SkillHud.CooldownRow(phaseRow, SkillKeys.SecondaryLabel, "Phase Round: ", HudClock.Key(phase, time));
				hud.Add(diveRow);
				hud.Add(phaseRow);
				break;
			case SurvivorId.Mercenary:
			{
				int num2 = HudClock.Key(assault, time);
				int num3 = (chain.InChain ? chain.DashesUsed : (-1));
				if (assaultRow.Stale(version, num2, num3, T.AssaultMaxDashes))
				{
					string text2 = SkillKeys.UtilityLabel + "Blinding Assault: ";
					int assaultMaxDashes = T.AssaultMaxDashes;
					string text3 = "/" + assaultMaxDashes + ")";
					HudRow hudRow = assaultRow;
					string text4 = text2 + HudClock.Status(num2) + ((num3 >= 0) ? (" (dash " + num3 + text3) : "");
					assaultMaxDashes = T.AssaultMaxDashes;
					hudRow.Set(text4, text2 + "88.8s (dash " + assaultMaxDashes + text3);
				}
				hud.Add(assaultRow);
				int num4 = (parry.Active ? (-2) : HudClock.Key(parryCd, time));
				if (parryRow.Stale(version, num4))
				{
					string text5 = SkillKeys.SecondaryLabel + "Parry: ";
					parryRow.Set(text5 + ((num4 == -2) ? "active" : HudClock.Status(num4)), text5 + "88.8s");
				}
				hud.Add(parryRow);
				break;
			}
			case SurvivorId.MulT:
			{
				int num = (SurvivorState.TransportActive ? (-2) : HudClock.Key(transport, time));
				if (transportRow.Stale(version, num))
				{
					string text = SkillKeys.UtilityLabel + "Transport Mode: ";
					transportRow.Set(text + ((num == -2) ? "active" : HudClock.Status(num)), text + "88.8s");
				}
				hud.Add(transportRow);
				if (T.BlastCanister.Enabled)
				{
					SkillHud.CooldownRow(canisterRow, SkillKeys.SecondaryLabel, "Blast Canister: ", HudClock.Key(canister, time));
					hud.Add(canisterRow);
				}
				break;
			}
			}
			SkillHud.Draw(hud);
		}

		private static void DrawParryVignette()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Invalid comparison between Unknown and I4
			if (Event.current != null && (int)Event.current.type == 7)
			{
				SkillHud.ParryVignette(ParryLook.Shown(ParryLook.Vignette(Time.time, ParryPose.StartedAt, ParryPose.EndsAt, ParryPose.Active, ParryPose.Scheduled), ParryScreen.MenuOpen(), ParryScreen.Dead()));
			}
		}
	}
	[HarmonyPatch(typeof(HitableMob), "Hit", new Type[]
	{
		typeof(int),
		typeof(float),
		typeof(int),
		typeof(Vector3),
		typeof(int)
	})]
	internal static class BackstabPatch
	{
		internal static readonly CritRoll LastRoll = new CritRoll();

		[HarmonyPriority(200)]
		[HarmonyBefore(new string[] { "evan.muck.crowbar" })]
		[HarmonyAfter(new string[] { "evan.muck.pennib" })]
		private static void Prefix(HitableMob __instance, ref int __0, ref int __2, int __4)
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!SurvivorState.Active || SurvivorState.Session.Locked != SurvivorId.Bandit || !Backstab.Scales(__0, __4, __2, LastRoll.CritIn(Time.frameCount), SurvivorHits.Dealing) || (Object)(object)__instance == (Object)null || (Object)(object)PlayerMovement.Instance == (Object)null)
				{
					return;
				}
				Vector3 forward = ((Component)__instance).transform.forward;
				Vector3 val = ((Component)__instance).transform.position - ((Component)PlayerMovement.Instance).transform.position;
				if (Backstab.IsBehind(new Vec3(forward.x, forward.y, forward.z), new Vec3(val.x, val.y, val.z)))
				{
					int num = __0;
					__0 = Backstab.CritDamage(__0);
					if (__2 == 0)
					{
						__2 = 1;
					}
					SurvivorsPlugin.Verbose($"Backstab {num} -> {__0} on mob {((Hitable)__instance).GetId()}");
				}
			}
			catch (Exception arg)
			{
				SurvivorsPlugin.LogOnce("BackstabPatch", $"Survivors backstab failed: {arg}");
			}
		}
	}
	[HarmonyPatch(typeof(PowerupCalculations), "GetDamageMultiplier", new Type[]
	{
		typeof(bool),
		typeof(float)
	})]
	internal static class CritRollPatch
	{
		private static void Postfix(DamageResult __result)
		{
			try
			{
				BackstabPatch.LastRoll.Record(__result?.crit ?? false, Time.frameCount);
			}
			catch (Exception arg)
			{
				SurvivorsPlugin.LogOnce("CritRollPatch", $"Survivors crit roll hook failed: {arg}");
			}
		}
	}
	internal sealed class BanditSkills : MonoBehaviour
	{
		private struct Strike
		{
			public int BaseDamage;

			public float Strength;

			public bool Ok;
		}

		private struct MobBody
		{
			public HitableMob Mob;

			public Collider Collider;
		}

		private const float DashExitSpeed = 6.5f;

		private readonly Cooldown dagger = new Cooldown();

		private readonly Cooldown smokeCd = new Cooldown();

		private readonly Cooldown special = new Cooldown();

		private readonly SmokeCloak cloak = new SmokeCloak();

		private readonly SpreadBloom bloom = new SpreadBloom();

		private readonly HemorrhageLedger hemorrhage = new HemorrhageLedger();

		private readonly PendingKill pendingKill = new PendingKill();

		private readonly List<HemorrhageTick> ticks = new List<HemorrhageTick>();

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

		private readonly List<MobBody> bodies = new List<MobBody>();

		private readonly HudRow gunRow = new HudRow();

		private readonly HudRow daggerRow = new HudRow();

		private readonly HudRow smokeRow = new HudRow();

		private readonly HudRow specialRow = new HudRow();

		private readonly List<HudRow> hud = new List<HudRow>();

		private const float ResetFlash = 0.4f;

		private static readonly Color BackstabRed = new Color(1f, 0.1f, 0.1f);

		private BanditMagazine magazine;

		private int desperado;

		private bool busy;

		private Vector3 revolverPoint;

		private float resetFlashUntil;

		private bool flashAll;

		private static BanditSkills active;

		private static BanditTuning B => SurvivorState.Tuning.Bandit;

		private static float Shell => WeaponTuning.Interval(B.ReloadShell, AbilityController.AttackSpeed());

		private static SurvivorsConfig Cfg => SurvivorsPlugin.Cfg;

		private static string SecondaryName
		{
			get
			{
				if (B.Secondary != BanditSecondaryStyle.Shiv)
				{
					return "Serrated Dagger";
				}
				return "Serrated Shiv";
			}
		}

		private void Awake()
		{
			magazine = new BanditMagazine(B.MagazineSize);
			active = this;
			hud.AddRange(new HudRow[4] { gunRow, daggerRow, smokeRow, specialRow });
		}

		private void OnDestroy()
		{
			if ((Object)(object)active == (Object)(object)this)
			{
				active = null;
			}
		}

		public void StopAll()
		{
			((MonoBehaviour)this).StopAllCoroutines();
			busy = false;
			if (cloak.Cancel())
			{
				SurvivorState.CloakActive = false;
				LobbySync.PublishCloak(cloaked: false);
			}
			hemorrhage.Clear();
			pendingKill.Clear();
			bloom.Reset();
			dagger.Reset();
			smokeCd.Reset();
			special.Reset();
			desperado = 0;
			resetFlashUntil = 0f;
			magazine = new BanditMagazine(B.MagazineSize);
		}

		private void Update()
		{
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			if (!SurvivorState.Active || SurvivorState.Session.Locked != SurvivorId.Bandit)
			{
				return;
			}
			try
			{
				float time = Time.time;
				TickHemorrhage();
				if (!Alive())
				{
					DropCloak();
					return;
				}
				bloom.Tick(time, B.BloomCool);
				if (!busy)
				{
					magazine.Tick(time, Shell, Keys.PrimaryHeld(SurvivorId.Bandit));
				}
				if (cloak.Tick(time))
				{
					EndCloak(blast: true);
				}
				if (!AbilityController.InputBlocked())
				{
					if (Keys.Use(SkillKeys.Utility, "Smoke Bomb", smokeCd, time))
					{
						CastSmoke(time);
					}
					if (Keys.Use(SkillKeys.Secondary, SecondaryName, dagger, time, busy))
					{
						CastSecondary(time);
					}
					if (Keys.Use(SkillKeys.Special, BanditLoadout.SpecialName(B.Special), special, time))
					{
						CastSpecial(time);
					}
					if (Keys.PrimaryHeld(SurvivorId.Bandit) && !busy && magazine.CanFire(time))
					{
						CastPrimary(time);
					}
				}
			}
			catch (Exception arg)
			{
				SurvivorsPlugin.LogOnce("BanditSkills", $"Survivors Bandit update failed: {arg}");
			}
		}

		private void CastSmoke(float now)
		{
			if (cloak.Active)
			{
				EndCloak(blast: true);
			}
			smokeCd.Start(now, B.SmokeCooldown);
			cloak.Start(now, B.SmokeDuration);
			SurvivorState.CloakActive = true;
			LobbySync.PublishCloak(cloaked: true);
			Hop();
			SurvivorsPlugin.Verbose("Smoke Bomb");
			SmokeBlast(end: false);
		}

		private void CastSecondary(float now)
		{
			Strike strike = TakeStrike();
			if (strike.Ok)
			{
				EndCloak(blast: true);
				dagger.Start(now, B.DaggerCooldown);
				magazine.Pause(now, B.ReloadDelay, Shell);
				SurvivorsPlugin.Verbose(SecondaryName);
				if (B.Secondary == BanditSecondaryStyle.Shiv)
				{
					FireShiv(strike);
				}
				else
				{
					((MonoBehaviour)this).StartCoroutine(Lunge(strike));
				}
			}
		}

		private void CastSpecial(float now)
		{
			Strike strike = TakeStrike();
			if (strike.Ok)
			{
				EndCloak(blast: true);
				special.Start(now, B.SpecialCooldown);
				magazine.Pause(now, B.ReloadDelay, Shell);
				SurvivorsPlugin.Verbose(BanditLoadout.SpecialName(B.Special));
				FireRevolver(strike);
			}
		}

		private void CastPrimary(float now)
		{
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			if (!Hitscan.TryAim(out var origin, out var forward))
			{
				return;
			}
			Strike strike = TakeStrike();
			if (strike.Ok)
			{
				EndCloak(blast: true);
				magazine.Fire(now, WeaponTuning.Interval(B.ShotInterval, AbilityController.AttackSpeed()), B.ReloadDelay, Shell, emptySkipsDelay: true, Time.deltaTime);
				if (B.Primary == BanditPrimaryStyle.Blast)
				{
					SurvivorsPlugin.Verbose("Blast");
					FireBlast(origin, forward, strike);
				}
				else
				{
					SurvivorsPlugin.Verbose($"Burst {magazine.Stocks}/{magazine.Capacity}");
					FireBurst(origin, forward, strike);
				}
				bloom.OnShot();
			}
		}

		private void FireBurst(Vector3 origin, Vector3 forward, Strike strike)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: 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_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: 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_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: 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_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			float bloomDegrees = B.BurstBloom * bloom.Heat;
			Vector3 val = Tracer.Muzzle(origin, forward);
			AbilityController.Recoil();
			int num = 0;
			for (int i = 0; i < B.PelletCount; i++)
			{
				BanditMath.Pellet(i, B.PelletCount, B.BurstSpread, bloomDegrees, out var yaw, out var pitch);
				Vector3 val2 = Aim(forward, yaw, pitch);
				HitscanShot hitscanShot = Hitscan.Cast(origin, val2, B.ShotRange, B.ShotRadius);
				if (i == 0)
				{
					LocalFx.Cast(SkillFxEvent.Shot(ShotStyle.Burst, SkillFxPlayer.V(val), SkillFxPlayer.V(origin + val2 * hitscanShot.Stop)));
				}
				else
				{
					SkillFx.Segment(val, origin + val2 * hitscanShot.Stop, Cfg.TracerColor.Value, 0.03f, 0.1f);
				}
				if (hitscanShot.Hit)
				{
					float falloff = BanditMath.Falloff(hitscanShot.Distance, B.FalloffStart, B.FalloffEnd, B.FalloffFloor);
					bool flag = Behind(hitscanShot.Mob);
					float coefficient = BanditMath.Coefficient(B.PelletDamage, flag, 2f, 1f, 1f, falloff);
					if (Hit(hitscanShot.Mob, AbilityDamage.Hit(strike.BaseDamage, coefficient, strike.Strength), hitscanShot.Point, flag))
					{
						num++;
					}
				}
			}
			SurvivorsPlugin.Verbose(SkillLog.Outcome("Burst", num));
		}

		private void FireBlast(Vector3 origin, Vector3 forward, Strike strike)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: 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_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			HitscanShot shot = Hitscan.Cast(origin, forward, B.ShotRange, B.ShotRadius);
			CastShot(ShotStyle.Blast, origin, forward, shot);
			AbilityController.Recoil();
			bool flag = false;
			if (shot.Hit)
			{
				bool flag2 = Behind(shot.Mob);
				float coefficient = BanditMath.Coefficient(B.BlastDamage, flag2, 2f, 1f, 1f, 1f);
				flag = Hit(shot.Mob, AbilityDamage.Hit(strike.BaseDamage, coefficient, strike.Strength), shot.Point, flag2);
			}
			SurvivorsPlugin.Verbose(SkillLog.Outcome("Blast", flag ? 1 : 0));
		}

		private static void CastShot(ShotStyle style, Vector3 origin, Vector3 forward, HitscanShot shot)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			LocalFx.Cast(SkillFxEvent.Shot(style, SkillFxPlayer.V(Tracer.Muzzle(origin, forward)), SkillFxPlayer.V(origin + forward * shot.Stop)));
		}

		private void FireShiv(Strike strike)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: 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_00a2: Unknown result type (might be due to invalid IL or missing references)
			if (!Hitscan.TryAim(out var origin, out var forward))
			{
				SurvivorsPlugin.Verbose(SkillLog.NotCast("Serrated Shiv", "no camera to aim from"));
				return;
			}
			HitscanShot shot = Hitscan.Cast(origin, forward, B.ShivRange, B.ShotRadius);
			CastShot(ShotStyle.Shiv, origin, forward, shot);
			bool flag = false;
			if (shot.Hit)
			{
				bool flag2 = Behind(shot.Mob);
				float coefficient = BanditMath.Coefficient(B.ShivDamage, flag2, 2f, 1f, 1f, 1f);
				int damage = AbilityDamage.Hit(strike.BaseDamage, coefficient, strike.Strength);
				flag = Hit(shot.Mob, damage, shot.Point, flag2);
				if (flag && flag2)
				{
					ApplyHemorrhage(shot.Mob, strike);
				}
			}
			SurvivorsPlugin.Verbose(SkillLog.Outcome("Serrated Shiv", flag ? 1 : 0));
		}

		private void FireRevolver(Strike strike)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: 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)
			string skill = BanditLoadout.SpecialName(B.Special);
			if (!Hitscan.TryAim(out var origin, out var forward))
			{
				SurvivorsPlugin.Verbose(SkillLog.NotCast(skill, "no camera to aim from"));
				return;
			}
			HitscanShot shot = Hitscan.Cast(origin, forward, B.SpecialRange, B.ShotRadius);
			CastShot(ShotStyle.Revolver, origin, forward, shot);
			AbilityController.Recoil();
			if (!shot.Hit)
			{
				SurvivorsPlugin.Verbose(SkillLog.Outcome(skill, 0));
				return;
			}
			revolverPoint = shot.Point;
			int hp = ((Hitable)shot.Mob).hp;
			int maxHp = ((Hitable)shot.Mob).maxHp;
			float slayer = BanditMath.Slayer(hp, maxHp, B.SlayerPerMissing, B.SlayerCap);
			float bonus = ((B.Special == BanditSpecialStyle.Desperado) ? BanditMath.Desperado(desperado, B.DesperadoPerStack) : 1f);
			bool behind = Behind(shot.Mob);
			float coefficient = BanditMath.Coefficient(B.SpecialDamage, behind, 2f, slayer, bonus, 1f);
			int damage = AbilityDamage.Hit(strike.BaseDamage, coefficient, strike.Strength);
			HitableMob mob = shot.Mob;
			bool flag = pendingKill.Shoot(((Hitable)mob).GetId(), Time.time, LocalClient.serverOwner, () => Hit(mob, damage, shot.Point, behind));
			SurvivorsPlugin.Verbose(SkillLog.Outcome(skill, flag ? 1 : 0));
		}

		internal static void OnLocalKill(int killType, int mobId)
		{
			if (!((Object)(object)active == (Object)null) && killType == -1 && SurvivorState.Active && SurvivorState.Session.Locked == SurvivorId.Bandit)
			{
				active.ApplyKill(mobId);
			}
		}

		private void ApplyKill(int mobId)
		{
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			SpecialOutcome specialOutcome = BanditMath.OnKill(B.Special, pendingKill.TryConsume(mobId, Time.time));
			if (specialOutcome.ResetCooldowns)
			{
				BanditMath.ApplyLightsOut(dagger, smokeCd, special, magazine);
				SurvivorsPlugin.Verbose("Lights Out reset cooldowns");
			}
			if (specialOutcome.AddStack)
			{
				desperado = BanditMath.AddDesperadoStack(desperado);
				SurvivorsPlugin.Verbose($"Desperado x{desperado}");
			}
			if (specialOutcome.ResetCooldowns || specialOutcome.AddStack)
			{
				SkillFx.Ring(revolverPoint, 0.3f, 1.5f, FxColors.Gold, 0.12f, 0.3f);
				resetFlashUntil = Time.time + 0.4f;
				flashAll = specialOutcome.ResetCooldowns;
			}
		}

		private IEnumerator Lunge(Strike strike)
		{
			busy = true;
			SurvivorState.DashActive = true;
			HitLedger claimed = new HitLedger();
			Vector3 dir = Vector3.zero;
			float exit = 6.5f;
			bool num = BeginLunge(out dir, out exit);
			int hits = 0;
			if (num)
			{
				Vector3 start = LocalFx.Feet();
				LungeStarted(dir);
				float end = Time.time + B.DaggerDuration;
				while (Time.time < end && LungeStep(dir, strike, claimed, ref hits))
				{
					yield return (object)new WaitForFixedUpdate();
				}
				ExitLunge(dir, exit);
				LocalFx.Cast(SkillFxEvent.Dash(DashStyle.Dagger, SkillFxPlayer.V(start), SkillFxPlayer.V(LocalFx.Feet())));
			}
			SurvivorsPlugin.Verbose(SkillLog.Outcome("Serrated Dagger", hits));
			SurvivorState.DashActive = false;
			busy = false;
		}

		private static bool BeginLunge(out Vector3 dir, out float exit)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: 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_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: 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_00d5: Unknown result type (might be due to invalid IL or missing references)
			dir = Vector3.zero;
			exit = 6.5f;
			try
			{
				PlayerMovement instance = PlayerMovement.Instance;
				dir = instance.orientation.forward;
				dir.y = 0f;
				if (((Vector3)(ref dir)).sqrMagnitude < 0.0001f)
				{
					dir = ((Component)instance).transform.forward;
				}
				dir.y = 0f;
				((Vector3)(ref dir)).Normalize();
				Rigidbody rb = instance.GetRb();
				if ((Object)(object)rb != (Object)null)
				{
					Vector3 val = new Vector3(rb.velocity.x, 0f, rb.velocity.z);
					float magnitude = ((Vector3)(ref val)).magnitude;
					exit = Mathf.Max(6.5f, magnitude);
					rb.velocity = new Vector3(rb.velocity.x, Mathf.Max(0f, rb.velocity.y), rb.velocity.z);
				}
				return true;
			}
			catch (Exception arg)
			{
				SurvivorsPlugin.LogOnce("dagger-begin", $"Survivors Serrated Dagger failed to start: {arg}");
				return false;
			}
		}

		private static void LungeStarted(Vector3 dir)
		{
			//IL_0030: 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_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: 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)
			try
			{
				if (Hitscan.TryAim(out var origin, out var forward))
				{
					SkillFx.Arc(origin, forward, PlayerMovement.Instance.playerCam.right, 1.6f, 120f, FxColors.Bandit);
				}
				SkillFx.SpeedLines(dir, FxColors.Bandit, B.DaggerDuration);
			}
			catch (Exception arg)
			{
				SurvivorsPlugin.LogOnce("dagger-fx", $"Survivors: drawing the Serrated Dagger failed: {arg}");
			}
		}

		private bool LungeStep(Vector3 dir, Strike strike, HitLedger claimed, ref int hits)
		{
			//IL_002b: 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_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!Alive())
				{
					return false;
				}
				Rigidbody rb = PlayerMovement.Instance.GetRb();
				if ((Object)(object)rb == (Object)null)
				{
					return false;
				}
				rb.isKinematic = false;
				rb.velocity = dir * B.DaggerSpeed + Vector3.up * rb.velocity.y;
				hits += Slash(strike, claimed);
				return true;
			}
			catch (Exception arg)
			{
				SurvivorsPlugin.LogOnce("dagger-step", $"Survivors Serrated Dagger step failed: {arg}");
				return false;
			}
		}

		private static void ExitLunge(Vector3 dir, float exit)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!((Object)(object)PlayerMovement.Instance == (Object)null))
				{
					Rigidbody rb = PlayerMovement.Instance.GetRb();
					if (!((Object)(object)rb == (Object)null))
					{
						rb.velocity = dir * exit + Vector3.up * rb.velocity.y;
					}
				}
			}
			catch (Exception arg)
			{
				SurvivorsPlugin.LogOnce("dagger-exit", $"Survivors Serrated Dagger exit failed: {arg}");
			}
		}

		private int Slash(Strike strike, HitLedger claimed)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: 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_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			Vector3 position = ((Component)PlayerMovement.Instance).transform.position;
			Snapshot(bodies);
			float daggerRadius = B.DaggerRadius;
			int num = 0;
			for (int i = 0; i < bodies.Count; i++)
			{
				MobBody mobBody = bodies[i];
				if ((Object)(object)mobBody.Mob == (Object)null || ((Hitable)mobBody.Mob).hp <= 0)
				{
					continue;
				}
				Vector3 val;
				if (!((Object)(object)mobBody.Collider != (Object)null))
				{
					val = ((Component)mobBody.Mob).transform.position;
				}
				else
				{
					Bounds bounds = mobBody.Collider.bounds;
					val = ((Bounds)(ref bounds)).ClosestPoint(position);
				}
				Vector3 val2 = val;
				Vector3 val3 = val2 - position;
				if (((Vector3)(ref val3)).sqrMagnitude > daggerRadius * daggerRadius || !claimed.TryClaim(((Hitable)mobBody.Mob).GetId()))
				{
					continue;
				}
				bool flag = Behind(mobBody.Mob);
				float coefficient = BanditMath.Coefficient(B.DaggerDamage, flag, 2f, 1f, 1f, 1f);
				int damage = AbilityDamage.Hit(strike.BaseDamage, coefficient, strike.Strength);
				if (Hit(mobBody.Mob, damage, val2, flag))
				{
					num++;
					if (flag)
					{
						ApplyHemorrhage(mobBody.Mob, strike);
					}
				}
			}
			return num;
		}

		private void SmokeBlast(bool end)
		{
			//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_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: 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_00ed: 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)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: 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_015a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)PlayerMovement.Instance == (Object)null)
			{
				return;
			}
			Vector3 position = ((Component)PlayerMovement.Instance).transform.position;
			LocalFx.Cast(SkillFxEvent.Smoke(end, SkillFxPlayer.V(position), B.SmokeRadius));
			Strike strike = TakeStrike();
			if (!strike.Ok)
			{
				return;
			}
			Snapshot(bodies);
			float smokeRadius = B.SmokeRadius;
			HitLedger hitLedger = new HitLedger();
			int num = 0;
			for (int i = 0; i < bodies.Count; i++)
			{
				MobBody mobBody = bodies[i];
				if ((Object)(object)mobBody.Mob == (Object)null || ((Hitable)mobBody.Mob).hp <= 0 || !hitLedger.TryClaim(((Hitable)mobBody.Mob).GetId()))
				{
					continue;
				}
				Vector3 val;
				if (!((Object)(object)mobBody.Collider != (Object)null))
				{
					val = ((Component)mobBody.Mob).transform.position;
				}
				else
				{
					Bounds bounds = mobBody.Collider.bounds;
					val = ((Bounds)(ref bounds)).ClosestPoint(position);
				}
				Vector3 val2 = val;
				Vector3 val3 = val2 - position;
				if (!(((Vector3)(ref val3)).sqrMagnitude > smokeRadius * smokeRadius))
				{
					bool flag = Behind(mobBody.Mob);
					float coefficient = BanditMath.Coefficient(B.SmokeDamage, flag, 2f, 1f, 1f, 1f);
					if (Hit(mobBody.Mob, AbilityDamage.Hit(strike.BaseDamage, coefficient, strike.Strength), val2, flag))
					{
						num++;
					}
				}
			}
			SurvivorsPlugin.Verbose(SkillLog.Outcome(end ? "Smoke Bomb end blast" : "Smoke Bomb blast", num));
		}

		private static void Hop()
		{
			//IL_003f: 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_0064: 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)
			try
			{
				if (!(B.SmokeHop <= 0f) && !((Object)(object)PlayerMovement.Instance == (Object)null))
				{
					Rigidbody rb = PlayerMovement.Instance.GetRb();
					if (!((Object)(object)rb == (Object)null))
					{
						rb.isKinematic = false;
						rb.velocity = new Vector3(rb.velocity.x, Mathf.Max(rb.velocity.y, B.SmokeHop), rb.velocity.z);
					}
				}
			}
			catch (Exception arg)
			{
				SurvivorsPlugin.LogOnce("smoke-hop", $"Survivors Smoke Bomb hop failed: {arg}");
			}
		}

		private void EndCloak(bool blast)
		{
			if (cloak.Cancel() || SurvivorState.CloakActive)
			{
				SurvivorState.CloakActive = false;
				LobbySync.PublishCloak(cloaked: false);
				if (blast)
				{
					SmokeBlast(end: true);
				}
			}
		}

		private void DropCloak()
		{
			if (cloak.Active || SurvivorState.CloakActive)
			{
				cloak.Cancel();
				SurvivorState.CloakActive = false;
				LobbySync.PublishCloak(cloaked: false);
				SkillFxNet.Send(SkillFxEvent.Uncloak());
			}
		}

		private void TickHemorrhage()
		{
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			ticks.Clear();
			hemorrhage.Collect(Time.time, ticks);
			tickTotals.Clear();
			for (int i = 0; i < ticks.Count; i++)
			{
				int mobId = ticks[i].MobId;
				tickTotals.TryGetValue(mobId, out var value);
				tickTotals[mobId] = value + ticks[i].Damage;
			}
			foreach (KeyValuePair<int, int> tickTotal in tickTotals)
			{
				HitableMob val = MobLookup.Find(tickTotal.Key);
				if (!((Object)(object)val == (Object)null) && ((Hitable)val).hp > 0)
				{
					Hit(val, tickTotal.Value, ((Component)val).transform.position, crit: false, HitSource.Tick);
				}
			}
		}

		private void ApplyHemorrhage(HitableMob mob, Strike strike)
		{
			int num = BanditMath.HemorrhageTicks(B.HemorrhageDuration, B.HemorrhageTick);
			float coefficient = BanditMath.HemorrhagePerTick(B.HemorrhageDamage, num);
			int perTick = AbilityDamage.Hit(strike.BaseDamage, coefficient, strike.Strength);
			if (hemorrhage.Apply(((Hitable)mob).GetId(), perTick, Time.time, B.HemorrhageDuration, B.HemorrhageTick))
			{
				SurvivorsPlugin.Verbose($"Hemorrhage x{hemorrhage.Stacks(((Hitable)mob).GetId())} on mob {((Hitable)mob).GetId()}");
				LocalFx.Cast(SkillFxEvent.Bleed(((Hitable)mob).GetId(), B.HemorrhageDuration));
			}
		}

		private bool Hit(HitableMob mob, int damage, Vector3 point, bool crit, HitSource source = HitSource.Skill)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: 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_0061: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)mob == (Object)null || damage <= 0)
			{
				return false;
			}
			int hitEffect = (crit ? 1 : 0);
			int id = ((Hitable)mob).GetId();
			SurvivorHits.Deal(mob, damage, hitEffect, point, source);
			SurvivorsPlugin.Verbose($"Bandit hit mob {id} for {damage}");
			if (source == HitSource.Skill)
			{
				SkillFx.Spark(point, crit ? BackstabRed : FxColors.Bandit);
				if (crit)
				{
					SkillFx.Spark(point, Color.white);
				}
			}
			return true;
		}

		private static bool Behind(HitableMob mob)
		{
			//IL_001e: 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_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: 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_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)mob == (Object)null || (Object)(object)PlayerMovement.Instance == (Object)null)
			{
				return false;
			}
			Vector3 forward = ((Component)mob).transform.forward;
			Vector3 val = ((Component)mob).transform.position - ((Component)PlayerMovement.Instance).transform.position;
			return Backstab.IsBehind(new Vec3(forward.x, forward.y, forward.z), new Vec3(val.x, val.y, val.z));
		}

		private static Vector3 Aim(Vector3 forward, float yawDeg, float pitchDeg)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: 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_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_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: 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)
			Vector3 val = Vector3.Cross(Vector3.up, forward);
			if (((Vector3)(ref val)).sqrMagnitude < 1E-06f)
			{
				val = Vector3.right;
			}
			((Vector3)(ref val)).Normalize();
			Vector3 val2 = Vector3.Cross(forward, val);
			Vector3 normalized = ((Vector3)(ref val2)).normalized;
			val2 = forward + val * Mathf.Tan(yawDeg * (MathF.PI / 180f)) + normalized * Mathf.Tan(pitchDeg * (MathF.PI / 180f));
			return ((Vector3)(ref val2)).normalized;
		}

		private static Strike TakeStrike()
		{
			try
			{
				return new Strike
				{
					BaseDamage = KitDamageReader.Read(),
					Strength = PowerupInventory.Instance.GetStrengthMultiplier((int[])null),
					Ok = true
				};
			}
			catch (Exception arg)
			{
				SurvivorsPlugin.LogOnce("bandit-strike", $"Survivors Bandit failed to read damage: {arg}");
				return default(Strike);
			}
		}

		private static void Snapshot(List<MobBody> into)
		{
			into.Clear();
			if ((Object)(object)MobManager.Instance == (Object)null || MobManager.Instance.mobs == null)
			{
				return;
			}
			foreach (Mob value in MobManager.Instance.mobs.Values)
			{
				if (!((Object)(object)value == (Object)null))
				{
					HitableMob component = ((Component)value).GetComponent<HitableMob>();
					if (!((Object)(object)component == (Object)null) && ((Hitable)component).hp > 0)
					{
						into.Add(new MobBody
						{
							Mob = component,
							Collider = ((Component)component).GetComponentInChildren<Collider>()
						});
					}
				}
			}
		}

		private static bool Alive()
		{
			if ((Object)(object)PlayerStatus.Instance != (Object)null && (Object)(object)PlayerMovement.Instance != (Object)null)
			{
				return !PlayerStatus.Instance.IsPlayerDead();
			}
			return false;
		}

		private void OnGUI()
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Invalid comparison between Unknown and I4
			if (SurvivorState.Session.Locked != SurvivorId.Bandit || !SurvivorState.Active || !Alive())
			{
				return;
			}
			if (cloak.Active && (int)Event.current.type == 7)
			{
				SkillHud.CloakVignette();
			}
			if (SkillHud.Visible)
			{
				float time = Time.time;
				int version = SkillKeys.Version;
				bool flag = time < resetFlashUntil;
				gunRow.Highlight = flag && flashAll;
				daggerRow.Highlight = flag && flashAll;
				smokeRow.Highlight = flag && flashAll;
				specialRow.Highlight = flag;
				bool flag2 = magazine.Stocks <= 0 || magazine.Reloading;
				int num = (flag2 ? HudClock.Tenths(magazine.ReloadRemaining(time, Shell)) : (magazine.CanFire(time) ? (-1) : HudClock.Tenths(magazine.ShotRemaining(time))));
				bool flag3 = SkillHud.HoldingPrimary(SurvivorId.Bandit);
				int shownStocks = magazine.ShownStocks;
				if (gunRow.Stale(version, shownStocks, flag2 ? (-2 - num) : num, flag3 ? magazine.Capacity : (-magazine.Capacity)))
				{
					string text = SkillKeys.PrimaryLabel + BanditLoadout.PrimaryName(B.Primary) + " " + shownStocks + "/" + magazine.Capacity + ": ";
					string text2 = SkillHud.HoldHint(SurvivorId.Bandit);
					gunRow.Set(text + (flag2 ? ("reload " + HudClock.Seconds(num)) : HudClock.Status(num)) + (flag3 ? "" : text2), text + "reload 88.8s" + text2);
				}
				int num2 = HudClock.Key(dagger, time);
				if (daggerRow.Stale(version, num2))
				{
					string text3 = SkillKeys.SecondaryLabel + BanditLoadout.SecondaryName(B.Secondary) + ": ";
					daggerRow.Set(text3 + HudClock.Status(num2), text3 + "88.8s");
				}
				int num3 = HudClock.Key(smokeCd, time);
				int num4 = (cloak.Active ? HudClock.Tenths(cloak.Remaining(time)) : (-1));
				if (smokeRow.Stale(version, num3, num4))
				{
					string text4 = SkillKeys.UtilityLabel + "Smoke Bomb: ";
					smokeRow.Set(text4 + HudClock.Status(num3) + (cloak.Active ? ("  cloak " + HudClock.Seconds(num4)) : ""), text4 + "88.8s  cloak 88.8s");
				}
				int num5 = HudClock.Key(special, time);
				int num6 = ((B.Special == BanditSpecialStyle.Desperado) ? desperado : 0);
				if (specialRow.Stale(version, num5, num6))
				{
					string text5 = SkillKeys.SpecialLabel + BanditLoadout.SpecialName(B.Special) + ": ";
					string text6 = ((num6 > 0) ? (" x" + num6.ToString(CultureInfo.InvariantCulture)) : "");
					specialRow.Set(text5 + HudClock.Status(num5) + text6, text5 + "88.8s" + text6);
				}
				SkillHud.Draw(hud);
			}
		}
	}
	internal sealed class BodyDirector : MonoBehaviour
	{
		private const float Interval = 0.5f;

		private static readonly FieldInfo SteamToLobbyId = AccessTools.Field(typeof(LobbyVisuals), "steamToLobbyId");

		private float next;

		public static SurvivorId OwnPick
		{
			get
			{
				SurvivorsConfig cfg = SurvivorsPlugin.Cfg;
				if (!cfg.Enabled.Value)
				{
					return SurvivorId.None;
				}
				return cfg.SelectedSurvivor.Value;
			}
		}

		public static SurvivorId PickFor(int muckId)
		{
			int myId = (((Object)(object)LocalClient.instance != (Object)null) ? LocalClient.instance.myId : (-1));
			return BodyPicks.ForPlayer(muckId, myId, OwnPick, LobbySync.Members);
		}

		public static void ApplyPlayer(GameObject rig, int muckId)
		{
			SurvivorBody.Apply(rig, PickFor(muckId));
			int myId = (((Object)(object)LocalClient.instance != (Object)null) ? LocalClient.instance.myId : (-1));
			SurvivorBody.SetCloaked(rig, BodyPicks.Cloaked(muckId, myId, SurvivorState.CloakActive, LobbySync.Members, RemoteFx.Cloaks, Time.unscaledTime));
		}

		private void Update()
		{
			if (!(Time.unscaledTime < next))
			{
				next = Time.unscaledTime + 0.5f;
				Reconcile();
			}
		}

		public static void Reconcile()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Scene activeScene = SceneManager.GetActiveScene();
				if (((Scene)(ref activeScene)).name == "GameAfterLobby")
				{
					ReconcilePlayers();
				}
				else
				{
					ReconcileLobbyFigures();
				}
			}
			catch (Exception arg)
			{
				SurvivorsPlugin.LogOnce("BodyDirector", $"Survivors: updating survivor bodies failed: {arg}");
			}
		}

		private static void ReconcilePlayers()
		{
			if (GameManager.players == null)
			{
				return;
			}
			foreach (PlayerManager value in GameManager.players.Values)
			{
				if (!((Object)(object)value == (Object)null) && !((Object)(object)value.onlinePlayer == (Object)null))
				{
					ApplyPlayer(((Component)value.onlinePlayer).gameObject, value.id);
				}
			}
		}

		private static void ReconcileLobbyFigures()
		{
			LobbyVisuals instance = LobbyVisuals.Instance;
			if ((Object)(object)instance == (Object)null || instance.lobbyPlayers == null)
			{
				return;
			}
			Dictionary<int, ulong> dictionary = new Dictionary<int, ulong>();
			if (SteamToLobbyId?.GetValue(instance) is Dictionary<ulong, int> dictionary2)
			{
				foreach (KeyValuePair<ulong, int> item in dictionary2)
				{
					dictionary[item.Value] = item.Key;
				}
			}
			for (int i = 0; i < instance.lobbyPlayers.Length; i++)
			{
				ulong value;
				SurvivorId pick = (dictionary.TryGetValue(i, out value) ? BodyPicks.ForSteamId(value, LobbySync.Rows) : SurvivorId.None);
				SurvivorBody.Apply(instance.lobbyPlayers[i], pick);
			}
		}
	}
	[HarmonyPatch(typeof(GameManager), "SpawnPlayer")]
	internal static class SpawnPlayerPatch
	{
		private static void Postfix(int __0)
		{
			try
			{
				if (GameManager.players.TryGetValue(__0, out var value) && (Object)(object)value != (Object)null && (Object)(object)value.onlinePlayer != (Object)null)
				{
					BodyDirector.ApplyPlayer(((Component)value.onlinePlayer).gameObject, __0);
				}
			}
			catch (Exception arg)
			{
				SurvivorsPlugin.LogOnce("SpawnPlayerPatch", $"Survivors: applying a survivor body on spawn failed: {arg}");
			}
		}
	}
	[HarmonyPatch(typeof(PlayerRagdoll), "SetRagdoll")]
	internal static class RagdollPatch
	{
		private static void Prefix(PlayerRagdoll __instance, out Rig<Transform> __state)
		{
			__state = default(Rig<Transform>);
			try
			{
				if ((Object)(object)__instance.ragdoll != (Object)null)
				{
					__state = SurvivorBody.FindRig(((Component)__instance.ragdoll).transform);
				}
			}
			catch (Exception arg)
			{
				SurvivorsPlugin.LogOnce("RagdollPatch.Prefix", $"Survivors: finding the ragdoll rig failed: {arg}");
			}
		}

		private static void Postfix(PlayerRagdoll __instance, int __0, Rig<Transform> __state)
		{
			try
			{
				SurvivorBody.Apply(__state, BodyDirector.PickFor(__0), ((Object)__instance).name);
			}
			catch (Exception arg)
			{
				SurvivorsPlugin.LogOnce("RagdollPatch", $"Survivors: applying a survivor body to a ragdoll failed: {arg}");
			}
		}
	}
	internal static class EngineerFx
	{
		public static readonly Color Glow = new Color(0.35f, 0.95f, 1f);

		private static readonly Color Blast = new Color(1f, 0.75f, 0.3f);

		private const float ShieldAlpha = 0.35f;

		public static readonly Vector3 Muzzle = new Vector3(0f, 1.08f, 0.9f);

		public static GameObject Play(EngStyle style, Vector3 at, float size, float seconds)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: 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_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			switch (style)
			{
			case EngStyle.Blast:
				SkillFx.Flash(at, Blast, size * 0.5f, 0.12f);
				SkillFx.Ring(at, 0.3f, size, Blast, 0.2f, 0.25f);
				SkillFx.Puffs(at, 0.2f, size * 0.6f, 4, FxColors.Smoke, 0.8f, 0.6f);
				return null;
			case EngStyle.Mine:
				return Prop("EngineerMine", FxAssets.Mine, at, 0f, 1f, Color.white);
			case EngStyle.Turret:
				return Prop("EngineerTurret", FxAssets.Turret, at, size, 1f, Color.white);
			case EngStyle.Shield:
			{
				GameObject val = Prop("EngineerShield", FxAssets.Shield, at, 0f, size, new Color(1f, 1f, 1f, 0.35f));
				if ((Object)(object)val == (Object)null)
				{
					return null;
				}
				val.AddComponent<BubbleBlocker>().Begin(size);
				Object.Destroy((Object)(object)val, (seconds > 0f) ? seconds : 0.1f);
				SkillFx.Ring(at, size, size, Glow, 0.15f, (seconds > 0f) ? seconds : 0.1f);
				return val;
			}
			default:
				return null;
			}
		}

		public static GameObject GrenadeBody(Vector3 at, float life)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("SurvivorsEngineerGrenade");
			val.transform.position = at;
			Mesh val2 = FxAssets.Grenade ?? FxAssets.Icosahedron;
			FxAssets.AddMesh(val, val2, ((Object)(object)val2 == (Object)(object)FxAssets.Grenade) ? Color.white : Glow);
			if ((Object)(object)val2 != (Object)(object)FxAssets.Grenade)
			{
				val.transform.localScale = Vector3.one * 0.3f;
			}
			Object.Destroy((Object)(object)val, life);
			return val;
		}

		private static GameObject Prop(string name, Mesh mesh, Vector3 at, float yaw, float scale, Color tint)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: 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_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("Survivors" + name);
			val.transform.position = at;
			val.transform.rotation = Quaternion.Euler(0f, yaw, 0f);
			val.transform.localScale = Vector3.one * scale;
			if ((Object)(object)mesh != (Object)null)
			{
				FxAssets.AddMesh(val, mesh, tint);
			}
			return val;
		}
	}
	internal sealed class EngineerSkills : MonoBehaviour
	{
		private readonly GrenadeCharge charge = new GrenadeCharge();

		private readonly Cooldown mineCd = new Cooldown();

		private readonly Cooldown shieldCd = new Cooldown();

		private readonly Cooldown turretCd = new Cooldown();

		private readonly MineField mines = new MineField();

		private readonly TurretRoster turrets = new TurretRoster();

		private readonly Dictionary<int, GameObject> mineObjects = new Dictionary<int, GameObject>();

		private readonly Dictionary<int, EngineerTurret> turretObjects = new Dictionary<int, EngineerTurret>();

		private readonly List<Vector3> mobPositions = new List<Vector3>();

		private readonly List<HitableMob> mobs = new List<HitableMob>();

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

		private readonly HashSet<HitableMob> struck = new HashSet<HitableMob>();

		private readonly HudRow grenadeRow = new HudRow();

		private readonly HudRow mineRow = new HudRow();

		private readonly HudRow shieldRow = new HudRow();

		private readonly HudRow turretRow = new HudRow();

		private readonly List<HudRow> hud = new List<HudRow>();

		private int volleyLeft;

		private float nextVolleyShot;

		private static EngineerTuning E => SurvivorState.Tuning.Engineer;

		private void Awake()
		{
			hud.AddRange(new HudRow[4] { grenadeRow, mineRow, shieldRow, turretRow });
		}

		public void StopAll()
		{
			charge.Cancel();
			volleyLeft = 0;
			mineCd.Reset();
			shieldCd.Reset();
			turretCd.Reset();
			foreach (int item in new List<int>(mineObjects.Keys))
			{
				DropMine(item);
			}
			mines.Clear();
			foreach (int item2 in new List<int>(turretObjects.Keys))
			{
				DropTurret(item2);
			}
			turrets.Clear();
		}

		private void Update()
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			if (!SurvivorState.Active || SurvivorState.Session.Locked != SurvivorId.Engineer)
			{
				return;
			}
			try
			{
				float time = Time.time;
				SyncWorld();
				if (!Alive())
				{
					charge.Cancel();
					volleyLeft = 0;
					return;
				}
				TickMines(time);
				TickVolley(time);
				if (!AbilityController.InputBlocked())
				{
					if (Keys.Use(SkillKeys.Secondary, "Pressure Mines", mineCd, time))
					{
						LayMine(time);
					}
					if (Keys.Use(SkillKeys.Utility, "Bubble Shield", shieldCd, time))
					{
						CastShield(time);
					}
					if (Keys.Use(SkillKeys.Special, "TR12 Gauss Auto-Turret", turretCd, time))
					{
						PlaceTurret(time);
					}
					bool flag = Keys.PrimaryHeld(SurvivorId.Engineer);
					if (flag && volleyLeft == 0)
					{
						charge.Press(time);
					}
					else if (!flag && charge.Charging)
					{
						StartVolley(time, charge.Release(time, E.GrenadeChargePer, E.GrenadeMax));
					}
				}
			}
			catch (Exception arg)
			{
				SurvivorsPlugin.LogOnce("EngineerSkills", $"Survivors Engineer update failed: {arg}");
			}
		}

		private void SyncWorld()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			Scene activeScene = SceneManager.GetActiveScene();
			string worldKey = ((Scene)(ref activeScene)).handle + "/" + GameManager.GetSeed();
			List<int> list = turrets.Sync(worldKey);
			if (list.Count == 0)
			{
				return;
			}
			foreach (int item in list)
			{
				DropTurret(item);
			}
			foreach (int item2 in new List<int>(mineObjects.Keys))
			{
				DropMine(item2);
			}
			mines.Clear();
		}

		private void StartVolley(float now, int count)
		{
			if (count > 0)
			{
				SurvivorsPlugin.Verbose(EngineerLog.Volley(count));
				volleyLeft = count;
				nextVolleyShot = now;
			}
		}

		private void TickVolley(float now)
		{
			while (volleyLeft > 0 && now >= nextVolleyShot)
			{
				volleyLeft--;
				nextVolleyShot += E.GrenadeVolleyGap;
				FireGrenade();
			}
		}

		private void FireGrenade()
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			if (Hitscan.TryAim(out var origin, out var forward))
			{
				Vector3 at = Tracer.HeldMuzzle(SurvivorId.Engineer, origin, forward, Tracer.Muzzle(origin, forward));
				Vector3 val = Random.insideUnitSphere * 0.04f;
				Vector3 val2 = forward + Vector3.up * 0.12f + val;
				Vector3 normalized = ((Vector3)(ref val2)).normalized;
				EngineerGrenade.Launch(at, normalized * E.GrenadeSpeed, E.GrenadeFuse, Blast);
			}
		}

		private int Blast(Vector3 center, float radius, float coefficient, string skill)
		{
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			int num2 = Damage(coefficient);
			if (num2 > 0)