Decompiled source of BetterSpectate v1.0.0

BetterSpectate.dll

Decompiled 6 months ago
using 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);
			}
		}
	}
}