Decompiled source of Spectate v1.7.0
Spectate.dll
Decompiled 4 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text.RegularExpressions; using System.Threading.Tasks; using AIGraph; using Agents; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using BepInEx.Unity.IL2CPP.Utils.Collections; using CellMenu; using Clonesoft.Json; using Clonesoft.Json.Linq; using CullingSystem; using EEC.CustomAbilities.EMP.Handlers; using EOS.Modules.World.EMP.Handlers; using EOSExt.EMP.Impl.Handlers; using Enemies; using GameData; using Globals; using HarmonyLib; using Il2CppInterop.Runtime.Injection; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using Localization; using Microsoft.CodeAnalysis; using Player; using PlayerSync.Network.Impl; using PlayerSync.Sync.Ammo; using PlayerSync.Sync.Stamina; using SNetwork; using Spectate.Assets; using Spectate.Config; using Spectate.Data; using Spectate.Interop; using Spectate.Localization; using Spectate.Network; using Spectate.Network.Impl; using Spectate.Patches; using Spectate.Patches.Compat; using Spectate.Patches.Compat.Targets; using Spectate.UI; using Spectate.UI.Support; using Spectate.Utility; using Spectate.Utility.Ext; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = "")] [assembly: AssemblyCompany("Spectate")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("Spectate")] [assembly: AssemblyTitle("Spectate")] [assembly: AssemblyVersion("1.0.0.0")] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace Spectate { public class AgentTarget { public readonly PlayerAgent Agent; public Transform Transform => ((Component)Agent).transform; public FPSCamera? FPSCamera => Agent.FPSCamera; public FirstPersonItemHolder? FPHolder => Agent.FPItemHolder; public PlayerLocomotion Locomotion => Agent.Locomotion; public PLOC_State LocomotionStateEnum => Locomotion.m_currentStateEnum; public bool IsDowned => (int)LocomotionStateEnum == 7; public PlayerSyncModelData PlayerModel => Agent.PlayerSyncModel; public SNet_Player SAgent => Agent.Owner; public ulong Lookup => SAgent.Lookup; public bool IsBot => SAgent.IsBot; public bool IsLocal => SAgent.IsLocal; public Dam_PlayerDamageBase Damage => Agent.Damage; public float Health => ((Dam_SyncedDamageBase)Damage).Health / ((Dam_SyncedDamageBase)Damage).HealthMax; public float HealthPercent => Health * 100f; public float Infection => Damage.Infection; public float InfectionPercent => Infection * 100f; public PlayerInventoryBase? Inventory => Agent.Inventory; public InventorySlot? ActiveItemSlot { get { //IL_0014: Unknown result type (might be due to invalid IL or missing references) PlayerInventoryBase? inventory = Inventory; if (inventory == null) { return null; } return inventory.WieldedSlot; } } public AIG_CourseNode? CourseNode { get { if (IsCaptured) { PouncerTracker? instance = PouncerTracker.Instance; if (instance != null && instance.TryGetCapturingPouncer(Agent, out PouncerBehaviour capturingPouncer)) { object obj; if (capturingPouncer == null) { obj = null; } else { EnemyAI ai = ((EnemyBehaviour)capturingPouncer).m_ai; if (ai == null) { obj = null; } else { EnemyAgent enemyAgent = ai.m_enemyAgent; obj = ((enemyAgent != null) ? ((Agent)enemyAgent).CourseNode : null); } } if (obj == null) { obj = ((Agent)Agent).CourseNode; } return (AIG_CourseNode?)obj; } } return ((Agent)Agent).CourseNode; } } public PlayerBackpack? Backpack => PlayerBackpackManager.GetBackpack(Agent.Owner); public PlayerAmmoStorage? AmmoStorage { get { PlayerBackpack? backpack = Backpack; if (backpack == null) { return null; } return backpack.AmmoStorage; } } public bool HasClipData { get { if (IsLocal) { return true; } if (PlayerManager.GetLocalPlayerAgent().Owner.IsMaster && IsBot) { return true; } if (PeerInfoManager.Supported(SAgent)) { return true; } return false; } } public bool IsCaptured { get { if ((Object)(object)PouncerTracker.Instance != (Object)null) { return PouncerTracker.Instance.IsCaptured(Agent); } return false; } } public static bool CanSpectate(PlayerAgent agent) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Invalid comparison between Unknown and I4 bool num = (int)agent.Locomotion.m_currentStateEnum == 7; bool flag = (Object)(object)PouncerTracker.Instance != (Object)null && PouncerTracker.Instance.IsCaptured(agent); if (num) { return !flag; } return false; } public AgentTarget(PlayerAgent agent) { Agent = agent; } public void SetRigActive(bool active) { Util.SetTargetActiveIfDiff(((Component)Agent.PlayerSyncModel).gameObject, active); } public void SetHostHiddenRigActive(bool active) { PlayerSyncModelData playerSyncModel = Agent.PlayerSyncModel; if (((Il2CppArrayBase<GameObject>)(object)playerSyncModel.m_gfxHead).Count == 0 || ((Il2CppArrayBase<GameObject>)(object)playerSyncModel.m_gfxArms).Count == 0) { Util.FindAndSortGfxParts(((Component)playerSyncModel).gameObject, out GameObject[] gfxHead, out GameObject[] gfxArms, out GameObject[] gfxTorso, out GameObject[] gfxLegs); playerSyncModel.m_gfxHead = Il2CppReferenceArray<GameObject>.op_Implicit(gfxHead); playerSyncModel.m_gfxArms = Il2CppReferenceArray<GameObject>.op_Implicit(gfxArms); playerSyncModel.m_gfxTorso = Il2CppReferenceArray<GameObject>.op_Implicit(gfxTorso); playerSyncModel.m_gfxLegs = Il2CppReferenceArray<GameObject>.op_Implicit(gfxLegs); } playerSyncModel.SetHeadVisible(active, active); playerSyncModel.SetArmsVisible(active, active); } public void SetRigTorsoLegsActive(bool active) { PlayerSyncModelData playerSyncModel = Agent.PlayerSyncModel; playerSyncModel.SetGFXVisible(playerSyncModel.m_gfxLegs, active, active); playerSyncModel.SetGFXVisible(playerSyncModel.m_gfxTorso, active, active); } public static bool operator ==(AgentTarget? a, AgentTarget? b) { if ((object)a == b) { return true; } if ((object)a == null) { if ((object)b != null) { return (Object)(object)b.Agent == (Object)null; } return true; } if ((object)b == null) { return (Object)(object)a.Agent == (Object)null; } return (Object)(object)a.Agent == (Object)(object)b.Agent; } public static bool operator !=(AgentTarget? a, AgentTarget? b) { return !(a == b); } public override bool Equals(object? other) { if (other is AgentTarget agentTarget) { return this == agentTarget; } return false; } public override int GetHashCode() { return ((Object)Agent).GetHashCode(); } } public static class Events { public static event Action? OnSessionEnd; public static event Action? OnSessionStart; public static event Action? OnAnyPlayerDeath; public static event Action? OnCheckpointReload; public static event Action? OnGameDataInitialized; public static event Action<SNet_Player>? OnPlayerJoinLobby; public static event Action? OnPageLoadoutPlayerListUpdate; internal static void RaiseSessionEnd() { Events.OnSessionEnd?.Invoke(); } internal static void RaiseSessionStart() { Events.OnSessionStart?.Invoke(); } internal static void RaiseAnyPlayerDeath() { Events.OnAnyPlayerDeath?.Invoke(); } internal static void RaiseCheckpointReload() { Events.OnCheckpointReload?.Invoke(); } internal static void RaiseGameDataInitialized() { Events.OnGameDataInitialized?.Invoke(); } internal static void RaisePlayerJoinLobby(SNet_Player player) { Events.OnPlayerJoinLobby?.Invoke(player); } internal static void RaiseLoadoutPlayerListUpdate() { Events.OnPageLoadoutPlayerListUpdate?.Invoke(); } } public class InputHelper { public static int GetAlphaNumKeyDown() { int result = -1; for (int i = 0; i < 10; i++) { if (Input.GetKeyDown((KeyCode)(48 + i))) { result = i; break; } } return result; } public static bool OnlyModifies(KeyCode mod, KeyCode altMod = (KeyCode)0, KeyCode nonMod = (KeyCode)0) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Invalid comparison between Unknown and I4 //IL_0010: 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: Invalid comparison between Unknown and I4 //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Invalid comparison between Unknown and I4 //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Invalid comparison between Unknown and I4 //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Invalid comparison between Unknown and I4 //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Invalid comparison between Unknown and I4 //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Invalid comparison between Unknown and I4 //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Invalid comparison between Unknown and I4 //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Invalid comparison between Unknown and I4 //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Invalid comparison between Unknown and I4 //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Invalid comparison between Unknown and I4 //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Invalid comparison between Unknown and I4 bool num = Input.GetKey(mod) || Input.GetKey(altMod) || Input.GetKeyDown(nonMod); bool flag = false; if ((int)mod != 304 && (int)mod != 303 && (int)altMod != 304 && (int)altMod != 303) { flag |= Input.GetKey((KeyCode)304) || Input.GetKey((KeyCode)303); } if ((int)mod != 306 && (int)mod != 305 && (int)altMod != 306 && (int)altMod != 305) { flag |= Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305); } if ((int)mod != 308 && (int)mod != 307 && (int)altMod != 308 && (int)altMod != 307) { flag |= Input.GetKey((KeyCode)308) || Input.GetKey((KeyCode)307); } if (num) { return !flag; } return false; } public static Vector2 GetMouseDelta() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) return new Vector2(InputMapper.GetAxis.Invoke((InputAction)3, (eFocusState)0), InputMapper.GetAxis.Invoke((InputAction)4, (eFocusState)0)); } } internal static class Logger { private static ManualLogSource _mLogSource; public static bool Ready => _mLogSource != null; public static void Setup() { _mLogSource = Logger.CreateLogSource("io.takina.gtfo.Spectate"); } public static void SetupFromInit(ManualLogSource logSource) { _mLogSource = logSource; } private static string Format(object data) { return data.ToString(); } public static void Debug(object msg) { if (ConfigMgr.Debug) { _mLogSource.LogDebug((object)Format(msg)); } } public static void Debug(string fmt, params object[] args) { if (ConfigMgr.Debug) { _mLogSource.LogDebug((object)Format(string.Format(fmt, args))); } } public static void Info(object msg) { _mLogSource.LogInfo((object)Format(msg)); } public static void Info(string fmt, params object[] args) { _mLogSource.LogInfo((object)Format(string.Format(fmt, args))); } public static void Warn(object msg) { _mLogSource.LogWarning((object)Format(msg)); } public static void Warn(string fmt, params object[] args) { _mLogSource.LogWarning((object)Format(string.Format(fmt, args))); } public static void Error(object msg) { _mLogSource.LogError((object)Format(msg)); } public static void Error(string fmt, params object[] args) { _mLogSource.LogError((object)Format(string.Format(fmt, args))); } public static void Fatal(object msg) { _mLogSource.LogFatal((object)Format(msg)); } public static void Fatal(string fmt, params object[] args) { _mLogSource.LogFatal((object)Format(string.Format(fmt, args))); } } [BepInPlugin("io.takina.gtfo.Spectate", "Spectate", "1.7.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BasePlugin { public const string NAME = "Spectate"; public const string GUID = "io.takina.gtfo.Spectate"; public const string VERSION = "1.7.0"; public const string GUID_PlayerSync = "io.takina.gtfo.PlayerSync"; public const string GUID_EOSExtEMP = "Inas.EOSExt.EMP"; public const string GUID_EOSAmor = "Amor.ExcellentObjectiveSetup"; public const string GUID_EEC = "GTFO.EECustomization"; public const string ASM_ClonesoftJson = "Clonesoft.Json"; public static readonly PlugVersion PlugVersion = new PlugVersion("1.7.0"); public static GameObject? PluginObject; internal static string PluginFolder { get; private set; } = ""; public event Action? OnManagersSetup; public override void Load() { //IL_0024: 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) //IL_00a7: Expected O, but got Unknown //IL_00ac: Expected O, but got Unknown Logger.Setup(); Logger.Info("Spectate [io.takina.gtfo.Spectate @ 1.7.0]"); JObject.Parse("{}"); Harmony val = new Harmony("io.takina.gtfo.Spectate"); ConfigMgr.Process(); PluginFolder = Path.GetDirectoryName(((BaseChainloader<BasePlugin>)(object)IL2CPPChainloader.Instance).Plugins["io.takina.gtfo.Spectate"].Location) ?? ""; RegisterIl2CppTypes(); Events.OnGameDataInitialized += Loc.InsertToGtfoDB; OnManagersSetup += Initialize; Global.OnAllManagersSetup += Action.op_Implicit(this.OnManagersSetup); Logger.Info("Patching..."); ApplyPatches(val); ApplyCompatPatches(val); Logger.Info("Finished Patching"); } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] private static void ApplyPatches(Harmony h) { ApplyPatch<EventPatch>(h); ApplyPatch<UIPatch>(h); ApplyPatch<CameraPatch>(h); ApplyPatch<AnimationPatch>(h); ApplyPatch<PouncerPatch>(h); ApplyPatch<TooltipFixPatch>(h); ApplyPatch<StaminaPatch>(h); ApplyPatch<SpectateSupportDisplay>(h); ApplyPatch<Net>(h); } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] private static void ApplyCompatPatches(Harmony h) { if (ConfigMgr.DevEnables(eDevOpts.CompatPatchUsingReflection) || ConfigMgr.CompatPatchUseReflection) { CompatPatcher.PatchAll(h); return; } ApplyPatchIfLoaded<EOSExtEMP_Compat>(h, "Inas.EOSExt.EMP"); ApplyPatchIfLoaded<ECC_Compat>(h, "GTFO.EECustomization"); ApplyPatchIfLoaded<EOSAmor_Compat>(h, "Amor.ExcellentObjectiveSetup"); } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] private static void ApplyPatchIfLoaded<T>(Harmony h, string guid) { if (((BaseChainloader<BasePlugin>)(object)IL2CPPChainloader.Instance).Plugins.ContainsKey(guid)) { ApplyPatch<T>(h); Logger.Info("Applied compat patches for plugin with GUID " + guid + "."); } else { Logger.Info("Plugin with GUID " + guid + " not found, skipping compat patches for it."); } } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] private static void ApplyPatch<T>(Harmony h) { h.PatchAll(typeof(T)); } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] private static void RegisterIl2CppTypes() { if (ConfigMgr.Il2CppTypeDiscoveryForRegUseReflection) { Type[] types = Assembly.GetExecutingAssembly().GetTypes(); foreach (Type type in types) { if (type.GetCustomAttribute<RegisterIl2CppAttribute>() != null) { ClassInjector.RegisterTypeInIl2Cpp(type); } } } else { ClassInjector.RegisterTypeInIl2Cpp<PeerInfoManager>(); ClassInjector.RegisterTypeInIl2Cpp<SpectateSupportDisplay>(); ClassInjector.RegisterTypeInIl2Cpp<SpectateUI>(); ClassInjector.RegisterTypeInIl2Cpp<SpectatorCountUI>(); ClassInjector.RegisterTypeInIl2Cpp<Wm>(); ClassInjector.RegisterTypeInIl2Cpp<PouncerTracker>(); ClassInjector.RegisterTypeInIl2Cpp<PouncerTrackingDart>(); ClassInjector.RegisterTypeInIl2Cpp<SpectateCam>(); ClassInjector.RegisterTypeInIl2Cpp<SpectateConfigUpdater>(); } } private void Initialize() { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown Logger.Debug("Loading Assets..."); SharedAssetLibrary.Load(); EmojiLibrary.Load(); Logger.Debug("Initializing Spectate GameObject's..."); PluginObject = new GameObject("io.takina.gtfo.Spectate"); Object.DontDestroyOnLoad((Object)(object)PluginObject); SpectateUI spectateUI = PluginObject.AddComponent<SpectateUI>(); PluginObject.AddComponent<SpectateCam>(); PluginObject.AddComponent<SpectateConfigUpdater>(); PluginObject.AddComponent<PouncerTracker>(); PluginObject.AddComponent<SpectatorCountUI>(); PluginObject.AddComponent<PeerInfoManager>(); PluginObject.AddComponent<Wm>(); spectateUI.ReplicateUI(); } public override bool Unload() { ConfigMgr.WriteConfigIfDirty(); return true; } } [RegisterIl2Cpp] public class PouncerTracker : MonoBehaviour { private const float CleanupDelay = 10f; private float _timeUntilCleanup; public static PouncerTracker? Instance { get; private set; } public bool IsLocalPlayerSnatched { get; private set; } private List<PouncerBehaviour> ActivePouncers { get; } = new List<PouncerBehaviour>(); public PouncerTracker(IntPtr ptr) : base(ptr) { } private void Awake() { if ((Object)(object)Instance == (Object)null) { Instance = this; } else if ((Object)(object)Instance != (Object)(object)this) { Object.Destroy((Object)(object)this); } } private void Update() { if (_timeUntilCleanup > 0f) { _timeUntilCleanup -= Time.deltaTime; if (_timeUntilCleanup <= 0f) { CleanupPouncers(); _timeUntilCleanup = 10f; } } } public void RegisterPouncer(PouncerBehaviour pouncer) { if (!ActivePouncers.Contains(pouncer)) { ActivePouncers.Add(pouncer); ((Component)pouncer).gameObject.AddComponent<PouncerTrackingDart>(); IsLocalPlayerSnatched = true; } } public void UnregisterPouncer(GameObject pouncer) { foreach (PouncerBehaviour activePouncer in ActivePouncers) { if ((Object)(object)((Component)activePouncer).gameObject == (Object)(object)pouncer || ((Il2CppObjectBase)((Component)activePouncer).gameObject).Pointer == ((Il2CppObjectBase)pouncer).Pointer) { ActivePouncers.Remove(activePouncer); break; } } } public bool IsCaptured(PlayerAgent agent) { foreach (PouncerBehaviour activePouncer in ActivePouncers) { if (!((Object)(object)activePouncer == (Object)null) && !((Object)(object)activePouncer.CapturedPlayer == (Object)null) && ((Il2CppObjectBase)activePouncer.CapturedPlayer).Pointer == ((Il2CppObjectBase)agent).Pointer) { return true; } } return false; } public bool TryGetCapturingPouncer(PlayerAgent agent, out PouncerBehaviour? capturingPouncer) { foreach (PouncerBehaviour activePouncer in ActivePouncers) { if (!((Object)(object)activePouncer == (Object)null) && !((Object)(object)activePouncer.CapturedPlayer == (Object)null) && ((Il2CppObjectBase)activePouncer.CapturedPlayer).Pointer == ((Il2CppObjectBase)agent).Pointer) { capturingPouncer = activePouncer; return true; } } capturingPouncer = null; return false; } private void CleanupPouncers() { ActivePouncers.RemoveAll((PouncerBehaviour p) => (Object)(object)p == (Object)null || !((Component)p).gameObject.activeInHierarchy); } } [RegisterIl2Cpp] public class PouncerTrackingDart : MonoBehaviour { public PouncerTrackingDart(IntPtr ptr) : base(ptr) { } private void OnDestroy() { if ((Object)(object)PouncerTracker.Instance != (Object)null) { PouncerTracker.Instance.UnregisterPouncer(((Component)this).gameObject); } } } [RegisterIl2Cpp] public class SpectateCam : MonoBehaviour { private bool _wasActive; private int _lastTargetPlayerIdx = -1; private bool _freecam = ConfigMgr.DefaultFreecamView; private bool _freecamFollow = ConfigMgr.AutoTransitionToFollowView; private float _freeLookReturnTimer; private PouncerBehaviour? _lastPouncer; public const float DownToSpectateDelay = 1f; private float _pitch = ConfigMgr.CameraPitchAngleDeg; private float _yaw; private float _pitchTarget = ConfigMgr.CameraPitchAngleDeg; private float _yawTarget; private float _eyeY; private float _eyeYTarget; private Vector3 _eyeXZ = Vector3.zero; private Vector3 _eyeXZTarget = Vector3.zero; private Vector3 _camPosComputed = Vector3.zero; public const float DefaultCameraYPositionLerpGain = 11f; public const float DefaultCameraXZPositionLerpGain = 15f; public const float DefaultCameraLerpGain = 6f; public const float DefaultOrbitCenterVerticalOffset = 0.325f; public const float DefaultPitchAngleDeg = -18.75f; public const float DefaultDistanceFromEye = 0.625f; public const float DefaultScrollSensitivity = 0.5f; public const float DefaultFreecamSensitivity = 1f; public const float OrbitCenterVerticalOffsetMin = -1f; public const float OrbitCenterVerticalOffsetMax = 5f; public const float DistanceMin = 0.1f; public const float DistanceMax = 5f; public const float DistanceOffsetPouncer = 1.3f; public const float PitchAngleDegMin = -89f; public const float PitchAngleDegMax = 89f; public const float SpectateMessageSendInterval = 0.5f; private float _timeSinceLastSpectateMessage = 1.5f; private AgentTarget? _self; private AgentTarget? _target; public static SpectateCam? Instance { get; set; } public bool SelfReady { get { if (_self != null) { return (Object)(object)_self.FPSCamera != (Object)null; } return false; } } public bool TargetReady => _target != null; public bool Active { get; private set; } public bool Freecam => _freecam; public C_Node? LastLocalCullNode { get; private set; } public Vector3 DiegeticCamDir { get; private set; } = Vector3.forward; public Vector3 DiegeticPlayerRigScale { get; private set; } = Vector3.one; public Vector3 CameraPos { get { //IL_0054: 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_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) if (!Active) { AgentTarget? self = _self; Vector3? obj; if ((object)self == null) { obj = null; } else { FPSCamera? fPSCamera = self.FPSCamera; obj = ((fPSCamera != null) ? new Vector3?(fPSCamera.Position) : ((Vector3?)null)); } return (Vector3)(((??)obj) ?? Vector3.zero); } return _camPosComputed; } } public AgentTarget? Self { get { if (_self == null) { _self = new AgentTarget(PlayerManager.GetLocalPlayerAgent()); } return _self; } } public AgentTarget? Target => _target; public bool CanSpectate { get { PlayerAgent localPlayerAgent = PlayerManager.GetLocalPlayerAgent(); bool flag = (Object)(object)localPlayerAgent != (Object)null && AgentTarget.CanSpectate(localPlayerAgent); return ConfigMgr.DevEnables(eDevOpts.AllowSpectatingAnytime) || flag; } } public event Action? OnActive; public SpectateCam(IntPtr ptr) : base(ptr) { }//IL_001e: 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_002e: 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) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_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) private void Awake() { if ((Object)(object)Instance != (Object)null && (Object)(object)Instance != (Object)(object)this) { Object.Destroy((Object)(object)((Component)this).gameObject); return; } Instance = this; Object.DontDestroyOnLoad((Object)(object)this); Events.OnSessionStart += delegate { Load(); }; Events.OnSessionEnd += delegate { Unload(); }; Events.OnAnyPlayerDeath += delegate { if (ConfigMgr.PreferSpectateAlive && (_target == null || _target.IsDowned)) { TrySetAnyNonLocalTarget(); } }; Events.OnCheckpointReload += delegate { if (Active) { Detach(); } Unload(); }; } public bool Load() { PlayerAgent localPlayerAgent = PlayerManager.GetLocalPlayerAgent(); if ((Object)(object)localPlayerAgent == (Object)null) { Logger.Error("SpectateCam: Failed to load - local player agent is null"); return false; } _self = new AgentTarget(localPlayerAgent); return (Object)(object)_self.FPSCamera != (Object)null; } public bool Unload() { SetActive(active: false); _self = null; _target = null; return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void SetTarget(PlayerAgent agent) { _target = new AgentTarget(agent); } public void TryAttachDelayed(float delay) { ((MonoBehaviour)this).StartCoroutine(CollectionExtensions.WrapToIl2Cpp(TryAttachDelayedCoroutine(delay))); } private IEnumerator TryAttachDelayedCoroutine(float delay) { yield return (object)new WaitForSeconds(delay); if (!Attach()) { Logger.Warn("SpectateCam: TryAttachDelayed failed to attach after delay"); } } public bool Attach() { //IL_0062: 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_00c5: 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) if (Active) { return true; } if (!SelfReady && !Load()) { Logger.Error("SpectateCam: Attach failed - self not ready and cannot be loaded"); return false; } if (!CanSpectate) { return false; } if (!TargetReady && !TrySetAnyNonLocalTarget()) { Logger.Warn("SpectateCam: Attach failed - no valid target available"); return false; } DiegeticCamDir = _self.FPSCamera.Forward; DiegeticPlayerRigScale = ((Component)_self.PlayerModel).gameObject.transform.localScale; AgentTarget? self = _self; object lastLocalCullNode; if ((object)self == null) { lastLocalCullNode = null; } else { AIG_CourseNode? courseNode = self.CourseNode; lastLocalCullNode = ((courseNode != null) ? courseNode.m_cullNode : null); } LastLocalCullNode = (C_Node?)lastLocalCullNode; ((Component)_self.PlayerModel).gameObject.transform.localScale = Vector3.one; GuiManager.PlayerLayer.ApplyMovementSway(Vector3.zero); GuiManager.CrosshairLayer.ShowPrecisionDot(); SpectateUI.Instance?.UpdateForAttach(); SetRelatedActive(spectateActive: true); UpdateCull(); SetActive(active: true); Util.ClearGlassLiquid(); return true; } public bool Detach() { //IL_00f6: Unknown result type (might be due to invalid IL or missing references) if (!Active) { return true; } if (!SelfReady && !Load()) { Logger.Error("SpectateCam: Detach failed - self not ready and cannot be loaded"); Logger.Info("SpectateCam: Detach falling back to Unload"); Unload(); return false; } SetActive(active: false); SetRelatedActive(spectateActive: false); RevertCull(); SpectateUI.Instance?.UpdateForDetach(); CrosshairGuiLayer crosshairLayer = GuiManager.CrosshairLayer; if (crosshairLayer != null) { FirstPersonItemHolder? fPHolder = _self.FPHolder; float? obj; if (fPHolder == null) { obj = null; } else { ItemEquippable wieldedItem = fPHolder.WieldedItem; obj = ((wieldedItem != null) ? new float?(wieldedItem.HipFireCrosshairSize) : ((float?)null)); } crosshairLayer.ShowSpreadCircle(obj ?? 40f); } PlayerGuiLayer playerLayer = GuiManager.PlayerLayer; if (playerLayer != null) { PUI_LocalPlayerStatus playerStatus = playerLayer.m_playerStatus; if (playerStatus != null) { playerStatus.ResetDamageAnimation(); } } ((Component)_self.PlayerModel).gameObject.transform.localScale = DiegeticPlayerRigScale; return true; } private void SetActive(bool active) { if (active && !_wasActive) { this.OnActive?.Invoke(); } _wasActive = Active; Active = active; if (!Active) { _yaw = 0f; _pitch = ConfigMgr.CameraPitchAngleDeg; _freeLookReturnTimer = 0f; } } internal void SetRelatedActive(bool spectateActive) { if (!SelfReady) { Logger.Error("SpectateCam: SetRelatedActive failed - self not ready"); return; } if (ConfigMgr.ShowPlayerBodyWhenSpectating) { _self.SetHostHiddenRigActive(spectateActive); if (!Self.IsDowned && !spectateActive) { _self.SetRigTorsoLegsActive(active: true); } else { _self.SetRigTorsoLegsActive(spectateActive); } } else { _self.SetRigActive(!spectateActive); } _self.Agent.DeadDebugMode = spectateActive; _self.PlayerModel.GhostEnabled = spectateActive; Util.SetTargetActiveIfDiff((Behaviour?)(object)_self.Inventory, !spectateActive); PlayerInventoryBase? inventory = _self.Inventory; Util.SetTargetActiveIfDiff((inventory != null) ? ((Component)inventory.m_flashlight).gameObject : null, !spectateActive); FirstPersonItemHolder? fPHolder = _self.FPHolder; Util.SetTargetActiveIfDiff((fPHolder != null) ? ((Component)fPHolder).gameObject : null, !spectateActive); FirstPersonItemHolder? fPHolder2 = _self.FPHolder; if (fPHolder2 != null) { PlayerFPSBody fPSArms = fPHolder2.FPSArms; if (fPSArms != null) { fPSArms.SetVisible(!spectateActive && !_self.IsDowned); } } FPSCamera fPSCamera = _self.FPSCamera; if ((Object)(object)fPSCamera != (Object)null) { fPSCamera.MouseLookEnabled = !spectateActive; fPSCamera.PlayerAgentRotationEnabled = !spectateActive; } } private void Update() { if (!((Behaviour)this).enabled || !((Component)this).gameObject.activeInHierarchy) { return; } SpectatorMessageUpdate(); ProcessInput(); UpdateTransitions(); if (Active) { if (!TargetReady || !SelfReady) { Detach(); return; } UpdateLerp(_freecam); UpdateCamera(); UpdateCull(); } } private void SpectatorMessageUpdate() { if (!Active || !TargetReady) { _timeSinceLastSpectateMessage = 1.5f; return; } _timeSinceLastSpectateMessage += Time.deltaTime; if (!(_timeSinceLastSpectateMessage < 0.5f)) { Logger.Debug("SpectateCam: Sending spectate target update messages"); _timeSinceLastSpectateMessage = 0f; NetHelper.InvokeWithAllSupportedPlayers(delegate(SNet_Player player) { NetImpl.SendSpectateTargetState(player, Target.SAgent); }); } } private void UpdateTransitions() { if (SelfReady) { if (Active && !_self.IsDowned && !ConfigMgr.DevEnables(eDevOpts.AllowSpectatingAnytime)) { Detach(); } else if (Active && ConfigMgr.AutoTransitionToFollowView && _freeLookReturnTimer >= 0f) { _freeLookReturnTimer -= Time.deltaTime; } } } private void ProcessInput() { //IL_0017: 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_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: 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_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Invalid comparison between Unknown and I4 //IL_01d3: Unknown result type (might be due to invalid IL or missing references) if (!InputMapper.Current.FocusStateFilterPass((eFocusState)4)) { return; } if (CanSpectate && Input.GetKeyDown(ConfigMgr.GetKeybind(SpectateInputAction.ToggleSpectate))) { if (Active) { if (!Detach()) { Logger.Warn("SpectateCam: Failed to detach SpecCam"); } } else if (!Attach()) { Logger.Warn("SpectateCam: Failed to attach SpecCam"); } } if (!Active) { return; } if (Input.GetKeyDown(ConfigMgr.GetKeybind(SpectateInputAction.ToggleFreecam))) { if (_freecam) { _freecam = false; OnFree2Follow(); } else { _freecam = true; OnFollow2Free(); } } if (_freecam && Input.GetKeyDown(ConfigMgr.GetKeybind(SpectateInputAction.ToggleAutoFollow))) { ConfigMgr.AutoTransitionToFollowView = !ConfigMgr.AutoTransitionToFollowView; SpectateUI.Instance?.MarkUIDirty(); } Vector2 mouseDelta = InputHelper.GetMouseDelta(); if (_freecam && mouseDelta != Vector2.zero) { AdjustYaw(mouseDelta.x * ConfigMgr.FreecamSensitivity); AdjustPitch(mouseDelta.y * ConfigMgr.FreecamSensitivity); if (ConfigMgr.AutoTransitionToFollowView) { _freecamFollow = false; _freeLookReturnTimer = ConfigMgr.AutoTransitionDelay; } } if (_freecam) { if (Input.GetKey((KeyCode)273)) { AdjustPitch(ConfigMgr.FreecamSensitivity); } if (Input.GetKey((KeyCode)274)) { AdjustPitch(0f - ConfigMgr.FreecamSensitivity); } if (Input.GetKey((KeyCode)276)) { AdjustYaw(0f - ConfigMgr.FreecamSensitivity); } if (Input.GetKey((KeyCode)275)) { AdjustYaw(ConfigMgr.FreecamSensitivity); } } if (Input.GetKeyDown((KeyCode)323)) { TrySetNextTarget(); } if (Input.GetKeyDown((KeyCode)324)) { TrySetPreviousTarget(); } if ((int)FocusStateManager.Current.m_currentState != 6) { int alphaNumKeyDown = InputHelper.GetAlphaNumKeyDown(); if (alphaNumKeyDown > 0) { TrySetTargetByIdx(alphaNumKeyDown - 1, overridePreferAlive: true); } } float num = Input.mouseScrollDelta.y * ConfigMgr.ScrollSensitivity; if (InputMapper.Current.FocusStateFilterPass((eFocusState)6) || !(Mathf.Abs(num) > 0f)) { return; } if (InputHelper.OnlyModifies((KeyCode)304, (KeyCode)303, (KeyCode)0)) { if (_freecam) { SpectateUI.Instance?.WarnFreecamNoAdjustPitch(); } else { ConfigMgr.CameraPitchAngleDeg -= 0.5f * num; } } else if (InputHelper.OnlyModifies((KeyCode)306, (KeyCode)305, (KeyCode)0)) { ConfigMgr.CameraOrbitVerticalOffset = Mathf.Clamp(ConfigMgr.CameraOrbitVerticalOffset + 0.05f * num, -1f, 5f); } else { ConfigMgr.CameraDistance = Mathf.Clamp(ConfigMgr.CameraDistance - 0.05f * num, 0.1f, 5f); } } private void UpdateCamera() { //IL_0054: 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_0070: 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_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: 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) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: 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_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012e: 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_0138: 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_013a: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) if (!SelfReady || !TargetReady) { Logger.Error("SpectateCam: UpdateCamera failed - target or self not ready"); return; } if (ConfigMgr.AutoTransitionToFollowView && _freecam) { if (_freeLookReturnTimer < 0.001f) { _freecamFollow = true; } if (_freecamFollow) { UpdateYawPitchWithFollowView(instant: false); } } SetEye(GetTargetOrbitCenter()); Vector3 val = _eyeXZ + Vector3.up * _eyeY; Vector3 forward = ((Agent)_target.Agent).Forward; Vector3 val2 = ((Vector3)(ref forward)).normalized; if (_freecam) { val2 = Quaternion.Euler(0f, _yaw, 0f) * Vector3.forward; } float num = (float)Math.PI / 180f * (_freecam ? _pitch : ConfigMgr.CameraPitchAngleDeg); Vector3 val3 = val2 + Vector3.up * Mathf.Tan(num); ((Vector3)(ref val3)).Normalize(); float num2 = ConfigMgr.CameraDistance; if (_target.IsCaptured) { num2 = Mathf.Clamp(num2 + 1.3f, 0.1f, 5f); } _camPosComputed = val - val3 * num2; RaycastHit val4 = default(RaycastHit); if (Physics.Raycast(val, -val3, ref val4, num2, LayerManager.MASK_WORLD)) { _camPosComputed = val4.m_Point + val3 * 0.1f; } _self.FPSCamera.OverridePositionAndRotation(_camPosComputed, Quaternion.LookRotation(val3)); _self.FPSCamera.OverrideFieldOfView((float)CellSettingsManager.GetIntValue((eCellSettingID)52)); } private void UpdateLerp(bool freecamEnabled) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) _eyeXZ = Vector3.Lerp(_eyeXZ, _eyeXZTarget, Time.deltaTime * ConfigMgr.CameraXZLerpGain); _eyeY = Mathf.Lerp(_eyeY, _eyeYTarget, Time.deltaTime * ConfigMgr.CameraYLerpGain); if (freecamEnabled) { _yaw = Mathf.LerpAngle(_yaw, _yawTarget, Time.deltaTime * ConfigMgr.FreecamLerpGain); float num = _yawTarget - _yaw; _yaw = Mathf.Repeat(_yaw, 360f); _yawTarget = _yaw + num; _pitch = Mathf.LerpAngle(_pitch, _pitchTarget, Time.deltaTime * ConfigMgr.FreecamLerpGain); } } private void UpdateCull() { //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_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0047: 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_007d: 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_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0041: 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) if (!SelfReady || !TargetReady) { Logger.Error("SpectateCam: UpdateCull failed - target or self not ready"); return; } Vector3 val = GetTargetOrbitCenter(); RaycastHit val2 = default(RaycastHit); if (Physics.Raycast(val, Vector3.down, ref val2, 64f, LayerManager.MASK_WORLD)) { val = val2.m_Point; } CameraManager.CullingPosition = val; CameraManager.CullingDirection = _self.FPSCamera.Forward; _self.Agent.m_movingCuller.UpdatePosition(((Agent)_self.Agent).m_dimensionIndex, val); C_Node currentNode = _self.Agent.m_movingCuller.CurrentNode; AIG_CourseNode? courseNode = _target.CourseNode; C_Node val3 = ((courseNode != null) ? courseNode.m_cullNode : null); if (val3 != null) { if (((Il2CppObjectBase)currentNode).Pointer != ((Il2CppObjectBase)val3).Pointer) { _self.Agent.m_movingCuller.SetCurrentNode(val3); } } else { Logger.Warn("SpectateCam: UpdateCull - failed to sync cull nodes, target node is null"); } } private void RevertCull() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: 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_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: 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_0042: 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_0048: Unknown result type (might be due to invalid IL or missing references) if (!SelfReady) { Logger.Error("SpectateCam: RevertCull failed - self is not ready"); return; } Vector3 val = ((Agent)_self.Agent).Position; RaycastHit val2 = default(RaycastHit); if (Physics.Raycast(val, Vector3.down, ref val2, 64f, LayerManager.MASK_WORLD)) { val = val2.m_Point; } CameraManager.CullingPosition = val; CameraManager.CullingDirection = _self.FPSCamera.Forward; _self.Agent.m_movingCuller.UpdatePosition(((Agent)_self.Agent).m_dimensionIndex, val); C_Node currentNode = _self.Agent.m_movingCuller.CurrentNode; if (LastLocalCullNode != null) { IntPtr? intPtr = ((currentNode != null) ? new IntPtr?(((Il2CppObjectBase)currentNode).Pointer) : ((IntPtr?)null)); C_Node? lastLocalCullNode = LastLocalCullNode; if (intPtr != ((lastLocalCullNode != null) ? new IntPtr?(((Il2CppObjectBase)lastLocalCullNode).Pointer) : ((IntPtr?)null))) { _self.Agent.m_movingCuller.SetCurrentNode(LastLocalCullNode); } } else { Logger.Warn("SpectateCam: RevertCull - failed to sync cull nodes self or target node is null"); } } public void ClearTarget() { _target = null; _lastTargetPlayerIdx = -1; } private bool TrySetAnyNonLocalTarget() { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) SNet_PlayerSlotManager slots = SNet.Slots; List<SNet_Player> val = ((slots != null) ? slots.SlottedPlayers : null); if (val == null || val.Count == 0) { return false; } float num = float.MaxValue; _lastTargetPlayerIdx = -1; for (int i = 0; i < val.Count; i++) { if (SimulateSetTargetIdx(i, out PlayerAgent agent)) { float num2 = Vector3.Distance(((Agent)agent).Position, ((Agent)_self.Agent).Position); if (num2 < num) { num = num2; _lastTargetPlayerIdx = i; } } } if (_lastTargetPlayerIdx != -1) { return TrySetTargetByIdx(_lastTargetPlayerIdx); } return false; } private bool TrySetNextTarget() { SNet_PlayerSlotManager slots = SNet.Slots; int num = ((slots == null) ? ((int?)null) : slots.SlottedPlayers?.Count) ?? (-1); if (num <= 0) { return false; } for (int i = 1; i <= num; i++) { int playerIdx = (_lastTargetPlayerIdx + i) % num; if (TrySetTargetByIdx(playerIdx)) { return true; } } return false; } private bool TrySetPreviousTarget() { SNet_PlayerSlotManager slots = SNet.Slots; int num = ((slots == null) ? ((int?)null) : slots.SlottedPlayers?.Count) ?? (-1); if (num <= 0) { return false; } for (int i = 1; i <= num; i++) { int playerIdx = (_lastTargetPlayerIdx - i + num) % num; if (TrySetTargetByIdx(playerIdx)) { return true; } } return false; } private bool SimulateSetTargetIdx(int playerIdx, [NotNullWhen(true)] out PlayerAgent? agent, bool overridePreferAlive = false) { //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Invalid comparison between Unknown and I4 agent = null; SNet_PlayerSlotManager slots = SNet.Slots; List<SNet_Player> val = ((slots != null) ? slots.SlottedPlayers : null); if (val == null || val.Count == 0) { return false; } if (playerIdx >= 0 && playerIdx < val.Count) { if (val[playerIdx].IsLocal) { return false; } SNet_Player obj = val[playerIdx]; object obj2; if (obj == null) { obj2 = null; } else { SNet_IPlayerAgent playerAgent = obj.PlayerAgent; obj2 = ((playerAgent != null) ? ((Il2CppObjectBase)playerAgent).Cast<PlayerAgent>() : null); } agent = (PlayerAgent?)obj2; if ((Object)(object)agent == (Object)null) { return false; } if (!overridePreferAlive && ConfigMgr.PreferSpectateAlive && (int)agent.Locomotion.m_currentStateEnum == 7) { return false; } return true; } return false; } private bool TrySetTargetByIdx(int playerIdx, bool overridePreferAlive = false) { //IL_0092: 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_0067: Invalid comparison between Unknown and I4 SNet_PlayerSlotManager slots = SNet.Slots; List<SNet_Player> val = ((slots != null) ? slots.SlottedPlayers : null); if (val == null || val.Count == 0) { return false; } if (playerIdx >= 0 && playerIdx < val.Count) { if (val[playerIdx].IsLocal) { return false; } PlayerAgent val2 = ((Il2CppObjectBase)val[playerIdx].PlayerAgent).Cast<PlayerAgent>(); if (!overridePreferAlive && ConfigMgr.PreferSpectateAlive && (int)val2.Locomotion.m_currentStateEnum == 7) { return false; } SetTarget(val2); bool flag = playerIdx != _lastTargetPlayerIdx; if ((ConfigMgr.NoPosLerpOnSwitchTarget && flag) || !_freecam) { SetEye(GetTargetOrbitCenter(), instant: true); } _lastTargetPlayerIdx = playerIdx; return true; } return false; } private Vector3 GetTargetOrbitCenter() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: 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_00a9: Unknown result type (might be due to invalid IL or missing references) Vector3 result = Vector3.zero; PlayerAgent agent; if (_target != null) { agent = _target.Agent; if (_target.IsCaptured) { if (!((Object)(object)_lastPouncer == (Object)null)) { PlayerAgent capturedPlayer = _lastPouncer.CapturedPlayer; if (!(((capturedPlayer != null) ? ((Il2CppObjectBase)capturedPlayer).Pointer : IntPtr.Zero) != ((Il2CppObjectBase)agent).Pointer)) { goto IL_0083; } } PouncerTracker? instance = PouncerTracker.Instance; if (instance != null && instance.TryGetCapturingPouncer(agent, out PouncerBehaviour capturingPouncer)) { _lastPouncer = capturingPouncer; } goto IL_0083; } result = _target.Agent.m_eyePosition; } goto IL_00bd; IL_0083: result = (((Object)(object)_lastPouncer == (Object)null) ? agent.m_eyePosition : ((Component)_lastPouncer).transform.position); goto IL_00bd; IL_00bd: result.y += ConfigMgr.CameraOrbitVerticalOffset; return result; } private void OnFollow2Free() { if (!TargetReady) { Logger.Error("SpectateCam: OnTransitionToFreecam failed - target not ready"); return; } UpdateYawPitchWithFollowView(instant: true); SpectateUI.Instance?.MarkUIDirty(); } private void OnFree2Follow() { if (!TargetReady) { Logger.Error("SpectateCam: OnTransitionToFollow failed - target not ready"); return; } UpdateYawPitchWithFollowView(instant: true); SpectateUI.Instance?.MarkUIDirty(); } private void UpdateYawPitchWithFollowView(bool instant) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) SetYaw(Vector3.SignedAngle(Vector3.forward, ((Agent)_target.Agent).Forward, Vector3.up), instant); SetPitch(ConfigMgr.CameraPitchAngleDeg, instant); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void AdjustPitch(float deltaPitch, bool instant = false) { SetPitch(_pitchTarget + deltaPitch, instant); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void AdjustYaw(float deltaYaw, bool instant = false) { SetYaw(_yawTarget + deltaYaw, instant); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void SetPitch(float pitch, bool instant = false) { pitch = Mathf.Clamp(pitch, -89f, 89f); _pitchTarget = pitch; if (instant) { _pitch = pitch; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void SetYaw(float yaw, bool instant = false) { _yawTarget = yaw; if (instant) { _yaw = yaw; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void SetEyeXZ(Vector3 eyeXZ, bool instant = false) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) eyeXZ.y = 0f; _eyeXZTarget = eyeXZ; if (instant) { _eyeXZ = eyeXZ; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void SetEyeY(float eyeY, bool instant = false) { _eyeYTarget = eyeY; if (instant) { _eyeY = eyeY; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void SetEye(Vector3 pos, bool instant = false) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) SetEyeXZ(pos, instant); SetEyeY(pos.y, instant); } } [RegisterIl2Cpp] public class SpectateConfigUpdater : MonoBehaviour { public const float UpdateIntervalSeconds = 5f; private Coroutine _configUpdateCoroutine; public SpectateConfigUpdater(IntPtr ptr) : base(ptr) { } private void Awake() { _configUpdateCoroutine = ((MonoBehaviour)this).StartCoroutine(CollectionExtensions.WrapToIl2Cpp(ConfigUpdateCoroutine())); } private IEnumerator ConfigUpdateCoroutine() { while (true) { yield return (object)new WaitForSeconds(5f); ConfigMgr.WriteConfigIfDirty(); } } private void OnApplicationQuit() { ConfigMgr.WriteConfigIfDirty(); } } public static class Util { public const float GOOD_ENOUGH_DEG_EPS = 0.2f; public const int PARTICLE_BUFFER_SIZE = 1024; public static readonly Array EmptyParticleBuffer; static Util() { EmptyParticleBuffer = ((Il2CppObjectBase)Il2CppStructArray<particleData>.op_Implicit((particleData[])(object)new particleData[1024])).Cast<Array>(); } public static bool GoodEnoughDeg(float a, float b) { return NearlyEqual(a, b, 0.2f); } public static bool NearlyEqual(float a, float b, float eps = 0.0001f) { return Mathf.Abs(a - b) < eps; } public static bool SetTargetActiveIfDiff(GameObject? obj, bool active) { if ((Object)(object)obj == (Object)null) { return false; } if (obj.activeSelf != active) { obj.SetActive(active); } return active; } public static void SetTargetActiveIfDiff(Behaviour? beh, bool active) { if (!((Object)(object)beh == (Object)null) && beh.enabled != active) { beh.enabled = active; } } public static void FindAndSortGfxParts(GameObject root, out GameObject[] gfxHead, out GameObject[] gfxArms, out GameObject[] gfxTorso, out GameObject[] gfxLegs, bool includeInactive = true) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: 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_005b: Expected I4, but got Unknown PlayerGfxPart[] array = Il2CppArrayBase<PlayerGfxPart>.op_Implicit(root.GetComponentsInChildren<PlayerGfxPart>(includeInactive)); List<GameObject> list = new List<GameObject>(); List<GameObject> list2 = new List<GameObject>(); List<GameObject> list3 = new List<GameObject>(); List<GameObject> list4 = new List<GameObject>(); PlayerGfxPart[] array2 = array; foreach (PlayerGfxPart val in array2) { PlayerGFXType type = val.m_type; switch ((int)type) { case 0: list.Add(((Component)val).gameObject); break; case 2: case 3: list2.Add(((Component)val).gameObject); break; case 1: case 4: list3.Add(((Component)val).gameObject); break; case 5: list4.Add(((Component)val).gameObject); break; } } gfxHead = list.ToArray(); gfxArms = list2.ToArray(); gfxTorso = list3.ToArray(); gfxLegs = list4.ToArray(); } public static void ClearGlassLiquid() { //IL_0069: Unknown result type (might be due to invalid IL or missing references) if (ScreenLiquidManager.hasSystem) { GlassLiquidSystem currentSystem = ScreenLiquidManager.currentSystem; ScreenLiquidManager.Clear(); currentSystem.cbParticles.SetData(EmptyParticleBuffer); currentSystem.particleWrap = 0; RenderTexture active = RenderTexture.active; RenderTexture[] array = (RenderTexture[])(object)new RenderTexture[4] { currentSystem.rtParams, currentSystem.rtColor, currentSystem.rtParams_db, currentSystem.rtColor_db }; for (int i = 0; i < array.Length; i++) { RenderTexture.active = array[i]; GL.Clear(false, true, Color.clear); } RenderTexture.active = active; } } } } namespace Spectate.Utility { public static class FileUtils { public static IEnumerable<string> FileInEachSubdirectory(string dir, string fileName) { foreach (string item in Directory.EnumerateDirectories(dir)) { string text = Path.Combine(item, fileName); if (File.Exists(text)) { yield return text; } } } public static IEnumerable<string> FilesWithExtensionRecursive(string dir, string extension) { if (!Directory.Exists(dir)) { yield break; } string text = (extension.StartsWith(".") ? extension : ("." + extension)); foreach (string item in Directory.EnumerateFiles(dir, "*" + text, SearchOption.AllDirectories)) { yield return item; } } } public class LocaleUtils { public static readonly Dictionary<string, string> LocaleKeyToDisplayName = new Dictionary<string, string> { { "en", "English" }, { "fr", "Français" }, { "de", "Deutsch" }, { "es", "Español" }, { "it", "Italiano" }, { "ja", "日本語" }, { "ko", "한국어" }, { "pt-BR", "Português (Brasil)" }, { "ru", "Русский" }, { "pl", "Polski" }, { "zh-Hans", "简体中文" }, { "zh-Hant", "繁體中文" } }; public static string LocaleKeyFromEnum(Language key) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected I4, but got Unknown //IL_009f: Unknown result type (might be due to invalid IL or missing references) return (key - 1) switch { 0 => "en", 1 => "fr", 3 => "de", 4 => "es", 2 => "it", 8 => "ja", 9 => "ko", 6 => "pt-BR", 5 => "ru", 7 => "pl", 11 => "zh-Hans", 10 => "zh-Hant", _ => throw new ArgumentOutOfRangeException("key", key, null), }; } public static void SetTranslation(TextDataBlock db, string locale, string text) { switch (locale) { case "en": db.English = text; break; case "fr": db.French = LanguageData.op_Implicit(text); break; case "de": db.German = LanguageData.op_Implicit(text); break; case "es": db.Spanish = LanguageData.op_Implicit(text); break; case "it": db.Italian = LanguageData.op_Implicit(text); break; case "ja": db.Japanese = LanguageData.op_Implicit(text); break; case "ko": db.Korean = LanguageData.op_Implicit(text); break; case "pt-BR": db.Portuguese_Brazil = LanguageData.op_Implicit(text); break; case "ru": db.Russian = LanguageData.op_Implicit(text); break; case "pl": db.Polish = LanguageData.op_Implicit(text); break; case "zh-Hans": db.Chinese_Simplified = LanguageData.op_Implicit(text); break; case "zh-Hant": db.Chinese_Traditional = LanguageData.op_Implicit(text); break; } } public static uint GetSlotGearSelectTextDbId(InventorySlot slot) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected I4, but got Unknown //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Invalid comparison between Unknown and I4 switch (slot - 1) { default: if ((int)slot == 10) { return 490u; } return 0u; case 0: return 487u; case 1: return 488u; case 2: return 489u; } } public static string GetSlotGearSelectLocalizedText(InventorySlot slot) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return Text.Get(GetSlotGearSelectTextDbId(slot)); } } public class TMPUtils { public static bool CreateTMP(string name, Transform? parent, [NotNullWhen(true)] out TextMeshPro? tp) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) WatermarkGuiLayer watermarkLayer = GuiManager.WatermarkLayer; object obj; if (watermarkLayer == null) { obj = null; } else { PUI_Watermark watermark = watermarkLayer.m_watermark; obj = ((watermark != null) ? watermark.m_fpsText : null); } TextMeshPro val = (TextMeshPro)obj; if ((Object)(object)parent == (Object)null || (Object)(object)val == (Object)null) { tp = null; return false; } tp = Object.Instantiate<TextMeshPro>(val, parent, false); ((Object)tp).name = name; ((TMP_Text)tp).rectTransform.anchorMax = new Vector2(0.5f, 0.5f); ((TMP_Text)tp).rectTransform.anchorMin = new Vector2(0.5f, 0.5f); ((TMP_Text)tp).rectTransform.pivot = new Vector2(0.5f, 0.5f); ((TMP_Text)tp).rectTransform.anchoredPosition3D = new Vector3(0f, 0f, 0f); ((Transform)((TMP_Text)tp).rectTransform).localRotation = Quaternion.Euler(0f, 0f, 0f); ((TMP_Text)tp).enableAutoSizing = false; ((TMP_Text)tp).overflowMode = (TextOverflowModes)0; ((TMP_Text)tp).alignment = (TextAlignmentOptions)513; ((TMP_Text)tp).fontSize = 16f; return true; } public static bool CreateTMPUGUI(string name, Transform? parent, [NotNullWhen(true)] out TextMeshProUGUI? tp) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0088: 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_00c8: Unknown result type (might be due to invalid IL or missing references) WatermarkGuiLayer watermarkLayer = GuiManager.WatermarkLayer; object obj; if (watermarkLayer == null) { obj = null; } else { PUI_Watermark watermark = watermarkLayer.m_watermark; obj = ((watermark != null) ? watermark.m_fpsText : null); } TextMeshPro val = (TextMeshPro)obj; GameObject val2 = new GameObject(); val2.transform.SetParent(parent, false); tp = val2.AddComponent<TextMeshProUGUI>(); ((Object)tp).name = name; ((TMP_Text)tp).rectTransform.anchorMax = new Vector2(0.5f, 0.5f); ((TMP_Text)tp).rectTransform.anchorMin = new Vector2(0.5f, 0.5f); ((TMP_Text)tp).rectTransform.pivot = new Vector2(0.5f, 0.5f); ((TMP_Text)tp).rectTransform.anchoredPosition3D = new Vector3(0f, 0f, 0f); ((Transform)((TMP_Text)tp).rectTransform).localRotation = Quaternion.Euler(0f, 0f, 0f); ((TMP_Text)tp).enableAutoSizing = false; ((TMP_Text)tp).overflowMode = (TextOverflowModes)0; ((TMP_Text)tp).alignment = (TextAlignmentOptions)514; ((TMP_Text)tp).fontSize = 16f; if ((Object)(object)val != (Object)null) { ((TMP_Text)tp).font = ((TMP_Text)val).font; ((TMP_Text)tp).fontSharedMaterial = ((TMP_Text)val).fontSharedMaterial; } return true; } } } namespace Spectate.Utility.Ext { public static class VectorExt { public static Vector2 ToVector2(this float val) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) return new Vector2(val, val); } public static Vector3 ToVector3(this float val) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) return new Vector3(val, val, val); } } } namespace Spectate.UI { public enum eSpectateUIComp { SpectateTarget, Title, ViewMode, Menu } public enum eSpectateMenuItem { HideMenu, ShowMenu, EnterSpectate, ExitSpectate, ToggleFreecam, EnableFreecamAutoTransition, DisableFreecamAutoTransition, SwitchPlayer, SelectPlayer, AdjustDistance, AdjustOrbitCenterHeight, AdjustFollowPitch } public enum eSpectateUIState { FPDowned, FPNotDowned, HideMenu, ShowMenu } public struct MenuKeybindEntry { private string _keyLocaleKey; private bool _isAssociated; private SpectateInputAction _associatedAction; public bool IsAssociated => _isAssociated; public MenuKeybindEntry(string keyLocaleKey) { _isAssociated = false; _associatedAction = SpectateInputAction.None; _keyLocaleKey = keyLocaleKey; } public MenuKeybindEntry(SpectateInputAction action) { _isAssociated = true; _associatedAction = action; _keyLocaleKey = ""; } public override string ToString() { //IL_000e: Unknown result type (might be due to invalid IL or missing references) if (_isAssociated) { return CellSettingGlobals.GetLocalizedKeyCode(ConfigMgr.GetKeybind(_associatedAction)); } return Loc.T(_keyLocaleKey); } } [RegisterIl2Cpp] public class SpectateUI : MonoBehaviour { private bool _wasUIActive; private bool _isUIDirty = true; private eSpectateUIState _uiState = eSpectateUIState.FPNotDowned; private eSpectateUIState _uiStatePrev = eSpectateUIState.FPNotDowned; private eSpectateUIState _uiStateRendered = eSpectateUIState.ShowMenu; private PlayerAgent? _lastRenderedTarget; private readonly List<GameObject> _uiRoots = new List<GameObject>(); private readonly Dictionary<eSpectateUIComp, bool> _uiCompState = new Dictionary<eSpectateUIComp, bool>(); private bool _wantToRevertPUIStatus; private const float MenuCenterOffsetX = 300f; private const float MenuLeftPadding = 50f; private const float MenuElementWidth = 250f; private const float MenuCenterDivideSpacing = 5f; private const int MaxMenuItems = 10; private const float MenuItemSpacing = 27f; private const float MenuOptionHeight = 22f; private const float MenuKeybindHeight = 16f; private static readonly Vector3 HeadBeaconAlignOffset = new Vector3(0f, 0.3f, 0f); private const float SpectateInvOffsetY = -20f; private const string SpecTargetTextColor = "FFFFFF"; private const string StateHighlightColor = "04B065"; private GameObject? _navMarkerTrackTarget; private NavMarker? _navMarker; private TextMeshPro? _specTargetTmp; private GameObject? _menuObj; private TextMeshPro? _menuTitleTmp; private TextMeshPro? _menuViewModeTmp; private GameObject? _menuListObj; private readonly List<(TextMeshPro?, TextMeshPro?)> _menuItemsTmp = new List<(TextMeshPro, TextMeshPro)>(10); private SpriteRenderer? _menuBackground; private PUI_Inventory? _spectateInv; private string _specTargetStr = ""; private string _menuViewModeStr = ""; private readonly List<(string, string)> _menuItemsToRender = new List<(string, string)>(); private readonly Dictionary<eSpectateMenuItem, (string, MenuKeybindEntry)> _menuItems = new Dictionary<eSpectateMenuItem, (string, MenuKeybindEntry)> { [eSpectateMenuItem.ShowMenu] = ("menu.label.showMenu", new MenuKeybindEntry(SpectateInputAction.ToggleMenu)), [eSpectateMenuItem.HideMenu] = ("menu.label.hideMenu", new MenuKeybindEntry(SpectateInputAction.ToggleMenu)), [eSpectateMenuItem.EnterSpectate] = ("menu.label.enterSpectate", new MenuKeybindEntry(SpectateInputAction.ToggleSpectate)), [eSpectateMenuItem.ExitSpectate] = ("menu.label.exitSpectate", new MenuKeybindEntry(SpectateInputAction.ToggleSpectate)), [eSpectateMenuItem.ToggleFreecam] = ("menu.label.toggleFreecam", new MenuKeybindEntry(SpectateInputAction.ToggleFreecam)), [eSpectateMenuItem.EnableFreecamAutoTransition] = ("menu.label.enableAutoFollow", new MenuKeybindEntry(SpectateInputAction.ToggleAutoFollow)), [eSpectateMenuItem.DisableFreecamAutoTransition] = ("menu.label.disableAutoFollow", new MenuKeybindEntry(SpectateInputAction.ToggleAutoFollow)), [eSpectateMenuItem.SwitchPlayer] = ("menu.label.switchPlayer", new MenuKeybindEntry("menu.key.switchPlayer")), [eSpectateMenuItem.SelectPlayer] = ("menu.label.selectPlayer", new MenuKeybindEntry("menu.key.selectPlayer")), [eSpectateMenuItem.AdjustDistance] = ("menu.label.distance", new MenuKeybindEntry("menu.key.distance")), [eSpectateMenuItem.AdjustOrbitCenterHeight] = ("menu.label.orbitCenterHeight", new MenuKeybindEntry("menu.key.orbitCenterHeight")), [eSpectateMenuItem.AdjustFollowPitch] = ("menu.label.followPitch", new MenuKeybindEntry("menu.key.followPitch")) }; public static SpectateUI? Instance { get; private set; } public eSpectateUIState UIState => _uiState; public bool InPlayerStatusUpdate { get; private set; } public PUI_Inventory? SpectateInventory => _spectateInv; private string MenuTitleStr => "<allcaps>" + Loc.T("mod.name") + "</allcaps>"; public SpectateUI(IntPtr ptr) : base(ptr) { } private void Awake() { if ((Object)(object)Instance != (Object)null && (Object)(object)Instance != (Object)(object)this) { Object.Destroy((Object)(object)((Component)this).gameObject); return; } Instance = this; Object.DontDestroyOnLoad((Object)(object)this); Events.OnSessionEnd += Unload; Events.OnCheckpointReload += Unload; } public void Unload() { SetSpectateInventoryActive(active: false); HideNavMarker(); _uiState = eSpectateUIState.FPNotDowned; _uiStatePrev = eSpectateUIState.FPNotDowned; _uiStateRendered = eSpectateUIState.ShowMenu; _lastRenderedTarget = null; _wantToRevertPUIStatus = false; } public void ShowNavMarker() { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Expected O, but got Unknown //IL_0066: Unknown result type (might be due to invalid IL or missing references) PlayerAgent localPlayerAgent = PlayerManager.GetLocalPlayerAgent(); if ((Object)(object)localPlayerAgent == (Object)null) { Logger.Warn("SpectateUI: Cannot show nav marker: no local player!"); return; } if ((Object)(object)_navMarkerTrackTarget == (Object)null) { Transform nameNavMarkerAlign = localPlayerAgent.PlayerSyncModel.m_nameNavMarkerAlign; _navMarkerTrackTarget = new GameObject("io.takina.gtfo.Spectate_SpectateNavMarkerTrackTarget"); _navMarkerTrackTarget.transform.SetParent(nameNavMarkerAlign, false); _navMarkerTrackTarget.transform.localPosition = HeadBeaconAlignOffset; } if ((Object)(object)_navMarker == (Object)null) { _navMarker = GuiManager.NavMarkerLayer.PlaceCustomMarker((NavMarkerOption)262168, _navMarkerTrackTarget, "Spectate LocalPlayer", 0f, false); } _navMarker.SetTrackingObject(_navMarkerTrackTarget); _navMarker.SetVisible(true); _navMarker.SetIconScale(0.4f); _navMarker.SetTitle(localPlayerAgent.InteractionName); _navMarker.SetAlpha(2f); _navMarker.SetSignInfo("<#FFF><allcaps>" + Loc.T("ui.navMarkerYou") + "</allcaps></color>"); } public void HideNavMarker() { if ((Object)(object)_navMarker != (Object)null) { _navMarker.SetVisible(false); } } private bool AllTMPExist() { if ((Object)(object)_menuObj != (Object)null) { return (Object)(object)_specTargetTmp != (Object)null; } return false; } private void Update() { if (_wantToRevertPUIStatus) { AgentTarget agentTarget = SpectateCam.Instance?.Self; if (agentTarget != null) { UpdatePlayerStatusUI(agentTarget); _wantToRevertPUIStatus = false; } } _ = SpectateCam.Instance; SpectateCam? instance = SpectateCam.Instance; if (instance == null || !instance.CanSpectate) { SetUIActive(active: false); } else if (CheckOrCreateTMP()) { ProcessInput(); SpectateCam? instance2 = SpectateCam.Instance; if (instance2 != null && instance2.Active) { UpdatePlayerStatusUI(SpectateCam.Instance.Target); UpdatePlayerInventoryUI(SpectateCam.Instance.Target); _lastRenderedTarget = SpectateCam.Instance.Target?.Agent; } SetUIActive(active: true); if (_isUIDirty || UIState != _uiStateRendered) { RefreshUI(); } } } public void UpdateForAttach() { if (_uiStatePrev == eSpectateUIState.HideMenu || _uiStatePrev == eSpectateUIState.ShowMenu) { SetUIState(_uiStatePrev); } else { SetUIState(eSpectateUIState.HideMenu); } SetSpectateInventoryActive(active: true); ((Component)GuiManager.PlayerLayer.m_playerStatus).gameObject.SetActive(true); if (ConfigMgr.ShowLocalPlayerNavMarker) { ShowNavMarker(); } } public void UpdateForDetach() { bool valueOrDefault = SpectateCam.Instance?.Self?.IsDowned == true; SetUIState((!valueOrDefault) ? eSpectateUIState.FPNotDowned : eSpectateUIState.FPDowned); _wantToRevertPUIStatus = true; SetSpectateInventoryActive(active: false); HideNavMarker(); } private void ProcessInput() { //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (InputMapper.Current.FocusStateFilterPass((eFocusState)4) && Input.GetKeyDown(ConfigMgr.GetKeybind(SpectateInputAction.ToggleMenu))) { switch (UIState) { case eSpectateUIState.ShowMenu: SetUIState(eSpectateUIState.HideMenu); break; case eSpectateUIState.HideMenu: SetUIState(eSpectateUIState.ShowMenu); break; } } } public void ReplicateUI() { if (!CheckOrCreateTMP()) { Logger.Error("SpectateUI: Failed to create TMP elements for spectate UI!"); } if (!CheckOrCreatePUI_Inventory()) { Logger.Error("SpectateUI: Failed to create PUI_Inventory elements for spectate UI!"); } } private bool CheckOrCreatePUI_Inventory() { //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) PlayerGuiLayer playerLayer = GuiManager.PlayerLayer; PUI_Inventory val = ((playerLayer != null) ? playerLayer.Inventory : null); if ((Object)(object)val == (Object)null) { Logger.Warn("SpectateUI: Failed to find PUI_Inventory for spectate UI replication!"); return false; } if ((Object)(object)_spectateInv != (Object)null) { return true; } _spectateInv = Object.Instantiate<PUI_Inventory>(val, ((Component)val).transform.parent, false); RectTransform rectTrans = ((RectTransformComp)_spectateInv).RectTrans; for (int i = 0; i < ((Transform)rectTrans).childCount; i++) { Object.Destroy((Object)(object)((Component)((Transform)rectTrans).GetChild(i)).gameObject); } _spectateInv.Setup((GuiLayer)(object)GuiManager.PlayerLayer); ((Object)_spectateInv).name = "io.takina.gtfo.Spectate_SpectateInventory"; _spectateInv.m_headerRoot = Object.Instantiate<GameObject>(val.m_headerRoot, (Transform)(object)((RectTransformComp)_spectateInv).RectTrans, false); _spectateInv.m_headerTxt = _spectateInv.m_headerRoot.GetComponentInChildren<TextMeshPro>(); Vector3 localPosition = ((Transform)((RectTransformComp)_spectateInv).RectTrans).localPosition; localPosition.y += -20f; ((Transform)((RectTransformComp)_spectateInv).RectTrans).localPosition = localPosition; ((Component)_spectateInv).gameObject.SetActive(false); return true; } private bool CheckOrCreateTMP() { //IL_009a: 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_00dd: 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_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Expected O, but got Unknown //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0186: 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_01ec: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Unknown result type (might be due to invalid IL or missing references) //IL_028b: Unknown result type (might be due to invalid IL or missing references) //IL_02af: 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_02ee: Unknown result type (might be due to invalid IL or missing references) //IL_02f8: Expected O, but got Unknown //IL_0310: Unknown result type (might be due to invalid IL or missing references) //IL_0326: Unknown result type (might be due to invalid IL or missing references) //IL_033c: Unknown result type (might be due to invalid IL or missing references) //IL_0352: Unknown result type (might be due to invalid IL or missing references) //IL_035e: Unknown result type (might be due to invalid IL or missing references) //IL_0404: Unknown result type (might be due to invalid IL or missing references) //IL_041f: Unknown result type (might be due to invalid IL or missing references) //IL_044a: Unknown result type (might be due to invalid IL or missing references) //IL_0480: Unknown result type (might be due to invalid IL or missing references) //IL_049b: Unknown result type (might be due to invalid IL or missing references) //IL_04c6: Unknown result type (might be due to invalid IL or missing references) //IL_0598: Unknown result type (might be due to invalid IL or missing references) if (AllTMPExist()) { return true; } RemoveAllUIRoots(); PlayerGuiLayer playerLayer = GuiManager.PlayerLayer; PUI_LocalPlayerStatus val = ((playerLayer != null) ? playerLayer.m_playerStatus : null); if ((Object)(object)val == (Object)null) { return false; } PlayerGuiLayer playerLayer2 = GuiManager.PlayerLayer; Transform val2 = ((playerLayer2 != null) ? ((GuiLayer)playerLayer2).CustomComponentRoot : null); if ((Object)(object)val2 == (Object)null) { return false; } TextMeshPro healthText = val.m_healthText; _specTargetTmp = CreateTMPFrom(healthText, "io.takina.gtfo.Spectate_SpectateTarget"); RegisterUIRoot(((Component)_specTargetTmp).gameObject); ((Transform)((TMP_Text)_specTargetTmp).rectTransform).SetParent(val2, false); ((TMP_Text)_specTargetTmp).rectTransform.anchorMin = new Vector2(0.5f, 1f); ((TMP_Text)_specTargetTmp).rectTransform.anchorMax = new Vector2(0.5f, 1f); ((TMP_Text)_specTargetTmp).rectTransform.anchoredPosition3D = new Vector3(0f, -200f, 0f); ((TMP_Text)_specTargetTmp).rectTransform.sizeDelta = new Vector2(300f, 100f); ((TMP_Text)_specTargetTmp).alignment = (TextAlignmentOptions)514; _menuObj = new GameObject("io.takina.gtfo.Spectate_SpectateMenu"); RegisterUIRoot(_menuObj); RectTransform val3 = _menuObj.AddComponent<RectTransform>(); ((Transform)val3).SetParent(val2, false); ((Transform)val3).localScale = Vector3.one; val3.anchorMin = new Vector2(0f, 0.5f); val3.anchorMax = new Vector2(0f, 0.5f); val3.pivot = new Vector2(0.5f, 0.5f); val3.anchoredPosition3D = new Vector3(300f, 0f, 0f); _menuTitleTmp = CreateTMPFrom(healthText, "io.takina.gtfo.Spectate_MenuTitle"); ((Transform)((TMP_Text)_menuTitleTmp).rectTransform).SetParent(_menuObj.transform, false); ((TMP_Text)_menuTitleTmp).rectTransform.pivot = new Vector2(1f, 0f); ((TMP_Text)_menuTitleTmp).rectTransform.anchoredPosition3D = new Vector3(0f, 55f, 0f); ((TMP_Text)_menuTitleTmp).rectTransform.sizeDelta = new Vector2(250f, 45f); ((TMP_Text)_menuTitleTmp).alignment = (TextAlignmentOptions)516; _menuViewModeTmp = CreateTMPFrom(healthText, "io.takina.gtfo.Spectate_ViewMode"); ((Transform)((TMP_Text)_menuViewModeTmp).rectTransform).SetParent(_menuObj.transform, false); ((TMP_Text)_menuViewModeTmp).rectTransform.pivot = new Vector2(1f, 0f); ((TMP_Text)_menuViewModeTmp).rectTransform.anchoredPosition3D = new Vector3(0f, 13f, 0f); ((TMP_Text)_menuViewModeTmp).rectTransform.sizeDelta = new Vector2(250f, 40f); ((TMP_Text)_menuViewModeTmp).alignment = (TextAlignmentOptions)516; _menuListObj = new GameObject("io.takina.gtfo.Spectate_SpectateMenuList"); RectTransform val4 = _menuListObj.AddComponent<RectTransform>(); ((Transform)val4).SetParent((Transform)(object)val3, false); ((Transform)val4).localScale = Vector3.one; val4.anchorMin = new Vector2(0.5f, 0.5f); val4.anchorMax = new Vector2(0.5f, 0.5f); val4.pivot = new Vector2(0.5f, 0.5f); val4.anchoredPosition3D = Vector3.zero; for (int i = 0; i < 10; i++) { TextMeshPro val5 = CreateTMPFrom(healthText, $"{"io.takina.gtfo.Spectate"}_MenuOption_{i}"); TextMeshPro val6 = CreateTMPFrom(healthText, $"{"io.takina.gtfo.Spectate"}_MenuKeybind_{i}"); ((Transform)((TMP_Text)val5).rectTransform).SetParent((Transform)(object)val4, false); ((TMP_Text)val5).rectTransform.pivot = new Vector2(1f, 0.5f); ((TMP_Text)val5).rectTransform.sizeDelta = new Vector2(250f, 22f); ((TMP_Text)val5).rectTransform.anchoredPosition3D = new Vector3(0f, 0f - ((float)i + 0.5f) * 27f, 0f); ((TMP_Text)val5).alignment = (TextAlignmentOptions)516; ((Transform)((TMP_Text)val6).rectTransform).SetParent((Transform)(object)val4, false); ((TMP_Text)val6).rectTransform.pivot = new Vector2(0f, 0.5f); ((TMP_Text)val6).rectTransform.sizeDelta = new Vector2(250f, 16f); ((TMP_Text)val6).rectTransform.anchoredPosition3D = new Vector3(5f, 0f - ((float)i + 0.5f) * 27f, 0f); ((TMP_Text)val6).alignment = (TextAlignmentOptions)513; _menuItemsTmp.Add((val5, val6)); } RefreshUI(); Transform val7 = val2.Find("PUI_CommunicationMenu(Clone)/Root/Backround"); if ((Object)(object)val7 == (Object)null) { Logger.Warn("SpectateUI: Can't find bg transform in CommunicationMenu! Using \"" + ((Object)val2).name + "\""); return true; } GameObject val8 = Object.Instantiate<GameObject>(((Component)val7).gameObject); ((Object)val8).name = "io.takina.gtfo.Spectate_MenuBackground"; _menuBackground = val8.GetComponent<SpriteRenderer>(); ((Renderer)_menuBackground).sortingOrder = -20; RectTransform component = val8.GetComponent<RectTransform>(); ((Transform)component).SetParent(_menuObj.transform); component.anchoredPosition3D = new Vector3(-25f, -55f, 0f); return true; } internal static TextMeshPro CreateTMPFrom(TextMeshPro from, string name) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0049: 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_009e: 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) TextMeshPro obj = Object.Instantiate<TextMeshPro>(from); ((Object)obj).name = name; ((Graphic)obj).color = new Color(1f, 1f, 1f, 0.8f); ((TMP_Text)obj).enableAutoSizing = true; ((TMP_Text)obj).autoSizeTextContainer = false; ((TMP_Text)obj).enableWordWrapping = false; ((TMP_Text)obj).overflowMode = (TextOverflowModes)1; ((TMP_Text)obj).margin = Vector4.zero; ((TMP_Text)obj).fontSize = 26f; ((TMP_Text)obj).fontSizeMin = 0f; ((TMP_Text)obj).fontSizeMax = 69f; ((TMP_Text)obj).rectTransform.anchorMin = new Vector2(0.5f, 0.5f); ((TMP_Text)obj).rectTransform.anchorMax = new Vector2(0.5f, 0.5f); ((TMP_Text)obj).rectTransform.pivot = new Vector2(0.5f, 0.5f); return obj; } public void SetSpectateInventoryActive(bool active) { PUI_Inventory? spectateInv = _spectateInv; if (spectateInv != null) { ((RectTransformComp)spectateInv).SetVisible(active); } if (active) { ((RectTransformComp)GuiManager.PlayerLayer.Inventory).SetVisible(false); } else { GuiManager.PlayerLayer.UpdateGUIElementsVisibility((eFocusState)4); } } public void UpdatePlayerStatusUI(AgentTarget? target) { if (target == null) { Logger.Error("SpectateUI: Could not update player status text: no target!"); return; } InPlayerStatusUpdate = true; PlayerGuiLayer playerLayer = GuiManager.PlayerLayer; PUI_LocalPlayerStatus val = ((playerLayer != null) ? playerLayer.m_playerStatus : null); if (val != null) { val.UpdateHealth(target.Health, false); } if (val != null) { val.UpdateInfection(target.Infection, target.Agent.InfectionTargetHealthRel); } if (val != null) { val.m_boosterIconActiveDisplay.UpdateBoosterIconsActiveState(target.SAgent); } StaminaPatch.RevertStaminaBpmDisplay(val); PlayerAgent? lastRenderedTarget = _lastRenderedTarget; if (((lastRenderedTarget != null) ? new IntPtr?(((Il2CppObjectBase)lastRenderedTarget).Pointer) : ((IntPtr?)null)) != ((Il2CppObjectBase)target.Agent).Pointer && val != null) { val.ResetDamageAnimation(); } InPlayerStatusUpdate = false; } public void UpdatePlayerInventoryUI(AgentTarget? target) { //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: 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_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_0197: 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_01ea: Unknown result type (might be due to invalid IL or missing references) if (target == null) { Logger.Error("SpectateUI: Could not update player inventory text: no target!"); return; } if ((Object)(object)_spectateInv == (Object)null) { if (!CheckOrCreatePUI_Inventory()) { Logger.Error("SpectateUI: Could not update player inventory text: failed to create PUI_Inventory!"); return; } PUI_Inventory? spectateInv = _spectateInv; if (spectateInv != null) { ((Component)spectateInv).gameObject.SetActive(true); } } if ((Object)(object)target.Agent.Owner == (Object)null) { Logger.Warn("SpectateUI: UpdatePlayerInventoryUI: SNet Owner is null"); return; } PlayerBackpack backpack = target.Backpack; PlayerAmmoStorage ammoStorage = target.AmmoStorage; if (backpack == null || ammoStorage == null) { Logger.Warn("SpectateUI: Target backpack/ammostorage is null, cannot update inventory UI!"); return; } InventorySlot valueOrDefault = target.ActiveItemSlot.GetValueOrDefault(); bool flag = AmmoSync.ReserveIncludesClip(target.SAgent); _spectateInv.SetHeader(target.Agent.InteractionName, Color.white); BackpackItem val2 = default(BackpackItem); foreach (InventorySlot item in (Il2CppArrayBase<InventorySlot>)(object)_spectateInv.m_slotGUIOrder) { PUI_InventoryItem val = _spectateInv.m_inventorySlots[item]; if (!backpack.TryGetBackpackItem(item, ref val2)) { val.SetState((ePUI_InventortyItemState)0); continue; } InventorySlotAmmo inventorySlotAmmo = ammoStorage.GetInventorySlotAmmo(item); ItemDataBlock itemDataBlock = val2.Instance.ItemDataBlock; string archetypeName = val2.Instance.ArchetypeName; string publicName = val2.Instance.PublicName; int num = (int)ammoStorage.GetClipAmmoFromSlot(item); int num2 = inventorySlotAmmo.BulletsInPack; float num3 = (float)(num2 + num) * inventorySlotAmmo.BulletsToRelConv; if (flag) { num2 -= num; } val.SetArchetypeName(archetypeName); val.SetDetailedName(publicName); val.SetState((ePUI_InventortyItemState)((item != valueOrDefault) ? 1 : 2)); val.SetAll(num, num2, num3); if (itemDataBlock != null) { val.SetAllShowFlags(target.HasClipData && itemDataBlock.GUIShowAmmoClip, itemDataBlock.GUIShowAmmoPack, itemDataBlock.GUIShowAmmoTotalRel, itemDataBlock.GUIShowAmmoInfinite); } _spectateInv.SetSlotAmmo(item, num, num2, num3); } PUI_Inventory val3 = default(PUI_Inventory); if (CM_PageMap.TryGetInventoryWithSlotIndex(target.Agent.Owner.PlayerSlotIndex(), ref val3)) { _spectateInv.SetFlashLightIcon(((Component)val3.m_iconDisplay.FlashLightIcon.Enabled).gameObject.activeSelf); } _spectateInv.UpdateSlotPositions(); } private void AddMenuItem(eSpectateMenuItem item, bool enableUI = true) { if (_menuItemsToRender.Count >= 10) { Logger.Warn("SpectateUI: AddMenuItem (" + item.ToString() + ") but menu is full!"); return; } if (_menuItems.TryGetValue(item, out (string, MenuKeybindEntry) value)) { var (key, menuKeybindEntry) = value; _menuItemsToRender.Add((Loc.T(key), menuKeybindEntry.ToString())); } else { Logger.Warn("SpectateUI: Tried to add unknown menu item!"); } if (enableUI) { EnableUI(eSpectateUIComp.Menu); } } private void UpdateViewMode(bool freecam, bool enableUI = true) { string text = "<allcaps>" + Loc.T("menu.cam.freeLook") + "</allcaps>"; string text2 = "<allcaps>" + Loc.T("menu.cam.follow") + "</allcaps>"; string text3 = (freecam ? $"<#{"04B065"}FF>{text}</color>" : ("<#FFFFFF60>" + text + "</color>")); string text4 = ((!freecam) ? $"<#{"04B065"}FF>{text2}</color>" : ("<#FFFFFF60>" + text2 + "</color>")); _menuViewModeStr = text3 + " / " + text4; if (enableUI) { EnableUI(eSpectateUIComp.ViewMode); } } private void UpdateSpectateTargetText(bool enableUI = true) { SpectateCam? instance = SpectateCam.Instance; object obj; if (instance == null) { obj = null; } else { AgentTarget? target = instance.Target; obj = (((object)target != null) ? target.Agent.InteractionName : null); } if (obj == null) { obj = "<#FFDE21>Unknown</color>"; } string value = (string)obj; _specTargetStr = $"<#{"FFFFFF"}>Spectating\n<size=36>{value}</size></color>"; if (enableUI) { EnableUI(eSpectateUIComp.SpectateTarget); } } public void SetUIState(eSpectateUIState state) { _uiStatePrev = _uiState; _uiState = state; } public void MarkUIDirty() { _isUIDirty = true; } private void RefreshUI() { RefreshUI(SpectateCam.Instance?.Freecam ?? false); } private void RefreshUI(bool freecam) { _isUIDirty = false; _uiStateRendered = _uiState; ClearUI(); switch (_uiState) { case eSpectateUIState.ShowMenu: EnableUI(eSpectateUIComp.Title); UpdateViewMode(freecam); AddMenuItem(eSpectateMenuItem.ExitSpectate); AddMenuItem(eSpectateMenuItem.HideMenu); AddMenuItem(eSpectateMenuItem.ToggleFreecam); AddMenuItem(eSpectateMenuItem.SwitchPlayer); AddMenuItem(eSpectateMenuItem.SelectPlayer); AddMenuItem(eSpectateMenuItem.AdjustDistance); AddMenuItem(eSpectateMenuItem.AdjustOrbitCenterHeight); if (freecam) { if (ConfigMgr.AutoTransitionToFollowView) { AddMenuItem(eSpectateMenuItem.DisableFreecamAutoTransition); } else { AddMenuItem(eSpectateMenuItem.EnableFreecamAutoTransition); } } else { AddMenuItem(eSpectateMenuItem.AdjustFollowPitch); } break; case eSpectateUIState.HideMenu: EnableUI(eSpectateUIComp.Title); UpdateViewMode(freecam); AddMenuItem(eSpectateMenuItem.ExitSpectate); AddMenuItem(eSpectateMenuItem.ShowMenu); break; case eSpectateUIState.FPDowned: case eSpectateUIState.FPNotDowned: AddMenuItem(eSpectateMenuItem.EnterSpectate); break; } RenderUI(); } private void ClearUI() { _menuViewModeStr = ""; _menuItemsToRender.Clear(); eSpectateUIComp[] values = Enum.GetValues<eSpectateUIComp>(); foreach (eSpectateUIComp key in values) { _uiCompState[key] = false; } } private void RemoveAllUIRoots() { foreach (GameObject uiRoot in _uiRoots) { Object.Destroy((Object)(object)uiRoot); } _specTargetTmp = null; _menuObj = null; _menuTitleTmp = null; _menuViewModeTmp = null; _menuListObj = null; _menuBackground = null; _menuItemsTmp.Clear(); _uiRoots.Clear(); } private void RegisterUIRoot(GameObject root) { if (!_uiRoots.Contains(root)) { _uiRoots.Add(root); root.gameObject.SetActive(false); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void EnableUI(eSpectateUIComp comp) { _uiCompState[comp] = true; } private void RenderUI() { foreach (var (eSpectateUIComp3, flag2) in _uiCompState) { switch (eSpectateUIComp3) { case eSpectateUIComp.SpectateTarget: if (flag2) { UpdateText(_specTargetTmp, _specTargetStr); } Util.SetTargetActiveIfDiff((Behaviour?)(object)_specTargetTmp, flag2); break; case eSpectateUIComp.Title: if (flag2) { UpdateText(_menuTitleTmp, MenuTitleStr); } Util.SetTargetActiveIfDiff((Behaviour?)(object)_menuTitleTmp, flag2); break; case eSpectateUIComp.ViewMode: if (flag2) { UpdateText(_menuViewModeTmp, _menuViewModeStr); } Util.SetTargetActiveIfDiff((Behaviour?)(object)_menuViewModeTmp, flag2); break; case eSpectateUIComp.Menu: if (flag2) { int count = _menuItemsToRender.Count; for (int i = 0; i < 10; i++) { var (val, val2) = _menuItemsTmp[i]; if (i < count) { var (text, text2) = _menuItemsToRender[i]; UpdateText(val, "<allcaps>" + text + "</allcaps>"); UpdateText(val2, "<color=orange>[" + text2 + "]</color>"); Util.SetTargetActiveIfDiff((Behaviour?)(object)val, active: true); Util.SetTargetActiveIfDiff((Behaviour?)(object)val2, active: true); } else { Util.SetTargetActiveIfDiff((Behaviour?)(object)val, active: false); Util.SetTargetActiveIfDiff((Behaviour?)(object)val2, active: false); } } } Util.SetTargetActiveIfDiff(_menuListObj, flag2); break; } } } private void SetUIActive(bool active) { if (_wasUIActive == active) { return; } foreach (GameObject uiRoot in _uiRoots) { Util.SetTargetActiveIfDiff(uiRoot, active); } _wasUIActive = active; } private void UpdateText(TextMeshPro? tmp, string newText) { if (!((Object)(object)tmp == (Object)null) && !(((TMP_Text)tmp).text == newText) && newText != ((TMP_Text)tmp).text) { ((TMP_Text)tmp).text = newText; ForceTMPUpdate(tmp); } } private void ForceTMPUpdate(TextMeshPro? tmp) { if (!((Object)(object)tmp == (Object)null)) { ((Graphic)tmp).SetAllDirty(); ((TMP_Text)tmp).ForceMeshUpdate(false, false); } } public void WarnFreecamNoAdjustPitch() { } } [RegisterIl2Cpp] public class SpectatorCountUI : MonoBehaviour { private class SpectatorData { public ulong Lookup { get; set; } public float TimeSinceLastUpdate { get; set; } public bool IsSpectating(SNet_Player player) { if ((Object)(object)player == (Object)null) { return false; } return player.Lookup == Lookup; } } private const float SpectateTtl = 1f; private readonly Dictionary<ulong, SpectatorData> _spectatorData = new Dictionary<ulong, SpectatorData>(); private const float IconSize = 24f; private const float IconTextSpacing = 18f; private const float TextContainerWidth = 48f; private const float UIGroupOffsetFromTop = 85f; private RectTransform? _uiContainer; private SpriteRenderer? _iconSr; private TMP_Text? _spectatorCountTxt; private void Awake() { Net.RegisterHandler(254, HandlePacket); CreateUI(); } private void OnDestroy() { Net.UnregisterHandler(254); } private void Update() { foreach (ulong key in _spectatorData.Keys) { _spectatorData[key].TimeSinceLastUpdate += Time.unscaledDeltaTime; if (_spectatorData[key].TimeSinceLastUpdate > 1f) { _spectatorData.Remove(key); } } bool flag = SpectateCam.Instance?.Active ?? false; if (flag) { SpectateCam? instance = SpectateCam.Instance; if (instance == null || !instance.TargetReady) { _spectatorCountTxt.text = "0"; return; } } SNet_Player player = (SNet_Player)(flag ? ((object)SpectateCam.Instance.Target.SAgent) : ((object)SNet.Loca