using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using GameNetcodeStuff; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using Unity.Netcode; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("SpectateEnemy")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyDescription("My first plugin")] [assembly: AssemblyFileVersion("1.3.0.0")] [assembly: AssemblyInformationalVersion("1.3.0+28a7d0fc18c0b8500f41a159031458ea920f633a")] [assembly: AssemblyProduct("SpectateEnemy")] [assembly: AssemblyTitle("SpectateEnemy")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.3.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace SpectateEnemy { [BepInPlugin("SpectateEnemy", "SpectateEnemy", "1.3.0")] public class Plugin : BaseUnityPlugin { public static int spectatedEnemyIndex = -1; public static bool spectatingEnemies = false; public static MethodInfo raycastSpectate = null; private ConfigEntry spectateTurrets; private ConfigEntry spectateLandmines; private ConfigEntry spectatePassives; public static bool doSpectateTurrets; public static bool doSpectateLandmines; public static bool doSpectatePassives; private Harmony harmony; private void Awake() { //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Expected O, but got Unknown spectateTurrets = ((BaseUnityPlugin)this).Config.Bind("Config", "Spectate Turrets", false, "Enables spectating turrets."); doSpectateTurrets = spectateTurrets.Value; spectateLandmines = ((BaseUnityPlugin)this).Config.Bind("Config", "Spectate Landmines", false, "Enables spectating landmines."); doSpectateLandmines = spectateLandmines.Value; spectatePassives = ((BaseUnityPlugin)this).Config.Bind("Config", "Spectate Passives", false, "Enables spectating passive enemies, such as Docile Locust Bees and Manticoils."); doSpectatePassives = spectatePassives.Value; harmony = new Harmony("SpectateEnemy"); harmony.PatchAll(); raycastSpectate = AccessTools.Method(typeof(PlayerControllerB), "RaycastSpectateCameraAroundPivot", (Type[])null, (Type[])null); ((BaseUnityPlugin)this).Logger.LogInfo((object)"SpectateEnemy loaded!"); } } public class Spectatable : MonoBehaviour { public SpectatableType type = SpectatableType.Enemy; public string enemyName = "Enemy"; } public enum SpectatableType { Enemy, Turret, Landmine } public static class MyPluginInfo { public const string PLUGIN_GUID = "SpectateEnemy"; public const string PLUGIN_NAME = "SpectateEnemy"; public const string PLUGIN_VERSION = "1.3.0"; } } namespace SpectateEnemy.Patches { [HarmonyPatch(typeof(EnemyAI), "Start")] public class EnemyAI_Patches { private static void Postfix(EnemyAI __instance) { if (Plugin.doSpectatePassives || (!(__instance.enemyType.enemyName == "Docile Locust Bees") && !(__instance.enemyType.enemyName == "Manticoil"))) { Spectatable spectatable = ((Component)__instance).gameObject.AddComponent(); spectatable.enemyName = __instance.enemyType.enemyName; } } } [HarmonyPatch(typeof(GameNetworkManager), "Disconnect")] public class GameNetworkManager_Patches { private static void Postfix() { Plugin.spectatedEnemyIndex = -1; Plugin.spectatingEnemies = false; } } [HarmonyPatch(typeof(HUDManager), "Update")] public class HUDManager_Patches { private static void Postfix(HUDManager __instance) { //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) if (!GameNetworkManager.Instance.localPlayerController.isPlayerDead) { return; } if (StartOfRound.Instance.shipIsLeaving) { Light component = ((Component)__instance.playersManager.spectateCamera).GetComponent(); if ((Object)(object)component != (Object)null) { ((Behaviour)component).enabled = false; } return; } TextMeshProUGUI holdButtonToEndGameEarlyText = __instance.holdButtonToEndGameEarlyText; ((TMP_Text)holdButtonToEndGameEarlyText).text = ((TMP_Text)holdButtonToEndGameEarlyText).text + "\n\n\n\n\nSwitch to " + (Plugin.spectatingEnemies ? "Players" : "Enemies") + ": [E]\nToggle Flashlight : [RMB] (Click)"; MovementActions movement = __instance.playerActions.Movement; if (((MovementActions)(ref movement)).PingScan.WasReleasedThisFrame()) { Light component2 = ((Component)__instance.playersManager.spectateCamera).GetComponent(); if ((Object)(object)component2 != (Object)null) { ((Behaviour)component2).enabled = !((Behaviour)component2).enabled; } } } } [HarmonyPatch(typeof(Landmine), "Start")] public class Landmine_Patches { private static void Postfix(Landmine __instance) { if (Plugin.doSpectateLandmines) { Spectatable spectatable = ((Component)__instance).gameObject.AddComponent(); spectatable.type = SpectatableType.Landmine; spectatable.enemyName = "Landmine"; } } } internal class Handler { public static Spectatable[] spectatorList; public static bool Spectate() { if (Plugin.spectatingEnemies) { if (spectatorList.Length == 0) { Plugin.spectatingEnemies = false; return true; } Plugin.spectatedEnemyIndex++; if (Plugin.spectatedEnemyIndex >= spectatorList.Length) { Plugin.spectatedEnemyIndex = 0; } return false; } return true; } } [HarmonyPatch(typeof(PlayerControllerB), "Interact_performed")] public class PlayerControllerB_Interact { private static bool Prefix(PlayerControllerB __instance) { if (((NetworkBehaviour)__instance).IsOwner && __instance.isPlayerDead && (!((NetworkBehaviour)__instance).IsServer || __instance.isHostPlayerObject)) { Plugin.spectatingEnemies = !Plugin.spectatingEnemies; if (Plugin.spectatingEnemies) { Handler.spectatorList = Object.FindObjectsByType((FindObjectsSortMode)0); if (Handler.spectatorList.Length == 0) { Plugin.spectatingEnemies = false; return true; } if (Plugin.spectatedEnemyIndex == -1 || Plugin.spectatedEnemyIndex >= Handler.spectatorList.Length) { Plugin.spectatedEnemyIndex = 0; } __instance.spectatedPlayerScript = null; } else { __instance.spectatedPlayerScript = ((IEnumerable)__instance.playersManager.allPlayerScripts).FirstOrDefault((Func)((PlayerControllerB x) => !x.isPlayerDead)); } } return true; } } [HarmonyPatch(typeof(PlayerControllerB), "ActivateItem_performed")] public class PlayerControllerB_Use { private static bool Prefix(PlayerControllerB __instance) { if (((NetworkBehaviour)__instance).IsOwner && __instance.isPlayerDead && !StartOfRound.Instance.shipIsLeaving && (!((NetworkBehaviour)__instance).IsServer || __instance.isHostPlayerObject)) { return Handler.Spectate(); } return true; } } [HarmonyPatch(typeof(PlayerControllerB), "LateUpdate")] public class PlayerControllerB_LateUpdate { private static void Postfix(PlayerControllerB __instance) { //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.spectatingEnemies) { return; } if (Handler.spectatorList.Length == 0) { Plugin.spectatingEnemies = false; return; } if (Plugin.spectatedEnemyIndex >= Handler.spectatorList.Length) { Plugin.spectatedEnemyIndex = 0; } Spectatable obj = Handler.spectatorList[Plugin.spectatedEnemyIndex]; if ((Object)(object)obj == (Object)null) { Plugin.spectatedEnemyIndex++; if (Plugin.spectatedEnemyIndex >= Handler.spectatorList.Length) { Plugin.spectatedEnemyIndex = 0; } return; } Vector3? spectatePosition = GetSpectatePosition(obj); if (!spectatePosition.HasValue) { Plugin.spectatedEnemyIndex++; if (Plugin.spectatedEnemyIndex >= Handler.spectatorList.Length) { Plugin.spectatedEnemyIndex = 0; } return; } if (obj.enemyName == "Enemy") { TryFixName(ref obj); } __instance.spectateCameraPivot.position = spectatePosition.Value + GetZoomDistance(obj); ((TMP_Text)HUDManager.Instance.spectatingPlayerText).text = "(Spectating: " + obj.enemyName + ")"; Plugin.raycastSpectate.Invoke(__instance, Array.Empty()); } private static void TryFixName(ref Spectatable obj) { EnemyAI val = default(EnemyAI); Turret val2 = default(Turret); Landmine val3 = default(Landmine); if (((Component)obj).gameObject.TryGetComponent(ref val)) { obj.enemyName = val.enemyType.enemyName; } else if (((Component)obj).gameObject.TryGetComponent(ref val2)) { obj.enemyName = "Turret"; } else if (((Component)obj).gameObject.TryGetComponent(ref val3)) { obj.enemyName = "Landmine"; } } private static Vector3 GetZoomDistance(Spectatable obj) { //IL_0015: 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_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: 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_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) if (obj.enemyName == "ForestGiant") { return Vector3.up * 3f; } if (obj.enemyName == "MouthDog" || obj.enemyName == "Jester") { return Vector3.up * 2f; } return Vector3.up; } private static Vector3? GetSpectatePosition(Spectatable obj) { //IL_00b2: 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_0043: 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) if (obj.type == SpectatableType.Enemy) { EnemyAI component = ((Component)obj).GetComponent(); if ((Object)(object)component != (Object)null) { return ((Object)(object)component.eye == (Object)null) ? ((Component)component).transform.position : component.eye.position; } } else if (obj.type == SpectatableType.Turret) { Turret component2 = ((Component)obj).GetComponent(); if ((Object)(object)component2 != (Object)null) { return ((Component)component2.centerPoint).transform.position; } } else { if (obj.type == SpectatableType.Landmine) { return ((Component)obj).transform.position; } Debug.LogError((object)("[SpectateEnemy]: Error when spectating: no handler for SpectatableType " + obj.type)); } return null; } } [HarmonyPatch(typeof(PlayerControllerB), "SpectateNextPlayer")] public class PlayerControllerB_SpectateNext { private static bool Prefix() { return !Plugin.spectatingEnemies; } } [HarmonyPatch(typeof(StartOfRound), "ShipLeave")] public class StartOfRound_Patches { private static void Postfix() { Plugin.spectatedEnemyIndex = -1; Plugin.spectatingEnemies = false; } } [HarmonyPatch(typeof(Turret), "Start")] public class Turret_Patches { private static void Postfix(Turret __instance) { if (Plugin.doSpectateTurrets) { Spectatable spectatable = ((Component)__instance).gameObject.AddComponent(); spectatable.type = SpectatableType.Turret; spectatable.enemyName = "Turret"; } } } }