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 RefinedSpectating v1.1.0
RefinedSpectating.dll
Decompiled an hour ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using LethalCompanyInputUtils.Api; using Microsoft.CodeAnalysis; using TMPro; using Unity.Collections; using Unity.Netcode; using UnityEngine; using UnityEngine.Events; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Utilities; using UnityEngine.Rendering; using UnityEngine.Rendering.HighDefinition; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("slivkonsin")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.1.0.0")] [assembly: AssemblyInformationalVersion("1.1.0")] [assembly: AssemblyProduct("RefinedSpectating")] [assembly: AssemblyTitle("RefinedSpectating")] [assembly: AssemblyVersion("1.1.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [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 RefinedSpectating { internal static class CameraRig { internal enum Target { None, Player, Enemy, Freecam } [CompilerGenerated] private static class <>O { public static UnityAction <0>__Apply; } private const float PlayerBaseDistance = 1.3f; private const float MinEnemyBase = 1.5f; private const float MaxEnemyBase = 15f; private const float CollisionRadius = 0.2f; private const float WallGap = 0.1f; private const float MinDistance = 0.3f; private const float SnapDistance = 25f; private const int DoorMask = 512; private const int TerrainMask = 33554432; private static int _wallMask = -1; private static readonly RaycastHit[] Hits = (RaycastHit[])(object)new RaycastHit[16]; private static readonly Dictionary<string, float> EnemyBase = new Dictionary<string, float>(); private static bool _active; private static float _zoomTarget = 1f; private static float _zoom = 1f; private static float _distance = 1.3f; private static float _minZoom = 1f; private static float _maxZoom = 4f; private static Vector3 _pivot; private static Vector3 _pivotVelocity; private static Object _lastTarget; private static float _glideUntil; private static Camera _fovCam; private static float _fovBase = -1f; private static float _fovSet = -1f; public static Target CurrentTarget { get; private set; } public static float Zoom => _zoom; public static float Distance => _distance; public static bool Collided { get; private set; } public static Vector3 Pivot => _pivot; public static void Install() { //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_001b: Expected O, but got Unknown object obj = <>O.<0>__Apply; if (obj == null) { UnityAction val = Apply; <>O.<0>__Apply = val; obj = (object)val; } Application.onBeforeRender += (UnityAction)obj; } public static void Update() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) Settings cfg = Plugin.Cfg; if (Spectator.InputBlocked()) { return; } Mouse current = Mouse.current; if (current == null) { return; } float y = ((InputControl<Vector2>)(object)current.scroll).ReadValue().y; if (!(Mathf.Abs(y) < 0.01f)) { float num = ((Mathf.Abs(y) >= 10f) ? (y / 120f) : y); if (Freecam.Active) { Freecam.OnScroll(num); } else if (_active && cfg.Zoom.Value) { _zoomTarget = Mathf.Clamp(Mathf.Clamp(_zoomTarget, _minZoom, _maxZoom) * Mathf.Pow(cfg.ZoomStep.Value, 0f - num), _minZoom, _maxZoom); } } } private static void Apply() { try { ApplyInner(); } catch (Exception ex) { _active = false; DiagnosticsTelemetry.Errors++; if (Plugin.Cfg.DebugLog.Value) { Plugin.Log.LogWarning((object)("Camera: " + ex)); } } } private static void ApplyInner() { //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0214: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Unknown result type (might be due to invalid IL or missing references) //IL_022c: Unknown result type (might be due to invalid IL or missing references) //IL_0231: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_0245: Unknown result type (might be due to invalid IL or missing references) //IL_0247: Unknown result type (might be due to invalid IL or missing references) //IL_024c: Unknown result type (might be due to invalid IL or missing references) //IL_0251: Unknown result type (might be due to invalid IL or missing references) //IL_02b2: Unknown result type (might be due to invalid IL or missing references) //IL_02b4: Unknown result type (might be due to invalid IL or missing references) //IL_02b9: Unknown result type (might be due to invalid IL or missing references) //IL_02be: Unknown result type (might be due to invalid IL or missing references) //IL_025f: 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_0355: Unknown result type (might be due to invalid IL or missing references) //IL_0357: Unknown result type (might be due to invalid IL or missing references) //IL_035c: Unknown result type (might be due to invalid IL or missing references) //IL_0361: Unknown result type (might be due to invalid IL or missing references) //IL_02fe: Unknown result type (might be due to invalid IL or missing references) //IL_0300: Unknown result type (might be due to invalid IL or missing references) //IL_0305: Unknown result type (might be due to invalid IL or missing references) //IL_030a: Unknown result type (might be due to invalid IL or missing references) //IL_02ce: Unknown result type (might be due to invalid IL or missing references) //IL_02d0: Unknown result type (might be due to invalid IL or missing references) //IL_02d5: Unknown result type (might be due to invalid IL or missing references) //IL_02da: Unknown result type (might be due to invalid IL or missing references) //IL_03c0: Unknown result type (might be due to invalid IL or missing references) //IL_03c5: Unknown result type (might be due to invalid IL or missing references) //IL_03ca: Unknown result type (might be due to invalid IL or missing references) //IL_03e7: Unknown result type (might be due to invalid IL or missing references) //IL_03ec: Unknown result type (might be due to invalid IL or missing references) //IL_0334: Unknown result type (might be due to invalid IL or missing references) //IL_0339: Unknown result type (might be due to invalid IL or missing references) //IL_0349: Unknown result type (might be due to invalid IL or missing references) //IL_034e: Unknown result type (might be due to invalid IL or missing references) //IL_0460: Unknown result type (might be due to invalid IL or missing references) //IL_0465: Unknown result type (might be due to invalid IL or missing references) //IL_046c: Unknown result type (might be due to invalid IL or missing references) //IL_0471: Unknown result type (might be due to invalid IL or missing references) //IL_047d: Unknown result type (might be due to invalid IL or missing references) Settings cfg = Plugin.Cfg; GameNetworkManager instance = GameNetworkManager.Instance; PlayerControllerB val = (((Object)(object)instance != (Object)null) ? instance.localPlayerController : null); StartOfRound instance2 = StartOfRound.Instance; bool spectatingEnemies = SpectateEnemiesCompat.SpectatingEnemies; if (!cfg.Enabled.Value || !((Object)(object)val != (Object)null) || !val.isPlayerDead || !((Object)(object)instance2 != (Object)null) || !((Object)(object)instance2.spectateCamera != (Object)null) || !((Object)(object)val.spectateCameraPivot != (Object)null) || instance2.overrideSpectateCamera || !(val.isInGameOverAnimation <= 0f) || (!spectatingEnemies && instance2.allPlayersDead)) { _active = false; CurrentTarget = Target.None; Freecam.Reset(); RestoreFov(); return; } ApplyFov(instance2.spectateCamera); Transform transform = ((Component)instance2.spectateCamera).transform; Transform spectateCameraPivot = val.spectateCameraPivot; float num = Mathf.Min(Time.unscaledDeltaTime, 0.1f); if (Freecam.Active) { Freecam.Apply(transform, spectateCameraPivot, val, num); _active = false; _pivot = transform.position; CurrentTarget = Target.Freecam; return; } Object val3; float num2; float num3; float num4; if (spectatingEnemies) { EnemyAI val2 = SpectateEnemiesCompat.CurrentEnemy(); val3 = (Object)(object)val2; num2 = (((Object)(object)val2 != (Object)null) ? EnemyBaseDistance(val2) : 1.5f); num3 = cfg.EnemyMaxZoom.Value; num4 = cfg.EnemyMinZoom.Value; CurrentTarget = Target.Enemy; } else { val3 = (Object)(object)val.spectatedPlayerScript; if (val3 == (Object)null && (Object)(object)HUDManager.Instance != (Object)null && (Object)(object)HUDManager.Instance.spectatingPlayerText != (Object)null && ((TMP_Text)HUDManager.Instance.spectatingPlayerText).text.Length > 0) { ((TMP_Text)HUDManager.Instance.spectatingPlayerText).text = ""; } num2 = 1.3f; num3 = cfg.PlayerMaxZoom.Value; num4 = 1f; CurrentTarget = Target.Player; } if (!cfg.Zoom.Value) { num4 = (num3 = 1f); } _minZoom = num4; _maxZoom = num3; if (spectatingEnemies && !cfg.SEOverrideZoom.Value) { _active = false; return; } Vector3 val4 = spectateCameraPivot.position + Vector3.up * Height(spectateCameraPivot.position, num2); if (!_active) { _active = true; _pivot = val4; _pivotVelocity = Vector3.zero; _distance = (Freecam.Returning ? (num2 * Mathf.Clamp(_zoomTarget, num4, num3)) : Vector3.Distance(transform.position, val4)); _zoom = Mathf.Clamp(_zoomTarget, num4, num3); _lastTarget = val3; } Vector3 val5; if (val3 != _lastTarget) { _lastTarget = val3; val5 = val4 - _pivot; if (((Vector3)(ref val5)).sqrMagnitude > 625f) { _pivot = val4; _pivotVelocity = Vector3.zero; } else { _glideUntil = Time.unscaledTime + 0.45f; } } if (cfg.SmoothCamera.Value) { val5 = val4 - _pivot; if (((Vector3)(ref val5)).sqrMagnitude < 625f) { float num5 = ((Time.unscaledTime < _glideUntil) ? 0.12f : 0.035f); _pivot = Vector3.SmoothDamp(_pivot, val4, ref _pivotVelocity, num5, float.PositiveInfinity, num); goto IL_0366; } } _pivot = val4; _pivotVelocity = Vector3.zero; goto IL_0366; IL_0366: float num6 = Mathf.Clamp(_zoomTarget, num4, num3); _zoom = Mathf.Exp(Mathf.Lerp(Mathf.Log(_zoom), Mathf.Log(num6), 1f - Mathf.Exp((0f - cfg.ZoomSpeed.Value) * num))); if (spectatingEnemies) { SpectateEnemiesCompat.ShowZoom(_zoom); } Vector3 val6 = -spectateCameraPivot.forward; float num7 = num2 * _zoom; float num8 = (cfg.CameraCollision.Value ? FreeDistance(_pivot, val6, num7) : num7); Collided = num8 < num7 - 0.01f; float num9 = Mathf.Min(num7, num8); if (num9 < _distance) { _distance = num9; } else { _distance = Mathf.Lerp(_distance, num9, 1f - Mathf.Exp((0f - (Collided ? 5f : cfg.ZoomSpeed.Value)) * num)); } transform.position = _pivot + val6 * _distance; transform.LookAt(_pivot); Freecam.BlendOut(transform, num); } private static void ApplyFov(Camera c) { float value = Plugin.Cfg.FovIncrease.Value; if (value <= 0f) { RestoreFov(); return; } float fieldOfView = c.fieldOfView; if ((Object)(object)_fovCam != (Object)(object)c || _fovSet < 0f || Mathf.Abs(fieldOfView - _fovSet) > 0.01f) { _fovBase = fieldOfView; } _fovCam = c; _fovSet = Mathf.Clamp(_fovBase + value, 1f, 170f); c.fieldOfView = _fovSet; } private static void RestoreFov() { if ((Object)(object)_fovCam != (Object)null && _fovBase > 0f && Mathf.Abs(_fovCam.fieldOfView - _fovSet) <= 0.01f) { _fovCam.fieldOfView = _fovBase; } _fovCam = null; _fovBase = (_fovSet = -1f); } private static float Height(Vector3 from, float baseDistance) { //IL_003d: 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) float num = Plugin.Cfg.CameraHeight.Value * Mathf.Clamp(baseDistance / 1.3f, 1f, 4f); if (num <= 0f) { return 0f; } RaycastHit val = default(RaycastHit); if (_wallMask != -1 && Physics.Raycast(from, Vector3.up, ref val, num + 0.25f, _wallMask, (QueryTriggerInteraction)1)) { num = Mathf.Max(0f, ((RaycastHit)(ref val)).distance - 0.25f); } return num; } private static float FreeDistance(Vector3 pivot, Vector3 dir, float max) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) if (_wallMask == -1) { int num = 1342179585; try { num = Traverse.Create((object)GameNetworkManager.Instance.localPlayerController).Field<int>("walkableSurfacesNoPlayersMask").Value; } catch { } _wallMask = num | 0x2000000; } float num2 = max; RaycastHit val = default(RaycastHit); if (Physics.SphereCast(pivot, 0.2f, dir, ref val, max, _wallMask, (QueryTriggerInteraction)1)) { num2 = Mathf.Min(num2, ((RaycastHit)(ref val)).distance - 0.1f); } int num3 = Physics.SphereCastNonAlloc(pivot, 0.2f, dir, Hits, max, 512, (QueryTriggerInteraction)1); for (int i = 0; i < num3; i++) { if ((Object)(object)((RaycastHit)(ref Hits[i])).collider != (Object)null && (Object)(object)((Component)((RaycastHit)(ref Hits[i])).collider).GetComponentInParent<DoorLock>() != (Object)null) { num2 = Mathf.Min(num2, ((RaycastHit)(ref Hits[i])).distance - 0.1f); } } return Mathf.Max(0.3f, num2); } private static float EnemyBaseDistance(EnemyAI e) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: 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_0076: 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) string text = (((Object)(object)e.enemyType != (Object)null) ? e.enemyType.enemyName : ((object)e).GetType().Name); if (EnemyBase.TryGetValue(text, out var value)) { return value; } Bounds val = default(Bounds); bool flag = false; Renderer[] componentsInChildren = ((Component)e).GetComponentsInChildren<Renderer>(); foreach (Renderer val2 in componentsInChildren) { if ((val2 is SkinnedMeshRenderer || val2 is MeshRenderer) && val2.enabled) { if (!flag) { val = val2.bounds; flag = true; } else { ((Bounds)(ref val)).Encapsulate(val2.bounds); } } } float num = (flag ? Mathf.Max(((Bounds)(ref val)).size.y, Mathf.Max(((Bounds)(ref val)).size.x, ((Bounds)(ref val)).size.z)) : 1.5f); if (!flag) { return 1.5f; } value = Mathf.Clamp(num * 0.9f, 1.5f, 15f); EnemyBase[text] = value; if (Plugin.Cfg.DebugLog.Value) { Plugin.Log.LogInfo((object)$"Enemy {text}: size {num:0.0} m -> zoom 1x at {value:0.0} m"); } return value; } } internal static class Controls { private static readonly Regex FlashlightLine = new Regex("Flashlight : \\[[^\\]\\n]*\\]"); public static bool VoteHooked { get; private set; } private static bool UsingOwnKey { get { if (VoteHooked && Plugin.Cfg.Enabled.Value) { return Plugin.Cfg.VoteOnOwnKey.Value; } return false; } } public static void Install(Harmony h) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Expected O, but got Unknown //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Expected O, but got Unknown try { MethodInfo methodInfo = AccessTools.Method(typeof(HUDManager), "Update", (Type[])null, (Type[])null); HarmonyMethod val = new HarmonyMethod(typeof(Controls), "VoteTranspiler", (Type[])null); HarmonyMethod val2 = new HarmonyMethod(typeof(Controls), "AfterHudUpdate", (Type[])null); val2.priority = 0; val2.after = new string[1] { "SpectateEnemy" }; h.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val2, val, (HarmonyMethod)null, (HarmonyMethod)null); MethodInfo methodInfo2 = AccessTools.Method(typeof(HUDManager), "DisplaySpectatorTip", (Type[])null, (Type[])null); if (methodInfo2 != null) { h.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(Controls), "BeforeTip", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Vote key unavailable, right mouse keeps voting: " + ex.Message)); } try { h.Patch((MethodBase)AccessTools.Method(typeof(PlayerControllerB), "Interact_performed", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Controls), "BeforeInteract", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } catch (Exception ex2) { Plugin.Log.LogWarning((object)("Couldn't stop E from switching players: " + ex2.Message)); } Plugin.Inputs.Previous.performed += OnPrevious; } private static void OnPrevious(CallbackContext ctx) { if (!((CallbackContext)(ref ctx)).performed || !Plugin.Cfg.Enabled.Value || !Plugin.Cfg.CycleBackwards.Value) { return; } try { PlayerControllerB val = Spectator.Local(); StartOfRound instance = StartOfRound.Instance; if ((Object)(object)val == (Object)null || !val.isPlayerDead || (Object)(object)instance == (Object)null || instance.overrideSpectateCamera || instance.shipIsLeaving || val.isInGameOverAnimation > 0f || Spectator.InputBlocked() || Freecam.Active) { return; } if (SpectateEnemiesCompat.SpectatingEnemies) { SpectateEnemiesCompat.PreviousEnemy(); } else { if ((Object)(object)val.spectatedPlayerScript == (Object)null || val.spectatedPlayerScript.isPlayerDead) { return; } PlayerControllerB[] allPlayerScripts = instance.allPlayerScripts; int num = Array.IndexOf(allPlayerScripts, val.spectatedPlayerScript); for (int i = 0; i < allPlayerScripts.Length; i++) { num = (num - 1 + allPlayerScripts.Length) % allPlayerScripts.Length; PlayerControllerB val2 = allPlayerScripts[num]; if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2 == (Object)(object)val) && !val2.isPlayerDead && val2.isPlayerControlled) { if (!((Object)(object)val2 == (Object)(object)val.spectatedPlayerScript)) { val.spectatedPlayerScript = val2; instance.SetPlayerSafeInShip(); val.SetSpectatedPlayerEffects(false); } break; } } } } catch (Exception ex) { DiagnosticsTelemetry.Errors++; Plugin.Log.LogWarning((object)("Previous player: " + ex.Message)); } } private static bool BeforeInteract(PlayerControllerB __instance) { if (!Plugin.Cfg.Enabled.Value) { return true; } bool flag = ((NetworkBehaviour)__instance).IsOwner && __instance.isPlayerDead; if (flag && Freecam.Active) { return false; } if (flag) { return !Plugin.Cfg.InteractDoesntCycle.Value; } return true; } private static IEnumerable<CodeInstruction> VoteTranspiler(IEnumerable<CodeInstruction> code) { //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Expected O, but got Unknown List<CodeInstruction> list = new List<CodeInstruction>(code); MethodInfo methodInfo = AccessTools.Method(typeof(InputAction), "IsPressed", (Type[])null, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(Controls), "VotePressed", (Type[])null, (Type[])null); int index = -1; int num = 0; for (int i = 1; i < list.Count; i++) { if (CodeInstructionExtensions.Calls(list[i], methodInfo) && list[i - 1].operand is MethodInfo { Name: "get_PingScan" }) { index = i; num++; } } if (num == 1) { list[index] = CodeInstructionExtensions.MoveBlocksFrom(CodeInstructionExtensions.MoveLabelsFrom(new CodeInstruction(OpCodes.Call, (object)methodInfo2), list[index]), list[index]); VoteHooked = true; } else { Plugin.Log.LogWarning((object)$"Vote key: expected one spot in the game's code, found {num}. Right mouse keeps voting."); } return list; } private static bool VotePressed(InputAction scan) { if (!Plugin.Cfg.Enabled.Value || !Plugin.Cfg.VoteOnOwnKey.Value || Plugin.Inputs?.VoteLeave == null) { return scan.IsPressed(); } if (Plugin.Inputs.VoteLeave.IsPressed()) { return !Spectator.InputBlocked(); } return false; } private static void AfterHudUpdate(HUDManager __instance) { if (!Plugin.Cfg.Enabled.Value) { return; } TextMeshProUGUI holdButtonToEndGameEarlyText = __instance.holdButtonToEndGameEarlyText; if (!((Object)(object)holdButtonToEndGameEarlyText == (Object)null) && !string.IsNullOrEmpty(((TMP_Text)holdButtonToEndGameEarlyText).text)) { string s = ((TMP_Text)holdButtonToEndGameEarlyText).text; if (UsingOwnKey) { s = ReplaceKey(s); } s = FixSpectateEnemiesControls(s); if (s != ((TMP_Text)holdButtonToEndGameEarlyText).text) { ((TMP_Text)holdButtonToEndGameEarlyText).text = s; } } } private static string FixSpectateEnemiesControls(string s) { if (!Plugin.Cfg.SEReplaceFlashlight.Value || s.IndexOf("Flashlight : [", StringComparison.Ordinal) < 0) { return s; } List<string> list = new List<string>(); if (Plugin.Cfg.VisionEnabled.Value) { list.Add("Vision : [" + KeyName(Plugin.Inputs.Vision, "F") + "]"); } if (Plugin.Cfg.CycleBackwards.Value) { list.Add("Previous : [" + KeyName(Plugin.Inputs.Previous, "Right Button") + "]"); } if (Plugin.Cfg.FreecamEnabled.Value) { list.Add("Freecam : [" + KeyName(Plugin.Inputs.Freecam, "V") + "]"); } string text = string.Join("\n", list); s = FlashlightLine.Replace(s, text, 1); if (text.Length != 0) { return s; } return s.Replace("\n\n", "\n"); } private static void BeforeTip(ref string tipText) { if (UsingOwnKey && tipText != null) { tipText = ReplaceKey(tipText); } } private static string ReplaceKey(string s) { if (s.IndexOf("[RMB]", StringComparison.Ordinal) < 0 && s.IndexOf("[R-", StringComparison.OrdinalIgnoreCase) < 0) { return s; } string newValue = "[" + KeyName(Plugin.Inputs.VoteLeave, "Space") + "]"; return s.Replace("[RMB]", newValue).Replace("[R-trigger]", newValue).Replace("[R-Trigger]", newValue); } internal static string KeyName(InputAction a, string fallback) { //IL_001c: 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_0024: 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_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) bool flag = (Object)(object)StartOfRound.Instance != (Object)null && StartOfRound.Instance.localPlayerUsingController; try { Enumerator<InputBinding> enumerator = a.bindings.GetEnumerator(); try { while (enumerator.MoveNext()) { InputBinding current = enumerator.Current; string text = ((InputBinding)(ref current)).effectivePath ?? ""; if (text.StartsWith("<Gamepad>", StringComparison.OrdinalIgnoreCase) == flag && text.Length > 0) { return InputControlPath.ToHumanReadableString(text, (HumanReadableStringOptions)2, (InputControl)null); } } } finally { ((IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose(); } } catch { } return fallback; } } internal static class Freecam { private const float SprintMultiplier = 3f; private const float MaxDistanceFromOrigin = 20000f; private const float Accelerate = 6f; private const float Brake = 4.5f; private const float Look = 20f; private const float SpeedChange = 8f; private static Vector3 _pos; private static Vector3 _vel; private static Vector3 _travelTarget; private static Vector3 _travelVel; private static bool _travelling; private static Quaternion _rot; private static float _speed = 1f; private static float _speedTarget = 1f; private static float _followTimer; private static bool _blending; private static float _blendT; private static Vector3 _fromPos; private static Quaternion _fromRot; private static InputAction _move; private static InputAction _sprint; private static bool _active; public static bool Active { get { return _active; } private set { _active = value; InputPause.Set("freecam", value); SpectateEnemiesCompat.PauseSwap(value); } } public static float SpeedMultiplier => _speedTarget; public static bool Returning => _blending; public static void Install() { Plugin.Inputs.Freecam.performed += OnToggle; } private static void OnToggle(CallbackContext ctx) { if (((CallbackContext)(ref ctx)).performed) { if (Active) { Exit(); } else if (!Spectator.InputBlocked()) { Enter(null); } } } private static bool CanEnter(out Transform cam) { cam = null; Settings cfg = Plugin.Cfg; PlayerControllerB val = Spectator.Local(); StartOfRound instance = StartOfRound.Instance; if (!cfg.Enabled.Value || !cfg.FreecamEnabled.Value || (Object)(object)val == (Object)null || !val.isPlayerDead || (Object)(object)instance == (Object)null || (Object)(object)instance.spectateCamera == (Object)null || instance.overrideSpectateCamera || instance.shipIsLeaving) { return false; } cam = ((Component)instance.spectateCamera).transform; return true; } public static void Enter(Vector3? travelTo) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: 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_002b: 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_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_006a: 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) try { if (CanEnter(out var cam)) { if (!Active) { _pos = cam.position; _rot = cam.rotation; _vel = Vector3.zero; _speed = _speedTarget; _blending = false; _followTimer = 0f; Active = true; } if (travelTo.HasValue) { _travelTarget = travelTo.Value; _travelVel = Vector3.zero; _travelling = true; } } } catch (Exception ex) { Active = false; DiagnosticsTelemetry.Errors++; Plugin.Log.LogWarning((object)("Freecam: " + ex.Message)); } } public static void Exit() { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) if (!Active) { return; } Active = false; _travelling = false; StartOfRound instance = StartOfRound.Instance; PlayerControllerB val = Spectator.Local(); if ((Object)(object)instance == (Object)null || (Object)(object)instance.spectateCamera == (Object)null || (Object)(object)val == (Object)null) { return; } _fromPos = ((Component)instance.spectateCamera).transform.position; _fromRot = ((Component)instance.spectateCamera).transform.rotation; _blending = true; _blendT = 0f; try { if (SpectateEnemiesCompat.SpectatingEnemies) { SpectateEnemiesCompat.SwitchToPlayers(val); } PlayerControllerB val2 = NearestLivingPlayer(val, _fromPos); if ((Object)(object)val2 != (Object)null) { val.spectatedPlayerScript = val2; val.SetSpectatedPlayerEffects(false); } else if ((Object)(object)HUDManager.Instance != (Object)null) { ((TMP_Text)HUDManager.Instance.spectatingPlayerText).text = ""; } } catch (Exception ex) { DiagnosticsTelemetry.Errors++; Plugin.Log.LogWarning((object)("Leaving freecam: " + ex.Message)); } } public static void OnScroll(float notches) { _speedTarget = Mathf.Clamp(_speedTarget * Mathf.Pow(1.15f, notches), 0.2f, 5f); } private static float Rate(float perSecond, float dt) { float num = Mathf.Max(0.05f, Plugin.Cfg.FreecamSmoothing.Value); return 1f - Mathf.Exp((0f - perSecond / num) * dt); } public static void Apply(Transform cam, Transform pivot, PlayerControllerB local, float dt) { //IL_003f: 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_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: 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_0123: 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_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_0174: 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_019b: 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_020d: Unknown result type (might be due to invalid IL or missing references) //IL_0212: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Unknown result type (might be due to invalid IL or missing references) //IL_021d: Unknown result type (might be due to invalid IL or missing references) //IL_0222: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Unknown result type (might be due to invalid IL or missing references) //IL_025d: 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_0247: Unknown result type (might be due to invalid IL or missing references) //IL_024c: Unknown result type (might be due to invalid IL or missing references) //IL_028a: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_0203: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Unknown result type (might be due to invalid IL or missing references) if (_move == null) { InputActionAsset actions = InputSystem.actions; _move = ((actions != null) ? actions.FindAction("Move", false) : null); InputActionAsset actions2 = InputSystem.actions; _sprint = ((actions2 != null) ? actions2.FindAction("Sprint", false) : null); } Vector2 val = Vector2.zero; bool flag = false; float num = 0f; if (!Spectator.InputBlocked()) { try { if (_move != null) { val = _move.ReadValue<Vector2>(); } } catch { } try { flag = _sprint != null && _sprint.IsPressed(); } catch { } try { if (Plugin.Inputs.FreecamUp.IsPressed()) { num += 1f; } if (Plugin.Inputs.FreecamDown.IsPressed()) { num -= 1f; } } catch { } } _rot = Quaternion.Slerp(_rot, pivot.rotation, Rate(20f, dt)); _speed = Mathf.Lerp(_speed, _speedTarget, Rate(8f, dt)); float num2 = Plugin.Cfg.FreecamSpeed.Value * _speed * (flag ? 3f : 1f); Vector3 val2 = _rot * new Vector3(val.x, 0f, val.y) + Vector3.up * num; if (((Vector3)(ref val2)).sqrMagnitude > 1f) { ((Vector3)(ref val2)).Normalize(); } Vector3 val3 = val2 * num2; _vel = Vector3.Lerp(_vel, val3, Rate((((Vector3)(ref val3)).sqrMagnitude > 0.01f) ? 6f : 4.5f, dt)); if (_travelling) { if (!(((Vector2)(ref val)).sqrMagnitude > 0.01f) && num == 0f) { Vector3 val4 = _pos - _travelTarget; if (!(((Vector3)(ref val4)).sqrMagnitude < 0.01f)) { _pos = Vector3.SmoothDamp(_pos, _travelTarget, ref _travelVel, 0.35f, float.PositiveInfinity, dt); goto IL_020d; } } _travelling = false; } goto IL_020d; IL_020d: _pos += _vel * dt; if (((Vector3)(ref _pos)).sqrMagnitude > 400000000f) { _pos = ((Vector3)(ref _pos)).normalized * 20000f; } cam.position = _pos; cam.rotation = _rot; _followTimer -= dt; if (_followTimer <= 0f) { _followTimer = 1f; PlayerControllerB val5 = NearestLivingPlayer(local, _pos); if ((Object)(object)val5 != (Object)null && (Object)(object)val5 != (Object)(object)local.spectatedPlayerScript && !SpectateEnemiesCompat.SpectatingEnemies) { local.spectatedPlayerScript = val5; local.SetSpectatedPlayerEffects(false); } } if ((Object)(object)HUDManager.Instance != (Object)null && (Object)(object)HUDManager.Instance.spectatingPlayerText != (Object)null && ((TMP_Text)HUDManager.Instance.spectatingPlayerText).text != "(Freecam)") { ((TMP_Text)HUDManager.Instance.spectatingPlayerText).text = "(Freecam)"; } } public static void BlendOut(Transform cam, float dt) { //IL_004c: 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_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0069: 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) if (_blending) { _blendT += dt / Mathf.Max(0.05f, Plugin.Cfg.FreecamReturnTime.Value); float num = Mathf.Clamp01(_blendT); float num2 = num * num * (3f - 2f * num); cam.position = Vector3.Lerp(_fromPos, cam.position, num2); cam.rotation = Quaternion.Slerp(_fromRot, cam.rotation, num2); if (num >= 1f) { _blending = false; } } } public static void Reset() { if (_active) { Active = false; } _blending = false; _travelling = false; } private static PlayerControllerB NearestLivingPlayer(PlayerControllerB local, Vector3 from) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null) { return null; } PlayerControllerB result = null; float num = float.MaxValue; PlayerControllerB[] allPlayerScripts = instance.allPlayerScripts; foreach (PlayerControllerB val in allPlayerScripts) { if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)local) && !val.isPlayerDead && val.isPlayerControlled) { Vector3 val2 = ((Component)val).transform.position - from; float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude; if (sqrMagnitude < num) { num = sqrMagnitude; result = val; } } } return result; } } internal static class InputPause { private static readonly HashSet<string> Reasons = new HashSet<string>(); private static InputAction _use; private static bool _paused; public static void Set(string reason, bool on) { if (!(on ? Reasons.Add(reason) : Reasons.Remove(reason))) { return; } if (_use == null) { InputActionAsset actions = InputSystem.actions; _use = ((actions != null) ? actions.FindAction("ActivateItem", false) : null); } if (_use == null) { return; } try { if (Reasons.Count > 0 && !_paused && _use.enabled) { _use.Disable(); _paused = true; } else if (Reasons.Count == 0 && _paused) { _use.Enable(); _paused = false; } } catch { } } } internal sealed class Inputs : LcInputActions { [InputAction("<Mouse>/rightButton", Name = "Spectate previous", GamepadPath = "<Gamepad>/leftShoulder")] public InputAction Previous { get; set; } [InputAction("<Keyboard>/space", Name = "Vote ship leave early (hold)", GamepadPath = "<Gamepad>/buttonNorth")] public InputAction VoteLeave { get; set; } [InputAction("<Keyboard>/f", Name = "Spectator vision", GamepadPath = "<Gamepad>/dpad/up")] public InputAction Vision { get; set; } [InputAction("<Keyboard>/v", Name = "Freecam", GamepadPath = "<Gamepad>/dpad/down")] public InputAction Freecam { get; set; } [InputAction("<Keyboard>/e", Name = "Freecam up", GamepadPath = "<Gamepad>/rightTrigger")] public InputAction FreecamUp { get; set; } [InputAction("<Keyboard>/q", Name = "Freecam down", GamepadPath = "<Gamepad>/leftTrigger")] public InputAction FreecamDown { get; set; } } [BepInPlugin("slivkonsin.RefinedSpectating", "Refined Spectating", "1.1.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public const string Guid = "slivkonsin.RefinedSpectating"; public const string Name = "Refined Spectating"; public const string Version = "1.1.0"; internal static ManualLogSource Log; internal static Settings Cfg; internal static Harmony Harmony; internal static Inputs Inputs; private static bool _lateInitDone; private void Awake() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown //IL_0052: 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_005d: Expected O, but got Unknown //IL_005d: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; Cfg = new Settings(((BaseUnityPlugin)this).Config); Harmony = new Harmony("slivkonsin.RefinedSpectating"); Inputs = new Inputs(); Controls.Install(Harmony); CameraRig.Install(); Vision.Install(); Freecam.Install(); GameObject val = new GameObject("RefinedSpectating"); Object.DontDestroyOnLoad((Object)val); ((Object)val).hideFlags = (HideFlags)61; val.AddComponent<Runner>(); SceneManager.sceneLoaded += OnSceneLoaded; Log.LogInfo((object)("Refined Spectating 1.1.0 loaded (vote key: " + (Controls.VoteHooked ? "ok" : "unavailable") + ").")); } private static void OnSceneLoaded(Scene s, LoadSceneMode m) { if (!_lateInitDone) { _lateInitDone = true; SpectateEnemiesCompat.Init(Harmony); } } } internal sealed class Runner : MonoBehaviour { private void Update() { try { Spectator.Update(); } catch (Exception ex) { DiagnosticsTelemetry.Errors++; Plugin.Log.LogWarning((object)("Spectator state: " + ex.Message)); } try { CameraRig.Update(); } catch (Exception ex2) { DiagnosticsTelemetry.Errors++; Plugin.Log.LogWarning((object)("Zoom: " + ex2.Message)); } try { SpectateNet.Update(); } catch (Exception ex3) { DiagnosticsTelemetry.Errors++; Plugin.Log.LogWarning((object)("Spectate sharing: " + ex3.Message)); } try { SpectatorUi.Update(); } catch (Exception ex4) { DiagnosticsTelemetry.Errors++; Plugin.Log.LogWarning((object)("\"Watching too\" label: " + ex4.Message)); } try { Vision.Update(); } catch (Exception ex5) { DiagnosticsTelemetry.Errors++; Plugin.Log.LogWarning((object)("Vision: " + ex5.Message)); } try { SpectateEnemiesCompat.UpdateInputs(Plugin.Cfg); } catch { } try { DiagnosticsTelemetry.UpdateLocal(); } catch { } } } internal sealed class Settings { public readonly ConfigEntry<bool> Enabled; public readonly ConfigEntry<bool> Zoom; public readonly ConfigEntry<bool> CameraCollision; public readonly ConfigEntry<bool> SmoothCamera; public readonly ConfigEntry<float> CameraHeight; public readonly ConfigEntry<float> FovIncrease; public readonly ConfigEntry<float> PlayerMaxZoom; public readonly ConfigEntry<float> EnemyMaxZoom; public readonly ConfigEntry<float> EnemyMinZoom; public readonly ConfigEntry<float> ZoomStep; public readonly ConfigEntry<float> ZoomSpeed; public readonly ConfigEntry<bool> CycleBackwards; public readonly ConfigEntry<bool> VoteOnOwnKey; public readonly ConfigEntry<bool> InteractDoesntCycle; public readonly ConfigEntry<bool> VisionEnabled; public readonly ConfigEntry<float> VisionLight; public readonly ConfigEntry<float> VisionLightRange; public readonly ConfigEntry<float> VisionBrightness; public readonly ConfigEntry<float> VisionShadowLift; public readonly ConfigEntry<float> VisionTransition; public readonly ConfigEntry<bool> FreecamEnabled; public readonly ConfigEntry<float> FreecamSpeed; public readonly ConfigEntry<float> FreecamReturnTime; public readonly ConfigEntry<float> FreecamSmoothing; public readonly ConfigEntry<bool> WatchingTooLabel; private readonly ConfigEntry<int> _version; private const int CurrentVersion = 1; public readonly ConfigEntry<bool> SEOverrideZoom; public readonly ConfigEntry<bool> SEReplaceFlashlight; public readonly ConfigEntry<bool> SEFixSwitchBack; public readonly ConfigEntry<bool> DebugLog; public readonly ConfigEntry<bool> PreviewUi; private static AcceptableValueRange<float> R(float a, float b) { return new AcceptableValueRange<float>(a, b); } public Settings(ConfigFile c) { //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Expected O, but got Unknown //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Expected O, but got Unknown //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Expected O, but got Unknown //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Expected O, but got Unknown //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Expected O, but got Unknown //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Expected O, but got Unknown //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Expected O, but got Unknown //IL_029f: Unknown result type (might be due to invalid IL or missing references) //IL_02a9: Expected O, but got Unknown //IL_02d8: Unknown result type (might be due to invalid IL or missing references) //IL_02e2: Expected O, but got Unknown //IL_0311: Unknown result type (might be due to invalid IL or missing references) //IL_031b: Expected O, but got Unknown //IL_034a: Unknown result type (might be due to invalid IL or missing references) //IL_0354: Expected O, but got Unknown //IL_0383: Unknown result type (might be due to invalid IL or missing references) //IL_038d: Expected O, but got Unknown //IL_03d8: Unknown result type (might be due to invalid IL or missing references) //IL_03e2: Expected O, but got Unknown //IL_0411: Unknown result type (might be due to invalid IL or missing references) //IL_041b: Expected O, but got Unknown //IL_044a: Unknown result type (might be due to invalid IL or missing references) //IL_0454: Expected O, but got Unknown Enabled = c.Bind<bool>("1. General", "Enabled", true, "Turns the whole mod on or off."); Zoom = c.Bind<bool>("2. Camera", "Zoom", true, "Scroll to zoom the spectator camera in and out."); PlayerMaxZoom = c.Bind<float>("2. Camera", "PlayerMaxZoom", 4f, new ConfigDescription("How far you can zoom out from a player. 1 is the normal distance.", (AcceptableValueBase)(object)R(1f, 10f), Array.Empty<object>())); EnemyMaxZoom = c.Bind<float>("2. Camera", "EnemyMaxZoom", 7f, new ConfigDescription("How far you can zoom out from an enemy (SpectateEnemies). 1 is a distance that fits the enemy's size.", (AcceptableValueBase)(object)R(1f, 15f), Array.Empty<object>())); EnemyMinZoom = c.Bind<float>("2. Camera", "EnemyMinZoom", 0.4f, new ConfigDescription("How close you can zoom in on an enemy. Lower is closer.", (AcceptableValueBase)(object)R(0.1f, 1f), Array.Empty<object>())); ZoomStep = c.Bind<float>("2. Camera", "ZoomStep", 1.15f, new ConfigDescription("How much one scroll step zooms. Higher is faster.", (AcceptableValueBase)(object)R(1.02f, 2f), Array.Empty<object>())); ZoomSpeed = c.Bind<float>("2. Camera", "ZoomSpeed", 12f, new ConfigDescription("How quickly the camera glides to the new zoom. Higher is snappier.", (AcceptableValueBase)(object)R(2f, 40f), Array.Empty<object>())); CameraHeight = c.Bind<float>("2. Camera", "CameraHeight", 0.3f, new ConfigDescription("Raises the camera a little above what you are watching, in metres. Bigger enemies get a bit more. 0 = vanilla height.", (AcceptableValueBase)(object)R(0f, 1.5f), Array.Empty<object>())); FovIncrease = c.Bind<float>("2. Camera", "FovIncrease", 6f, new ConfigDescription("Widens the field of view while spectating, on top of whatever it normally is (works with FOV mods). 0 = unchanged.", (AcceptableValueBase)(object)R(0f, 30f), Array.Empty<object>())); CameraCollision = c.Bind<bool>("2. Camera", "CameraCollision", true, "Keeps the camera from going through walls, closed doors and the ground."); SmoothCamera = c.Bind<bool>("2. Camera", "SmoothCamera", true, "Takes the shakiness out of the camera following someone, and glides between nearby players when switching."); CycleBackwards = c.Bind<bool>("3. Controls", "CycleBackwards", true, "Lets you go back to the previous player (right mouse by default)."); VoteOnOwnKey = c.Bind<bool>("3. Controls", "VoteOnOwnKey", true, "Voting for the ship to leave early uses its own key (hold Space by default) instead of right mouse."); InteractDoesntCycle = c.Bind<bool>("3. Controls", "InteractDoesntCycle", true, "E no longer switches players while spectating, since the mouse buttons do that now."); VisionEnabled = c.Bind<bool>("4. Vision", "Enabled", true, "Spectator vision (F by default) lights up the area around the camera and whoever you're watching, so you can actually see what's going on."); VisionLight = c.Bind<float>("4. Vision", "LightStrength", 5.5f, new ConfigDescription("How bright the vision light is, compared to the game's own night vision.", (AcceptableValueBase)(object)R(0.5f, 20f), Array.Empty<object>())); VisionLightRange = c.Bind<float>("4. Vision", "LightRange", 32f, new ConfigDescription("How far the vision light reaches, in metres.", (AcceptableValueBase)(object)R(5f, 80f), Array.Empty<object>())); VisionBrightness = c.Bind<float>("4. Vision", "ExtraBrightness", 0.2f, new ConfigDescription("A little extra brightness for the whole screen on top of the light. 0 = none.", (AcceptableValueBase)(object)R(0f, 3f), Array.Empty<object>())); VisionShadowLift = c.Bind<float>("4. Vision", "SoftenShadows", 0.05f, new ConfigDescription("Lifts the darkest parts of the screen a little. 0 = none.", (AcceptableValueBase)(object)R(0f, 1f), Array.Empty<object>())); VisionTransition = c.Bind<float>("4. Vision", "TransitionTime", 0.7f, new ConfigDescription("Seconds it takes to fade vision in or out.", (AcceptableValueBase)(object)R(0f, 3f), Array.Empty<object>())); FreecamEnabled = c.Bind<bool>("6. Freecam", "Enabled", true, "Freecam (V by default) lets you fly the camera anywhere. Move with your normal movement keys, sprint to go faster, scroll to change speed.\nTurning it off brings you back to the nearest living player."); FreecamSpeed = c.Bind<float>("6. Freecam", "Speed", 8f, new ConfigDescription("Flying speed in metres per second.", (AcceptableValueBase)(object)R(1f, 50f), Array.Empty<object>())); FreecamReturnTime = c.Bind<float>("6. Freecam", "ReturnTime", 0.4f, new ConfigDescription("Seconds it takes to glide back to a player after turning freecam off.", (AcceptableValueBase)(object)R(0f, 2f), Array.Empty<object>())); FreecamSmoothing = c.Bind<float>("6. Freecam", "Smoothing", 1f, new ConfigDescription("How smooth flying and looking around feel. Higher is smoother, lower is snappier.", (AcceptableValueBase)(object)R(0.2f, 4f), Array.Empty<object>())); WatchingTooLabel = c.Bind<bool>("7. Other spectators", "WatchingTooLabel", true, "Shows \"Watching (name) too\" under another spectator's picture when they're watching the same player or enemy as you.\nOnly works with players who also have this mod."); SEOverrideZoom = c.Bind<bool>("5. SpectateEnemies", "UseThisZoom", true, "Use this mod's zoom while spectating enemies instead of SpectateEnemies' own."); SEReplaceFlashlight = c.Bind<bool>("5. SpectateEnemies", "DisableFlashlight", true, "Turns off SpectateEnemies' flashlight (right mouse), since vision does the job better and right mouse goes back a player."); SEFixSwitchBack = c.Bind<bool>("5. SpectateEnemies", "SwitchBackToNearestPlayer", true, "When switching from enemies back to players, go to the player nearest to the camera instead of the first one in the list."); DebugLog = c.Bind<bool>("9. Debug", "DebugLog", false, "Writes extra info to the log. Only useful for testing."); PreviewUi = c.Bind<bool>("9. Debug", "PreviewUi", false, "Shows the \"Watching too\" label under your own picture with a made-up name, so you can see how it looks while playing solo.\nOnly changes what you see; nothing is sent to anyone."); _version = c.Bind<int>("9. Debug", "ConfigVersion", 0, "Used to update old default values. Don't change this."); Migrate(); } private void Migrate() { if (_version.Value >= 1) { return; } if (_version.Value < 1) { if (Mathf.Approximately(VisionLight.Value, 4f)) { VisionLight.Value = 5.5f; } if (Mathf.Approximately(VisionLightRange.Value, 25f)) { VisionLightRange.Value = 32f; } if (Mathf.Approximately(FreecamReturnTime.Value, 0.35f)) { FreecamReturnTime.Value = 0.4f; } } _version.Value = 1; } } internal static class SpectateEnemiesCompat { private const string Guid = "SpectateEnemy"; private static Type _seType; private static FieldInfo _instance; private static FieldInfo _spectating; private static FieldInfo _index; private static FieldInfo _list; private static FieldInfo _zoom; private static MethodInfo _isMenuOpen; private static object _inputs; private static PropertyInfo _zoomIn; private static PropertyInfo _zoomOut; private static PropertyInfo _flashlight; private static PropertyInfo _swap; private static bool _swapPaused; private static FieldInfo _enemyInstance; private static bool _switchBackPatched; private static float _inputCheck; public static bool Found { get; private set; } private static object Instance { get { if (!Found) { return null; } return _instance.GetValue(null); } } public static bool SpectatingEnemies { get { try { object instance = Instance; return instance != null && (bool)_spectating.GetValue(instance); } catch { return false; } } } public static bool MenuOpen { get { try { object instance = Instance; return instance != null && _isMenuOpen != null && (bool)_isMenuOpen.Invoke(instance, null); } catch { return false; } } } public static void Init(Harmony harmony) { //IL_0206: 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) //IL_0228: Expected O, but got Unknown //IL_0228: Expected O, but got Unknown try { if (!Chainloader.PluginInfos.TryGetValue("SpectateEnemy", out var value) || (Object)(object)value.Instance == (Object)null) { return; } Assembly assembly = ((object)value.Instance).GetType().Assembly; _seType = assembly.GetType("SpectateEnemy.SpectateEnemies"); Type type = assembly.GetType("SpectateEnemy.Plugin"); Type type2 = assembly.GetType("SpectateEnemy.Spectatable"); if (_seType == null) { Plugin.Log.LogWarning((object)"SpectateEnemies found, but not a version this mod understands. Support stays off."); return; } _instance = _seType.GetField("Instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); _spectating = _seType.GetField("SpectatingEnemies", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _index = _seType.GetField("SpectatedEnemyIndex", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _list = _seType.GetField("SpectatorList", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _zoom = _seType.GetField("ZoomLevel", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _isMenuOpen = _seType.GetMethod("IsMenuOpen", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); _enemyInstance = type2?.GetField("enemyInstance", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _inputs = (type?.GetField("Inputs", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))?.GetValue(null); if (_inputs != null) { Type type3 = _inputs.GetType(); _zoomIn = type3.GetProperty("ZoomInKey", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _zoomOut = type3.GetProperty("ZoomOutKey", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _flashlight = type3.GetProperty("FlashlightKey", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); _swap = type3.GetProperty("SwapKey", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } Found = _instance != null && _spectating != null; if (!Found) { Plugin.Log.LogWarning((object)"SpectateEnemies found, but not a version this mod understands. Support stays off."); return; } MethodInfo methodInfo = AccessTools.Method(_seType, "ToggleSpectatingMode", (Type[])null, (Type[])null); if (methodInfo != null) { harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(SpectateEnemiesCompat), "BeforeToggle", (Type[])null), new HarmonyMethod(typeof(SpectateEnemiesCompat), "AfterToggle", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _switchBackPatched = true; } Plugin.Log.LogInfo((object)("SpectateEnemies found (zoom: " + Ok(_zoom != null && _zoomIn != null) + ", flashlight: " + Ok(_flashlight != null) + ", enemy list: " + Ok(_list != null && _index != null && _enemyInstance != null) + ", switch back: " + Ok(_switchBackPatched) + ").")); } catch (Exception ex) { Found = false; Plugin.Log.LogWarning((object)("SpectateEnemies support off: " + ex.Message)); } } private static string Ok(bool b) { if (!b) { return "unavailable"; } return "ok"; } public static EnemyAI CurrentEnemy() { try { object instance = Instance; if (instance == null || _list == null || _index == null || !SpectatingEnemies) { return null; } Array array = _list.GetValue(instance) as Array; int num = (int)_index.GetValue(instance); if (array == null || num < 0 || num >= array.Length) { return null; } return EnemyOf(array.GetValue(num)); } catch { return null; } } private static EnemyAI EnemyOf(object spectatable) { if (spectatable == null) { return null; } if (_enemyInstance != null) { object? value = _enemyInstance.GetValue(spectatable); EnemyAI val = (EnemyAI)((value is EnemyAI) ? value : null); if (val != null && (Object)(object)val != (Object)null) { return val; } } Component val2 = (Component)((spectatable is Component) ? spectatable : null); if (val2 == null || !((Object)(object)val2 != (Object)null)) { return null; } return val2.GetComponent<EnemyAI>(); } public static bool PreviousEnemy() { try { object instance = Instance; if (instance == null || _list == null || _index == null) { return false; } if (!(_list.GetValue(instance) is Array { Length: not 0 } array)) { return false; } int num = (int)_index.GetValue(instance); for (int i = 0; i < array.Length; i++) { num = (num - 1 + array.Length) % array.Length; object value = array.GetValue(num); Object val = (Object)((value is Object) ? value : null); if (val == null || !(val == (Object)null)) { EnemyAI val2 = EnemyOf(value); if (!((Object)(object)val2 != (Object)null) || !val2.isEnemyDead) { _index.SetValue(instance, num); return true; } } } } catch (Exception ex) { DiagnosticsTelemetry.Errors++; Plugin.Log.LogWarning((object)("Previous enemy: " + ex.Message)); } return false; } public static void SwitchToPlayers(PlayerControllerB local) { try { object instance = Instance; if (instance != null && SpectatingEnemies && AnyoneAlive(local)) { MethodInfo methodInfo = AccessTools.Method(_seType, "ToggleSpectatingMode", (Type[])null, (Type[])null); if (methodInfo != null) { methodInfo.Invoke(instance, new object[1] { local }); } else { _spectating.SetValue(instance, false); } } } catch (Exception ex) { DiagnosticsTelemetry.Errors++; Plugin.Log.LogWarning((object)("Switching back to players: " + ex.Message)); } } private static bool AnyoneAlive(PlayerControllerB local) { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null) { return false; } PlayerControllerB[] allPlayerScripts = instance.allPlayerScripts; foreach (PlayerControllerB val in allPlayerScripts) { if ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)local && !val.isPlayerDead && val.isPlayerControlled) { return true; } } return false; } public static void ShowZoom(float zoom) { try { object instance = Instance; if (instance != null && _zoom != null) { _zoom.SetValue(instance, Mathf.Round(zoom * 10f) / 10f); } } catch { } } public static void PauseSwap(bool pause) { if (!Found || _inputs == null || _swap == null) { return; } try { object? value = _swap.GetValue(_inputs); InputAction val = (InputAction)((value is InputAction) ? value : null); if (val != null) { if (pause && !_swapPaused && val.enabled) { val.Disable(); _swapPaused = true; } else if (!pause && _swapPaused) { val.Enable(); _swapPaused = false; } } } catch { } } public static void UpdateInputs(Settings cfg) { if (Found && _inputs != null) { _inputCheck -= Time.unscaledDeltaTime; if (!(_inputCheck > 0f)) { _inputCheck = 1f; bool value = cfg.Enabled.Value; Set(_zoomIn, !value || !cfg.Zoom.Value || !cfg.SEOverrideZoom.Value); Set(_zoomOut, !value || !cfg.Zoom.Value || !cfg.SEOverrideZoom.Value); Set(_flashlight, !value || !cfg.SEReplaceFlashlight.Value); } } } private static void Set(PropertyInfo p, bool enabled) { try { object? obj = p?.GetValue(_inputs); InputAction val = (InputAction)((obj is InputAction) ? obj : null); if (val != null && val.enabled != enabled) { if (enabled) { val.Enable(); } else { val.Disable(); } if (!enabled && p == _flashlight) { TurnOffSpectateLight(); } } } catch { } } private static void TurnOffSpectateLight() { StartOfRound instance = StartOfRound.Instance; Light val = default(Light); if ((Object)(object)instance != (Object)null && (Object)(object)instance.spectateCamera != (Object)null && ((Component)instance.spectateCamera).TryGetComponent<Light>(ref val)) { ((Behaviour)val).enabled = false; } } private static bool BeforeToggle(PlayerControllerB __0) { try { if (!Plugin.Cfg.Enabled.Value || !SpectatingEnemies || AnyoneAlive(__0)) { return true; } Spectator.Tip("Nobody alive to spectate"); return false; } catch { return true; } } private static void AfterToggle(PlayerControllerB __0) { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: 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) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) try { if (!Plugin.Cfg.Enabled.Value || !Plugin.Cfg.SEFixSwitchBack.Value || SpectatingEnemies || (Object)(object)__0 == (Object)null) { return; } StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.spectateCamera == (Object)null) { return; } Vector3 position = ((Component)instance.spectateCamera).transform.position; PlayerControllerB val = null; float num = float.MaxValue; PlayerControllerB[] allPlayerScripts = instance.allPlayerScripts; foreach (PlayerControllerB val2 in allPlayerScripts) { if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2 == (Object)(object)__0) && !val2.isPlayerDead && val2.isPlayerControlled) { Vector3 val3 = ((Component)val2).transform.position - position; float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude; if (sqrMagnitude < num) { num = sqrMagnitude; val = val2; } } } if (!((Object)(object)val == (Object)null)) { __0.spectatedPlayerScript = val; __0.SetSpectatedPlayerEffects(false); } } catch (Exception ex) { DiagnosticsTelemetry.Errors++; Plugin.Log.LogWarning((object)("Switch back from enemies: " + ex.Message)); } } } internal enum WatchKind : byte { None, Player, Enemy, Freecam } internal struct WatchState { public WatchKind Kind; public int Player; public ulong Enemy; public Vector3 Pos; public float Received; public bool SameTarget(in WatchState o) { if (Kind == o.Kind && Kind != WatchKind.None && Kind != WatchKind.Freecam) { if (Kind != WatchKind.Player) { return Enemy == o.Enemy; } return Player == o.Player; } return false; } } internal static class SpectateNet { [CompilerGenerated] private static class <>O { public static HandleNamedMessageDelegate <0>__OnFromClient; public static HandleNamedMessageDelegate <1>__OnFromHost; } private const string ToHost = "slivkonsin.RefinedSpectating.Watch"; private const string ToClients = "slivkonsin.RefinedSpectating.WatchRelay"; private const byte Format = 1; private const float Heartbeat = 2f; private const float MinGap = 0.2f; private const float Expire = 7f; public static readonly Dictionary<int, WatchState> Others = new Dictionary<int, WatchState>(); public static WatchState Mine; public static int MessagesIn; public static int MessagesOut; private static CustomMessagingManager _registered; private static WatchState _lastSent; private static float _lastSendTime = -100f; private static readonly List<ulong> Targets = new List<ulong>(); private static readonly List<int> Stale = new List<int>(); public static void Update() { //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Expected O, but got Unknown //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Expected O, but got Unknown NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || !singleton.IsListening || singleton.CustomMessagingManager == null) { _registered = null; Others.Clear(); Mine = default(WatchState); return; } if (_registered != singleton.CustomMessagingManager) { _registered = singleton.CustomMessagingManager; CustomMessagingManager registered = _registered; object obj = <>O.<0>__OnFromClient; if (obj == null) { HandleNamedMessageDelegate val = OnFromClient; <>O.<0>__OnFromClient = val; obj = (object)val; } registered.RegisterNamedMessageHandler("slivkonsin.RefinedSpectating.Watch", (HandleNamedMessageDelegate)obj); CustomMessagingManager registered2 = _registered; object obj2 = <>O.<1>__OnFromHost; if (obj2 == null) { HandleNamedMessageDelegate val2 = OnFromHost; <>O.<1>__OnFromHost = val2; obj2 = (object)val2; } registered2.RegisterNamedMessageHandler("slivkonsin.RefinedSpectating.WatchRelay", (HandleNamedMessageDelegate)obj2); Others.Clear(); _lastSendTime = -100f; } Mine = ComputeMine(); float realtimeSinceStartup = Time.realtimeSinceStartup; Stale.Clear(); PlayerControllerB[] array = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.allPlayerScripts : null); foreach (KeyValuePair<int, WatchState> other in Others) { if (array == null || other.Key < 0 || other.Key >= array.Length || (Object)(object)array[other.Key] == (Object)null || (!array[other.Key].isPlayerControlled && !array[other.Key].isPlayerDead) || realtimeSinceStartup - other.Value.Received > 7f) { Stale.Add(other.Key); } } foreach (int item in Stale) { Others.Remove(item); } if (((Mine.Kind == _lastSent.Kind && Mine.Player == _lastSent.Player && Mine.Enemy == _lastSent.Enemy) || !(realtimeSinceStartup - _lastSendTime > 0.2f)) && !(realtimeSinceStartup - _lastSendTime > 2f)) { return; } _lastSent = Mine; _lastSendTime = realtimeSinceStartup; try { Send(singleton, in Mine); } catch (Exception ex) { DiagnosticsTelemetry.Errors++; Plugin.Log.LogWarning((object)("Sending spectate state: " + ex.Message)); } } private static WatchState ComputeMine() { //IL_007e: 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) WatchState result = new WatchState { Kind = WatchKind.None, Player = -1 }; PlayerControllerB val = Spectator.Local(); StartOfRound instance = StartOfRound.Instance; if ((Object)(object)val == (Object)null || (Object)(object)instance == (Object)null || !val.isPlayerDead || !Plugin.Cfg.Enabled.Value) { return result; } if (Freecam.Active) { result.Kind = WatchKind.Freecam; if ((Object)(object)instance.spectateCamera != (Object)null) { result.Pos = ((Component)instance.spectateCamera).transform.position; } return result; } if (SpectateEnemiesCompat.SpectatingEnemies) { EnemyAI val2 = SpectateEnemiesCompat.CurrentEnemy(); if ((Object)(object)val2 != (Object)null && (Object)(object)((NetworkBehaviour)val2).NetworkObject != (Object)null && ((NetworkBehaviour)val2).NetworkObject.IsSpawned) { result.Kind = WatchKind.Enemy; result.Enemy = ((NetworkBehaviour)val2).NetworkObject.NetworkObjectId; } return result; } PlayerControllerB spectatedPlayerScript = val.spectatedPlayerScript; if ((Object)(object)spectatedPlayerScript != (Object)null && !spectatedPlayerScript.isPlayerDead) { result.Kind = WatchKind.Player; result.Player = Array.IndexOf(instance.allPlayerScripts, spectatedPlayerScript); if (result.Player < 0) { result.Kind = WatchKind.None; } } return result; } private static int LocalIndex() { StartOfRound instance = StartOfRound.Instance; PlayerControllerB val = Spectator.Local(); if (!((Object)(object)instance != (Object)null) || !((Object)(object)val != (Object)null)) { return -1; } return Array.IndexOf(instance.allPlayerScripts, val); } private unsafe static void Send(NetworkManager nm, in WatchState s) { //IL_002a: 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_0036: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) if (nm.IsServer) { Relay(nm, LocalIndex(), in s, 0uL); return; } FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(64, (Allocator)2, -1); try { byte b = 1; ((FastBufferWriter)(ref val)).WriteValueSafe<byte>(ref b, default(ForPrimitives)); WriteBody(val, in s); nm.CustomMessagingManager.SendNamedMessage("slivkonsin.RefinedSpectating.Watch", 0uL, val, (NetworkDelivery)2); MessagesOut++; } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } private static void WriteBody(FastBufferWriter w, in WatchState s) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: 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_0029: 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_003f: Unknown result type (might be due to invalid IL or missing references) byte kind = (byte)s.Kind; ((FastBufferWriter)(ref w)).WriteValueSafe<byte>(ref kind, default(ForPrimitives)); ((FastBufferWriter)(ref w)).WriteValueSafe<int>(ref s.Player, default(ForPrimitives)); ((FastBufferWriter)(ref w)).WriteValueSafe<ulong>(ref s.Enemy, default(ForPrimitives)); ((FastBufferWriter)(ref w)).WriteValueSafe(ref s.Pos); } private static WatchState ReadBody(FastBufferReader r) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: 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_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002c: 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) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006c: 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_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: 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_008f: Unknown result type (might be due to invalid IL or missing references) byte b = default(byte); ((FastBufferReader)(ref r)).ReadValueSafe<byte>(ref b, default(ForPrimitives)); int player = default(int); ((FastBufferReader)(ref r)).ReadValueSafe<int>(ref player, default(ForPrimitives)); ulong enemy = default(ulong); ((FastBufferReader)(ref r)).ReadValueSafe<ulong>(ref enemy, default(ForPrimitives)); Vector3 val = default(Vector3); ((FastBufferReader)(ref r)).ReadValueSafe(ref val); WatchState result = new WatchState { Kind = (WatchKind)((b <= 3) ? b : 0), Player = player, Enemy = enemy, Pos = val }; if (float.IsNaN(val.x) || float.IsNaN(val.y) || float.IsNaN(val.z)) { result.Pos = Vector3.zero; } return result; } private static void OnFromClient(ulong sender, FastBufferReader r) { //IL_0031: 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_0044: Unknown result type (might be due to invalid IL or missing references) try { NetworkManager singleton = NetworkManager.Singleton; StartOfRound instance = StartOfRound.Instance; if ((Object)(object)singleton == (Object)null || !singleton.IsServer || (Object)(object)instance == (Object)null) { return; } byte b = default(byte); ((FastBufferReader)(ref r)).ReadValueSafe<byte>(ref b, default(ForPrimitives)); if (b == 1) { WatchState s = ReadBody(r); if (instance.ClientPlayerList.TryGetValue(sender, out var value)) { MessagesIn++; Store(value, s); Relay(singleton, value, in s, sender); } } } catch (Exception ex) { DiagnosticsTelemetry.Errors++; if (Plugin.Cfg.DebugLog.Value) { Plugin.Log.LogWarning((object)("Spectate state from client: " + ex.Message)); } } } private unsafe static void Relay(NetworkManager nm, int index, in WatchState s, ulong except) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0070: 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_0083: 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_00a1: Unknown result type (might be due to invalid IL or missing references) if (index < 0) { return; } Targets.Clear(); foreach (ulong connectedClientsId in nm.ConnectedClientsIds) { if (connectedClientsId != except && connectedClientsId != 0L) { Targets.Add(connectedClientsId); } } if (Targets.Count == 0) { return; } FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(64, (Allocator)2, -1); try { byte b = 1; ((FastBufferWriter)(ref val)).WriteValueSafe<byte>(ref b, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref index, default(ForPrimitives)); WriteBody(val, in s); nm.CustomMessagingManager.SendNamedMessage("slivkonsin.RefinedSpectating.WatchRelay", (IReadOnlyList<ulong>)Targets, val, (NetworkDelivery)2); MessagesOut++; } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } } private static void OnFromHost(ulong sender, FastBufferReader r) { //IL_000e: 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_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002c: 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) try { if (sender == 0L) { byte b = default(byte); ((FastBufferReader)(ref r)).ReadValueSafe<byte>(ref b, default(ForPrimitives)); if (b == 1) { int index = default(int); ((FastBufferReader)(ref r)).ReadValueSafe<int>(ref index, default(ForPrimitives)); WatchState s = ReadBody(r); MessagesIn++; Store(index, s); } } } catch (Exception ex) { DiagnosticsTelemetry.Errors++; if (Plugin.Cfg.DebugLog.Value) { Plugin.Log.LogWarning((object)("Spectate state from host: " + ex.Message)); } } } private static void Store(int index, WatchState s) { StartOfRound instance = StartOfRound.Instance; if (!((Object)(object)instance == (Object)null) && index >= 0 && index < instance.allPlayerScripts.Length && index != LocalIndex()) { if (s.Kind == WatchKind.Player && (s.Player < 0 || s.Player >= instance.allPlayerScripts.Length)) { s.Kind = WatchKind.None; } s.Received = Time.realtimeSinceStartup; if (s.Kind == WatchKind.None) { Others.Remove(index); } else { Others[index] = s; } if (Plugin.Cfg.DebugLog.Value) { Plugin.Log.LogInfo((object)("Spectate: " + instance.allPlayerScripts[index].playerUsername + " -> " + Describe(in s))); } } } public static PlayerControllerB PlayerAt(int index) { StartOfRound instance = StartOfRound.Instance; if (!((Object)(object)instance != (Object)null) || index < 0 || index >= instance.allPlayerScripts.Length) { return null; } return instance.allPlayerScripts[index]; } public static EnemyAI EnemyById(ulong id) { NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || singleton.SpawnManager == null) { return null; } if (!singleton.SpawnManager.SpawnedObjects.TryGetValue(id, out var value) || !((Object)(object)value != (Object)null)) { return null; } return ((Component)value).GetComponent<EnemyAI>(); } public static string Describe(in WatchState s) { switch (s.Kind) { case WatchKind.Player: { PlayerControllerB val2 = PlayerAt(s.Player); if (!((Object)(object)val2 != (Object)null)) { return "someone"; } return val2.playerUsername; } case WatchKind.Enemy: { EnemyAI val = EnemyById(s.Enemy); if (!((Object)(object)val != (Object)null) || !((Object)(object)val.enemyType != (Object)null)) { return "an enemy"; } return val.enemyType.enemyName; } case WatchKind.Freecam: return "Freecam"; default: return ""; } } } internal static class Spectator { private static MethodInfo _tip; public static PlayerControllerB Local() { GameNetworkManager instance = GameNetworkManager.Instance; if (!((Object)(object)instance != (Object)null)) { return null; } return instance.localPlayerController; } public static bool InputBlocked() { PlayerControllerB val = Local(); if ((Object)(object)val == (Object)null) { return true; } if (val.isTypingChat || val.inTerminalMenu) { return true; } if ((Object)(object)val.quickMenuManager != (Object)null && val.quickMenuManager.isMenuOpen) { return true; } return SpectateEnemiesCompat.MenuOpen; } public static void Tip(string text) { try { if (_tip == null) { _tip = AccessTools.Method(typeof(HUDManager), "DisplaySpectatorTip", (Type[])null, (Type[])null); } if (_tip != null && (Object)(object)HUDManager.Instance != (Object)null) { _tip.Invoke(HUDManager.Instance, new object[1] { text }); } } catch { } } public static void Update() { //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) PlayerControllerB val = Local(); StartOfRound instance = StartOfRound.Instance; if (!(RefinedSpectatingApi.IsSpectating = Plugin.Cfg.Enabled.Value && (Object)(object)val != (Object)null && val.isPlayerDead && (Object)(object)instance != (Object)null && (Object)(object)instance.spectateCamera != (Object)null)) { RefinedSpectatingApi.SpectatedPlayer = null; return; } RefinedSpectatingApi.CameraPosition = ((Component)instance.spectateCamera).transform.position; RefinedSpectatingApi.SpectatedPlayer = (SpectateEnemiesCompat.SpectatingEnemies ? null : val.spectatedPlayerScript); } } public static class RefinedSpectatingApi { public const int ApiVersion = 1; public static bool IsSpectating; public static Vector3 CameraPosition; public static PlayerControllerB SpectatedPlayer; } internal static class SpectatorUi { private static readonly Color Tint = new Color(1f, 0.87f, 0.7f, 1f); private const float Size = 0.38f; private static FieldRef<HUDManager, Dictionary<Animator, PlayerControllerB>> _boxes; private static bool _boxesTried; private static readonly Dictionary<PlayerControllerB, FadeText> Labels = new Dictionary<PlayerControllerB, FadeText>(); private static readonly List<PlayerControllerB> Remove = new List<PlayerControllerB>(); private static HUDManager _hudFor; private static bool Preview => Plugin.Cfg.PreviewUi.Value; public static void Update() { //IL_021c: Unknown result type (might be due to invalid IL or missing references) HUDManager instance = HUDManager.Instance; PlayerControllerB val = Spectator.Local(); StartOfRound instance2 = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || (Object)(object)val == (Object)null || (Object)(object)instance2 == (Object)null) { HideAll(); return; } if ((Object)(object)_hudFor != (Object)(object)instance) { Labels.Clear(); _hudFor = instance; } WatchState o = SpectateNet.Mine; Dictionary<Animator, PlayerControllerB> dictionary = Boxes(instance); bool flag = Plugin.Cfg.Enabled.Value && Plugin.Cfg.WatchingTooLabel.Value && val.isPlayerDead && dictionary != null; foreach (FadeText value4 in Labels.Values) { value4.Seen = false; } if (flag) { foreach (KeyValuePair<Animator, PlayerControllerB> item in dictionary) { Animator key = item.Key; PlayerControllerB value = item.Value; if ((Object)(object)key == (Object)null || (Object)(object)value == (Object)null || ((Object)(object)value == (Object)(object)val && !Preview) || !((Component)key).gameObject.activeInHierarchy) { continue; } Transform transform = ((Component)key).transform; RectTransform val2 = (RectTransform)(object)((transform is RectTransform) ? transform : null); if ((Object)(object)val2 == (Object)null) { continue; } if (!Labels.TryGetValue(value, out var value2) || value2.Dead) { TextMeshProUGUI componentInChildren = ((Component)key).GetComponentInChildren<TextMeshProUGUI>(true); if ((Object)(object)componentInChildren == (Object)null) { continue; } value2 = FadeText.Clone(componentInChildren, ((Transform)val2).parent, "RS_WatchingToo", 0.85f); Labels[value] = value2; } value2.Seen = true; value2.Under(val2, 0.38f); string text = ""; WatchState value3; if (Preview) { text = "Watching Preview Bob too"; } else if (SpectateNet.Others.TryGetValue(Array.IndexOf(instance2.allPlayerScripts, value), out value3) && value3.SameTarget(in o)) { text = "Watching " + SpectateNet.Describe(in value3) + " too"; } value2.Set(text, Tint); value2.Tick(); } } Remove.Clear(); foreach (KeyValuePair<PlayerControllerB, FadeText> label in Labels) { if (!label.Value.Seen) { label.Value.Set(""); label.Value.Tick(); } if ((Object)(object)label.Key == (Object)null || label.Value.Dead) { Remove.Add(label.Key); } } foreach (PlayerControllerB item2 in Remove) { Labels[item2].Destroy(); Labels.Remove(item2); } } private static Dictionary<Animator, PlayerControllerB> Boxes(HUDManager hud) { if (!_boxesTried) { _boxesTried = true; try { _boxes = AccessTools.FieldRefAccess<HUDManager, Dictionary<Animator, PlayerControllerB>>("spectatingPlayerBoxes"); } catch (Exception ex) { Plugin.Log.LogWarning((object)("\"Watching too\" label unavailable: " + ex.Message)); } } try { return (_boxes != null) ? _boxes.Invoke(hud) : null; } catch { return null; } } private static void HideAll() { foreach (FadeText value in Labels.Values) { value.SetNow(""); } } } internal sealed class FadeText { private const float FadeOut = 0.12f; private const float FadeIn = 0.25f; private readonly TextMeshProUGUI _tmp; private readonly float _maxAlpha; private readonly float _baseSize; private readonly Color _baseColor; private string _target = ""; private string _shown = ""; private Color? _targetColor; private float _alpha; public bool Seen; private static readonly Vector3[] Corners = (Vector3[])(object)new Vector3[4]; public bool Dead => (Object)(object)_tmp == (Object)null; private FadeText(TextMeshProUGUI tmp, float maxAlpha) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) _tmp = tmp; _maxAlpha = maxAlpha; _baseColor = ((Graphic)tmp).color; _baseSize = ((TMP_Text)tmp).fontSize; } public static FadeText Clone(TextMeshProUGUI template, Transform parent, string name, float alpha) { GameObject val = Object.Instantiate<GameObject>(((Component)template).gameObject, parent, false); ((Object)val).name = name; for (int num = val.transform.childCount - 1; num >= 0; num--) { Object.Destroy((Object)(object)((Component)val.transform.GetChild(num)).gameObject); } Component[] components = val.GetComponents<Component>(); foreach (Component val2 in components) { if (!(val2 is Transform) && !(val2 is CanvasRenderer) && !(val2 is TextMeshProUGUI)) { Object.Destroy((Object)(object)val2); } } TextMeshProUGUI component = val.GetComponent<TextMeshProUGUI>(); ((Graphic)component).raycastTarget = false; ((TMP_Text)component).enableWordWrapping = false; ((TMP_Text)component).overflowMode = (TextOverflowModes)0; ((TMP_Text)component).text = ""; val.SetActive(true); FadeText fadeText = new FadeText(component, alpha); fadeText.Apply(); return fadeText; } public void Under(RectTransform box, float size) { //IL_0022: 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_00a3: 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) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_0175: 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_0180: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) if (!Dead) { ((TMP_Text)_tmp).fontSize = _baseSize * size; ((TMP_Text)_tmp).margin = Vector4.zero; ((TMP_Text)_tmp).alignment = (TextAlignmentOptions)257; RectTransform val = box; RawImage val2 = ((Component)box).GetComponent<RawImage>() ?? ((Component)box).GetComponentInChildren<RawImage>(true); if ((Object)(object)val2 != (Object)null) { val = ((Graphic)val2).rectTransform; } val.GetWorldCorners(Corners); RectTransform rectTransform = ((TMP_Text)_tmp).rectTransform; Vector2 val3 = default(Vector2); ((Vector2)(ref val3))..ctor(0.5f, 0.5f); rectTransform.anchorMax = val3; rectTransform.anchorMin = val3; rectTransform.pivot = new Vector2(0f, 1f); float num = Mathf.Max(0.0001f, ((Transform)rectTransform).lossyScale.x); rectTransform.sizeDelta = new Vector2(Vector3.Distance(Corners[0], Corners[3]) / num, ((TMP_Text)_tmp).fontSize); float num2 = Vector3.Distance(Corners[0], Corners[1]); float num3 = PixelHeight(val, Corners); float num4 = ((num3 > 0.01f) ? (num2 / num3) : 1f); float num5 = Mathf.Max(num2 * 0.05f, 2f * num4); Vector3 val4 = Corners[0]; Vector3 val5 = Corners[1] - Corners[0]; ((Transform)rectTransform).position = val4 - ((Vector3)(ref val5)).normalized * num5; } } private static float PixelHeight(RectTransform pic, Vector3[] corners) { //IL_0011: 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_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_0031: 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) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) Canvas componentInParent = ((Component)pic).GetComponentInParent<Canvas>(); Camera obj = (((Object)(object)componentInParent != (Object)null && (int)componentInParent.renderMode != 0) ? componentInParent.worldCamera : null); Vector2 val = RectTransformUtility.WorldToScreenPoint(obj, corners[0]); Vector2 val2 = RectTransformUtility.WorldToScreenPoint(obj, corners[1]); return Vector2.Distance(val, val2); } public void Set(string text, Color? color = null) { _target = text ?? ""; _targetColor = color; } public void SetNow(string text) { string obj = text ?? ""; string target = obj; _shown = obj; _target = target; _alpha = 0f; Apply(); } public void Tick() { if (Dead) { return; } float unscaledDeltaTime = Time.unscaledDeltaTime; if (_target != _shown) { _alpha = Mathf.MoveTowards(_alpha, 0f, unscaledDeltaTime / 0.12f); if (_alpha <= 0f) { _shown = _target; } } else { _alpha = Mathf.MoveTowards(_alpha, (_shown.Length > 0) ? 1f : 0f, unscaledDeltaTime / 0.25f); } Apply(); } private void Apply() { //IL_004c: 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_0051: 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) if (!Dead) { if (((TMP_Text)_tmp).text != _shown) { ((TMP_Text)_tmp).text = _shown; } Color color = (Color)(((??)_targetColor) ?? _baseColor); color.a = _baseColor.a * _maxAlpha * _alpha; ((Graphic)_tmp).color = color; bool flag = _alpha > 0f && _shown.Length > 0; if (((Behaviour)_tmp).enabled != flag) { ((Behaviour)_tmp).enabled = flag; } } } public void Destroy() { if (!Dead) { Object.Destroy((Object)(object)((Component)_tmp).gameObject); } } } public static class DiagnosticsTelemetry { public static bool Spectating; public static int Target; public static float Zoom; public static float CameraDistance; public static bool CameraBlocked; public static float VisionAmount; public static bool VisionLightVisible; public static bool SpectateEnemiesFound; public static bool SpectatingEnemies; public static bool VoteKeyHooked; public static bool Freecam; public static int OtherSpectatorsKnown; public static int MessagesIn; public static int MessagesOut; public static int Errors; internal static void UpdateLocal() { Spectating = RefinedSpectatingApi.IsSpectating; Target = (int)CameraRig.CurrentTarget; Zoom = CameraRig.Zoom; CameraDistance = CameraRig.Distance; CameraBlocked = CameraRig.Collided; VisionAmount = Vision.Weight; VisionLightVisible = Vision.LightVisible; SpectateEnemiesFound = SpectateEnemiesCompat.Found; SpectatingEnemies = SpectateEnemiesCompat.SpectatingEnemies; VoteKeyHooked = Controls.VoteHooked; Freecam = RefinedSpectating.Freecam.Active; OtherSpectatorsKnown = SpectateNet.Others.Count; MessagesIn = SpectateNet.MessagesIn; MessagesOut = SpectateNet.MessagesOut; } } internal static class Vision { [CompilerGenerated] private static class <>O { public static Action<CallbackContext> <0>__OnToggle; public static UnityAction <1>__Place; } private static Light _camLight; private static Light _targetLight; private const float BaseIntensity = 343.06f; private static readonly Color BaseColor = new Color(0.396f, 0.415f, 0.394f, 1f); private static float _retryAt; private static bool _loggedError; private static Volume _volume; private static ColorAdjustments _color; private static ShadowsMidtonesHighlights _shadows; private static bool _wanted; private static bool _volumeFailed; private static float _weight; public static bool On => _wanted; public static float Weight => _weight; public static bool LightVisible { get { if ((Object)(object)_camLight != (Object)null && ((Behaviour)_camLight).enabled && _camLight.cullingMask != 0) { return _camLight.intensity > 0f; } return false; } } private static float Eased => _weight * _weight * (3f - 2f * _weight); public static void Install() { //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_0045: Expected O, but got Unknown Plugin.Inputs.Vision.performed += OnToggle; object obj = <>O.<1>__Place; if (obj == null) { UnityAction val = Place; <>O.<1>__Place = val; obj = (object)val; } Application.onBeforeRender += (UnityAction)obj; } private static void OnToggle(CallbackContext ctx) { if (((CallbackContext)(ref ctx)).performed && Plugin.Cfg.Enabled.Value && Plugin.Cfg.VisionEnabled.Value) { PlayerControllerB val = Spectator.Local(); if (!((Object)(object)val == (Object)null) && val.isPlayerDead && !Spectator.InputBlocked()) { _wanted = !_wanted; } } } public static void Update() { Settings cfg = Plugin.Cfg; PlayerControllerB val = Spectator.Local(); bool flag = (Object)(object)val != (Object)null && val.isPlayerDead; if (!flag) { _wanted = false; } bool flag2 = _wanted && flag && cfg.Enabled.Value && cfg.VisionEnabled.Value; float num = Mathf.Max(0.01f, cfg.VisionTransition.Value); _weight = Mathf.MoveTowards(_weight, flag2 ? 1f : 0f, Time.unscaledDeltaTime / num); } private static void Place() { //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_018a: 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) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) try { float eased = Eased; if (eased <= 0f && (Object)(object)_camLight == (Object)null && (Object)(object)_volume == (Ob