Decompiled source of SuicideBomberMod v1.0.0

BepInEx/plugins/SuicideBomberMod/SuicideBomberMod.dll

Decompiled 3 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace SuicideBomberMod
{
	public static class BomberManager
	{
		public struct ExplosionRecord
		{
			public ulong BomberClientId;

			public Vector3 Position;

			public float Time;
		}

		private const float GroundImpactGracePeriod = 1.5f;

		private const float ExplosionAttributionWindow = 3.5f;

		private static readonly HashSet<ulong> ActiveBomberIds = new HashSet<ulong>();

		private static readonly HashSet<ulong> AllRoundBomberIds = new HashSet<ulong>();

		private static readonly HashSet<ulong> DetonatedBomberIds = new HashSet<ulong>();

		private static readonly Dictionary<ulong, float> BomberSpawnTimes = new Dictionary<ulong, float>();

		private static readonly List<ExplosionRecord> RecentExplosions = new List<ExplosionRecord>();

		private static readonly HashSet<int> BomberVictimPlayerIds = new HashSet<int>();

		private static Coroutine _landingSequenceCoroutine;

		public static void ResetRoundState()
		{
			if ((Object)(object)StartOfRound.Instance != (Object)null && _landingSequenceCoroutine != null)
			{
				((MonoBehaviour)StartOfRound.Instance).StopCoroutine(_landingSequenceCoroutine);
				_landingSequenceCoroutine = null;
			}
			ActiveBomberIds.Clear();
			AllRoundBomberIds.Clear();
			DetonatedBomberIds.Clear();
			BomberSpawnTimes.Clear();
			RecentExplosions.Clear();
			BomberVictimPlayerIds.Clear();
		}

		public static bool IsActiveBomber(PlayerControllerB player)
		{
			if ((Object)(object)player != (Object)null)
			{
				return ActiveBomberIds.Contains(player.playerClientId);
			}
			return false;
		}

		public static bool WasBomberThisRound(ulong playerClientId)
		{
			return AllRoundBomberIds.Contains(playerClientId);
		}

		public static bool IsTeammateKilledByBomber(int playerId)
		{
			if (BomberVictimPlayerIds.Contains(playerId))
			{
				return !AllRoundBomberIds.Contains((ulong)playerId);
			}
			return false;
		}

		public static bool HasPassedSpawnGracePeriod(PlayerControllerB player)
		{
			if (!BomberSpawnTimes.TryGetValue(player.playerClientId, out var value))
			{
				return true;
			}
			return Time.time - value >= 1.5f;
		}

		public static void SelectAndSpawnBombers(StartOfRound startOfRound)
		{
			ResetRoundState();
			if ((Object)(object)startOfRound == (Object)null || startOfRound.allPlayerScripts == null)
			{
				return;
			}
			List<PlayerControllerB> list = new List<PlayerControllerB>();
			PlayerControllerB[] allPlayerScripts = startOfRound.allPlayerScripts;
			foreach (PlayerControllerB val in allPlayerScripts)
			{
				if ((Object)(object)val != (Object)null && val.isPlayerControlled && !val.isPlayerDead)
				{
					list.Add(val);
				}
			}
			list.Sort((PlayerControllerB a, PlayerControllerB b) => a.playerClientId.CompareTo(b.playerClientId));
			if (list.Count == 0)
			{
				return;
			}
			List<PlayerControllerB> list2 = new List<PlayerControllerB>();
			int num = Mathf.Max(1, Plugin.Instance.MaxBombersPerRound.Value);
			int seed = startOfRound.randomMapSeed ^ 0x5B09B32;
			Random random = new Random(seed);
			for (int num2 = list.Count - 1; num2 > 0; num2--)
			{
				int index = random.Next(num2 + 1);
				PlayerControllerB value = list[num2];
				list[num2] = list[index];
				list[index] = value;
			}
			float num3 = Mathf.Clamp(Plugin.Instance.SpawnChancePercent.Value, 0f, 100f);
			foreach (PlayerControllerB item in list)
			{
				if (list2.Count < num)
				{
					double num4 = random.NextDouble() * 100.0;
					if (num4 < (double)num3)
					{
						list2.Add(item);
					}
					continue;
				}
				break;
			}
			if (list2.Count == 0)
			{
				Plugin.Log.LogInfo((object)$"[Round Seed {startOfRound.randomMapSeed}] No players rolled Suicide Bomber this round.");
			}
			else
			{
				_landingSequenceCoroutine = ((MonoBehaviour)startOfRound).StartCoroutine(DelayedLandingSpawnCoroutine(startOfRound, list2));
			}
		}

		private static IEnumerator DelayedLandingSpawnCoroutine(StartOfRound startOfRound, List<PlayerControllerB> selectedBombers)
		{
			float waitGenTimeout = 30f;
			while ((Object)(object)startOfRound != (Object)null && !startOfRound.shipDoorsEnabled && waitGenTimeout > 0f)
			{
				waitGenTimeout -= Time.deltaTime;
				yield return null;
			}
			if ((Object)(object)startOfRound == (Object)null || startOfRound.inShipPhase)
			{
				yield break;
			}
			yield return (object)new WaitForSeconds(4.5f);
			if ((Object)(object)startOfRound == (Object)null || startOfRound.inShipPhase)
			{
				yield break;
			}
			for (int i = 0; i < selectedBombers.Count; i++)
			{
				PlayerControllerB val = selectedBombers[i];
				if ((Object)(object)val != (Object)null && !val.isPlayerDead)
				{
					ActivateBomber(startOfRound, val, i, selectedBombers.Count);
				}
			}
		}

		private static Vector3 GetShipDoorExitDirection(StartOfRound startOfRound)
		{
			//IL_001b: 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_0020: 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_003a: 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_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: 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_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: 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_00c6: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = (((Object)(object)startOfRound.outsideDoorPosition != (Object)null) ? startOfRound.outsideDoorPosition.position : Vector3.zero);
			if ((Object)(object)startOfRound.shipInnerRoomBounds != (Object)null)
			{
				Bounds bounds = startOfRound.shipInnerRoomBounds.bounds;
				Vector3 center = ((Bounds)(ref bounds)).center;
				if (Vector3.Distance(center, val) < 25f)
				{
					Vector3 val2 = val - center;
					val2.y = 0f;
					if (((Vector3)(ref val2)).sqrMagnitude > 0.01f)
					{
						return ((Vector3)(ref val2)).normalized;
					}
				}
			}
			if ((Object)(object)startOfRound.elevatorTransform != (Object)null)
			{
				Vector3 val3 = val - startOfRound.elevatorTransform.position;
				val3.y = 0f;
				if (((Vector3)(ref val3)).sqrMagnitude > 0.01f && ((Vector3)(ref val3)).sqrMagnitude < 625f)
				{
					return ((Vector3)(ref val3)).normalized;
				}
			}
			return Vector3.left;
		}

		private static void ActivateBomber(StartOfRound startOfRound, PlayerControllerB bomber, int bomberIndex, int totalBombers)
		{
			//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_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: 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)
			ulong playerClientId = bomber.playerClientId;
			ActiveBomberIds.Add(playerClientId);
			AllRoundBomberIds.Add(playerClientId);
			BomberSpawnTimes[playerClientId] = Time.time;
			Vector3 spawnPositionAboveShip = GetSpawnPositionAboveShip(startOfRound, bomberIndex, totalBombers);
			if ((Object)(object)startOfRound.playersContainer != (Object)null)
			{
				((Component)bomber).transform.SetParent(startOfRound.playersContainer);
			}
			bomber.isInElevator = false;
			bomber.isInHangarShipRoom = false;
			bomber.isInsideFactory = false;
			bomber.isFallingNoJump = true;
			bomber.takingFallDamage = false;
			bomber.fallValue = 0f - Plugin.Instance.NormalDescentSpeed.Value;
			bomber.fallValueUncapped = 0f - Plugin.Instance.NormalDescentSpeed.Value;
			bomber.externalForceAutoFade = Vector3.zero;
			bomber.TeleportPlayer(spawnPositionAboveShip, false, 0f, false, true);
			Plugin.Log.LogInfo((object)$"Player '{bomber.playerUsername}' (ClientId: {playerClientId}) spawned as a SUICIDE BOMBER at {spawnPositionAboveShip}!");
			if ((Object)(object)GameNetworkManager.Instance != (Object)null && (Object)(object)GameNetworkManager.Instance.localPlayerController == (Object)(object)bomber)
			{
				ShowBomberHudNotification();
			}
		}

		private static Vector3 GetSpawnPositionAboveShip(StartOfRound startOfRound, int bomberIndex, int totalBombers)
		{
			//IL_001b: 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_0020: 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_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_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: 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_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: 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)
			Vector3 val = (((Object)(object)startOfRound.outsideDoorPosition != (Object)null) ? startOfRound.outsideDoorPosition.position : Vector3.zero);
			Vector3 shipDoorExitDirection = GetShipDoorExitDirection(startOfRound);
			Vector3 val2 = val + shipDoorExitDirection * 6.5f;
			Vector3 zero = Vector3.zero;
			if (totalBombers > 1)
			{
				float num = (float)bomberIndex / (float)totalBombers * (float)Math.PI * 2f;
				((Vector3)(ref zero))..ctor(Mathf.Cos(num) * 2.5f, 0f, Mathf.Sin(num) * 2.5f);
			}
			return val2 + zero + Vector3.up * Plugin.Instance.SpawnHeightAboveShip.Value;
		}

		private static void ShowBomberHudNotification()
		{
			//IL_0018: 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)
			if (!((Object)(object)HUDManager.Instance == (Object)null))
			{
				string arg = ((object)Plugin.Instance.FastDescentKey.Value).ToString();
				string text = ((object)Plugin.Instance.DetonateKey.Value).ToString();
				int value = Plugin.Instance.TeammateBodyCreditValue.Value;
				HUDManager.Instance.DisplayTip("SUICIDE BOMBER!", $"Look straight down! Hold [{arg} / Crouch] to dive faster, or press [{text}] to detonate. Teammates killed are worth {value} credits!", true, false, "LC_SuicideBomberTip");
				HUDManager.Instance.ChangeControlTipMultiple(new string[3]
				{
					$"Fast Dive : [{arg} / Crouch]",
					$"Detonate : [{text}]",
					"Steer : [W/A/S/D]"
				}, false, (Item)null);
			}
		}

		public static void TriggerLocalBomberDetonation(PlayerControllerB bomber)
		{
			//IL_0020: 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_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: 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_003b: 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_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)
			if (!((Object)(object)bomber == (Object)null) && IsActiveBomber(bomber) && !bomber.isPlayerDead)
			{
				Vector3 explosionPosition = ((Component)bomber).transform.position + Vector3.up * 0.5f;
				ExecuteExplosionOnClient(bomber, explosionPosition);
				bomber.KillPlayer(Vector3.up * 8f, true, (CauseOfDeath)3, 0, Vector3.zero, false);
			}
		}

		public static void ExecuteExplosionOnClient(PlayerControllerB bomber, Vector3 explosionPosition)
		{
			//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_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)bomber == (Object)null)
			{
				return;
			}
			ulong playerClientId = bomber.playerClientId;
			ActiveBomberIds.Remove(playerClientId);
			if (!DetonatedBomberIds.Add(playerClientId))
			{
				return;
			}
			RecentExplosions.Add(new ExplosionRecord
			{
				BomberClientId = playerClientId,
				Position = explosionPosition,
				Time = Time.time
			});
			float value = Plugin.Instance.ExplosionKillRange.Value;
			float value2 = Plugin.Instance.ExplosionDamageRange.Value;
			int value3 = Plugin.Instance.ExplosionNonLethalDamage.Value;
			float value4 = Plugin.Instance.ExplosionPhysicsForce.Value;
			Plugin.Log.LogInfo((object)$"Suicide Bomber '{bomber.playerUsername}' detonated at {explosionPosition}!");
			if ((Object)(object)StartOfRound.Instance != (Object)null && StartOfRound.Instance.allPlayerScripts != null)
			{
				PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts;
				foreach (PlayerControllerB val in allPlayerScripts)
				{
					if ((Object)(object)val != (Object)null && val.isPlayerControlled && !val.isPlayerDead && !WasBomberThisRound(val.playerClientId))
					{
						float num = Vector3.Distance(((Component)val).transform.position, explosionPosition);
						if (num <= value2 + 2f)
						{
							CheckAndRegisterPotentialVictim(val, explosionPosition);
						}
					}
				}
			}
			Landmine.SpawnExplosion(explosionPosition, true, value, value2, value3, value4, (GameObject)null, true);
		}

		public static void CheckAndRegisterVictimOnDeath(PlayerControllerB victim, CauseOfDeath causeOfDeath)
		{
			//IL_0068: 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_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Invalid comparison between Unknown and I4
			if ((Object)(object)victim == (Object)null || WasBomberThisRound(victim.playerClientId))
			{
				return;
			}
			float time = Time.time;
			float num = Plugin.Instance.ExplosionDamageRange.Value + 4f;
			for (int num2 = RecentExplosions.Count - 1; num2 >= 0; num2--)
			{
				ExplosionRecord explosionRecord = RecentExplosions[num2];
				if (!(time - explosionRecord.Time > 3.5f))
				{
					float num3 = Vector3.Distance(((Component)victim).transform.position, explosionRecord.Position);
					if ((int)causeOfDeath == 3 || num3 <= num)
					{
						int num4 = (int)victim.playerClientId;
						if (BomberVictimPlayerIds.Add(num4))
						{
							Plugin.Log.LogInfo((object)$"Teammate '{victim.playerUsername}' (PlayerId: {num4}) was killed by a Suicide Bomber! Setting body value to {Plugin.Instance.TeammateBodyCreditValue.Value} credits.");
						}
						break;
					}
				}
			}
		}

		private static void CheckAndRegisterPotentialVictim(PlayerControllerB player, Vector3 explosionPosition)
		{
			//IL_0014: 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)
			if (!WasBomberThisRound(player.playerClientId))
			{
				float num = Vector3.Distance(((Component)player).transform.position, explosionPosition);
				if (num <= Plugin.Instance.ExplosionKillRange.Value + 1.5f)
				{
					int item = (int)player.playerClientId;
					BomberVictimPlayerIds.Add(item);
				}
			}
		}

		public static void ApplyVictimBodyScrapValue(RagdollGrabbableObject ragdollGrabbable)
		{
			if ((Object)(object)ragdollGrabbable == (Object)null)
			{
				return;
			}
			int bodyID = ragdollGrabbable.bodyID;
			if (!IsTeammateKilledByBomber(bodyID))
			{
				return;
			}
			int value = Plugin.Instance.TeammateBodyCreditValue.Value;
			((GrabbableObject)ragdollGrabbable).SetScrapValue(value);
			ScanNodeProperties componentInChildren = ((Component)ragdollGrabbable).GetComponentInChildren<ScanNodeProperties>();
			if ((Object)(object)componentInChildren != (Object)null)
			{
				componentInChildren.scrapValue = value;
				componentInChildren.subText = $"Value: ${value}";
			}
			if ((Object)(object)ragdollGrabbable.ragdoll != (Object)null)
			{
				ScanNodeProperties componentInChildren2 = ((Component)ragdollGrabbable.ragdoll).GetComponentInChildren<ScanNodeProperties>();
				if ((Object)(object)componentInChildren2 != (Object)null)
				{
					componentInChildren2.scrapValue = value;
					componentInChildren2.subText = $"Value: ${value}";
				}
			}
		}

		public static IEnumerator EnsureVictimBodyValueCoroutine(PlayerControllerB victim)
		{
			int playerId = (int)victim.playerClientId;
			int creditValue = Plugin.Instance.TeammateBodyCreditValue.Value;
			float elapsed = 0f;
			while (elapsed < 3.5f && IsTeammateKilledByBomber(playerId))
			{
				if ((Object)(object)victim != (Object)null && (Object)(object)victim.deadBody != (Object)null)
				{
					ScanNodeProperties componentInChildren = ((Component)victim.deadBody).GetComponentInChildren<ScanNodeProperties>();
					if ((Object)(object)componentInChildren != (Object)null)
					{
						componentInChildren.scrapValue = creditValue;
						componentInChildren.subText = $"Value: ${creditValue}";
					}
					if ((Object)(object)victim.deadBody.grabBodyObject != (Object)null)
					{
						RagdollGrabbableObject component = ((Component)victim.deadBody.grabBodyObject).GetComponent<RagdollGrabbableObject>();
						if ((Object)(object)component != (Object)null)
						{
							ApplyVictimBodyScrapValue(component);
						}
					}
				}
				RagdollGrabbableObject[] allRagdolls = Object.FindObjectsOfType<RagdollGrabbableObject>();
				RagdollGrabbableObject[] array = allRagdolls;
				foreach (RagdollGrabbableObject val in array)
				{
					if ((Object)(object)val != (Object)null && val.bodyID == playerId)
					{
						ApplyVictimBodyScrapValue(val);
					}
				}
				elapsed += 0.25f;
				yield return (object)new WaitForSeconds(0.25f);
			}
		}
	}
}
namespace SuicideBomberMod.Patches
{
	[HarmonyPatch(typeof(PlayerControllerB))]
	internal static class PlayerControllerBPatches
	{
		private static float GetTargetDescentSpeed(PlayerControllerB player)
		{
			//IL_0046: 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)
			bool flag = player.inTerminalMenu || player.isTypingChat || ((Object)(object)player.quickMenuManager != (Object)null && player.quickMenuManager.isMenuOpen);
			bool flag2 = false;
			if (!flag)
			{
				if (Keyboard.current != null)
				{
					Key value = Plugin.Instance.FastDescentKey.Value;
					if ((int)value != 0 && ((ButtonControl)Keyboard.current[value]).isPressed)
					{
						flag2 = true;
					}
				}
				if (!flag2 && (Object)(object)IngamePlayerSettings.Instance != (Object)null && (Object)(object)IngamePlayerSettings.Instance.playerInput != (Object)null)
				{
					InputAction val = IngamePlayerSettings.Instance.playerInput.actions.FindAction("Crouch", false);
					if (val != null && val.IsPressed())
					{
						flag2 = true;
					}
				}
			}
			if (!flag2)
			{
				return Plugin.Instance.NormalDescentSpeed.Value;
			}
			return Plugin.Instance.FastDescentSpeed.Value;
		}

		[HarmonyPatch("Update")]
		[HarmonyPrefix]
		private static void Update_Prefix(PlayerControllerB __instance, ref float ___fallValue, ref float ___fallValueUncapped, ref bool ___isFallingNoJump, ref bool ___takingFallDamage)
		{
			if (!((Object)(object)__instance == (Object)null) && ((NetworkBehaviour)__instance).IsOwner && __instance.isPlayerControlled && !__instance.isPlayerDead && BomberManager.IsActiveBomber(__instance))
			{
				float targetDescentSpeed = GetTargetDescentSpeed(__instance);
				___isFallingNoJump = true;
				___takingFallDamage = false;
				___fallValue = 0f - targetDescentSpeed;
				___fallValueUncapped = 0f - targetDescentSpeed;
			}
		}

		[HarmonyPatch("Update")]
		[HarmonyPostfix]
		private static void Update_Postfix(PlayerControllerB __instance, ref float ___fallValue, ref float ___fallValueUncapped, ref bool ___isFallingNoJump, ref bool ___takingFallDamage, Vector2 ___moveInputVector)
		{
			//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_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)__instance == (Object)null || !((NetworkBehaviour)__instance).IsOwner || !__instance.isPlayerControlled || __instance.isPlayerDead || !BomberManager.IsActiveBomber(__instance))
			{
				return;
			}
			bool flag = __instance.inTerminalMenu || __instance.isTypingChat || ((Object)(object)__instance.quickMenuManager != (Object)null && __instance.quickMenuManager.isMenuOpen);
			if (!flag && Keyboard.current != null)
			{
				Key value = Plugin.Instance.DetonateKey.Value;
				if ((int)value != 0 && ((ButtonControl)Keyboard.current[value]).wasPressedThisFrame)
				{
					BomberManager.TriggerLocalBomberDetonation(__instance);
					return;
				}
			}
			float targetDescentSpeed = GetTargetDescentSpeed(__instance);
			___isFallingNoJump = true;
			___takingFallDamage = false;
			___fallValue = 0f - targetDescentSpeed;
			___fallValueUncapped = 0f - targetDescentSpeed;
			if ((Object)(object)__instance.thisController != (Object)null && !__instance.thisController.isGrounded && !flag && ((Vector2)(ref ___moveInputVector)).sqrMagnitude > 0.001f)
			{
				Vector3 val = ((Component)__instance).transform.right * ___moveInputVector.x + ((Component)__instance).transform.forward * ___moveInputVector.y;
				if (((Vector3)(ref val)).sqrMagnitude > 1f)
				{
					((Vector3)(ref val)).Normalize();
				}
				float value2 = Plugin.Instance.HorizontalSteerSpeed.Value;
				__instance.thisController.Move(val * (value2 * Time.deltaTime));
			}
			if (Plugin.Instance.DetonateOnGroundImpact.Value && (Object)(object)__instance.thisController != (Object)null && __instance.thisController.isGrounded && BomberManager.HasPassedSpawnGracePeriod(__instance))
			{
				BomberManager.TriggerLocalBomberDetonation(__instance);
			}
		}

		[HarmonyPatch("PlayerHitGroundEffects")]
		[HarmonyPrefix]
		private static bool PlayerHitGroundEffects_Prefix(PlayerControllerB __instance)
		{
			if (!BomberManager.IsActiveBomber(__instance))
			{
				return true;
			}
			if (Plugin.Instance.DetonateOnGroundImpact.Value && BomberManager.HasPassedSpawnGracePeriod(__instance))
			{
				BomberManager.TriggerLocalBomberDetonation(__instance);
			}
			return false;
		}

		[HarmonyPrefix]
		[HarmonyPatch("KillPlayer")]
		private static void KillPlayer_Prefix(PlayerControllerB __instance, CauseOfDeath causeOfDeath)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//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)
			if (!((Object)(object)__instance == (Object)null) && !__instance.isPlayerDead)
			{
				if (BomberManager.IsActiveBomber(__instance))
				{
					BomberManager.ExecuteExplosionOnClient(__instance, ((Component)__instance).transform.position + Vector3.up * 0.5f);
				}
				else
				{
					BomberManager.CheckAndRegisterVictimOnDeath(__instance, causeOfDeath);
				}
			}
		}

		[HarmonyPatch("KillPlayerClientRpc")]
		[HarmonyPrefix]
		private static void KillPlayerClientRpc_Prefix(PlayerControllerB __instance, int playerId, int causeOfDeath)
		{
			//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_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)
			if (!((Object)(object)__instance == (Object)null))
			{
				if (BomberManager.IsActiveBomber(__instance))
				{
					BomberManager.ExecuteExplosionOnClient(__instance, ((Component)__instance).transform.position + Vector3.up * 0.5f);
				}
				else
				{
					BomberManager.CheckAndRegisterVictimOnDeath(__instance, (CauseOfDeath)causeOfDeath);
				}
			}
		}

		[HarmonyPatch("SpawnDeadBody")]
		[HarmonyPostfix]
		private static void SpawnDeadBody_Postfix(PlayerControllerB __instance, int playerId, int causeOfDeath, PlayerControllerB deadPlayerController)
		{
			if (!((Object)(object)deadPlayerController == (Object)null))
			{
				if (!BomberManager.WasBomberThisRound(deadPlayerController.playerClientId))
				{
					BomberManager.CheckAndRegisterVictimOnDeath(deadPlayerController, (CauseOfDeath)causeOfDeath);
				}
				if (BomberManager.IsTeammateKilledByBomber(playerId))
				{
					((MonoBehaviour)__instance).StartCoroutine(BomberManager.EnsureVictimBodyValueCoroutine(deadPlayerController));
				}
			}
		}
	}
}
namespace SuicideBomberMod
{
	[BepInPlugin("lethalcompany.suicidebomber", "Suicide Bomber Mod", "1.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string ModGUID = "lethalcompany.suicidebomber";

		public const string ModName = "Suicide Bomber Mod";

		public const string ModVersion = "1.0.0";

		private readonly Harmony _harmony = new Harmony("lethalcompany.suicidebomber");

		public static Plugin Instance { get; private set; }

		internal static ManualLogSource Log { get; private set; }

		public ConfigEntry<float> SpawnChancePercent { get; private set; }

		public ConfigEntry<int> MaxBombersPerRound { get; private set; }

		public ConfigEntry<float> SpawnHeightAboveShip { get; private set; }

		public ConfigEntry<float> NormalDescentSpeed { get; private set; }

		public ConfigEntry<float> FastDescentSpeed { get; private set; }

		public ConfigEntry<float> HorizontalSteerSpeed { get; private set; }

		public ConfigEntry<Key> FastDescentKey { get; private set; }

		public ConfigEntry<Key> DetonateKey { get; private set; }

		public ConfigEntry<bool> DetonateOnGroundImpact { get; private set; }

		public ConfigEntry<int> TeammateBodyCreditValue { get; private set; }

		public ConfigEntry<float> ExplosionKillRange { get; private set; }

		public ConfigEntry<float> ExplosionDamageRange { get; private set; }

		public ConfigEntry<int> ExplosionNonLethalDamage { get; private set; }

		public ConfigEntry<float> ExplosionPhysicsForce { get; private set; }

		private void Awake()
		{
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			BindConfiguration();
			_harmony.PatchAll(typeof(Plugin).Assembly);
			Log.LogInfo((object)string.Format("{0} v{1} loaded! (Spawn chance: {2}%, Max bombers: {3}, Body value: {4} credits)", "Suicide Bomber Mod", "1.0.0", SpawnChancePercent.Value, MaxBombersPerRound.Value, TeammateBodyCreditValue.Value));
		}

		private void BindConfiguration()
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Expected O, but got Unknown
			SpawnChancePercent = ((BaseUnityPlugin)this).Config.Bind<float>("1. Spawn Settings", "SpawnChancePercent", 5f, new ConfigDescription("Percentage chance (0-100) for each player to spawn as a Suicide Bomber when landing on a moon.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), new object[0]));
			MaxBombersPerRound = ((BaseUnityPlugin)this).Config.Bind<int>("1. Spawn Settings", "MaxBombersPerRound", 2, new ConfigDescription("Maximum number of players that can become a Suicide Bomber in a single round.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 16), new object[0]));
			SpawnHeightAboveShip = ((BaseUnityPlugin)this).Config.Bind<float>("1. Spawn Settings", "SpawnHeightAboveShip", 45f, "Height (in meters) above the ship landing area where Suicide Bombers spawn and begin floating down.");
			NormalDescentSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("2. Movement & Controls", "NormalDescentSpeed", 1.5f, "Gentle downward floating speed (meters/sec) when not holding the fast-descent button.");
			FastDescentSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("2. Movement & Controls", "FastDescentSpeed", 14f, "Downward dive speed (meters/sec) while holding the fast-descent button (Left Ctrl / Crouch).");
			HorizontalSteerSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("2. Movement & Controls", "HorizontalSteerSpeed", 7.5f, "Horizontal air-steering speed (meters/sec) using WASD while floating down.");
			FastDescentKey = ((BaseUnityPlugin)this).Config.Bind<Key>("2. Movement & Controls", "FastDescentKey", (Key)55, "Key to hold (in addition to the in-game Crouch action) to move downwards faster while floating.");
			DetonateKey = ((BaseUnityPlugin)this).Config.Bind<Key>("2. Movement & Controls", "DetonateKey", (Key)16, "Key to press to manually detonate at any time while active as a Suicide Bomber.");
			DetonateOnGroundImpact = ((BaseUnityPlugin)this).Config.Bind<bool>("2. Movement & Controls", "DetonateOnGroundImpact", true, "If true, the Suicide Bomber automatically explodes upon touching the ground or ship roof.");
			TeammateBodyCreditValue = ((BaseUnityPlugin)this).Config.Bind<int>("3. Explosion & Rewards", "TeammateBodyCreditValue", 200, "Value in credits of a teammate's dead body when killed by a Suicide Bomber.");
			ExplosionKillRange = ((BaseUnityPlugin)this).Config.Bind<float>("3. Explosion & Rewards", "ExplosionKillRange", 7.5f, "Instant kill radius (in meters) of the Suicide Bomber explosion.");
			ExplosionDamageRange = ((BaseUnityPlugin)this).Config.Bind<float>("3. Explosion & Rewards", "ExplosionDamageRange", 11f, "Non-lethal damage radius (in meters) of the Suicide Bomber explosion.");
			ExplosionNonLethalDamage = ((BaseUnityPlugin)this).Config.Bind<int>("3. Explosion & Rewards", "ExplosionNonLethalDamage", 60, "Damage dealt to players in the outer damage range.");
			ExplosionPhysicsForce = ((BaseUnityPlugin)this).Config.Bind<float>("3. Explosion & Rewards", "ExplosionPhysicsForce", 35f, "Physics knockback force applied by the explosion.");
		}
	}
}
namespace SuicideBomberMod.Patches
{
	[HarmonyPatch(typeof(RagdollGrabbableObject))]
	internal static class RagdollGrabbableObjectPatches
	{
		[HarmonyPostfix]
		[HarmonyPatch("Start")]
		private static void Start_Postfix(RagdollGrabbableObject __instance)
		{
			BomberManager.ApplyVictimBodyScrapValue(__instance);
		}

		[HarmonyPostfix]
		[HarmonyPatch("Update")]
		private static void Update_Postfix(RagdollGrabbableObject __instance)
		{
			if (!((Object)(object)__instance == (Object)null))
			{
				int bodyID = __instance.bodyID;
				int value = Plugin.Instance.TeammateBodyCreditValue.Value;
				if (((GrabbableObject)__instance).scrapValue != value && BomberManager.IsTeammateKilledByBomber(bodyID))
				{
					BomberManager.ApplyVictimBodyScrapValue(__instance);
				}
			}
		}
	}
	[HarmonyPatch(typeof(StartOfRound))]
	internal static class StartOfRoundPatches
	{
		[HarmonyPrefix]
		[HarmonyPatch("openingDoorsSequence")]
		private static void OpeningDoorsSequence_Prefix(StartOfRound __instance)
		{
			BomberManager.SelectAndSpawnBombers(__instance);
		}

		[HarmonyPatch("TeleportPlayerInShipIfOutOfRoomBounds")]
		[HarmonyPrefix]
		private static bool TeleportPlayerInShipIfOutOfRoomBounds_Prefix()
		{
			if ((Object)(object)GameNetworkManager.Instance != (Object)null && BomberManager.IsActiveBomber(GameNetworkManager.Instance.localPlayerController))
			{
				return false;
			}
			return true;
		}

		[HarmonyPatch("EndOfGame")]
		[HarmonyPostfix]
		private static void EndOfGame_Postfix()
		{
			BomberManager.ResetRoundState();
		}
	}
}