Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of SuicideBomberMod v1.0.0
BepInEx/plugins/SuicideBomberMod/SuicideBomberMod.dll
Decompiled 3 hours agousing 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(); } } }