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 BetterSpectate v1.0.0
BetterSpectate.dll
Decompiled 2 years agousing System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using BetterSpectate.Compatibility; using BetterSpectate.Patches; using GameNetcodeStuff; using HarmonyLib; using SpectateEnemy; using UnityEngine; using UnityEngine.InputSystem; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("BetterSpectate")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("BetterSpectate")] [assembly: AssemblyCopyright("Copyright © 2024")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("89d76a0b-65c7-4246-8645-3d523e10258e")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.0.0")] namespace BetterSpectate { public class BetterSpectateAPI { public bool isModLoaded => (Object)(object)BetterSpectateBase.instance != (Object)null; public bool isFirstPersonSpectateEnabled => PlayerControllerB_Patch.GetFirstPersonEnabled(); public bool isZoomEnabled => PlayerControllerB_Patch.GetZoomEnabled(); public bool isFirstPersonSpectating => PlayerControllerB_Patch.IsPlayerInFirstPerson(); public float zoomDistance => PlayerControllerB_Patch.GetZoomDistance(); public float zoomSpeed => PlayerControllerB_Patch.GetZoomSpeed(); public float maxZoom => SpectateUtils.GetMaxZoom(); public float minZoom => SpectateUtils.GetMinZoom(); } public class ConfigSetup { private const string CONFIG_FILE_NAME = "Fusition.BetterSpectate"; private static ConfigFile config; private ConfigEntry<bool> isZoomEnabled; private ConfigEntry<float> maxZoomDistance; private ConfigEntry<float> minZoomDistance; private ConfigEntry<float> defaultZoomDistance; private ConfigEntry<float> zoomSpeed; private ConfigEntry<bool> isFirstPersonEnabled; private ConfigEntry<bool> isFirstPersonDefault; private ConfigEntry<string> firstPersonKeybind; public static void Initialize() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown string text = Path.Combine(Paths.ConfigPath, "Fusition.BetterSpectate.cfg"); config = new ConfigFile(text, true); ConfigEntry<bool> val = config.Bind<bool>("Zoom Settings", "Third Person Zoom Enabled", true, "Allows spectators to zoom in and out."); ConfigEntry<float> val2 = config.Bind<float>("Zoom Settings", "Max Zoom Distance", 15f, "Furthest distance a spectator can zoom out."); ConfigEntry<float> val3 = config.Bind<float>("Zoom Settings", "Min Zoom Distance", 1f, "Closest distance a spectator can zoom in."); ConfigEntry<float> val4 = config.Bind<float>("Zoom Settings", "Default Zoom Distance", 1.4f, "Distance a spectator is set upon death."); ConfigEntry<float> val5 = config.Bind<float>("Zoom Settings", "Zoom Speed", 0.4f, "Speed that scrolling will zoom in or out."); ConfigEntry<bool> val6 = config.Bind<bool>("General Settings", "First Person Spectate Enabled", true, "Allows spectators to enter a first person view."); ConfigEntry<bool> val7 = config.Bind<bool>("General Settings", "Default to First Person", true, "Defaults players to a first person view on death."); ConfigEntry<string> val8 = config.Bind<string>("General Settings", "First Person Keybind", "P", "Sets the keybind to switch between perspectives."); PlayerControllerB_Patch.SetFirstPersonEnabled(val6.Value); PlayerControllerB_Patch.SetFirstPersonToggle(val7.Value); PlayerControllerB_Patch.InitializeFirstPersonSpectateInputAction("<Keyboard>/" + val8.Value); PlayerControllerB_Patch.SetZoomEnabled(val.Value); PlayerControllerB_Patch.SetZoomDistance(val4.Value); PlayerControllerB_Patch.SetDefaultZoomDistance(val4.Value); PlayerControllerB_Patch.SetZoomSpeed(val5.Value); SpectateUtils.SetMaxZoom(val2.Value); SpectateUtils.SetMinZoom(val3.Value); } } [BepInPlugin("Fusition.BetterSpectate", "BetterSpectate", "1.0.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class BetterSpectateBase : BaseUnityPlugin { public const string MOD_GUID = "Fusition.BetterSpectate"; private const string MOD_NAME = "BetterSpectate"; private const string MOD_VERS = "1.0.0.0"; private readonly Harmony harmony = new Harmony("Fusition.BetterSpectate"); public static BetterSpectateBase instance; internal static ManualLogSource fusLogSource; private void Awake() { if ((Object)(object)instance == (Object)null) { instance = this; } ((Object)((Component)this).gameObject).hideFlags = (HideFlags)61; fusLogSource = Logger.CreateLogSource("Fusition.BetterSpectate"); fusLogSource.LogInfo((object)SpectateEnemyCompat.enabled); ConfigSetup.Initialize(); harmony.PatchAll(typeof(PlayerControllerB_Patch)); harmony.PatchAll(typeof(StartOfRound_Patch)); } } } namespace BetterSpectate.Patches { [HarmonyPatch(typeof(PlayerControllerB))] public class PlayerControllerB_Patch { private static bool isZoomEnabled; private static float zoomDistance = 1.4f; private static float zoomSpeed = 0.4f; private static float defaultZoomDistance; private static bool isFirstPersonEnabled; private static InputAction firstPersonSpectateAction; private static bool firstPersonSpectateToggle = false; private static bool isInFirstPerson = false; private static bool inputDisabledForCompat = false; [HarmonyPatch(typeof(PlayerControllerB), "LateUpdate")] [HarmonyPrefix] public static void LateUpdate_Patch(PlayerControllerB __instance) { //IL_0077: 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) if ((Object)(object)__instance == (Object)(object)GameNetworkManager.Instance.localPlayerController && (Object)(object)__instance.spectatedPlayerScript != (Object)null) { if (__instance.spectatedPlayerScript.isHoldingObject) { __instance.spectatedPlayerScript.currentlyHeldObjectServer.parentObject = (firstPersonSpectateToggle ? ((Component)__instance.spectatedPlayerScript.localItemHolder).transform : ((Component)__instance.spectatedPlayerScript.serverItemHolder).transform); } if (UnityInput.Current.mouseScrollDelta.y != 0f && isZoomEnabled) { ((Renderer)__instance.thisPlayerModel).enabled = false; SetZoomDistance((zoomDistance + UnityInput.Current.mouseScrollDelta.y / -120f * zoomSpeed).ZoomClamp()); } if (SpectateEnemyCompat.enabled) { SpectateEnemyCompat.CheckIfSpectatingEnemies(); } if (!inputDisabledForCompat && firstPersonSpectateAction.WasPressedThisFrame()) { SwitchPerspective(__instance); } } } [HarmonyPatch(typeof(PlayerControllerB), "RaycastSpectateCameraAroundPivot")] [HarmonyPrefix] public static bool RaycastSpectateCameraAroundPivot_Patch(PlayerControllerB __instance, RaycastHit ___hit, int ___walkableSurfacesNoPlayersMask) { //IL_004b: 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_0086: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance.spectatedPlayerScript != (Object)null) { Transform transform = ((Component)__instance.spectatedPlayerScript.visorCamera).transform; if (isFirstPersonEnabled && firstPersonSpectateToggle) { ((Component)__instance.playersManager.spectateCamera).transform.position = transform.position; ((Component)__instance.playersManager.spectateCamera).transform.rotation = transform.rotation; isInFirstPerson = true; } else if (isZoomEnabled) { RaycastCameraToZoomDistance(__instance, ___hit, ___walkableSurfacesNoPlayersMask); } return false; } return true; } [HarmonyPatch(typeof(PlayerControllerB), "SpectateNextPlayer")] [HarmonyPrefix] public static bool SpectateNextPlayer_Patch(PlayerControllerB __instance, RaycastHit ___hit, int ___walkableSurfacesNoPlayersMask) { //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) int num = 0; if ((Object)(object)__instance.spectatedPlayerScript != (Object)null) { num = (int)__instance.spectatedPlayerScript.playerClientId; if ((Object)(object)__instance == (Object)(object)GameNetworkManager.Instance.localPlayerController) { SetModelVisibilityForThirdPerson(__instance.spectatedPlayerScript); } } for (int i = 0; i < __instance.playersManager.allPlayerScripts.Length; i++) { num = (num + 1) % __instance.playersManager.allPlayerScripts.Length; if (__instance.playersManager.allPlayerScripts[num].isPlayerDead || !__instance.playersManager.allPlayerScripts[num].isPlayerControlled || !((Object)(object)__instance.playersManager.allPlayerScripts[num] != (Object)(object)__instance)) { continue; } __instance.spectatedPlayerScript = __instance.playersManager.allPlayerScripts[num]; if ((Object)(object)__instance == (Object)(object)GameNetworkManager.Instance.localPlayerController) { if (!firstPersonSpectateToggle) { BetterSpectateBase.fusLogSource.LogInfo((object)"Model visibility adjusted for dead player in third person"); SetModelVisibilityForThirdPerson(__instance.spectatedPlayerScript); } else { BetterSpectateBase.fusLogSource.LogInfo((object)"Model visibility adjusted for dead player in first person"); SetModelVisibilityForFirstPerson(__instance.spectatedPlayerScript); } } __instance.SetSpectatedPlayerEffects(false); return false; } if ((Object)(object)__instance.deadBody != (Object)null && ((Component)__instance.deadBody).gameObject.activeSelf) { __instance.spectateCameraPivot.position = __instance.deadBody.bodyParts[0].position; RaycastSpectateCameraAroundPivot_Patch(__instance, ___hit, ___walkableSurfacesNoPlayersMask); } StartOfRound.Instance.SetPlayerSafeInShip(); return false; } public static void InitializeFirstPersonSpectateInputAction(string binding) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected O, but got Unknown firstPersonSpectateAction = new InputAction("FirstPersonSpectatePressed", (InputActionType)0, binding, (string)null, (string)null, (string)null); firstPersonSpectateAction.Enable(); } public static bool IsPlayerInFirstPerson() { return isInFirstPerson; } public static bool GetZoomEnabled() { return isZoomEnabled; } public static void SetZoomEnabled(bool enabled) { isZoomEnabled = true; } public static bool GetFirstPersonEnabled() { return isFirstPersonEnabled; } public static void SetFirstPersonEnabled(bool enabled) { isFirstPersonEnabled = true; } public static void SwitchPerspective(PlayerControllerB controller) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) firstPersonSpectateToggle = !firstPersonSpectateToggle; if (!firstPersonSpectateToggle) { BetterSpectateBase.fusLogSource.LogInfo((object)"Player Toggled to Third Person"); SetModelVisibilityForThirdPerson(controller.spectatedPlayerScript); ((Component)controller.spectateCameraPivot).transform.rotation = ((Component)controller.spectatedPlayerScript.visorCamera).transform.rotation; zoomDistance = defaultZoomDistance; } else { BetterSpectateBase.fusLogSource.LogInfo((object)"Player Toggled to First Person"); SetModelVisibilityForFirstPerson(controller.spectatedPlayerScript); } } public static void SetFirstPersonToggle(bool value) { firstPersonSpectateToggle = value; } public static void SetModelVisibilityForFirstPerson(PlayerControllerB controller) { ((Renderer)controller.thisPlayerModelArms).enabled = true; ((Renderer)controller.thisPlayerModel).enabled = false; ((Renderer)controller.thisPlayerModelLOD1).enabled = false; ((Renderer)controller.thisPlayerModelLOD2).enabled = false; } public static void SetModelVisibilityForThirdPerson(PlayerControllerB controller) { ((Renderer)controller.thisPlayerModelArms).enabled = false; ((Renderer)controller.thisPlayerModel).enabled = true; ((Renderer)controller.thisPlayerModelLOD1).enabled = true; ((Renderer)controller.thisPlayerModelLOD2).enabled = true; } public static float GetZoomDistance() { return zoomDistance; } public static void SetZoomDistance(float value) { zoomDistance = value; } public static void SetDefaultZoomDistance(float value) { defaultZoomDistance = value; } public static float GetZoomSpeed() { return zoomSpeed; } public static void SetZoomSpeed(float value) { zoomSpeed = value; } public static void SetInputDisabled(bool value) { inputDisabledForCompat = value; } private static void RaycastCameraToZoomDistance(PlayerControllerB controller, RaycastHit hit, int walkableSurfacesNoPlayersMask) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //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) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0082: 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) Ray val = default(Ray); ((Ray)(ref val))..ctor(controller.spectateCameraPivot.position, -controller.spectateCameraPivot.forward); if (Physics.Raycast(val, ref hit, zoomDistance, walkableSurfacesNoPlayersMask, (QueryTriggerInteraction)1)) { ((Component)controller.playersManager.spectateCamera).transform.position = ((Ray)(ref val)).GetPoint(((RaycastHit)(ref hit)).distance - 0.25f); } else { ((Component)controller.playersManager.spectateCamera).transform.position = ((Ray)(ref val)).GetPoint(zoomDistance - 0.1f); } ((Component)controller.playersManager.spectateCamera).transform.LookAt(controller.spectateCameraPivot); } } internal static class SpectateUtils { private static float maxZoomDistance; private static float minZoomDistance; public static float ZoomClamp(this float value) { if (maxZoomDistance > minZoomDistance) { if (value > maxZoomDistance) { return maxZoomDistance; } if (value < minZoomDistance) { return minZoomDistance; } return value; } return PlayerControllerB_Patch.GetZoomDistance(); } public static float GetMaxZoom() { return maxZoomDistance; } public static void SetMaxZoom(float value) { maxZoomDistance = value; } public static float GetMinZoom() { return minZoomDistance; } public static void SetMinZoom(float value) { minZoomDistance = value; } } [HarmonyPatch(typeof(StartOfRound))] public class StartOfRound_Patch { [HarmonyPatch(typeof(StartOfRound), "ReviveDeadPlayers")] [HarmonyPostfix] public static void ReviveDeadPlayers_Patch(PlayerControllerB[] ___allPlayerScripts) { for (int i = 0; i < ___allPlayerScripts.Length; i++) { if ((Object)(object)___allPlayerScripts[i] != (Object)(object)GameNetworkManager.Instance.localPlayerController) { ((Renderer)___allPlayerScripts[i].thisPlayerModelArms).enabled = false; ((Renderer)___allPlayerScripts[i].thisPlayerModel).enabled = true; ((Renderer)___allPlayerScripts[i].thisPlayerModelLOD1).enabled = true; ((Renderer)___allPlayerScripts[i].thisPlayerModelLOD2).enabled = true; if (___allPlayerScripts[i].isHoldingObject) { ___allPlayerScripts[i].currentlyHeldObjectServer.parentObject = ((Component)___allPlayerScripts[i].serverItemHolder).transform; } } } } } } namespace BetterSpectate.Compatibility { public static class SpectateEnemyCompat { private static bool? _enabled; public static bool enabled { get { if (!_enabled.HasValue) { _enabled = Chainloader.PluginInfos.ContainsKey("SpectateEnemy"); } return _enabled.Value; } } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] public static void CheckIfSpectatingEnemies() { if (SpectateEnemiesAPI.IsLoaded && SpectateEnemiesAPI.IsSpectatingEnemies) { PlayerControllerB_Patch.SetFirstPersonToggle(value: false); PlayerControllerB_Patch.SetInputDisabled(value: true); } } } }