using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BasedSpectate.Behaviours; using BasedSpectate.Compat; using BasedSpectate.Patches; using BasedSpectate.Utils; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using DunGen; using GameNetcodeStuff; using HarmonyLib; using LethalCompanyInputUtils.Api; using LethalCompanyInputUtils.BindingPathEnums; using LethalConfig; using LethalConfig.ConfigItems; using LethalConfig.ConfigItems.Options; using Microsoft.CodeAnalysis; using TMPro; using Unity.Netcode; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.InputSystem.Utilities; using UnityEngine.Rendering.HighDefinition; using UnityEngine.UI; [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("BasedSpectateMode")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("0.0.3.0")] [assembly: AssemblyInformationalVersion("0.0.3")] [assembly: AssemblyProduct("BasedSpectateMode")] [assembly: AssemblyTitle("BasedSpectateMode")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.0.3.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [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 BasedSpectate { internal enum SpectateMode { Vanilla, Freecam } internal static class Config { internal static ConfigEntry StartMode { get; private set; } internal static ConfigEntry EnableModeToggle { get; private set; } internal static ConfigEntry ShowNextPlayerHint { get; private set; } internal static ConfigEntry ShowToggleModeHint { get; private set; } internal static ConfigEntry ShowBrightnessHint { get; private set; } internal static ConfigEntry ShowZoomHint { get; private set; } internal static ConfigEntry DefaultZoomDistance { get; private set; } internal static ConfigEntry MinZoomDistance { get; private set; } internal static ConfigEntry MaxZoomDistance { get; private set; } internal static ConfigEntry ZoomSpeed { get; private set; } internal static ConfigEntry CameraClipsThroughWalls { get; private set; } internal static void Bind(ConfigFile config) { //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Expected O, but got Unknown //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Expected O, but got Unknown //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Expected O, but got Unknown //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Expected O, but got Unknown StartMode = config.Bind("General", "Start spectate mode", SpectateMode.Vanilla, "Choose which spectate mode you start with after you die"); EnableModeToggle = config.Bind("General", "Enable mode toggle", true, "Allow switching spectate mode while dead"); ShowNextPlayerHint = config.Bind("General", "Show next player hint", true, "Show the Next Player line on the death screen"); ShowToggleModeHint = config.Bind("General", "Show toggle mode hint", true, "Show the Toggle Mode line on the death screen"); ShowBrightnessHint = config.Bind("General", "Show brightness hint", true, "Show the Brightness line on the death screen"); ShowZoomHint = config.Bind("General", "Show zoom hint", true, "Show the Zoom line on the death screen"); DefaultZoomDistance = config.Bind("Zoom", "Default zoom distance", 1.4f, new ConfigDescription("Starting zoom distance when spectating resets", (AcceptableValueBase)(object)new AcceptableValueRange(0.1f, 50f), Array.Empty())); MinZoomDistance = config.Bind("Zoom", "Minimum zoom distance", 1f, new ConfigDescription("Closest the spectate camera can zoom in", (AcceptableValueBase)(object)new AcceptableValueRange(0.1f, 50f), Array.Empty())); MaxZoomDistance = config.Bind("Zoom", "Maximum zoom distance", 15f, new ConfigDescription("Furthest the spectate camera can zoom out", (AcceptableValueBase)(object)new AcceptableValueRange(0.1f, 50f), Array.Empty())); ZoomSpeed = config.Bind("Zoom", "Zoom speed", 0.4f, new ConfigDescription("How fast the mouse wheel zooms the camera", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 5f), Array.Empty())); CameraClipsThroughWalls = config.Bind("Zoom", "Camera clips through walls", false, "Let the spectate camera pass through walls instead of stopping at them"); DefaultZoomDistance.SettingChanged += delegate { SpectateModePatches.ResetZoomDistance(); }; } } [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("MrHat.BasedSpectateMode", "BasedSpectateMode", "0.0.3")] internal class Plugin : BaseUnityPlugin { private const string LethalConfigGUID = "ainavt.lc.lethalconfig"; private const string CullFactoryGUID = "com.fumiko.CullFactory"; private const string LethalCompanyInputUtilsGUID = "com.rune580.LethalCompanyInputUtils"; internal const string modGUID = "MrHat.BasedSpectateMode"; internal const string modName = "BasedSpectateMode"; internal const string modVersion = "0.0.3"; internal static ManualLogSource mls; private readonly Harmony _harmony = new Harmony("MrHat.BasedSpectateMode"); internal static Plugin Instance { get; private set; } internal static SpectateMode CurrentMode { get; set; } private static bool hasLethalConfig => Chainloader.PluginInfos.ContainsKey("ainavt.lc.lethalconfig"); private static bool hasCullFactory => Chainloader.PluginInfos.ContainsKey("com.fumiko.CullFactory"); private void Awake() { Instance = this; mls = Logger.CreateLogSource("MrHat.BasedSpectateMode"); Config.Bind(((BaseUnityPlugin)this).Config); CurrentMode = Config.StartMode.Value; SpectateInputActions.Initialise(); if (hasLethalConfig) { LethalConfigCompat.Register(); mls.LogInfo((object)"LethalConfig compatibility ran"); } if (hasCullFactory) { mls.LogInfo((object)"CullFactory compatibility ran"); } else { _harmony.PatchAll(typeof(DisableVanillaCullingPatches)); } mls.LogDebug((object)"BasedSpectateMode patches running..."); _harmony.PatchAll(typeof(SpectateModePatches)); mls.LogInfo((object)"BasedSpectateMode loaded"); } } public static class MyPluginInfo { public const string PLUGIN_GUID = "BasedSpectateMode"; public const string PLUGIN_NAME = "BasedSpectateMode"; public const string PLUGIN_VERSION = "0.0.3"; } } namespace BasedSpectate.Utils { internal class SpectateInputActions : LcInputActions { private const string KeyboardAndMouseGroup = "KeyboardAndMouse"; internal static SpectateInputActions Instance { get; private set; } internal InputAction MoveForwardAction => ((LcInputActions)this).Asset["freecamMoveForward"]; internal InputAction MoveBackwardAction => ((LcInputActions)this).Asset["freecamMoveBackward"]; internal InputAction MoveLeftAction => ((LcInputActions)this).Asset["freecamMoveLeft"]; internal InputAction MoveRightAction => ((LcInputActions)this).Asset["freecamMoveRight"]; internal InputAction MoveUpAction => ((LcInputActions)this).Asset["freecamMoveUp"]; internal InputAction MoveDownAction => ((LcInputActions)this).Asset["freecamMoveDown"]; internal InputAction SprintAction => ((LcInputActions)this).Asset["freecamSprint"]; internal InputAction NextPlayerAction => ((LcInputActions)this).Asset["freecamNextPlayer"]; internal InputAction ToggleModeAction => ((LcInputActions)this).Asset["toggleSpectateMode"]; internal static InputAction MoveForward => Instance.MoveForwardAction; internal static InputAction MoveBackward => Instance.MoveBackwardAction; internal static InputAction MoveLeft => Instance.MoveLeftAction; internal static InputAction MoveRight => Instance.MoveRightAction; internal static InputAction MoveUp => Instance.MoveUpAction; internal static InputAction MoveDown => Instance.MoveDownAction; internal static InputAction Sprint => Instance.SprintAction; internal static InputAction NextPlayer => Instance.NextPlayerAction; internal static InputAction ToggleMode => Instance.ToggleModeAction; internal static void Initialise() { if (Instance == null) { Instance = new SpectateInputActions(); } } internal static Vector2 ReadMoveInput() { //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0078: 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_0085: Unknown result type (might be due to invalid IL or missing references) Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor((MoveRight.IsPressed() ? 1f : 0f) - (MoveLeft.IsPressed() ? 1f : 0f), (MoveForward.IsPressed() ? 1f : 0f) - (MoveBackward.IsPressed() ? 1f : 0f)); return (((Vector2)(ref val)).sqrMagnitude > 1f) ? ((Vector2)(ref val)).normalized : val; } internal static float ReadVerticalInput() { return (MoveUp.IsPressed() ? 1f : 0f) - (MoveDown.IsPressed() ? 1f : 0f); } internal static bool SprintIsPressed() { return Sprint.IsPressed(); } internal static bool NextPlayerWasPressedThisFrame() { return NextPlayer.WasPressedThisFrame(); } internal static bool ToggleModeWasPressedThisFrame() { return ToggleMode.WasPressedThisFrame(); } internal static bool ScrollWheelModifierIsPressed() { Keyboard current = Keyboard.current; int result; if (current == null || !((ButtonControl)current.leftShiftKey).isPressed) { Keyboard current2 = Keyboard.current; result = ((current2 != null && ((ButtonControl)current2.rightShiftKey).isPressed) ? 1 : 0); } else { result = 1; } return (byte)result != 0; } internal static string GetNextPlayerBindingText() { return GetBindingText(NextPlayer); } internal static string GetToggleModeBindingText() { return GetBindingText(ToggleMode); } public override void CreateInputActions(in InputActionMapBuilder builder) { builder.NewActionBinding().WithActionId("freecamMoveForward").WithActionType((InputActionType)1) .WithKbmPath("/w") .WithBindingName("Freecam Move Forward") .Finish(); builder.NewActionBinding().WithActionId("freecamMoveBackward").WithActionType((InputActionType)1) .WithKbmPath("/s") .WithBindingName("Freecam Move Backward") .Finish(); builder.NewActionBinding().WithActionId("freecamMoveLeft").WithActionType((InputActionType)1) .WithKbmPath("/a") .WithBindingName("Freecam Move Left") .Finish(); builder.NewActionBinding().WithActionId("freecamMoveRight").WithActionType((InputActionType)1) .WithKbmPath("/d") .WithBindingName("Freecam Move Right") .Finish(); builder.NewActionBinding().WithActionId("freecamMoveUp").WithActionType((InputActionType)1) .WithKbmPath("/space") .WithBindingName("Freecam Move Up") .Finish(); builder.NewActionBinding().WithActionId("freecamMoveDown").WithActionType((InputActionType)1) .WithKbmPath("/leftCtrl") .WithBindingName("Freecam Move Down") .Finish(); builder.NewActionBinding().WithActionId("freecamSprint").WithActionType((InputActionType)1) .WithKbmPath("/leftShift") .WithBindingName("Freecam Sprint") .Finish(); builder.NewActionBinding().WithActionId("freecamNextPlayer").WithActionType((InputActionType)1) .WithMouseControl((MouseControl)11) .WithBindingName("Freecam Next Player") .Finish(); builder.NewActionBinding().WithActionId("toggleSpectateMode").WithActionType((InputActionType)1) .WithKeyboardControl((KeyboardControl)42) .WithBindingName("Toggle Spectate Mode") .Finish(); } private static string FormatBindingText(string bindingText) { if (1 == 0) { } string result = bindingText switch { "Left Button" => "LMB", "Right Button" => "RMB", "Middle Button" => "MMB", "Left Shift" => "L.Shift", "Left Ctrl" => "L.Ctrl", "Left Control" => "L.Ctrl", _ => bindingText, }; if (1 == 0) { } return result; } private static string GetBindingText(InputAction action) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) Enumerator enumerator = action.bindings.GetEnumerator(); try { while (enumerator.MoveNext()) { InputBinding current = enumerator.Current; if (!((InputBinding)(ref current)).isComposite && !((InputBinding)(ref current)).isPartOfComposite && string.Equals(((InputBinding)(ref current)).groups, "KeyboardAndMouse", StringComparison.Ordinal) && !string.IsNullOrWhiteSpace(((InputBinding)(ref current)).effectivePath)) { string text = InputControlPath.ToHumanReadableString(((InputBinding)(ref current)).effectivePath, (HumanReadableStringOptions)2, (InputControl)null); if (!string.IsNullOrWhiteSpace(text)) { return FormatBindingText(text); } } } } finally { ((IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose(); } return "Unbound"; } } } namespace BasedSpectate.Patches { internal static class DisableVanillaCullingPatches { private static bool isFreecamSpectating; [HarmonyPatch(typeof(FreecamMode), "Enable")] [HarmonyPostfix] private static void EnablePostfix() { isFreecamSpectating = true; if ((Object)(object)StartOfRound.Instance?.occlusionCuller != (Object)null) { ((Behaviour)StartOfRound.Instance.occlusionCuller).enabled = false; } } [HarmonyPatch(typeof(FreecamMode), "Disable")] [HarmonyPostfix] private static void DisablePostfix() { if (isFreecamSpectating) { isFreecamSpectating = false; if ((Object)(object)StartOfRound.Instance?.occlusionCuller != (Object)null) { ((Behaviour)StartOfRound.Instance.occlusionCuller).enabled = true; } } } [HarmonyPatch(typeof(AdjacentRoomCullingModified), "UpdateRendererLists", new Type[] { typeof(List), typeof(List) })] [HarmonyPrefix] private static bool UpdateRendererListsPrefix() { return !isFreecamSpectating; } } internal static class SpectateModePatches { private static Vector2 holdButtonToEndGameEarlyMeterPosition; private static Vector2 holdButtonToEndGameEarlyVotesTextPosition; private static float zoomDistance; private static bool hasHoldButtonToEndGameEarlyPositions; private static bool wasSpectating; private const float HitDistanceOffset = 0.25f; private const float OpenDistanceOffset = 0.1f; internal static float CurrentZoomDistance => Mathf.Clamp(zoomDistance, Config.MinZoomDistance.Value, Config.MaxZoomDistance.Value); internal static void ResetZoomDistance() { zoomDistance = Mathf.Clamp(Config.DefaultZoomDistance.Value, Config.MinZoomDistance.Value, Config.MaxZoomDistance.Value); Plugin.mls.LogDebug((object)$"Spectate zoom distance reset to {zoomDistance}"); } [HarmonyPatch(typeof(StartOfRound), "Awake")] [HarmonyPostfix] private static void StartOfRoundAwakePostfix(StartOfRound __instance) { ((Component)__instance.spectateCamera).gameObject.AddComponent(); } [HarmonyPatch(typeof(StartOfRound), "SwitchCamera")] [HarmonyPostfix] private static void SwitchCameraPostfix(StartOfRound __instance, Camera newCamera) { FreecamMode component = ((Component)__instance.spectateCamera).GetComponent(); if ((Object)(object)newCamera == (Object)(object)__instance.spectateCamera) { PlayerControllerB localPlayerController = __instance.localPlayerController; if (localPlayerController != null && localPlayerController.isPlayerDead && Plugin.CurrentMode == SpectateMode.Freecam) { component.Enable(__instance.localPlayerController, keepCurrentCameraPosition: false); return; } } if (((Behaviour)component).enabled) { component.Disable(); } } [HarmonyPatch(typeof(StartOfRound), "SetSpectateCameraToGameOverMode")] [HarmonyPrefix] private static bool SetSpectateCameraToGameOverModePrefix() { return Plugin.CurrentMode != SpectateMode.Freecam; } [HarmonyPatch(typeof(PlayerControllerB), "LateUpdate")] [HarmonyPriority(800)] [HarmonyPrefix] private static void PlayerControllerBLateUpdatePrefix(PlayerControllerB __instance) { //IL_01ac: Unknown result type (might be due to invalid IL or missing references) if ((!((NetworkBehaviour)__instance).IsOwner || (((NetworkBehaviour)__instance).IsServer && !__instance.isHostPlayerObject)) && !__instance.isTestingPlayer) { return; } FreecamMode component = ((Component)__instance.playersManager.spectateCamera).GetComponent(); if (!__instance.isPlayerDead) { wasSpectating = false; Plugin.CurrentMode = Config.StartMode.Value; component.DisableBrightnessLights(); return; } if (!wasSpectating) { Plugin.CurrentMode = Config.StartMode.Value; ResetZoomDistance(); FreecamMode.ResetBrightness(); wasSpectating = true; } bool flag = __instance.isTypingChat || __instance.inTerminalMenu || __instance.quickMenuManager.isMenuOpen; if (!Config.EnableModeToggle.Value) { Plugin.CurrentMode = Config.StartMode.Value; } else if (!flag && SpectateInputActions.ToggleModeWasPressedThisFrame()) { Plugin.CurrentMode = ((Plugin.CurrentMode == SpectateMode.Vanilla) ? SpectateMode.Freecam : SpectateMode.Vanilla); Plugin.mls.LogDebug((object)$"Spectate mode set to {Plugin.CurrentMode}"); } if (Plugin.CurrentMode == SpectateMode.Freecam) { __instance.playersManager.overrideSpectateCamera = true; component.Enable(__instance, keepCurrentCameraPosition: true); return; } if (((Behaviour)component).enabled) { component.Disable(); } component.UpdateBrightnessLights(); if (__instance.playersManager.overrideSpectateCamera || flag || __instance.inSpecialInteractAnimation) { return; } Mouse current = Mouse.current; if (current == null) { return; } float y = ((InputControl)(object)current.scroll).ReadValue().y; if (!Mathf.Approximately(y, 0f)) { if (SpectateInputActions.ScrollWheelModifierIsPressed()) { FreecamMode.AdjustBrightness(y); } else { zoomDistance = Mathf.Clamp(zoomDistance + y / -120f * Config.ZoomSpeed.Value, Config.MinZoomDistance.Value, Config.MaxZoomDistance.Value); } } component.UpdateBrightnessLights(); } [HarmonyPatch(typeof(PlayerControllerB), "RaycastSpectateCameraAroundPivot")] [HarmonyPrefix] private static bool RaycastSpectateCameraAroundPivotPrefix(PlayerControllerB __instance) { //IL_0054: 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) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) if (((!((NetworkBehaviour)__instance).IsOwner || (((NetworkBehaviour)__instance).IsServer && !__instance.isHostPlayerObject)) && !__instance.isTestingPlayer) || !__instance.isPlayerDead || Plugin.CurrentMode != SpectateMode.Vanilla || __instance.playersManager.overrideSpectateCamera) { return true; } Ray val = default(Ray); ((Ray)(ref val))..ctor(__instance.spectateCameraPivot.position, -__instance.spectateCameraPivot.forward); Transform transform = ((Component)__instance.playersManager.spectateCamera).transform; float currentZoomDistance = CurrentZoomDistance; RaycastHit val2 = default(RaycastHit); transform.position = ((Config.CameraClipsThroughWalls.Value || !Physics.Raycast(val, ref val2, currentZoomDistance, __instance.walkableSurfacesNoPlayersMask, (QueryTriggerInteraction)1)) ? ((Ray)(ref val)).GetPoint(currentZoomDistance - 0.1f) : ((Ray)(ref val)).GetPoint(((RaycastHit)(ref val2)).distance - 0.25f)); transform.LookAt(__instance.spectateCameraPivot); return false; } [HarmonyPatch(typeof(HUDManager), "Update")] [HarmonyPostfix] private static void HUDManagerUpdatePostfix(HUDManager __instance) { //IL_001b: 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_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) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_023d: Unknown result type (might be due to invalid IL or missing references) //IL_0249: Unknown result type (might be due to invalid IL or missing references) //IL_024e: Unknown result type (might be due to invalid IL or missing references) //IL_0264: Unknown result type (might be due to invalid IL or missing references) //IL_0270: Unknown result type (might be due to invalid IL or missing references) //IL_0275: Unknown result type (might be due to invalid IL or missing references) //IL_0205: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) if (!hasHoldButtonToEndGameEarlyPositions) { holdButtonToEndGameEarlyMeterPosition = ((Graphic)__instance.holdButtonToEndGameEarlyMeter).rectTransform.anchoredPosition; holdButtonToEndGameEarlyVotesTextPosition = ((TMP_Text)__instance.holdButtonToEndGameEarlyVotesText).rectTransform.anchoredPosition; hasHoldButtonToEndGameEarlyPositions = true; } if (!((Object)(object)GameNetworkManager.Instance?.localPlayerController != (Object)null) || !GameNetworkManager.Instance.localPlayerController.isPlayerDead) { ((Graphic)__instance.holdButtonToEndGameEarlyMeter).rectTransform.anchoredPosition = holdButtonToEndGameEarlyMeterPosition; ((TMP_Text)__instance.holdButtonToEndGameEarlyVotesText).rectTransform.anchoredPosition = holdButtonToEndGameEarlyVotesTextPosition; return; } string text = ((TMP_Text)__instance.holdButtonToEndGameEarlyText).text; string text2 = string.Empty; int num = 0; if (Config.ShowNextPlayerHint.Value) { text2 = "[" + SpectateInputActions.GetNextPlayerBindingText() + "] Next Player"; num++; } if (Config.EnableModeToggle.Value && Config.ShowToggleModeHint.Value) { string text3 = "[" + SpectateInputActions.GetToggleModeBindingText() + "] Toggle Mode"; text2 = (string.IsNullOrEmpty(text2) ? text3 : (text2 + "\n" + text3)); num++; } if (Config.ShowZoomHint.Value && Plugin.CurrentMode == SpectateMode.Vanilla) { string text4 = $"[Scroll] Zoom: [{CurrentZoomDistance:0.0} / {Config.MaxZoomDistance.Value:0.0}]"; text2 = (string.IsNullOrEmpty(text2) ? text4 : (text2 + "\n" + text4)); num++; } if (Config.ShowBrightnessHint.Value) { string text5 = $"[Shift + Scroll] Light: [{FreecamMode.CurrentBrightness:0} / {FreecamMode.MaximumBrightness:0}]"; text2 = (string.IsNullOrEmpty(text2) ? text5 : (text2 + "\n" + text5)); num++; } if (num == 0) { ((Graphic)__instance.holdButtonToEndGameEarlyMeter).rectTransform.anchoredPosition = holdButtonToEndGameEarlyMeterPosition; ((TMP_Text)__instance.holdButtonToEndGameEarlyVotesText).rectTransform.anchoredPosition = holdButtonToEndGameEarlyVotesTextPosition; return; } float num2 = -9f * (float)num; ((Graphic)__instance.holdButtonToEndGameEarlyMeter).rectTransform.anchoredPosition = holdButtonToEndGameEarlyMeterPosition + new Vector2(0f, num2); ((TMP_Text)__instance.holdButtonToEndGameEarlyVotesText).rectTransform.anchoredPosition = holdButtonToEndGameEarlyVotesTextPosition + new Vector2(0f, num2); ((TMP_Text)__instance.holdButtonToEndGameEarlyText).text = (string.IsNullOrEmpty(text) ? text2 : (text2 + "\n" + text)); } } } namespace BasedSpectate.Compat { internal static class LethalConfigCompat { internal static void Register() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown //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_0058: Expected O, but got Unknown //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Expected O, but got Unknown //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Expected O, but got Unknown //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Expected O, but got Unknown //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Expected O, but got Unknown //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Expected O, but got Unknown //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Expected O, but got Unknown //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Expected O, but got Unknown //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Expected O, but got Unknown //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Expected O, but got Unknown //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Expected O, but got Unknown //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Expected O, but got Unknown //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Expected O, but got Unknown //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Expected O, but got Unknown //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Expected O, but got Unknown //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Expected O, but got Unknown //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Expected O, but got Unknown //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Expected O, but got Unknown //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Expected O, but got Unknown //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Expected O, but got Unknown //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Expected O, but got Unknown //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Expected O, but got Unknown //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Expected O, but got Unknown //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Expected O, but got Unknown //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Expected O, but got Unknown //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Expected O, but got Unknown LethalConfigManager.SkipAutoGen(); LethalConfigManager.AddConfigItem((BaseConfigItem)(object)new EnumDropDownConfigItem(Config.StartMode, new EnumDropDownOptions { RequiresRestart = false })); LethalConfigManager.AddConfigItem((BaseConfigItem)new BoolCheckBoxConfigItem(Config.EnableModeToggle, new BoolCheckBoxOptions { RequiresRestart = false })); LethalConfigManager.AddConfigItem((BaseConfigItem)new BoolCheckBoxConfigItem(Config.ShowNextPlayerHint, new BoolCheckBoxOptions { RequiresRestart = false })); LethalConfigManager.AddConfigItem((BaseConfigItem)new BoolCheckBoxConfigItem(Config.ShowToggleModeHint, new BoolCheckBoxOptions { RequiresRestart = false })); LethalConfigManager.AddConfigItem((BaseConfigItem)new BoolCheckBoxConfigItem(Config.ShowBrightnessHint, new BoolCheckBoxOptions { RequiresRestart = false })); LethalConfigManager.AddConfigItem((BaseConfigItem)new BoolCheckBoxConfigItem(Config.ShowZoomHint, new BoolCheckBoxOptions { RequiresRestart = false })); ConfigEntry defaultZoomDistance = Config.DefaultZoomDistance; FloatSliderOptions val = new FloatSliderOptions(); ((BaseRangeOptions)val).Min = 0.1f; ((BaseRangeOptions)val).Max = 50f; ((BaseOptions)val).RequiresRestart = false; LethalConfigManager.AddConfigItem((BaseConfigItem)new FloatSliderConfigItem(defaultZoomDistance, val)); ConfigEntry minZoomDistance = Config.MinZoomDistance; FloatSliderOptions val2 = new FloatSliderOptions(); ((BaseRangeOptions)val2).Min = 0.1f; ((BaseRangeOptions)val2).Max = 50f; ((BaseOptions)val2).RequiresRestart = false; LethalConfigManager.AddConfigItem((BaseConfigItem)new FloatSliderConfigItem(minZoomDistance, val2)); ConfigEntry maxZoomDistance = Config.MaxZoomDistance; FloatSliderOptions val3 = new FloatSliderOptions(); ((BaseRangeOptions)val3).Min = 0.1f; ((BaseRangeOptions)val3).Max = 50f; ((BaseOptions)val3).RequiresRestart = false; LethalConfigManager.AddConfigItem((BaseConfigItem)new FloatSliderConfigItem(maxZoomDistance, val3)); ConfigEntry zoomSpeed = Config.ZoomSpeed; FloatSliderOptions val4 = new FloatSliderOptions(); ((BaseRangeOptions)val4).Min = 0f; ((BaseRangeOptions)val4).Max = 5f; ((BaseOptions)val4).RequiresRestart = false; LethalConfigManager.AddConfigItem((BaseConfigItem)new FloatSliderConfigItem(zoomSpeed, val4)); LethalConfigManager.AddConfigItem((BaseConfigItem)new BoolCheckBoxConfigItem(Config.CameraClipsThroughWalls, new BoolCheckBoxOptions { RequiresRestart = false })); } } } namespace BasedSpectate.Behaviours { internal class FreecamMode : MonoBehaviour { private const float MoveSpeed = 10f; private const float SprintMultiplier = 5f; private const float SpeedLerp = 12f; private const float LookScale = 0.008f; private const float MinBrightness = 0f; private const float MaxBrightness = 800f; private const float BrightnessScrollSpeed = 200f; private const float BrightnessIntensityScale = 0.01f; private const float InsideFactoryY = -80f; private static float brightness; private readonly Light[] lights = (Light[])(object)new Light[4]; private PlayerControllerB? player; private float pitch; private float currentMoveSpeed; internal static float CurrentBrightness => Mathf.Clamp(brightness, 0f, MaximumBrightness); internal static float MaximumBrightness => 800f; internal static void ResetBrightness() { brightness = 0f; } internal static void AdjustBrightness(float scroll) { brightness = Mathf.Clamp(brightness + scroll / 120f * 200f, 0f, MaximumBrightness); } private void Awake() { //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) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0078: 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_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0087: 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) //IL_00a3: Expected O, but got Unknown //IL_00c1: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < lights.Length; i++) { if (1 == 0) { } Vector3 val = (Vector3)(i switch { 0 => new Vector3(50f, 0f, 0f), 1 => new Vector3(120f, 0f, 0f), 2 => new Vector3(50f, 90f, 0f), _ => new Vector3(50f, -90f, 0f), }); if (1 == 0) { } Vector3 eulerAngles = val; GameObject val2 = new GameObject(string.Format("{0} Freecam Light {1}", "BasedSpectateMode", i)); Light val3 = val2.AddComponent(); HDAdditionalLightData val4 = val2.AddComponent(); ((Object)val2).hideFlags = (HideFlags)52; val2.transform.eulerAngles = eulerAngles; val3.type = (LightType)1; val3.shadows = (LightShadows)0; ((Behaviour)val3).enabled = false; val4.affectsVolumetric = false; lights[i] = val3; } currentMoveSpeed = 10f; ((Behaviour)this).enabled = false; } internal void Enable(PlayerControllerB playerController, bool keepCurrentCameraPosition) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_005c: 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) if (!((Behaviour)this).enabled || !((Object)(object)player == (Object)(object)playerController)) { player = playerController; currentMoveSpeed = 10f; ((Component)this).transform.SetParent((Transform)null); if (!keepCurrentCameraPosition) { Transform transform = ((Component)playerController.gameplayCamera).transform; ((Component)this).transform.SetPositionAndRotation(transform.position, transform.rotation); } pitch = ((Component)this).transform.eulerAngles.x; if (pitch > 180f) { pitch -= 360f; } StartOfRound.Instance.overrideSpectateCamera = true; ((Behaviour)this).enabled = true; } } internal void Disable() { if (((Behaviour)this).enabled) { DisableBrightnessLights(); ((Behaviour)this).enabled = false; player = null; if ((Object)(object)StartOfRound.Instance != (Object)null) { StartOfRound.Instance.overrideSpectateCamera = false; } } } private void LateUpdate() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Unknown result type (might be due to invalid IL or missing references) //IL_028f: Unknown result type (might be due to invalid IL or missing references) //IL_0294: Unknown result type (might be due to invalid IL or missing references) //IL_029a: Unknown result type (might be due to invalid IL or missing references) //IL_02a5: Unknown result type (might be due to invalid IL or missing references) //IL_02aa: Unknown result type (might be due to invalid IL or missing references) //IL_02b0: Unknown result type (might be due to invalid IL or missing references) //IL_02b5: Unknown result type (might be due to invalid IL or missing references) //IL_02ba: Unknown result type (might be due to invalid IL or missing references) //IL_02c1: Unknown result type (might be due to invalid IL or missing references) //IL_02c6: Unknown result type (might be due to invalid IL or missing references) //IL_02cb: Unknown result type (might be due to invalid IL or missing references) //IL_02d4: Unknown result type (might be due to invalid IL or missing references) //IL_02e5: Unknown result type (might be due to invalid IL or missing references) //IL_02e7: Unknown result type (might be due to invalid IL or missing references) //IL_02ec: Unknown result type (might be due to invalid IL or missing references) PlayerControllerB? obj = player; if (obj == null || !obj.isPlayerDead) { Disable(); return; } bool flag = ((Component)this).transform.position.y < -80f; StartOfRound.Instance.overrideSpectateCamera = true; StartOfRound.Instance.SetOcclusionCullerToPosition(((Component)this).transform.position); StartOfRound.Instance.blackSkyVolume.weight = (flag ? 1f : 0f); TimeOfDay instance = TimeOfDay.Instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.sunDirect == (Object)null || (Object)(object)instance.sunIndirect == (Object)null) { Disable(); return; } ((Behaviour)instance.sunDirect).enabled = !flag; ((Behaviour)instance.sunIndirect).enabled = !flag; if (player.isTypingChat || player.inTerminalMenu || player.quickMenuManager.isMenuOpen) { return; } if (SpectateInputActions.NextPlayerWasPressedThisFrame()) { SwitchSpectatedPlayer(1); } Mouse current = Mouse.current; if (current != null) { float y = ((InputControl)(object)current.scroll).ReadValue().y; if (!Mathf.Approximately(y, 0f) && SpectateInputActions.ScrollWheelModifierIsPressed()) { AdjustBrightness(y); } } UpdateBrightnessLights(); Vector2 val = SpectateInputActions.ReadMoveInput(); float num = (float)IngamePlayerSettings.Instance.settings.lookSensitivity * 0.008f; MovementActions movement = player.playerActions.Movement; Vector2 val2 = num * ((MovementActions)(ref movement)).Look.ReadValue(); if (!IngamePlayerSettings.Instance.settings.invertYAxis) { val2.y *= -1f; } ((Component)this).transform.Rotate(0f, val2.x, 0f, (Space)0); pitch = Mathf.Clamp(pitch + val2.y, -90f, 90f); ((Component)this).transform.eulerAngles = new Vector3(pitch, ((Component)this).transform.eulerAngles.y, 0f); float num2 = (SpectateInputActions.SprintIsPressed() ? 50f : 10f); currentMoveSpeed = Mathf.Lerp(currentMoveSpeed, num2, 12f * Time.deltaTime); float num3 = SpectateInputActions.ReadVerticalInput(); Vector3 val3 = ((Component)this).transform.right * val.x + ((Component)this).transform.forward * val.y + Vector3.up * num3; Transform transform = ((Component)this).transform; transform.position += currentMoveSpeed * Time.deltaTime * val3; } internal void UpdateBrightnessLights() { float intensity = CurrentBrightness * 0.01f; bool enabled = !Mathf.Approximately(CurrentBrightness, 0f); for (int i = 0; i < lights.Length; i++) { ((Behaviour)lights[i]).enabled = enabled; lights[i].intensity = intensity; } } internal void DisableBrightnessLights() { for (int i = 0; i < lights.Length; i++) { if ((Object)(object)lights[i] != (Object)null) { ((Behaviour)lights[i]).enabled = false; } } } private void SwitchSpectatedPlayer(int direction) { //IL_011a: 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_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null) { return; } PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; int num = (int)(((Object)(object)player.spectatedPlayerScript == (Object)null) ? player.playerClientId : player.spectatedPlayerScript.playerClientId); for (int i = 0; i < allPlayerScripts.Length; i++) { num += direction; if (num < 0) { num = allPlayerScripts.Length - 1; } else if (num >= allPlayerScripts.Length) { num = 0; } PlayerControllerB val = allPlayerScripts[num]; if (!((Object)(object)val == (Object)(object)player) && !val.isPlayerDead && val.isPlayerControlled) { player.spectatedPlayerScript = val; player.SetSpectatedPlayerEffects(false); HUDManager.Instance.SetSpectatingTextToPlayer(val); Transform transform = ((Component)val.gameplayCamera).transform; float num2 = Mathf.Clamp(Config.DefaultZoomDistance.Value, Config.MinZoomDistance.Value, Config.MaxZoomDistance.Value); ((Component)this).transform.SetPositionAndRotation(transform.position - transform.forward * num2, transform.rotation); pitch = ((Component)this).transform.eulerAngles.x; if (pitch > 180f) { pitch -= 360f; } Plugin.mls.LogDebug((object)("Freecam spectating " + val.playerUsername)); break; } } } private void OnDestroy() { for (int i = 0; i < lights.Length; i++) { if ((Object)(object)lights[i] != (Object)null) { Object.Destroy((Object)(object)((Component)lights[i]).gameObject); } } } } }