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 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.Interop; 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.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] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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] [Microsoft.CodeAnalysis.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)(object)playerSyncModel.m_gfxHead).Count == 0 || ((Il2CppArrayBase)(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.op_Implicit(gfxHead); playerSyncModel.m_gfxArms = Il2CppReferenceArray.op_Implicit(gfxArms); playerSyncModel.m_gfxTorso = Il2CppReferenceArray.op_Implicit(gfxTorso); playerSyncModel.m_gfxLegs = Il2CppReferenceArray.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? 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 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 = 0, KeyCode nonMod = 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.6.4")] [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.6.4"; 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.6.4"); 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_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Expected O, but got Unknown //IL_009b: Expected O, but got Unknown Logger.Setup(); Logger.Info("Spectate [io.takina.gtfo.Spectate @ 1.6.4]"); JObject.Parse("{}"); Harmony val = new Harmony("io.takina.gtfo.Spectate"); ConfigMgr.Process(); PluginFolder = Path.GetDirectoryName(((BaseChainloader)(object)IL2CPPChainloader.Instance).Plugins["io.takina.gtfo.Spectate"].Location) ?? ""; RegisterIl2CppTypes(); 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(h); ApplyPatch(h); ApplyPatch(h); ApplyPatch(h); ApplyPatch(h); ApplyPatch(h); ApplyPatch(h); ApplyPatch(h); ApplyPatch(h); } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] private static void ApplyCompatPatches(Harmony h) { if (ConfigMgr.DevEnables(eDevOpts.CompatPatchUsingReflection) || ConfigMgr.CompatPatchUseReflection) { CompatPatcher.PatchAll(h); return; } ApplyPatchIfLoaded(h, "Inas.EOSExt.EMP"); ApplyPatchIfLoaded(h, "GTFO.EECustomization"); ApplyPatchIfLoaded(h, "Amor.ExcellentObjectiveSetup"); } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] private static void ApplyPatchIfLoaded(Harmony h, string guid) { if (((BaseChainloader)(object)IL2CPPChainloader.Instance).Plugins.ContainsKey(guid)) { ApplyPatch(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(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() != null) { ClassInjector.RegisterTypeInIl2Cpp(type); } } } else { ClassInjector.RegisterTypeInIl2Cpp(); ClassInjector.RegisterTypeInIl2Cpp(); ClassInjector.RegisterTypeInIl2Cpp(); ClassInjector.RegisterTypeInIl2Cpp(); ClassInjector.RegisterTypeInIl2Cpp(); ClassInjector.RegisterTypeInIl2Cpp(); ClassInjector.RegisterTypeInIl2Cpp(); ClassInjector.RegisterTypeInIl2Cpp(); ClassInjector.RegisterTypeInIl2Cpp(); } } 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(); PluginObject.AddComponent(); PluginObject.AddComponent(); PluginObject.AddComponent(); PluginObject.AddComponent(); PluginObject.AddComponent(); PluginObject.AddComponent(); 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 ActivePouncers { get; } = new List(); 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(); 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) : 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) : 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) { return; } Logger.Debug("SpectateCam: Sending spectate target update messages"); _timeSinceLastSpectateMessage = 0f; NetHelper.InvokeWithAllPlayers(delegate(SNet_Player player) { if (PeerInfoManager.Supported(player)) { NetImpl.SendSpectateTargetState(_target.Lookup == player.Lookup, player); } }); } 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) : null); C_Node? lastLocalCullNode = LastLocalCullNode; if (intPtr != ((lastLocalCullNode != null) ? new IntPtr?(((Il2CppObjectBase)lastLocalCullNode).Pointer) : 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 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) ? 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) ? 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 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() : 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 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(); 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.op_Implicit((particleData[])(object)new particleData[1024])).Cast(); } 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.op_Implicit(root.GetComponentsInChildren(includeInactive)); List list = new List(); List list2 = new List(); List list3 = new List(); List list4 = new List(); 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.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 _keyName; private bool _isAssociated; private SpectateInputAction _associatedAction; public MenuKeybindEntry(string keyName) { _isAssociated = false; _associatedAction = SpectateInputAction.None; _keyName = keyName; } public MenuKeybindEntry(SpectateInputAction action) { _isAssociated = true; _associatedAction = action; _keyName = ""; } 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 _keyName; } } [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 _uiRoots = new List(); private readonly Dictionary _uiCompState = new Dictionary(); 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 readonly string _menuTitleStr = "SPECTATE"; private string _menuViewModeStr = ""; private readonly List<(string, string)> _menuItemsStr = new List<(string, string)>(); private readonly Dictionary _menuItems = new Dictionary { [eSpectateMenuItem.ShowMenu] = ("Show Menu", new MenuKeybindEntry(SpectateInputAction.ToggleMenu)), [eSpectateMenuItem.HideMenu] = ("Hide Menu", new MenuKeybindEntry(SpectateInputAction.ToggleMenu)), [eSpectateMenuItem.EnterSpectate] = ("Enter Spectate", new MenuKeybindEntry(SpectateInputAction.ToggleSpectate)), [eSpectateMenuItem.ExitSpectate] = ("Exit Spectate", new MenuKeybindEntry(SpectateInputAction.ToggleSpectate)), [eSpectateMenuItem.ToggleFreecam] = ("Toggle Free-Look", new MenuKeybindEntry(SpectateInputAction.ToggleFreecam)), [eSpectateMenuItem.EnableFreecamAutoTransition] = ("Enable Auto-Follow", new MenuKeybindEntry(SpectateInputAction.ToggleAutoFollow)), [eSpectateMenuItem.DisableFreecamAutoTransition] = ("Disable Auto-Follow", new MenuKeybindEntry(SpectateInputAction.ToggleAutoFollow)), [eSpectateMenuItem.SwitchPlayer] = ("Switch Player", new MenuKeybindEntry("LMB / RMB")), [eSpectateMenuItem.SelectPlayer] = ("Select Player", new MenuKeybindEntry("1 - 8")), [eSpectateMenuItem.AdjustDistance] = ("Camera Distance", new MenuKeybindEntry("Scroll")), [eSpectateMenuItem.AdjustOrbitCenterHeight] = ("Camera Vertical Offset", new MenuKeybindEntry("Ctrl + Scroll")), [eSpectateMenuItem.AdjustFollowPitch] = ("Camera Pitch", new MenuKeybindEntry("Shift + Scroll")) }; public static SpectateUI? Instance { get; private set; } public eSpectateUIState UIState => _uiState; public bool InPlayerStatusUpdate { get; private set; } public PUI_Inventory? SpectateInventory => _spectateInv; 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>YOU"); } 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).GetValueOrDefault(); 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(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(val.m_headerRoot, (Transform)(object)((RectTransformComp)_spectateInv).RectTrans, false); _spectateInv.m_headerTxt = _spectateInv.m_headerRoot.GetComponentInChildren(); 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_0089: 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_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: 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) //IL_0115: Expected O, but got Unknown //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_027a: Unknown result type (might be due to invalid IL or missing references) //IL_029e: Unknown result type (might be due to invalid IL or missing references) //IL_02bd: Unknown result type (might be due to invalid IL or missing references) //IL_02dd: Unknown result type (might be due to invalid IL or missing references) //IL_02e7: Expected O, but got Unknown //IL_02ff: Unknown result type (might be due to invalid IL or missing references) //IL_0315: Unknown result type (might be due to invalid IL or missing references) //IL_032b: Unknown result type (might be due to invalid IL or missing references) //IL_0341: Unknown result type (might be due to invalid IL or missing references) //IL_034d: Unknown result type (might be due to invalid IL or missing references) //IL_03f3: Unknown result type (might be due to invalid IL or missing references) //IL_040e: Unknown result type (might be due to invalid IL or missing references) //IL_0439: Unknown result type (might be due to invalid IL or missing references) //IL_046f: Unknown result type (might be due to invalid IL or missing references) //IL_048a: Unknown result type (might be due to invalid IL or missing references) //IL_04b5: Unknown result type (might be due to invalid IL or missing references) //IL_0587: 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; } Transform parent = ((Component)val).transform.parent; TextMeshPro healthText = val.m_healthText; _specTargetTmp = CreateTMPFrom(healthText, "io.takina.gtfo.Spectate_SpectateTarget"); RegisterUIRoot(((Component)_specTargetTmp).gameObject); ((Transform)((TMP_Text)_specTargetTmp).rectTransform).SetParent(parent, 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 val2 = _menuObj.AddComponent(); ((Transform)val2).SetParent(parent, false); ((Transform)val2).localScale = Vector3.one; val2.anchorMin = new Vector2(0f, 0.5f); val2.anchorMax = new Vector2(0f, 0.5f); val2.pivot = new Vector2(0.5f, 0.5f); val2.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 val3 = _menuListObj.AddComponent(); ((Transform)val3).SetParent((Transform)(object)val2, false); ((Transform)val3).localScale = Vector3.one; val3.anchorMin = new Vector2(0.5f, 0.5f); val3.anchorMax = new Vector2(0.5f, 0.5f); val3.pivot = new Vector2(0.5f, 0.5f); val3.anchoredPosition3D = Vector3.zero; for (int i = 0; i < 10; i++) { TextMeshPro val4 = CreateTMPFrom(healthText, $"{"io.takina.gtfo.Spectate"}_MenuOption_{i}"); TextMeshPro val5 = CreateTMPFrom(healthText, $"{"io.takina.gtfo.Spectate"}_MenuKeybind_{i}"); ((Transform)((TMP_Text)val4).rectTransform).SetParent((Transform)(object)val3, false); ((TMP_Text)val4).rectTransform.pivot = new Vector2(1f, 0.5f); ((TMP_Text)val4).rectTransform.sizeDelta = new Vector2(250f, 22f); ((TMP_Text)val4).rectTransform.anchoredPosition3D = new Vector3(0f, 0f - ((float)i + 0.5f) * 27f, 0f); ((TMP_Text)val4).alignment = (TextAlignmentOptions)516; ((Transform)((TMP_Text)val5).rectTransform).SetParent((Transform)(object)val3, false); ((TMP_Text)val5).rectTransform.pivot = new Vector2(0f, 0.5f); ((TMP_Text)val5).rectTransform.sizeDelta = new Vector2(250f, 16f); ((TMP_Text)val5).rectTransform.anchoredPosition3D = new Vector3(5f, 0f - ((float)i + 0.5f) * 27f, 0f); ((TMP_Text)val5).alignment = (TextAlignmentOptions)513; _menuItemsTmp.Add((val4, val5)); } RefreshUI(); Transform val6 = parent.Find("PUI_CommunicationMenu(Clone)/Root/Backround"); if ((Object)(object)val6 == (Object)null) { Logger.Warn("SpectateUI: Can't find bg transform in CommunicationMenu! Using \"" + ((Object)parent).name + "\""); return true; } GameObject val7 = Object.Instantiate(((Component)val6).gameObject); ((Object)val7).name = "io.takina.gtfo.Spectate_MenuBackground"; _menuBackground = val7.GetComponent(); ((Renderer)_menuBackground).sortingOrder = -20; RectTransform component = val7.GetComponent(); ((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(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) : 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)(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 (_menuItemsStr.Count >= 10) { Logger.Warn("SpectateUI: AddMenuItem (" + item.ToString() + ") but menu is full!"); return; } if (_menuItems.TryGetValue(item, out (string, MenuKeybindEntry) value)) { _menuItemsStr.Add((value.Item1, value.Item2.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 = (freecam ? "<#04B065FF>FREE-LOOK" : "<#FFFFFF60>FREE-LOOK"); string text2 = ((!freecam) ? "<#04B065FF>FOLLOW" : "<#FFFFFF60>FOLLOW"); _menuViewModeStr = text + " / " + text2; 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"; } string value = (string)obj; _specTargetStr = $"<#{"FFFFFF"}>Spectating\n{value}"; 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 = ""; _menuItemsStr.Clear(); eSpectateUIComp[] values = Enum.GetValues(); 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 = _menuItemsStr.Count; for (int i = 0; i < 10; i++) { var (val, val2) = _menuItemsTmp[i]; if (i < count) { var (text, text2) = _menuItemsStr[i]; UpdateText(val, "" + text + ""); UpdateText(val2, "[" + text2 + "]"); 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 const float SpectateTTL = 1f; private readonly Dictionary _timeSinceLastActiveSpectate = new Dictionary(); private void Awake() { Net.RegisterHandler(254, HandlePacket); } private void OnDestroy() { Net.UnregisterHandler(254); } private void Update() { foreach (SNet_Player key in _timeSinceLastActiveSpectate.Keys) { _timeSinceLastActiveSpectate[key] += Time.unscaledDeltaTime; if (_timeSinceLastActiveSpectate[key] > 1f) { _timeSinceLastActiveSpectate.Remove(key); } } } public void CreateUI() { } private int GetSpectatorCount() { return _timeSinceLastActiveSpectate.Count; } private void HandlePacket(byte[] data, SNet_Player? sender) { if (data.Length != 1) { Logger.Warn("Expected Spectate"); } if ((Object)(object)sender == (Object)null) { Logger.Warn("Received spectate packet with null sender"); } else if (data[0] != 0) { Logger.Debug($"Player '{sender.GetName()}' ({sender.Lookup}) is spectating local player."); _timeSinceLastActiveSpectate[sender] = 0f; } else { _timeSinceLastActiveSpectate.Remove(sender); } } } [RegisterIl2Cpp] public class Wm : MonoBehaviour { private const float Itv = 0.01f; private TextMeshPro? _tp; private byte _fx = 77; private byte _dx = 42; private float _t; private int _ma; private int _mj; private int _mi; public Wm(IntPtr ptr) : base(ptr) { } private void Awake() { Ld(); Mk(); } private void Update() { if ((!((Object)(object)_tp == (Object)null) || Mk()) && Util.SetTargetActiveIfDiff(((Component)_tp).gameObject, !ConfigMgr.HideWm)) { _t += Time.deltaTime; if (!(_t < 0.01f)) { _t = 0f; ((TMP_Text)_tp).text = $"TKN[SPX{_ma:X2}{_fx:X2}{_mj:X2}{_dx:X2}{_mi:X2}]"; Nx(); } } } private void Ld() { int[] array = "1.6.4".Split('.').Select(int.Parse).ToArray(); _ma = array[0]; _mj = array[1]; _mi = array[2]; } private void Nx() { _fx = (byte)((_fx + 1) % 256); _dx = (byte)((_dx + 1) % 256); } private bool Mk() { //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: 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_010e: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) WatermarkGuiLayer watermarkLayer = GuiManager.WatermarkLayer; object obj; if (watermarkLayer == null) { obj = null; } else { PUI_Watermark watermark = watermarkLayer.m_watermark; obj = ((watermark != null) ? ((Component)watermark).transform.parent : null); } Transform val = (Transform)obj; WatermarkGuiLayer watermarkLayer2 = GuiManager.WatermarkLayer; object obj2; if (watermarkLayer2 == null) { obj2 = null; } else { PUI_Watermark watermark2 = watermarkLayer2.m_watermark; obj2 = ((watermark2 != null) ? watermark2.m_fpsText : null); } TextMeshPro val2 = (TextMeshPro)obj2; if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null) { return false; } _tp = Object.Instantiate(val2, val, false); ((Object)_tp).name = "SPWm"; ((TMP_Text)_tp).rectTransform.anchorMax = new Vector2(1f, 0.22f); ((TMP_Text)_tp).rectTransform.anchorMin = new Vector2(1f, 0.22f); ((TMP_Text)_tp).rectTransform.pivot = new Vector2(0.5f, 0f); ((TMP_Text)_tp).rectTransform.anchoredPosition3D = new Vector3(-10f, 0f, 0f); ((Transform)((TMP_Text)_tp).rectTransform).localRotation = Quaternion.Euler(0f, 0f, 90f); ((TMP_Text)_tp).rectTransform.sizeDelta = new Vector2(200f, 12f); ((TMP_Text)_tp).enableAutoSizing = true; ((TMP_Text)_tp).overflowMode = (TextOverflowModes)0; ((TMP_Text)_tp).alignment = (TextAlignmentOptions)514; ((Component)_tp).gameObject.SetActive(!ConfigMgr.HideWm); return true; } } } namespace Spectate.UI.Support { [RegisterIl2Cpp] [HarmonyPatch] public class SpectateSupportDisplay : MonoBehaviour { private const float PlayerSelectIconSize = 32.48f; private static readonly Color ColorSupported = new Color(0.02f, 0.69f, 0.4f, 1f); private static readonly Color ColorUnknown = new Color(0.5f, 0.1f, 0.5f, 1f); private static readonly Color ColorUnsupported = new Color(0.8f, 0.8f, 0f, 1f); private static readonly Color ColorNa = new Color(0.7f, 0.7f, 0.7f, 1f); private SpriteRenderer _iconSpriteRenderer; private RectTransform _container; private GameObject _containerGo; private CM_Item _cmItem; private BoxCollider2D _boxCollider; private CM_PlayerLobbyBar _lobbyBar; public bool SetupComplete { get; private set; } public int SlotIndex { get; private set; } = -1; private void Awake() { _lobbyBar = ((Component)this).GetComponent(); Setup(_lobbyBar); PeerInfoManager.OnPeerInfoUpdated += UpdateSupportInfo; Events.OnPageLoadoutPlayerListUpdate += UpdateSupportInfo; } private void OnDestroy() { PeerInfoManager.OnPeerInfoUpdated -= UpdateSupportInfo; Events.OnPageLoadoutPlayerListUpdate -= UpdateSupportInfo; } private void Setup(CM_PlayerLobbyBar? lobbyBar) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: 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_0139: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)lobbyBar == (Object)null) { Logger.Error("SpectateSupportDisplay cannot be Setup with null CM_PlayerLobbyBar, aborting setup"); return; } _lobbyBar = lobbyBar; SlotIndex = _lobbyBar.PlayerSlotIndex; Transform transform = _lobbyBar.m_nickNameGuix.transform; _containerGo = new GameObject("SupportDisplay"); _container = _containerGo.AddComponent(); _containerGo.layer = LayerManager.LAYER_UI; ((Transform)_container).SetParent(transform.parent, false); ((Transform)_container).localPosition = transform.localPosition - new Vector3(48.72f, 0f, 0f); _container.sizeDelta = new Vector2(transform.localScale.x, transform.localScale.y); float x = transform.localScale.x; _cmItem = _containerGo.AddComponent(); _cmItem.OnBtnPressCallback = Action.op_Implicit((Action)delegate { }); _boxCollider = _containerGo.AddComponent(); _boxCollider.size = x.ToVector2(); _lobbyBar.m_parentPage.UpdateCellMenuCursorItems(); _iconSpriteRenderer = new GameObject("SupportIcon").AddComponent(); ((Component)_iconSpriteRenderer).gameObject.layer = LayerManager.LAYER_UI; ((Component)_iconSpriteRenderer).transform.SetParent((Transform)(object)_container, false); ((Component)_iconSpriteRenderer).transform.localPosition = Vector3.zero; _iconSpriteRenderer.sprite = SharedAssetLibrary.SpectateIconSprite; ((Component)_iconSpriteRenderer).transform.localScale = x.ToVector3(); UpdateSupportInfo(); SetupComplete = true; } public void UpdateSupportInfo() { //IL_008a: 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_00a2: Unknown result type (might be due to invalid IL or missing references) if (SlotIndex >= ((Il2CppArrayBase)(object)SNet.Slots.PlayerSlots).Length || SlotIndex < 0) { Logger.Warn($"Invalid slot index={SlotIndex} for support display, did we call Setup()?"); return; } SNet_Player player = ((Il2CppArrayBase)(object)SNet.Slots.PlayerSlots)[SlotIndex].player; var (val, color) = GetDisplayInfo(player); _cmItem.TooltipInfo = val; _iconSpriteRenderer.color = color; CM_Tooltip tooltip = _lobbyBar.m_parentPage.m_tooltip; if ((Object)(object)tooltip != (Object)null && (Object)(object)((Component)tooltip).transform.parent == (Object)(object)_container) { tooltip.SetTooltip(val); } } private (TooltipInfo, Color) GetDisplayInfo(SNet_Player player) { //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_000c: 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_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_0217: Unknown result type (might be due to invalid IL or missing references) //IL_028a: Unknown result type (might be due to invalid IL or missing references) //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_026d: Unknown result type (might be due to invalid IL or missing references) //IL_0391: Unknown result type (might be due to invalid IL or missing references) //IL_0327: Unknown result type (might be due to invalid IL or missing references) //IL_0434: Unknown result type (might be due to invalid IL or missing references) //IL_0439: Unknown result type (might be due to invalid IL or missing references) //IL_0422: Unknown result type (might be due to invalid IL or missing references) //IL_0427: Unknown result type (might be due to invalid IL or missing references) //IL_042b: Unknown result type (might be due to invalid IL or missing references) //IL_0430: Unknown result type (might be due to invalid IL or missing references) //IL_0444: Unknown result type (might be due to invalid IL or missing references) //IL_0446: Unknown result type (might be due to invalid IL or missing references) //IL_0449: Unknown result type (might be due to invalid IL or missing references) //IL_043d: Unknown result type (might be due to invalid IL or missing references) //IL_0442: Unknown result type (might be due to invalid IL or missing references) TooltipInfo val = new TooltipInfo { PositionType = (TooltipPositionType)0, TooltipHeader = "Spectate", TooltipText = "", UseTooptip = true }; if ((Object)(object)player == (Object)null) { val.TooltipHeader = val.TooltipHeader + " <#" + CHex(ColorNa) + ">(N/A)"; val.TooltipText = "No player in slot"; return (val, ColorNa); } SNet_SessionHub sessionHub = SNet.SessionHub; if (sessionHub == null || !sessionHub.IsPlayerInHub(player)) { val.TooltipHeader = val.TooltipHeader + " <#" + CHex(ColorNa) + ">(N/A)"; val.TooltipText = "[ERROR] player not in SessionHub"; return (val, ColorNa); } if (player.IsBot) { val.TooltipHeader = val.TooltipHeader + " <#" + CHex(ColorNa) + ">(Bot)"; val.TooltipText = "Beep boop, I'm a bot!"; return (val, ColorNa); } if (!PeerInfoManager.TryGetPeerInfo(player, out PeerInfoManager.PeerInfo info)) { Logger.Error($"[SpectateSupportDisplay] can't get info for player (slot={SlotIndex}) '{player.NickName}' ({player.Lookup}) IsBot={player.IsBot} IsLocal={player.IsLocal} IsInLobby={player.IsInLobby} IsInSessionHub={player.IsInSessionHub}"); val.TooltipHeader = val.TooltipHeader + " <#" + CHex(ColorUnknown) + ">(Unknown)"; val.TooltipText = "[ERROR] player in SessionHub but has no PeerInfo"; return (val, ColorUnknown); } PeerInfo val2 = default(PeerInfo); bool flag = PeerInfoManager.TryGetPeerInfo(player, ref val2); TooltipInfo val3 = val; val3.TooltipHeader = "Spectate" + info.Support switch { PeerInfoManager.PeerSupport.Supported => " <#" + CHex(ColorSupported) + ">(Supported)", PeerInfoManager.PeerSupport.NotSupported => " <#" + CHex(ColorUnsupported) + ">(Unsupported)", PeerInfoManager.PeerSupport.Unknown => " <#" + CHex(ColorUnknown) + ">(Unknown)", _ => "", }; val3 = val; string tooltipText; switch (info.Support) { case PeerInfoManager.PeerSupport.Supported: { DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(51, 2); defaultInterpolatedStringHandler.AppendLiteral("<#63DBD5>v"); defaultInterpolatedStringHandler.AppendFormatted(info.PlugVersion); defaultInterpolatedStringHandler.AppendLiteral("\n"); defaultInterpolatedStringHandler.AppendLiteral("<#63DBD5>v"); defaultInterpolatedStringHandler.AppendFormatted(flag ? ((object)val2.PlugVersion) : "???"); defaultInterpolatedStringHandler.AppendLiteral(" [PlayerSync]\n"); tooltipText = defaultInterpolatedStringHandler.ToStringAndClear() + (player.IsLocal ? "This is you!" : "Complete features available with this player."); break; } case PeerInfoManager.PeerSupport.NotSupported: tooltipText = "Some features are limited."; break; case PeerInfoManager.PeerSupport.Unknown: tooltipText = "Waiting for player info...\n" + $"<#{CHex(ColorUnsupported)}>Attempts ({info.RequestCount} of {5})"; break; default: tooltipText = "N/A"; break; } val3.TooltipText = tooltipText; _cmItem.TooltipInfo = val; return (val, (Color)(info.Support switch { PeerInfoManager.PeerSupport.Supported => ColorSupported, PeerInfoManager.PeerSupport.NotSupported => ColorUnsupported, PeerInfoManager.PeerSupport.Unknown => ColorUnknown, _ => ColorNa, })); static string CHex(Color color) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return ColorUtility.ToHtmlStringRGB(color); } } [HarmonyPatch(typeof(CM_PlayerLobbyBar), "SetupFromPage")] [HarmonyPostfix] private static void OnLobbyBarSetupFromPage(CM_PlayerLobbyBar __instance) { ((Component)__instance).gameObject.AddComponent(); } } } namespace Spectate.Patches { [HarmonyPatch] public class AnimationPatch { public const float PreStop = 1f; public const int IntervalStopFrames = 10; [HarmonyPatch(typeof(PLOC_Downed), "Enter")] [HarmonyPostfix] public static void PLOC_Downed_Enter(PLOC_Downed __instance) { Events.RaiseAnyPlayerDeath(); if (((Agent)((PLOC_Base)__instance).m_owner).IsLocallyOwned && !((Object)(object)SpectateCam.Instance == (Object)null)) { Play_Player_PLOC_Down_Animation(__instance); if (!SpectateCam.Instance.Active) { PlayerSyncModelData playerSyncModel = ((PLOC_Base)__instance).m_owner.PlayerSyncModel; playerSyncModel.SetGFXVisible(playerSyncModel.m_gfxTorso, false, false); playerSyncModel.SetGFXVisible(playerSyncModel.m_gfxLegs, false, false); } else { ((MonoBehaviour)((PLOC_Base)__instance).m_owner).StartCoroutine(CollectionExtensions.WrapToIl2Cpp(BumF__kRandomSolutionRoutine())); } if (ConfigMgr.SwitchOnDeath && SpectateCam.Instance.CanSpectate && !SpectateCam.Instance.Active) { SpectateCam.Instance.TryAttachDelayed(1f); } } } [HarmonyPatch(typeof(PLOC_Downed), "Exit")] [HarmonyPostfix] public static void PLOC_Downed_Exit(PLOC_Downed __instance) { SpectateCam.Instance?.ClearTarget(); } private static IEnumerator BumF__kRandomSolutionRoutine() { yield return (object)new WaitForSeconds(1f); SpectateCam.Instance?.SetRelatedActive(spectateActive: false); int i = 0; while (i < 10) { yield return (object)new WaitForEndOfFrame(); int num = i + 1; i = num; } SpectateCam.Instance?.SetRelatedActive(SpectateCam.Instance.Active); } [HarmonyPatch(typeof(Dam_PlayerDamageLocal), "OnRevive")] [HarmonyPostfix] public static void Dam_PlayerDamageLocal_OnRevive(Dam_PlayerDamageLocal __instance) { if (((Agent)((Dam_PlayerDamageBase)__instance).Owner).IsLocallyOwned && !((Object)(object)SpectateCam.Instance == (Object)null)) { if (SpectateCam.Instance.Active) { SpectateCam.Instance.Detach(); } PlayerSyncModelData playerSyncModel = ((Dam_PlayerDamageBase)__instance).Owner.PlayerSyncModel; playerSyncModel.SetGFXVisible(playerSyncModel.m_gfxTorso, true, true); playerSyncModel.SetGFXVisible(playerSyncModel.m_gfxLegs, true, true); ((Dam_PlayerDamageBase)__instance).Owner.AnimatorBody.Play("Rifle_Movement"); } } private static void Play_Player_PLOC_Down_Animation(PLOC_Downed instance) { ((PLOC_Base)instance).m_owner.AnimatorBody.Play("Dead", 1); ((PLOC_Base)instance).m_owner.AnimatorArms.SetLayerWeight(7, 0f); ((PLOC_Base)instance).m_owner.AnimatorArms.SetLayerWeight(8, 0f); } } [HarmonyPatch] public class CameraPatch { [HarmonyPatch(typeof(LocalPlayerAgent), "get_CamPos")] [HarmonyPrefix] private static bool PlayerAgent_get_CamPos(ref Vector3 __result) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) SpectateCam? instance = SpectateCam.Instance; if (instance != null && instance.Active) { __result = SpectateCam.Instance.CameraPos; return false; } return true; } [HarmonyPatch(typeof(LocalPlayerAgent), "UpdateGlobalInput")] [HarmonyPrefix] private static void PlayerAgent_UpdateGlobalInput(LocalPlayerAgent __instance) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) SpectateCam? instance = SpectateCam.Instance; if (instance != null && instance.Active) { __instance.m_camPos = SpectateCam.Instance.CameraPos; } } [HarmonyPatch(typeof(PlayerAgent), "GetDetectionMod")] [HarmonyPrefix] private static void PlayerAgent_GetDetectionMod(PlayerAgent __instance, ref Vector3 dir, float distance) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) SpectateCam? instance = SpectateCam.Instance; if (instance != null && instance.Active) { dir = SpectateCam.Instance.DiegeticCamDir; } } [HarmonyPatch(typeof(PlayerSync), "SendLocomotion")] [HarmonyPrefix] private static void PlayerSync_SendLocomotion(PlayerSync __instance, PLOC_State state, Vector3 pos, ref Vector3 lookDir, float velFwd, float velRight) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) SpectateCam? instance = SpectateCam.Instance; if (instance != null && instance.Active) { lookDir = SpectateCam.Instance.DiegeticCamDir; } } } [HarmonyPatch] public class EventPatch { [HarmonyPatch(typeof(GS_ReadyToStopElevatorRide), "Enter")] [HarmonyPostfix] private static void GS_ReadyToStopElevatorRide_Enter() { Events.RaiseSessionStart(); } [HarmonyPatch(typeof(PLOC_Downed), "SyncEnter")] [HarmonyPostfix] private static void PLOC_Downed_SyncEnter() { Events.RaiseAnyPlayerDeath(); } [HarmonyPatch(typeof(RundownManager), "EndGameSession")] [HarmonyPrefix] private static void EndGameSession() { Events.RaiseSessionEnd(); } [HarmonyPatch(typeof(SNet_SessionHub), "LeaveHub")] [HarmonyPrefix] private static void LeaveHub() { Events.RaiseSessionEnd(); } [HarmonyPatch(typeof(SNet_SessionHub), "OnJoinedLobby")] [HarmonyPrefix] private static void PlayerJoin(SNet_Player player) { Events.RaisePlayerJoinLobby(player); } [HarmonyPatch(typeof(CheckpointManager), "OnStateChange")] [HarmonyPrefix] private static void CheckpointStateChange(pCheckpointState oldState, pCheckpointState newState) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 if ((int)newState.lastInteraction == 2) { Events.RaiseCheckpointReload(); } } [HarmonyPatch(typeof(CM_PageLoadout), "UpdatePlayerList")] [HarmonyPostfix] private static void CM_PageLoadout_UpdatePlayerList() { Events.RaiseLoadoutPlayerListUpdate(); } } [HarmonyPatch] public class PouncerPatch : MonoBehaviour { [HarmonyPatch(typeof(PouncerBehaviour), "Setup")] [HarmonyPostfix] private static void PouncerBehaviour_Setup__Postfix(PouncerBehaviour __instance) { if (!((Object)(object)__instance == (Object)null)) { PouncerTracker.Instance?.RegisterPouncer(__instance); } } } [HarmonyPatch] public class StaminaPatch { private static readonly Regex BpmSpriteRegex = new Regex("", RegexOptions.Compiled); private static float _bpm = 0f; [HarmonyPatch(typeof(PUI_LocalPlayerStatus), "UpdateStamina")] [HarmonyPrefix] private static void Prefix__PUI_LPS_UpdateStamina(ref float stamina) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) SpectateCam? instance = SpectateCam.Instance; if (instance == null || !instance.TargetReady || !SpectateCam.Instance.Active) { return; } SNet_Player sAgent = SpectateCam.Instance.Target.SAgent; if (!sAgent.IsLocal) { StaminaInfo val = default(StaminaInfo); if (sAgent.IsBot) { stamina = 1f; } else if (PeerInfoManager.Supported(sAgent) && StaminaSync.GetStaminaInfo(sAgent, ref val)) { stamina = val.Stamina; } } } [HarmonyPatch(typeof(PUI_LocalPlayerStatus), "UpdateBPM")] [HarmonyPrefix] private static void Prefix__PUI_LPS_UpdateBPM(PUI_LocalPlayerStatus __instance, float stamina) { SpectateCam? instance = SpectateCam.Instance; if (instance != null && instance.Active) { _bpm = Mathf.Lerp((float)__instance.m_BPMMax, (float)__instance.m_BPMMin, stamina); __instance.m_currentBPM = _bpm; __instance.m_BPMLastTextUpdateTime = 0f; } } [HarmonyPatch(typeof(PUI_LocalPlayerStatus), "UpdateBPM")] [HarmonyPostfix] private static void Postfix__PUI_LPS_UpdateBPM(PUI_LocalPlayerStatus __instance, float stamina) { SpectateCam? instance = SpectateCam.Instance; if (instance != null && instance.Active) { SetBpmText((TMP_Text)(object)__instance.m_pulseText, _bpm); } } public static void RevertStaminaBpmDisplay(PUI_LocalPlayerStatus? lps) { if (!((Object)(object)lps == (Object)null)) { PlayerAgent localPlayerAgent = PlayerManager.GetLocalPlayerAgent(); float num = ((localPlayerAgent != null) ? localPlayerAgent.Stamina.Stamina : 1f); float bpm = (lps.m_currentBPM = Mathf.Lerp((float)lps.m_BPMMax, (float)lps.m_BPMMin, num)); lps.m_BPMLastTextUpdateTime = 0f; SetBpmText((TMP_Text)(object)lps.m_pulseText, bpm); } } private static void SetBpmText(TMP_Text pulseText, float bpm) { if (!((Object)(object)pulseText == (Object)null)) { string text = pulseText.text; Match match = BpmSpriteRegex.Match(text); string text2 = (match.Success ? match.Value : ""); string text3 = bpm.ToString("N0"); pulseText.SetText("PULSE:" + text2 + text3, true); } } } [HarmonyPatch] public class TooltipFixPatch { [HarmonyPatch(typeof(CM_Tooltip), "UpdateTooltipSizes")] [HarmonyPrefix] private static bool UpdateTooltipSizes_Prefix(CM_Tooltip __instance) { //IL_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Unknown result type (might be due to invalid IL or missing references) TextMeshPro headerText = __instance.m_headerText; TextMeshPro text = __instance.m_text; UI_SpriteResizer background = __instance.m_background; UI_SpriteResizer outline = __instance.m_outline; RectTransformExtensions.SetLeft(((TMP_Text)text).rectTransform, __instance.PaddingLeft); RectTransformExtensions.SetRight(((TMP_Text)text).rectTransform, __instance.PaddingRight); RectTransformExtensions.SetBottom(((TMP_Text)text).rectTransform, __instance.PaddingBot); bool flag = string.IsNullOrWhiteSpace(((TMP_Text)headerText).text); if (!flag) { RectTransformExtensions.SetLeft(((TMP_Text)headerText).rectTransform, __instance.PaddingLeft); RectTransformExtensions.SetRight(((TMP_Text)headerText).rectTransform, __instance.PaddingRight); RectTransformExtensions.SetTop(((TMP_Text)headerText).rectTransform, __instance.PaddingTop); } ((TMP_Text)text).ForceMeshUpdate(false, false); ((TMP_Text)headerText).ForceMeshUpdate(false, false); float preferredHeight = ((TMP_Text)text).preferredHeight; float preferredHeight2 = ((TMP_Text)headerText).preferredHeight; float preferredWidth = ((TMP_Text)headerText).preferredWidth; float preferredWidth2 = ((TMP_Text)text).preferredWidth; if (flag) { RectTransformExtensions.SetTop(((TMP_Text)text).rectTransform, __instance.PaddingTop); } else { RectTransformExtensions.SetBottom(((TMP_Text)headerText).rectTransform, preferredHeight + __instance.PaddingBot + __instance.HeaderAndTextSpacing); RectTransformExtensions.SetTop(((TMP_Text)text).rectTransform, preferredHeight2 + __instance.PaddingTop + __instance.HeaderAndTextSpacing); } float num = Mathf.Max(preferredWidth2, preferredWidth); if (__instance.TrimWidth && num < __instance.MaxWidth) { float num2 = __instance.MaxWidth - num - __instance.PaddingLeft - __instance.PaddingRight; if ((Object)(object)background != (Object)null) { RectTransformExtensions.SetRight(background.Rect, num2); } if ((Object)(object)outline != (Object)null) { RectTransformExtensions.SetRight(outline.Rect, num2 + __instance.m_outlinePadding); } } else { if ((Object)(object)background != (Object)null) { RectTransformExtensions.SetRight(background.Rect, 0f); } if ((Object)(object)outline != (Object)null) { RectTransformExtensions.SetRight(outline.Rect, __instance.m_outlinePadding); } } float num3 = preferredHeight + preferredHeight2 + __instance.PaddingBot + __instance.PaddingTop + __instance.HeaderAndTextSpacing * 2f; ((RectTransformComp)__instance).SetSize(new Vector2(((RectTransformComp)__instance).GetSize().x, num3)); return false; } } [HarmonyPatch] public class UIPatch { [HarmonyPatch(typeof(PlayerGuiLayer), "UpdateHealth")] [HarmonyPrefix] private static bool PlayerGuiLayer_UpdateHealth(PlayerGuiLayer __instance) { return !(SpectateCam.Instance?.Active ?? false); } [HarmonyPatch(typeof(PlayerGuiLayer), "UpdateInfection")] [HarmonyPrefix] private static bool PlayerGuiLayer_UpdateInfection(PlayerGuiLayer __instance) { return !(SpectateCam.Instance?.Active ?? false); } [HarmonyPatch(typeof(GuiManager), "OnFocusStateChanged")] [HarmonyPostfix] private static void FocusStateChange(GuiManager __instance, eFocusState state) { SpectateCam? instance = SpectateCam.Instance; if (instance != null && instance.Active) { ((RectTransformComp)GuiManager.PlayerLayer.Inventory).SetVisible(false); } } [HarmonyPatch(typeof(PlayerGuiLayer), "UpdateGUIElementsVisibility")] [HarmonyPostfix] private static void UpdateGUIElementsVisibility(GuiManager __instance, eFocusState currentState) { SpectateCam? instance = SpectateCam.Instance; if (instance != null && instance.Active) { ((RectTransformComp)GuiManager.PlayerLayer.Inventory).SetVisible(false); } } [HarmonyPatch(typeof(PUI_BoosterIconActiveDisplay), "UpdateBoosterIconsActiveState")] [HarmonyPrefix] private static bool UpdateBoosterIconActiveState(PUI_BoosterIconActiveDisplay __instance) { SpectateCam? instance = SpectateCam.Instance; if (instance != null && instance.Active) { SpectateUI? instance2 = SpectateUI.Instance; if (instance2 != null && !instance2.InPlayerStatusUpdate) { return false; } } return true; } [HarmonyPatch(typeof(PlayerGuiLayer), "ApplyMovementSway")] [HarmonyPrefix] private static void PlayerGuiLayer_ApplyMovementSway(PlayerGuiLayer __instance, ref Vector3 sway) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Invalid comparison between Unknown and I4 //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) PlayerAgent localPlayerAgent = PlayerManager.GetLocalPlayerAgent(); if ((Object)(object)localPlayerAgent != (Object)null && (int)localPlayerAgent.Locomotion.m_currentStateEnum == 7) { sway = Vector3.zero; } } [HarmonyPatch(typeof(CM_PageMap), "OnEnable")] [HarmonyPostfix] private static void CM_PageMap_OnEnable(CM_PageMap __instance) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: 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_007b: Unknown result type (might be due to invalid IL or missing references) SpectateCam? instance = SpectateCam.Instance; if (instance != null && instance.Active && CellSettingsManager.GetBoolValue((eCellSettingID)307)) { AgentTarget? target = SpectateCam.Instance.Target; int num = (((object)target != null) ? target.SAgent.PlayerSlotIndex() : (-1)); if (num != -1) { Vector3 position = ((Component)((Il2CppArrayBase)(object)__instance.m_syncedPlayers)[num]).transform.position; Vector3 val = __instance.m_mapHolder.transform.InverseTransformPoint(position); Transform transform = __instance.m_mapMover.transform; transform.localPosition -= val; } } } } } namespace Spectate.Patches.Compat { [AttributeUsage(AttributeTargets.Method)] public class CompatPatchAttribute : Attribute { public HarmonyPatchType PatchType { get; } public string TypeName { get; } public string MethodName { get; } public CompatPatchAttribute(HarmonyPatchType patchType, string typeName, string methodName) { //IL_0007: 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) PatchType = patchType; TypeName = typeName; MethodName = methodName; } } public static class CompatPatcher { private static readonly HashSet PatchedTargetGUIDs = new HashSet(); public static bool IsPatched(string targetGuid) { return PatchedTargetGUIDs.Contains(targetGuid); } public static void PatchAll(Harmony h) { Harmony h2 = h; CollectionExtensions.Do((IEnumerable)AccessTools.GetTypesFromAssembly(new StackTrace().GetFrame(1)?.GetMethod()?.ReflectedType?.Assembly), (Action)TryPatchTarget); void TryPatchTarget(Type encapsulator) { //IL_026c: Unknown result type (might be due to invalid IL or missing references) //IL_0272: Invalid comparison between Unknown and I4 //IL_027c: Unknown result type (might be due to invalid IL or missing references) //IL_0282: Invalid comparison between Unknown and I4 //IL_028c: Unknown result type (might be due to invalid IL or missing references) //IL_0292: Invalid comparison between Unknown and I4 //IL_029c: Unknown result type (might be due to invalid IL or missing references) //IL_02a2: Invalid comparison between Unknown and I4 //IL_02ac: Unknown result type (might be due to invalid IL or missing references) //IL_02b2: Invalid comparison between Unknown and I4 //IL_02d1: Unknown result type (might be due to invalid IL or missing references) //IL_02e5: Unknown result type (might be due to invalid IL or missing references) //IL_02f9: Unknown result type (might be due to invalid IL or missing references) //IL_030d: Unknown result type (might be due to invalid IL or missing references) //IL_0321: Unknown result type (might be due to invalid IL or missing references) //IL_0345: Unknown result type (might be due to invalid IL or missing references) CompatTargetAttribute customAttribute = encapsulator.GetCustomAttribute(); if (customAttribute != null) { if (!((BaseChainloader)(object)IL2CPPChainloader.Instance).Plugins.ContainsKey(customAttribute.TargetGuid)) { Logger.Info($"Skipping compatibility patch [{encapsulator.FullName}] for {customAttribute.TargetGuid} since it is not loaded."); } else { foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(encapsulator)) { CompatPatchAttribute customAttribute2 = declaredMethod.GetCustomAttribute(); if (customAttribute2 != null) { Type type = AccessTools.TypeByName(customAttribute2.TypeName); if (type == null) { Logger.Error($"Failed to find type \"{customAttribute2.TypeName}\" for compat patch {declaredMethod.Name} in {encapsulator.FullName}!"); } else { MethodInfo methodInfo = AccessTools.Method(type, customAttribute2.MethodName, (Type[])null, (Type[])null); if (methodInfo == null) { Logger.Error($"Failed to find method \"{customAttribute2.TypeName}\" => \"{customAttribute2.MethodName}\" for compat patch {declaredMethod.Name} in {encapsulator.FullName}!"); } else { try { RuntimeHelpers.PrepareMethod(methodInfo.MethodHandle); } catch (Exception ex) { Logger.Warn($"PrepareMethod failed for {methodInfo.DeclaringType?.FullName}::{methodInfo.Name}: {ex.Message}"); } MethodInfo methodInfo2 = (((int)customAttribute2.PatchType == 1) ? declaredMethod : null); MethodInfo methodInfo3 = (((int)customAttribute2.PatchType == 2) ? declaredMethod : null); MethodInfo methodInfo4 = (((int)customAttribute2.PatchType == 3) ? declaredMethod : null); MethodInfo methodInfo5 = (((int)customAttribute2.PatchType == 4) ? declaredMethod : null); MethodInfo methodInfo6 = (((int)customAttribute2.PatchType == 6) ? declaredMethod : null); object obj = h2; obj = methodInfo; obj = (object)((!(methodInfo2 != null)) ? ((HarmonyMethod)null) : new HarmonyMethod(methodInfo2)); obj = (object)((!(methodInfo3 != null)) ? ((HarmonyMethod)null) : new HarmonyMethod(methodInfo3)); obj = (object)((!(methodInfo4 != null)) ? ((HarmonyMethod)null) : new HarmonyMethod(methodInfo4)); obj = (object)((!(methodInfo5 != null)) ? ((HarmonyMethod)null) : new HarmonyMethod(methodInfo5)); obj = (object)((!(methodInfo6 != null)) ? ((HarmonyMethod)null) : new HarmonyMethod(methodInfo6)); ((Harmony)obj).Patch((MethodBase)obj, (HarmonyMethod)obj, (HarmonyMethod)obj, (HarmonyMethod)obj, (HarmonyMethod)obj, (HarmonyMethod)obj); Logger.Info($"Patched {customAttribute2.PatchType} [{declaredMethod.Name}] to {customAttribute2.TypeName}::{customAttribute2.MethodName} for compat with {customAttribute.TargetGuid}"); } } } } PatchedTargetGUIDs.Add(customAttribute.TargetGuid); } } } } } [AttributeUsage(AttributeTargets.Class)] public class CompatTargetAttribute : Attribute { public string TargetGuid { get; } public CompatTargetAttribute(string targetGuid) { TargetGuid = targetGuid; } } } namespace Spectate.Patches.Compat.Targets { [HarmonyPatch] public class ECC_Compat { [HarmonyPatch(typeof(EMPPlayerHudHandler), "DeviceOn")] [HarmonyPrefix] private static bool Prefix__EMPPlayerHudHandler_DeviceOn() { return !(SpectateCam.Instance?.Active ?? false); } [HarmonyPatch(typeof(EMPPlayerHudHandler), "FlickerDevice")] [HarmonyPrefix] private static bool Prefix__EMPPlayerHudHandler_FlickerDevice() { return !(SpectateCam.Instance?.Active ?? false); } } [CompatTarget("GTFO.EECustomization")] public class ECC_Compat__Reflection { [CompatPatch(/*Could not decode attribute arguments.*/)] private static bool Prefix__EMPPlayerHudHandler_DeviceOn() { return !(SpectateCam.Instance?.Active ?? false); } [CompatPatch(/*Could not decode attribute arguments.*/)] private static bool Prefix__EMPPlayerHudHandler_FlickerDevice() { return !(SpectateCam.Instance?.Active ?? false); } } [HarmonyPatch] public class EOSAmor_Compat { [HarmonyPatch(typeof(EMPPlayerHudHandler), "DeviceOn")] [HarmonyPrefix] private static bool Prefix__EMPPlayerHudHandler_DeviceOn() { return !(SpectateCam.Instance?.Active ?? false); } [HarmonyPatch(typeof(EMPPlayerHudHandler), "FlickerDevice")] [HarmonyPrefix] private static bool Prefix__EMPPlayerHudHandler_FlickerDevice() { return !(SpectateCam.Instance?.Active ?? false); } } [CompatTarget("Amor.ExcellentObjectiveSetup")] public class EOSAmor_Compat__Reflection { [CompatPatch(/*Could not decode attribute arguments.*/)] private static bool Prefix__EMPPlayerHudHandler_DeviceOn() { return !(SpectateCam.Instance?.Active ?? false); } [CompatPatch(/*Could not decode attribute arguments.*/)] private static bool Prefix__EMPPlayerHudHandler_FlickerDevice() { return !(SpectateCam.Instance?.Active ?? false); } } [HarmonyPatch] public class EOSExtEMP_Compat { [HarmonyPatch(typeof(EMPPlayerHudHandler), "OnTick")] [HarmonyPrefix] private static bool EMPPlayerHudHandler_OnTick(object __instance) { return !(SpectateCam.Instance?.Active ?? false); } } [CompatTarget("Inas.EOSExt.EMP")] public class EOSExtEMP_Compat__Reflection { [CompatPatch(/*Could not decode attribute arguments.*/)] private static bool EMPPlayerHudHandler_OnTick(object __instance) { return !(SpectateCam.Instance?.Active ?? false); } } } namespace Spectate.Network { [HarmonyPatch] public class Net { private const ushort NetKeyBytes = 21328; private const int SNetChannel = 1; private static readonly Dictionary> MessageHandlers = new Dictionary>(); public static bool RegisterHandler(byte packetId, Action handler) { if (MessageHandlers.TryAdd(packetId, handler)) { return true; } Logger.Error($"[Net] Handler for packetId={packetId} is already registered to {MessageHandlers[packetId].Method.DeclaringType?.FullName ?? "???"}"); return false; } public static bool UnregisterHandler(byte packetId) { return MessageHandlers.Remove(packetId); } [HarmonyPatch(typeof(SNet_Replication), "RecieveBytes")] [HarmonyPrefix] private static bool OnReceive(Il2CppStructArray bytes, uint size, ulong messagerID) { if (!IsNetMessage(bytes)) { return true; } byte b = ((Il2CppArrayBase)(object)bytes)[2]; if (MessageHandlers.TryGetValue(b, out Action value)) { byte[] array = new byte[size - 3]; Array.Copy(Il2CppArrayBase.op_Implicit((Il2CppArrayBase)(object)bytes), 3L, array, 0L, size - 3); SNet_Player playerByID = NetHelper.GetPlayerByID(messagerID); value(array, playerByID); } else { Logger.Error($"Net (key={21328:X}) received message for packetId={b} with no handler, are we on the same version?"); } return false; } public static void SendBytes(byte[] data, byte packetId, SNet_Player target) { SendBytes(PrepareData(data, packetId), target); } public static void SendBytes(byte[] data, byte packetId, List targets) { SendBytes(PrepareData(data, packetId), targets); } private static void SendBytes(byte[] bytes, SNet_Player target) { Il2CppStructArray val = new Il2CppStructArray(bytes); SNet.Core.SendBytes(val, (SNet_SendQuality)2, 1, target); } private static void SendBytes(byte[] bytes, List targets) { Il2CppStructArray val = new Il2CppStructArray(bytes); SNet.Core.SendBytes(val, (SNet_SendQuality)2, 1, targets); } private static byte[] PrepareData(byte[] data, byte packetId = 0) { byte[] array = new byte[3 + data.Length]; ((Il2CppArrayBase)(object)BitConverter.GetBytes((ushort)21328)).CopyTo(array, 0); array[2] = packetId; data.CopyTo(array, 3); return array; } private static bool IsNetMessage(Il2CppStructArray bytes) { if (((Il2CppArrayBase)(object)bytes).Length < 2) { return false; } return BitConverter.ToUInt16(bytes, 0) == 21328; } } public static class NetHelper { public static SNet_Player? GetPlayerByID(ulong id) { Enumerator enumerator = SNet.LobbyPlayers.GetEnumerator(); while (enumerator.MoveNext()) { SNet_Player current = enumerator.Current; if (current.Lookup == id) { return current; } } return null; } public static void InvokeWithAllPlayers(Action sendMethod) { if (!SNet.IsInLobby) { return; } Enumerator enumerator = SNet.Lobby.Players.GetEnumerator(); while (enumerator.MoveNext()) { SNet_Player current = enumerator.Current; if (!((Object)(object)current == (Object)null) && !current.IsBot && !current.IsLocal) { sendMethod(current); } } } } public readonly record struct PlugVersion { public readonly byte Major; public readonly byte Minor; public readonly byte Patch; public PlugVersion() { Major = 0; Minor = 0; Patch = 0; } public PlugVersion(byte major, byte minor, byte patch) { Major = 0; Minor = 0; Patch = 0; Major = major; Minor = minor; Patch = patch; } public PlugVersion(string version) { Major = 0; Minor = 0; Patch = 0; string[] array = version.Split('.'); byte result; byte result2; byte result3; if (array.Length != 3) { Logger.Error("Invalid version string '" + version + "', expected format 'major.minor.patch'."); } else if (!byte.TryParse(array[0], out result)) { Logger.Error($"Invalid major version '{array[0]}' in version string '{version}'."); } else if (!byte.TryParse(array[1], out result2)) { Logger.Error($"Invalid minor version '{array[1]}' in version string '{version}'."); } else if (!byte.TryParse(array[2], out result3)) { Logger.Error($"Invalid patch version '{array[2]}' in version string '{version}'."); } else { Major = result; Minor = result2; Patch = result3; } } public PlugVersion(byte[] version) { Major = 0; Minor = 0; Patch = 0; if (version.Length < 3) { Logger.Error($"Invalid version byte array of length {version.Length}, expected at least 3."); } else { Major = version[0]; Minor = version[1]; Patch = version[2]; } } public PlugVersion(byte[] data, int offset) { Major = 0; Minor = 0; Patch = 0; if (data.Length < offset + 3) { Logger.Error($"Invalid version byte array of length {data.Length} with offset {offset}, expected at least {offset + 3}."); } else { Major = data[offset]; Minor = data[offset + 1]; Patch = data[offset + 2]; } } public byte[] ToByteArray() { return new byte[3] { Major, Minor, Patch }; } public static bool operator >(PlugVersion a, PlugVersion b) { if (a.Major != b.Major) { return a.Major > b.Major; } if (a.Minor != b.Minor) { return a.Minor > b.Minor; } return a.Patch > b.Patch; } public static bool operator <(PlugVersion a, PlugVersion b) { if (a.Major != b.Major) { return a.Major < b.Major; } if (a.Minor != b.Minor) { return a.Minor < b.Minor; } return a.Patch < b.Patch; } public override string ToString() { return $"{Major}.{Minor}.{Patch}"; } } } namespace Spectate.Network.Impl { public static class NetImpl { public const byte PacketIdxSendSpectatorTargetState = 254; public static void SendSpectateTargetState(bool spectating, SNet_Player player) { if (!((Object)(object)player == (Object)null) && !player.IsBot) { Net.SendBytes(new byte[1] { (byte)(spectating ? 1u : 0u) }, 254, player); } } } [RegisterIl2Cpp] public class PeerInfoManager : MonoBehaviour { public enum PeerSupport { Unknown, Supported, NotSupported } public class PeerInfo { public PeerSupport Support; public PlugVersion PlugVersion = new PlugVersion(); public SNet_Player? Player; public byte RequestCount { get; private set; } public bool MaxRequestsReached => RequestCount >= 5; public void IncrementRequestCount() { RequestCount++; } } public const byte PacketId = byte.MaxValue; public const byte KeyRequest = 105; public const byte KeyResponse = 103; public const int MaxRequestCount = 5; private const float PeerInfoUpdateInterval = 2f; private float _timeSinceLastUpdate; public static PeerInfoManager? Instance { get; private set; } private Dictionary PeerInfos { get; } = new Dictionary(); public static event Action? OnPeerInfoUpdated; private void Awake() { if ((Object)(object)Instance == (Object)null) { Instance = this; } else if ((Object)(object)Instance != (Object)(object)this) { Object.Destroy((Object)(object)this); Logger.Warn("Multiple instances of LobbyInfo detected, this should not happen!"); return; } Net.RegisterHandler(byte.MaxValue, HandlePacket); Events.OnPlayerJoinLobby += SendPeerInfoRequestSafe; } private void OnDestroy() { if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } Net.UnregisterHandler(byte.MaxValue); Events.OnPlayerJoinLobby -= SendPeerInfoRequestSafe; } private void Update() { _timeSinceLastUpdate += Time.deltaTime; if (_timeSinceLastUpdate >= 2f) { UpdateLobbyInfo(); _timeSinceLastUpdate = 0f; } } public static bool Supported(SNet_Player player) { if (player.IsLocal) { return true; } if (player.IsBot) { return false; } if ((Object)(object)Instance == (Object)null) { return false; } if (Instance.PeerInfos.TryGetValue(player.Lookup, out PeerInfo value)) { return value.Support == PeerSupport.Supported; } return false; } public static bool SupportUnknown(SNet_Player player) { if (player.IsLocal || player.IsBot) { return false; } if ((Object)(object)Instance == (Object)null) { return false; } if (Instance.PeerInfos.TryGetValue(player.Lookup, out PeerInfo value)) { return value.Support == PeerSupport.Unknown; } return false; } public static bool TryGetPeerInfo(SNet_Player player, [NotNullWhen(true)] out PeerInfo? info) { if ((Object)(object)player == (Object)null) { info = null; return false; } if (player.IsLocal) { info = new PeerInfo { Support = PeerSupport.Supported, PlugVersion = Plugin.PlugVersion, Player = player }; return true; } if ((Object)(object)Instance == (Object)null) { info = null; return false; } return Instance.PeerInfos.TryGetValue(player.Lookup, out info); } private void UpdateLobbyInfo() { SNet_SessionHub sessionHub = SNet.SessionHub; foreach (ulong item in PeerInfos.Keys.ToList()) { if (!sessionHub.IsPlayerInHub(PeerInfos[item].Player)) { DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(58, 2); defaultInterpolatedStringHandler.AppendLiteral("Player '"); SNet_Player? player = PeerInfos[item].Player; defaultInterpolatedStringHandler.AppendFormatted(((player != null) ? player.NickName : null) ?? "???"); defaultInterpolatedStringHandler.AppendLiteral("' ("); defaultInterpolatedStringHandler.AppendFormatted(item); defaultInterpolatedStringHandler.AppendLiteral(") is not in lobby anymore, removing their info."); Logger.Debug(defaultInterpolatedStringHandler.ToStringAndClear()); PeerInfos.Remove(item); PeerInfoManager.OnPeerInfoUpdated?.Invoke(); } } if (SNet.IsInLobby) { NetHelper.InvokeWithAllPlayers(RequestInfoFromPlayer); } } private void RequestInfoFromPlayer(SNet_Player player) { if (PeerInfos.TryGetValue(player.Lookup, out PeerInfo value)) { if (value.Support != 0) { return; } if (value.MaxRequestsReached) { value.Support = PeerSupport.NotSupported; PeerInfoManager.OnPeerInfoUpdated?.Invoke(); Logger.Info($"Player '{player.GetName()}' ({player.Lookup}) reached max request count without response, marking as not supported."); return; } } SendPeerInfoRequestSafe(player); } private void SendPeerInfoRequestSafe(SNet_Player player) { if (!player.IsBot && !player.IsLocal) { if (!PeerInfos.TryGetValue(player.Lookup, out PeerInfo value)) { value = new PeerInfo(); PeerInfos[player.Lookup] = value; } SendPeerInfoRequest(player); value.IncrementRequestCount(); PeerInfo peerInfo = value; if (peerInfo.Player == null) { peerInfo.Player = player; } PeerInfoManager.OnPeerInfoUpdated?.Invoke(); Logger.Debug($"Sent request for info to player '{player.GetName()}' ({player.Lookup}), RequestCount({value.RequestCount} of {5})"); } } private static void SendPeerInfoRequest(SNet_Player player) { if (!((Object)(object)player == (Object)null) && !player.IsBot) { Net.SendBytes(new byte[1] { 105 }, byte.MaxValue, player); } } private static void SendPeerInfoResponse(SNet_Player player) { if (!((Object)(object)player == (Object)null) && !player.IsBot) { byte[] array = new byte[4] { 103, 0, 0, 0 }; Array.Copy(Plugin.PlugVersion.ToByteArray(), 0, array, 1, 3); Net.SendBytes(array, byte.MaxValue, player); } } private void HandlePacket(byte[] data, SNet_Player? sender) { if ((Object)(object)sender == (Object)null) { Logger.Error("Received client info exchange packet with null sender, ignoring!"); return; } switch (data[0]) { case 105: SendPeerInfoResponse(sender); break; case 103: { if (data.Length < 4) { Logger.Error($"Received peer info response of invalid length {data.Length} from {sender.NickName} ({sender.Lookup}), expected 4."); break; } if (!PeerInfos.TryGetValue(sender.Lookup, out PeerInfo value)) { value = new PeerInfo(); PeerInfos[sender.Lookup] = value; } value.Support = PeerSupport.Supported; value.PlugVersion = new PlugVersion(data, 1); value.Player = sender; PeerInfoManager.OnPeerInfoUpdated?.Invoke(); Logger.Info($"Received peer info response from player '{sender.GetName()}' ({sender.Lookup}) version={value.PlugVersion}"); break; } } } } } namespace Spectate.Interop { [AttributeUsage(AttributeTargets.Class, Inherited = false)] public class RegisterIl2CppAttribute : Attribute { } } namespace Spectate.Config { public enum ConfigEntryRule { Min, Max } public class ConfigEntryExtended { private Dictionary _rules = new Dictionary(); private ConfigEntry _entry; public T Value { get { return _entry.Value; } set { _entry.Value = Enforce(value); } } public object BoxedValue { get { return ((ConfigEntryBase)_entry).BoxedValue; } set { ((ConfigEntryBase)_entry).BoxedValue = Enforce((T)value); } } public ConfigEntryExtended(ConfigEntry entry) { _entry = entry; } public bool AddRule(ConfigEntryRule rule, T ruleValue) { if (_rules.TryAdd(rule, ruleValue)) { Enforce(rule); return true; } return false; } private void Enforce() { T val = Enforce(_entry.Value); if (val != null && !val.Equals(_entry.Value)) { _entry.Value = val; } } private T Enforce(T val) { foreach (var (configEntryRule2, val3) in _rules) { switch (configEntryRule2) { case ConfigEntryRule.Min: if (Comparer.Default.Compare(val, val3) < 0) { val = val3; } break; case ConfigEntryRule.Max: if (Comparer.Default.Compare(val, val3) > 0) { val = val3; } break; } } return val; } private void Enforce(ConfigEntryRule rule) { T value = _entry.Value; value = Enforce(value, rule); if (value != null && !value.Equals(_entry.Value)) { _entry.Value = value; } } private T Enforce(T val, ConfigEntryRule rule) { if (!_rules.TryGetValue(rule, out var value)) { return val; } switch (rule) { case ConfigEntryRule.Min: if (Comparer.Default.Compare(val, value) < 0) { val = value; } break; case ConfigEntryRule.Max: if (Comparer.Default.Compare(val, value) > 0) { val = value; } break; } return val; } public static implicit operator ConfigEntryExtended(ConfigEntry entry) { return new ConfigEntryExtended(entry); } } internal static class ConfigMgr { private static readonly ConfigFile Conf; private static bool _configDirty; private static readonly FileSystemWatcher? ConfigWatcher; private static readonly Dictionary DevOptsStr2Enum; private static readonly ConfigEntryExtended DebugConf; private static readonly ConfigEntryExtended CompatPathUseReflectionConf; private static readonly ConfigEntryExtended Il2CppTypeDiscoveryForRegUseReflectionConf; private static readonly ConfigEntryExtended DevOptsConf; private static eDevOpts _devOptsVal; private static readonly ConfigEntryExtended SwitchOnDeathConf; private static readonly ConfigEntryExtended DefaultFreecamViewConf; private static readonly ConfigEntryExtended NoPosLerpOnSwitchTargetConf; private static readonly ConfigEntryExtended ShowPlayerBodyWhenSpectatingConf; private static readonly ConfigEntryExtended ShowLocalPlayerNavMarkerConf; private static readonly ConfigEntryExtended PreferSpectateAliveConf; private static readonly ConfigEntryExtended AutoTransitionToFollowViewConf; private static readonly ConfigEntryExtended AutoTransitionDelayConf; private static bool _autoTransitionToFollowViewCache; private static readonly ConfigEntryExtended ScrollSensitivityConf; private static readonly ConfigEntryExtended FreecamSensitivityConf; private static readonly ConfigEntryExtended FreecamLerpGainConf; private static readonly ConfigEntryExtended CameraXZLerpGainConf; private static readonly ConfigEntryExtended CameraYLerpGainConf; private static readonly ConfigEntryExtended CameraDistanceConf; private static readonly ConfigEntryExtended CameraOrbitVerticalOffsetConf; private static readonly ConfigEntryExtended CameraPitchAngleDegConf; private static float _cameraDistanceCache; private static float _cameraOrbitVerticalOffsetCache; private static float _cameraPitchAngleDegCache; private static readonly Dictionary> KeybindConfs; private static readonly Dictionary KeybindSettingConfs; private static readonly ConfigEntryExtended HideWmConf; public static bool Processed { get; private set; } public static bool Debug => DebugConf.Value; public static bool CompatPatchUseReflection => CompatPathUseReflectionConf.Value; public static bool Il2CppTypeDiscoveryForRegUseReflection => Il2CppTypeDiscoveryForRegUseReflectionConf.Value; public static bool SwitchOnDeath => SwitchOnDeathConf.Value; public static bool DefaultFreecamView => DefaultFreecamViewConf.Value; public static bool NoPosLerpOnSwitchTarget => NoPosLerpOnSwitchTargetConf.Value; public static bool ShowPlayerBodyWhenSpectating => ShowPlayerBodyWhenSpectatingConf.Value; public static bool ShowLocalPlayerNavMarker => ShowLocalPlayerNavMarkerConf.Value; public static bool PreferSpectateAlive => PreferSpectateAliveConf.Value; public static float AutoTransitionDelay => AutoTransitionDelayConf.Value; public static bool AutoTransitionToFollowView { get { return _autoTransitionToFollowViewCache; } set { _autoTransitionToFollowViewCache = value; _configDirty = true; } } public static float ScrollSensitivity => ScrollSensitivityConf.Value; public static float FreecamSensitivity => FreecamSensitivityConf.Value; public static float FreecamLerpGain => FreecamLerpGainConf.Value; public static float CameraXZLerpGain => CameraXZLerpGainConf.Value; public static float CameraYLerpGain => CameraYLerpGainConf.Value; public static float CameraDistance { get { return _cameraDistanceCache; } set { _cameraDistanceCache = value; _configDirty = true; } } public static float CameraOrbitVerticalOffset { get { return _cameraOrbitVerticalOffsetCache; } set { _cameraOrbitVerticalOffsetCache = value; _configDirty = true; } } public static float CameraPitchAngleDeg { get { return _cameraPitchAngleDegCache; } set { _cameraPitchAngleDegCache = value; _configDirty = true; } } public static bool HideWm => HideWmConf.Value; public static bool DevEnables(eDevOpts opt) { return _devOptsVal.HasFlag(opt); } public static KeyCode GetKeybind(SpectateInputAction action) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) if (KeybindConfs.TryGetValue(action, out ConfigEntryExtended value)) { return value.Value; } Logger.Error($"GetKeybind: no config found for action {action}"); return (KeyCode)0; } public static void Process() { Logger.Info($"debug={Debug}"); _cameraDistanceCache = CameraDistanceConf.Value; _cameraOrbitVerticalOffsetCache = CameraOrbitVerticalOffsetConf.Value; _cameraPitchAngleDegCache = CameraPitchAngleDegConf.Value; _autoTransitionToFollowViewCache = AutoTransitionToFollowViewConf.Value; _devOptsVal = eDevOpts.None; string[] array = DevOptsConf.Value.Split(' ', StringSplitOptions.RemoveEmptyEntries); foreach (string text in array) { if (DevOptsStr2Enum.TryGetValue(text.Trim().ToLower(), out var value)) { _devOptsVal |= value; } } SpectateUI.Instance?.MarkUIDirty(); Processed = true; } static ConfigMgr() { //IL_0099: 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_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Expected O, but got Unknown //IL_06d4: Unknown result type (might be due to invalid IL or missing references) Processed = false; _configDirty = false; DevOptsStr2Enum = new Dictionary { ["--any"] = eDevOpts.AllowSpectatingAnytime, ["--exp"] = eDevOpts.ExperimentalFeatures, ["--reflect-patch"] = eDevOpts.CompatPatchUsingReflection }; _devOptsVal = eDevOpts.None; _autoTransitionToFollowViewCache = false; _cameraDistanceCache = float.NaN; _cameraOrbitVerticalOffsetCache = float.NaN; _cameraPitchAngleDegCache = float.NaN; KeybindConfs = new Dictionary>(); Dictionary obj = new Dictionary { [SpectateInputAction.ToggleSpectate] = new KeybindSetting { Name = "Toggle Spectate", Description = "toggle spectate active/inactive", DefaultKey = (KeyCode)118 }, [SpectateInputAction.ToggleFreecam] = new KeybindSetting { Name = "Toggle Free-Look View", Description = "toggle free-look spectate view on/off", DefaultKey = (KeyCode)102 }, [SpectateInputAction.ToggleAutoFollow] = new KeybindSetting { Name = "Toggle Auto Follow View", Description = "toggle automatic transition to temporary follow view on/off", DefaultKey = (KeyCode)116 } }; KeybindSetting value = new KeybindSetting { Name = "Toggle Spectate Menu", Description = "toggle spectate action menu on/off", DefaultKey = (KeyCode)92 }; obj[SpectateInputAction.ToggleMenu] = value; KeybindSettingConfs = obj; string text = "Spectate.cfg"; string text2 = Path.Combine(Paths.ConfigPath, text); Logger.Info("cfgPath = " + text2); Conf = new ConfigFile(text2, true); ConfigWatcher = new FileSystemWatcher(Paths.ConfigPath, text) { NotifyFilter = NotifyFilters.LastWrite, EnableRaisingEvents = true }; ConfigWatcher.Changed += async delegate { ConfigWatcher.EnableRaisingEvents = false; await Task.Delay(500); Logger.Debug("Reloading config file due to external change..."); Conf.Reload(); await Task.Delay(250); Process(); await Task.Delay(250); ConfigWatcher.EnableRaisingEvents = true; }; int num = 1; string text3 = $"({num++}) Behavior"; SwitchOnDeathConf = Conf.Bind(text3, "Switch on Death", false, "automatically switch to spectate upon death"); DefaultFreecamViewConf = Conf.Bind(text3, "Default to Free-Look View", true, "start game sessions with free-look enabled by default"); NoPosLerpOnSwitchTargetConf = Conf.Bind(text3, "No Position Smoothing on Target Switch", false, "disable camera position smoothing when switching spectate targets in free-look"); ShowLocalPlayerNavMarkerConf = Conf.Bind(text3, "Show Local Player Nav Marker when Spectating", true, "show a nav marker to indicate the local player's position when spectating"); ShowPlayerBodyWhenSpectatingConf = Conf.Bind(text3, "Show Local Player Body when Spectating", true, "render the local player's body when spectating in third-person views"); PreferSpectateAliveConf = Conf.Bind(text3, "Prefer Spectating Alive Players", true, "automatically switch to an alive player when current spectate target dies; mouse prev/next target selection will only select alive players"); AutoTransitionToFollowViewConf = Conf.Bind(text3, "Auto Transition to Temporary Follow View", true, "automatically transition to temporary follow view after idling in free-look"); AutoTransitionDelayConf = Conf.Bind(text3, "Auto Transition Delay", 2f, "delay after no mouse input in seconds before automatically transitioning to temporary follow view (min: 0.2)"); AutoTransitionDelayConf.AddRule(ConfigEntryRule.Min, 0.2f); text3 = $"({num++}) Sensitivity"; ScrollSensitivityConf = Conf.Bind(text3, "Scroll Sensitivity", 0.5f, "sensitivity for scroll wheel based adjustments (camera distance, camera pitch, orbit center offset) (min: 0.01)"); ScrollSensitivityConf.AddRule(ConfigEntryRule.Min, 0.01f); FreecamSensitivityConf = Conf.Bind(text3, "Free-Look Sensitivity", 1f, "sensitivity for free-look mouse movement (min: 0.01)"); FreecamSensitivityConf.AddRule(ConfigEntryRule.Min, 0.01f); FreecamLerpGainConf = Conf.Bind(text3, "Free-Look Smoothing Rate", 6f, "rate at which camera snaps to target: higher is snappier, lower is smoother (min: 1.0)"); FreecamLerpGainConf.AddRule(ConfigEntryRule.Min, 1f); CameraXZLerpGainConf = Conf.Bind(text3, "Camera XZ-Position Smoothing Rate", 15f, "rate at which camera horizontal position snaps to target: higher is snappier, lower is smoother (min: 7.0)"); CameraXZLerpGainConf.AddRule(ConfigEntryRule.Min, 7f); CameraYLerpGainConf = Conf.Bind(text3, "Camera Y-Position Smoothing Rate", 11f, "rate at which camera vertical position snaps to target: higher is snappier, lower is smoother (min: 7.0)"); CameraYLerpGainConf.AddRule(ConfigEntryRule.Min, 7f); text3 = $"({num++}) Camera Settings "; CameraOrbitVerticalOffsetConf = Conf.Bind(text3, "Camera Orbit Vertical Offset", 0.325f, $"vertical offset of the camera orbit center/point from the player's position (min: {-1f:F2}, max: {5f:F2})"); CameraOrbitVerticalOffsetConf.AddRule(ConfigEntryRule.Min, -1f); CameraOrbitVerticalOffsetConf.AddRule(ConfigEntryRule.Max, 5f); CameraDistanceConf = Conf.Bind(text3, "Camera Distance", 0.625f, $"distance of the camera from the orbit point (min: {0.1f:F2}, max: {5f:F2})"); CameraDistanceConf.AddRule(ConfigEntryRule.Min, 0.1f); CameraDistanceConf.AddRule(ConfigEntryRule.Max, 5f); CameraPitchAngleDegConf = Conf.Bind(text3, "Camera Pitch Angle (deg)", -18.75f, $"pitch angle of the camera in degrees (min: {-89f:F2}, max: {89f:F2})"); CameraPitchAngleDegConf.AddRule(ConfigEntryRule.Min, -89f); CameraPitchAngleDegConf.AddRule(ConfigEntryRule.Max, 89f); text3 = $"({num++}) Keybinds"; foreach (KeyValuePair keybindSettingConf in KeybindSettingConfs) { keybindSettingConf.Deconstruct(out var key, out value); SpectateInputAction key2 = key; KeybindSetting keybindSetting = value; KeybindConfs[key2] = Conf.Bind(text3, keybindSetting.Name, keybindSetting.DefaultKey, keybindSetting.Description); } text3 = $"({num++}) Misc."; HideWmConf = Conf.Bind(text3, "Hide Version Watermark", false, "hide the version watermark on the bottom right of the screen"); text3 = "(Y) Experimental Features"; text3 = "(Z) Dev"; DebugConf = Conf.Bind(text3, "Enable Debug Logs", false, "debug logging"); Il2CppTypeDiscoveryForRegUseReflectionConf = Conf.Bind(text3, "Use Reflection for Type Discovery", true, "Whether to use reflection for type discovery (currently for registering type in il2cpp specifically)"); CompatPathUseReflectionConf = Conf.Bind(text3, "Apply Compatibility Patches Using Reflection", false, "Applies compatibility patches using direct reflection instead of Harmony. This has been observed to cause crashes with UE and FloLib."); DevOptsConf = Conf.Bind(text3, "Dev Options", "", "if you know you know"); } internal static void WriteConfigIfDirty() { if (_configDirty && Processed) { CameraDistanceConf.Value = _cameraDistanceCache; CameraOrbitVerticalOffsetConf.Value = _cameraOrbitVerticalOffsetCache; CameraPitchAngleDegConf.Value = _cameraPitchAngleDegCache; AutoTransitionToFollowViewConf.Value = _autoTransitionToFollowViewCache; Conf.Save(); _configDirty = false; } } } [Flags] public enum eDevOpts { None = 0, AllowSpectatingAnytime = 1, ExperimentalFeatures = 2, CompatPatchUsingReflection = 4 } public struct KeybindSetting { public string Name; public string Description; public KeyCode DefaultKey; } public enum SpectateInputAction { None, ToggleSpectate, ToggleFreecam, ToggleAutoFollow, ToggleMenu } } namespace Spectate.Assets { public static class AssetBundleLoader { private static readonly Dictionary Cache = new Dictionary(); public static AssetBundle? Load(string bundlePath) { if (Cache.TryGetValue(bundlePath, out AssetBundle value)) { return value; } string text = Path.Combine(Paths.BepInExRootPath, "Assets", "Spectate", bundlePath); Logger.Debug($"Loading bundle at '{bundlePath}' ==resolved=> '{text}'"); AssetBundle val = AssetBundle.LoadFromFile(text); if ((Object)(object)val == (Object)null) { Logger.Error("Failed to load bundle at " + bundlePath); return null; } Cache[bundlePath] = val; return val; } public static T? LoadAsset(string bundlePath, string assetPath) where T : Il2CppObjectBase { AssetBundle? obj = Load(bundlePath); if (obj == null) { return default(T); } Object obj2 = obj.LoadAsset(assetPath); if (obj2 == null) { return default(T); } return ((Il2CppObjectBase)obj2).TryCast(); } public static void Unload(string bundlePath, bool unloadAllObjects = false) { if (Cache.TryGetValue(bundlePath, out AssetBundle value)) { value.Unload(unloadAllObjects); Cache.Remove(bundlePath); } } public static void UnloadAll(bool unloadAllObjects = false) { foreach (AssetBundle value in Cache.Values) { value.Unload(unloadAllObjects); } Cache.Clear(); } } public static class AssetHelper { public static Sprite CreateSpriteFromTexture2D(Texture2D texture, float unitSize = 1f) { //IL_0019: 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) return Sprite.Create(texture, new Rect(0f, 0f, (float)((Texture)texture).width, (float)((Texture)texture).height), new Vector2(0.5f, 0.5f), (float)((Texture)texture).width / unitSize); } } public sealed class EmojiUV { [JsonProperty("u0")] public float U0; [JsonProperty("v0")] public float V0; [JsonProperty("u1")] public float U1; [JsonProperty("v1")] public float V1; public Rect ToRect() { //IL_002c: Unknown result type (might be due to invalid IL or missing references) return new Rect(U0, 1f - V1, U1 - U0, V1 - V0); } } public sealed class EmojiEntry { [JsonProperty("id")] public int Id; [JsonProperty("page")] public int Page; [JsonProperty("name")] public string Name = ""; [JsonProperty("uv")] public EmojiUV UV = new EmojiUV(); } public sealed class EmojiAtlasPage { [JsonProperty("page")] public int Page; [JsonProperty("atlas_size")] public int AtlasSize; [JsonProperty("cell_size")] public int CellSize; [JsonProperty("glyph_size")] public int GlyphSize; [JsonProperty("padding")] public int Padding; [JsonProperty("grid_width")] public int GridWidth; [JsonProperty("emojis")] public List Emojis = new List(); } public sealed class EmojiAtlasManifest { [JsonProperty("num_pages")] public int NumPages; [JsonProperty("atlas_size")] public int AtlasSize; [JsonProperty("cell_size")] public int CellSize; [JsonProperty("glyph_size")] public int GlyphSize; [JsonProperty("padding")] public int Padding; [JsonProperty("grid_width")] public int GridWidth; [JsonProperty("total_emojis")] public int TotalEmojis; [JsonProperty("pages")] public List Pages = new List(); } public static class EmojiLibrary { private const string BundlePath = "emojis"; private const string ManifestPath = "Assets/Emojis/manifest.json"; private const string PagePathFmt = "Assets/Emojis/atlas_{0:000}"; private static EmojiAtlasManifest? _manifest; private static readonly Dictionary _textures = new Dictionary(); private static readonly Dictionary _pages = new Dictionary(); private static readonly Dictionary _byName = new Dictionary(); private static readonly Dictionary _byId = new Dictionary(); public static bool IsLoaded { get; private set; } public static void Load() { if (IsLoaded) { return; } TextAsset val = AssetBundleLoader.LoadAsset("emojis", "Assets/Emojis/manifest.json"); if ((Object)(object)val == (Object)null) { Logger.Error("[EmojiLibrary] Failed to load manifest."); return; } _manifest = JsonConvert.DeserializeObject(val.text); if (_manifest == null) { Logger.Error("[EmojiLibrary] Failed to deserialize manifest."); return; } for (int i = 0; i < _manifest.NumPages; i++) { string text = $"Assets/Emojis/atlas_{i:000}" + ".json"; string text2 = $"Assets/Emojis/atlas_{i:000}" + ".png"; TextAsset val2 = AssetBundleLoader.LoadAsset("emojis", text); Texture2D val3 = AssetBundleLoader.LoadAsset("emojis", text2); if ((Object)(object)val2 == (Object)null || (Object)(object)val3 == (Object)null) { Logger.Error($"[EmojiLibrary] Failed to load page {i}. jsonPath={text}, texturePath={text2}"); continue; } EmojiAtlasPage emojiAtlasPage = JsonConvert.DeserializeObject(val2.text); if (emojiAtlasPage == null) { continue; } _textures[i] = val3; _pages[i] = emojiAtlasPage; foreach (EmojiEntry emoji in emojiAtlasPage.Emojis) { _byName[emoji.Name] = emoji; _byId[emoji.Id] = emoji; } } IsLoaded = true; Logger.Debug($"[EmojiLibrary] Loaded {_byId.Count} emojis across {_manifest.NumPages} atlas page(s)."); } public static void Unload() { AssetBundleLoader.Unload("emojis"); _textures.Clear(); _pages.Clear(); _byName.Clear(); _byId.Clear(); _manifest = null; IsLoaded = false; } public static EmojiEntry? GetByName(string name) { if (!_byName.TryGetValue(name, out EmojiEntry value)) { return null; } return value; } public static EmojiEntry? GetById(int id) { if (!_byId.TryGetValue(id, out EmojiEntry value)) { return null; } return value; } public static Texture2D? GetTexture(EmojiEntry entry) { if (!_textures.TryGetValue(entry.Page, out Texture2D value)) { return null; } return value; } public static Sprite? CreateSprite(EmojiEntry entry, float sizeUnits = 1f) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_002f: 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) if (!_textures.TryGetValue(entry.Page, out Texture2D value)) { return null; } Rect val = UVToPixelRect(entry.UV, ((Texture)value).width, ((Texture)value).height); return Sprite.Create(value, val, new Vector2(0.5f, 0.5f), ((Rect)(ref val)).width / sizeUnits); } public static Sprite? CreateSprite(string name, float sizeUnits = 1f) { EmojiEntry byName = GetByName(name); if (byName == null) { return null; } return CreateSprite(byName, sizeUnits); } public static Sprite? CreateSprite(int id, float sizeUnits = 1f) { EmojiEntry byId = GetById(id); if (byId == null) { return null; } return CreateSprite(byId, sizeUnits); } public static bool ApplyToRawImage(EmojiEntry entry, RawImage target) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) if (!_textures.TryGetValue(entry.Page, out Texture2D value)) { return false; } target.texture = (Texture)(object)value; target.uvRect = entry.UV.ToRect(); return true; } public static bool ApplyToRawImage(string name, RawImage target) { EmojiEntry byName = GetByName(name); if (byName != null) { return ApplyToRawImage(byName, target); } return false; } public static bool ApplyToRawImage(int id, RawImage target) { EmojiEntry byId = GetById(id); if (byId != null) { return ApplyToRawImage(byId, target); } return false; } public static RawImage? CreateRawImage(EmojiEntry entry, Transform parent, Vector2 size = default(Vector2)) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_003e: 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_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) if (!_textures.TryGetValue(entry.Page, out Texture2D value)) { return null; } if (size == default(Vector2)) { ((Vector2)(ref size))..ctor(64f, 64f); } GameObject val = new GameObject(entry.Name); val.transform.SetParent(parent, false); val.AddComponent().sizeDelta = size; RawImage obj = val.AddComponent(); obj.texture = (Texture)(object)value; obj.uvRect = entry.UV.ToRect(); return obj; } public static RawImage? CreateRawImage(string name, Transform parent, Vector2 size = default(Vector2)) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) EmojiEntry byName = GetByName(name); if (byName == null) { return null; } return CreateRawImage(byName, parent, size); } public static RawImage? CreateRawImage(int id, Transform parent, Vector2 size = default(Vector2)) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) EmojiEntry byId = GetById(id); if (byId == null) { return null; } return CreateRawImage(byId, parent, size); } private static Rect UVToPixelRect(EmojiUV uv, int texWidth, int texHeight) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) float num = uv.U0 * (float)texWidth; float num2 = (1f - uv.V1) * (float)texHeight; float num3 = (uv.U1 - uv.U0) * (float)texWidth; float num4 = (uv.V1 - uv.V0) * (float)texHeight; return new Rect(num, num2, num3, num4); } } public static class SharedAssetLibrary { private const string AssetBundlePath = "spectate"; public static Texture2D SpectateIconTexture { get; private set; } public static Sprite SpectateIconSprite { get; private set; } public static void Load() { Texture2D val = AssetBundleLoader.LoadAsset("spectate", "Assets/Spectate/eye_icon.png"); if ((Object)(object)val == (Object)null) { Logger.Fatal("Spectate icon texture is required but failed to load."); return; } SpectateIconTexture = val; SpectateIconSprite = AssetHelper.CreateSpriteFromTexture2D(SpectateIconTexture); } } }