using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics; using System.IO; using System.Linq; using System.Net; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Web; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using PEAKLib.Core; using POpusCodec.Enums; using Peak.UI; using Photon.Pun; using Photon.Realtime; using Photon.Voice; using Photon.Voice.Unity; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.Networking; using UnityEngine.SceneManagement; using UnityEngine.UI; using UnityEngine.Video; using Zorro.ControllerSupport; using sPEAKer.Scripts; using ytModule; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("onlystar")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyDescription("Some guy had it aboard. Does it still work?")] [assembly: AssemblyFileVersion("2.3.4.0")] [assembly: AssemblyInformationalVersion("2.3.4+e5624fcfabbe0f6135dbacefdd5426f4eb7b1f9e")] [assembly: AssemblyProduct("onlystar.sPEAKer")] [assembly: AssemblyTitle("sPEAKer")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("2.3.4.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace BepInEx { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [Conditional("CodeGeneration")] internal sealed class BepInAutoPluginAttribute : Attribute { public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null) { } } } namespace BepInEx.Preloader.Core.Patching { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [Conditional("CodeGeneration")] internal sealed class PatcherAutoPluginAttribute : Attribute { public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null) { } } } namespace sPEAKer { [BepInPlugin("onlystar.sPEAKer", "sPEAKer", "2.3.4")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { private static GameObject _prefab = null; public static PeakBundle Bundle = null; public static string PluginFolder; public static TMP_FontAsset PeakFont; public static bool MilestonesEnabled; public static bool LevelingAvailable; private ConfigEntry externalAudioFolderPathConfig = null; private ConfigEntry loadDemoSongConfig = null; private ConfigEntry extendedFormatSupportConfig = null; private ConfigEntry startupLoadingIndicatorConfig = null; private ConfigEntry fastLoadConfig = null; internal ConfigEntry showTrackDurationConfig = null; internal ConfigEntry displayAuthorInfoConfig = null; private ConfigEntry shuffleOnStartConfig = null; private ConfigEntry turnOnAndPlayConfig = null; private ConfigEntry soundEffectsEnabledConfig = null; internal ConfigEntry useSpatialAudioConfig = null; private ConfigEntry renderVisualizersConfig = null; internal ConfigEntry queueUIThemeConfig = null; private ConfigEntry spawnAtAirportConfig = null; private ConfigEntry spawnAtGameStartConfig = null; private ConfigEntry spawnWithF4Config = null; private ConfigEntry recallEnabledConfig = null; private ConfigEntry droneToggleKeyConfig = null; private ConfigEntry recallCommandKeyConfig = null; private ConfigEntry danceKeyConfig = null; private ConfigEntry spawnKeyConfig = null; private ConfigEntry queueUIKeyConfig = null; private ConfigEntry ytKeyConfig = null; private ConfigEntry hostOnlyKeyConfig = null; internal ConfigEntry autoPlayConfig = null; internal ConfigEntry maxCachedSongsConfig = null; internal ConfigEntry predownloadCountConfig = null; private ConfigEntry debugModeConfig = null; internal const bool Strange = true; public static Plugin Instance { get; private set; } = null; internal static ManualLogSource LogObj { get; private set; } = null; public static ushort ItemID { get; private set; } = 50592; public static string DefaultMusicPath { get; private set; } = ""; public static string ExternalAudioFolderPath { get; set; } = ""; public static bool LoadDemoSong { get; set; } public static bool ExtendedFormatSupport { get; set; } public static bool StartupLoadingIndicator { get; set; } public static bool FastLoad { get; set; } public static bool ShowTrackDuration { get; set; } public static bool DisplayAuthorInfo { get; set; } public static bool ShuffleOnStart { get; set; } public static bool TurnOnAndPlay { get; set; } public static bool SoundEffectsEnabled { get; set; } public static bool UseSpatialAudio { get; set; } public static bool RenderVisualizers { get; set; } public static string QueueUITheme { get; set; } = "default"; private static bool SpawnAtAirport { get; set; } private static bool SpawnAtGameStart { get; set; } private static bool SpawnWithF4 { get; set; } private static bool RecallEnabled { get; set; } public static KeyCode DroneToggleKey { get; set; } public static KeyCode RecallCommandKey { get; set; } public static KeyCode DanceKey { get; set; } private static KeyCode SpawnKey { get; set; } public static KeyCode QueueUIKey { get; set; } public static KeyCode YouTubeKey { get; set; } private static KeyCode HostOnlyKey { get; set; } public static bool AutoPlay { get; set; } = true; public static int MaxCachedSongs { get; set; } = 30; public static int PredownloadCount { get; set; } = 5; private static bool DebugMode { get; set; } public static event Action OnFontLoaded; public static event Action OnPlayerLaserHit; public static void FirePlayerLaserHit(int viewID, int effectType) { Plugin.OnPlayerLaserHit?.Invoke(viewID, effectType); } private void Awake() { Instance = this; LogObj = ((BaseUnityPlugin)this).Logger; SetUserConfigs(); Localization.Apply(); ApplyPatches(); Log("[Plugin] Plugin awakening..."); ModDefinition.GetOrCreate(((BaseUnityPlugin)this).Info.Metadata); PluginFolder = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location); LoadFont(); AudioMaster.EnsureInitialized(); Netcode.EnsureInitialized(); TheObserver.EnsureInitialized(); MilestonesEnabled = true; MilestoneTracker.EnsureInitialized(); MilestoneUI.EnsureInitialized(); LevelingAvailable = Chainloader.PluginInfos.ContainsKey("com.atomic.leveling"); if (!LevelingAvailable) { Log("Leveling is not installed. Milestones will work, but XP will be pending.", "warning"); } if (ytModuleCompatibility.enabled) { ytModuleCompatibility.EnsureYouTubePlayerInitialized(); ytModuleCompatibility.EnsureYouTubeQueueInitialized(); RateLimitUI.EnsureInitialized(); } try { Log("[Plugin] Attempting to load bundle..."); BundleLoader.LoadBundleWithName((BaseUnityPlugin)(object)this, "ps.peakbundle", (Action)InitSpeaker); Log("[Plugin] Bundle load request submitted"); } catch (Exception ex) { Log("[Plugin] Failed to load bundle: " + ex.Message, "error"); } } private void InitSpeaker(PeakBundle bundle) { Log("[Plugin] Initializing sPEAKer with bundle..."); Bundle = bundle; _prefab = Bundle.LoadAsset("assets/sPEAKer.prefab"); Log("[Plugin] Successfully loaded sPEAKer prefab."); SetupMaterials(_prefab); SceneManager.sceneLoaded += OnSceneLoaded; _prefab.AddComponent(); Bundle.Mod.RegisterContent(); Log("[Plugin] Plugin sPEAKer is loaded!"); } private static void SetupMaterials(GameObject gameObject) { Shader val = Shader.Find("W/Character"); if ((Object)(object)val == (Object)null) { Log("[Plugin] W/Character shader not found, falling back to W/Peak_Standard", "warning"); val = Shader.Find("W/Peak_Standard"); } if ((Object)(object)val == (Object)null) { Log("[Plugin] Neither W/Character nor W/Peak_Standard shader found!", "error"); return; } Renderer[] componentsInChildren = gameObject.GetComponentsInChildren(); Renderer[] array = componentsInChildren; foreach (Renderer val2 in array) { Material[] materials = val2.materials; Material[] array2 = materials; foreach (Material val3 in array2) { if (((Object)val3.shader).name == "Universal Render Pipeline/Lit" || ((Object)val3.shader).name == "Standard") { Texture val4 = val3.GetTexture("_BaseMap") ?? val3.GetTexture("_MainTex"); val3.shader = val; if ((Object)(object)val4 != (Object)null) { val3.SetTexture("_BaseTexture", val4); } Log("[Plugin] Applied W/Character shader to material: " + ((Object)val3).name); } } val2.materials = materials; } } private static void LoadFont() { if ((Object)(object)PeakFont != (Object)null) { return; } TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll(); TMP_FontAsset[] array2 = array; foreach (TMP_FontAsset val in array2) { if (((Object)val).name.Contains("DarumaDropOne") || ((Object)val).name.Contains("Daruma")) { PeakFont = val; Log("[Plugin] Found game font: " + ((Object)val).name); Plugin.OnFontLoaded?.Invoke(); Plugin.OnFontLoaded = null; return; } } Log("[Plugin] Daruma font not found", "warning"); } private void SetUserConfigs() { //IL_03ac: Unknown result type (might be due to invalid IL or missing references) //IL_03df: Unknown result type (might be due to invalid IL or missing references) //IL_0415: Unknown result type (might be due to invalid IL or missing references) //IL_0448: Unknown result type (might be due to invalid IL or missing references) //IL_047e: Unknown result type (might be due to invalid IL or missing references) //IL_04b1: Unknown result type (might be due to invalid IL or missing references) //IL_04e4: Unknown result type (might be due to invalid IL or missing references) DefaultMusicPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? throw new InvalidOperationException(), "..\\onlystar-sPEAKer Music"); externalAudioFolderPathConfig = ((BaseUnityPlugin)this).Config.Bind("Audio Loading", "ExternalAudioFolderPath", DefaultMusicPath, "Folder where the mod will first try to load mixtapes and external audio from.\nIt will default to \"/plugins/onlystar-sPEAKer Music\" in case of error."); ExternalAudioFolderPath = externalAudioFolderPathConfig.Value; loadDemoSongConfig = ((BaseUnityPlugin)this).Config.Bind("Audio Loading", "LoadDemoSong", true, "Include a demo song in your track list."); LoadDemoSong = loadDemoSongConfig.Value; extendedFormatSupportConfig = ((BaseUnityPlugin)this).Config.Bind("Audio Loading", "ExtendedFormatSupport", true, "Enables loading of .mp3 and .wav files. May impact performance."); ExtendedFormatSupport = extendedFormatSupportConfig.Value; startupLoadingIndicatorConfig = ((BaseUnityPlugin)this).Config.Bind("Audio Loading", "StartupLoadingIndicator", true, "Shows a loading indicator when audio loading is occuring."); StartupLoadingIndicator = startupLoadingIndicatorConfig.Value; fastLoadConfig = ((BaseUnityPlugin)this).Config.Bind("Audio Loading", "FastLoad", true, "Makes sPEAKer load songs during gameplay instead of at game start."); FastLoad = fastLoadConfig.Value; showTrackDurationConfig = ((BaseUnityPlugin)this).Config.Bind("Playback", "ShowTrackDuration", true, "Shows song's length and current time."); ShowTrackDuration = showTrackDurationConfig.Value; displayAuthorInfoConfig = ((BaseUnityPlugin)this).Config.Bind("Playback", "DisplayAuthorInfo", true, "Displays mixtape author under the stamina bar."); DisplayAuthorInfo = displayAuthorInfoConfig.Value; shuffleOnStartConfig = ((BaseUnityPlugin)this).Config.Bind("Playback", "ShuffleOnStart", false, "Start with Shuffle enabled by default. Saves you a toggle if you only want shuffled playback."); ShuffleOnStart = shuffleOnStartConfig.Value; turnOnAndPlayConfig = ((BaseUnityPlugin)this).Config.Bind("Playback", "TurnOnAndPlay", false, "Make sPEAKer start playing immediately after turning it on."); TurnOnAndPlay = turnOnAndPlayConfig.Value; soundEffectsEnabledConfig = ((BaseUnityPlugin)this).Config.Bind("Playback", "SoundEffectsEnabled", true, "Enables JBL sound effects."); SoundEffectsEnabled = soundEffectsEnabledConfig.Value; useSpatialAudioConfig = ((BaseUnityPlugin)this).Config.Bind("Playback", "UseSpatialAudio", true, "Enables proximity-based audio. If disabled, playback will sound globally instead of coming from the sPEAKer."); UseSpatialAudio = useSpatialAudioConfig.Value; renderVisualizersConfig = ((BaseUnityPlugin)this).Config.Bind("Playback", "RenderVisualizers", true, "Enables rendering audio visualizers on airport screens."); RenderVisualizers = renderVisualizersConfig.Value; queueUIThemeConfig = ((BaseUnityPlugin)this).Config.Bind("Playback", "QueueUITheme", "default", "Theme for the Queue UI. Options: default, royal_amethyst, deep_sea, pinky_promise, blood_moon, terminal, wood, cyberpunk, forest"); QueueUITheme = queueUIThemeConfig.Value; spawnAtAirportConfig = ((BaseUnityPlugin)this).Config.Bind("Spawning", "SpawnAtAirport", true, "Automatically spawn sPEAKer at the airport."); SpawnAtAirport = spawnAtAirportConfig.Value; spawnAtGameStartConfig = ((BaseUnityPlugin)this).Config.Bind("Spawning", "SpawnAtGameStart", true, "Automatically spawn sPEAKer around the crash site when a game starts."); SpawnAtGameStart = spawnAtGameStartConfig.Value; spawnWithF4Config = ((BaseUnityPlugin)this).Config.Bind("Spawning", "SpawnWithF4", true, "Allows the host to spawn a sPEAKer in their hands by pressing the spawn key (see Keybinds section).\nNotes:\n - Only one sPEAKer may exist at a time (for now).\n - Only the host can spawn a sPEAKer (for now)."); SpawnWithF4 = spawnWithF4Config.Value; recallEnabledConfig = ((BaseUnityPlugin)this).Config.Bind("Spawning", "RecallEnabled", true, "Enables recalling existing sPEAKer when pressing F4.\nNotes:\n - Only works if SpawnWithF4 is enabled.\n - sPEAKer will only be recalled if it's in the ground and not following any player."); RecallEnabled = recallEnabledConfig.Value; droneToggleKeyConfig = ((BaseUnityPlugin)this).Config.Bind("Keybinds", "DroneToggle", (KeyCode)112, "Drone Mode toggle."); DroneToggleKey = droneToggleKeyConfig.Value; hostOnlyKeyConfig = ((BaseUnityPlugin)this).Config.Bind("Keybinds", "HostOnlyKey", (KeyCode)104, "Toggles between streaming your voice or music."); HostOnlyKey = hostOnlyKeyConfig.Value; recallCommandKeyConfig = ((BaseUnityPlugin)this).Config.Bind("Keybinds", "RecallCommand", (KeyCode)306, "Hold this while pressing DroneToggle key to make sPEAKer \"recall\" to you instead of looking for people nearby."); RecallCommandKey = recallCommandKeyConfig.Value; danceKeyConfig = ((BaseUnityPlugin)this).Config.Bind("Keybinds", "DanceEmote", (KeyCode)111, "Makes sPEAKer dance when Drone Mode is active."); DanceKey = danceKeyConfig.Value; spawnKeyConfig = ((BaseUnityPlugin)this).Config.Bind("Keybinds", "SpawnKey", (KeyCode)285, "Spawn a sPEAKer if the corresponding spawning config is enabled."); SpawnKey = spawnKeyConfig.Value; queueUIKeyConfig = ((BaseUnityPlugin)this).Config.Bind("Keybinds", "QueueUIKey", (KeyCode)117, "Opens Queue UI."); QueueUIKey = queueUIKeyConfig.Value; ytKeyConfig = ((BaseUnityPlugin)this).Config.Bind("Keybinds", "YouTubeKey", (KeyCode)121, "Enter Youtube Radio mode. Hold Ctrl and this key to exit."); YouTubeKey = ytKeyConfig.Value; autoPlayConfig = ((BaseUnityPlugin)this).Config.Bind("YouTube Radio", "AutoPlay", true, "When enabled, reaching the end of the queue will automatically fetch 10 more recommendations and continue playing."); AutoPlay = autoPlayConfig.Value; maxCachedSongsConfig = ((BaseUnityPlugin)this).Config.Bind("YouTube Radio", "MaxCachedSongs", 30, "Maximum number of downloaded songs to keep cached. Older songs are deleted when this limit is exceeded. Range: 10-100."); MaxCachedSongs = Mathf.Clamp(maxCachedSongsConfig.Value, 10, 100); predownloadCountConfig = ((BaseUnityPlugin)this).Config.Bind("YouTube Radio", "PredownloadCount", 5, "Number of upcoming tracks to predownload. Higher values use more disk space but reduce buffering. Range: 1-20."); PredownloadCount = Mathf.Clamp(predownloadCountConfig.Value, 1, 20); debugModeConfig = ((BaseUnityPlugin)this).Config.Bind("zZzZz", "Debug", true, "Enable debug mode AKA verbose logging."); DebugMode = debugModeConfig.Value; } private static bool IsTextInputFocused() { EventSystem current = EventSystem.current; GameObject val = ((current != null) ? current.currentSelectedGameObject : null); if ((Object)(object)val == (Object)null) { return false; } TMP_InputField component = val.GetComponent(); if ((Object)(object)component != (Object)null && component.isFocused) { return true; } InputField component2 = val.GetComponent(); if ((Object)(object)component2 != (Object)null && component2.isFocused) { return true; } return false; } private void Update() { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Invalid comparison between Unknown and I4 //IL_026a: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Invalid comparison between Unknown and I4 //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_02df: Unknown result type (might be due to invalid IL or missing references) if (IsTextInputFocused()) { return; } if (Input.GetKeyDown(SpawnKey) && PhotonNetwork.IsMasterClient) { if (!SpawnWithF4) { return; } if (TheObserver.Instance.DoessPEAKerExist()) { if (RecallEnabled && TheObserver.Instance.CanRecallsPEAKer()) { Controller controllerInstance = TheObserver.Instance.GetControllerInstance(); controllerInstance.KillYourself(); Instance.SpawnAtHand(); } } else { Instance.SpawnAtHand(); } } if (Input.GetKeyDown(DanceKey)) { Controller controllerInstance2 = TheObserver.Instance.GetControllerInstance(); if ((Object)(object)controllerInstance2 != (Object)null && (int)controllerInstance2.item.itemState == 0) { Netcode.Instance.SendRequestDanceEmote(); } else { Log("[Plugin] Could not find controller to initiate Dance.", "warning"); } } if (Input.GetKeyDown(DroneToggleKey)) { Controller controllerInstance3 = TheObserver.Instance.GetControllerInstance(); if ((Object)(object)controllerInstance3 != (Object)null && (int)controllerInstance3.item.itemState == 0) { bool key = Input.GetKey(RecallCommandKey); bool key2 = Input.GetKey((KeyCode)307); bool key3 = Input.GetKey((KeyCode)303); if (key2 && key3) { if (controllerInstance3.droneBehaviour.IsFloating) { controllerInstance3.CycleDroneBehaviour(); } else { controllerInstance3.ToggleFloatMode(); ((MonoBehaviour)this).StartCoroutine(DelayedManualMode(controllerInstance3)); } } else if (key) { controllerInstance3.RecallToPlayer(Character.localCharacter); } else if (controllerInstance3.droneBehaviour.IsFloating && controllerInstance3.droneBehaviour.CurrentBehaviourMode == DroneBehaviourMode.Manual) { controllerInstance3.droneBehaviour.SwitchBehaviour(DroneBehaviourMode.Orbiting); controllerInstance3.ToggleFloatMode(); sPEAKerUI.Instance.ShowToast("Drone Mode: OFF"); } else { controllerInstance3.ToggleFloatMode(); } } else { Log("[Plugin] Could not find controller to initiate Drone/Recall mode.", "warning"); } } if (Input.GetKeyDown(QueueUIKey)) { if (!TheObserver.Instance.DoessPEAKerExist()) { return; } QueueUI.Instance.ToggleQueue(); } if (Input.GetKeyDown(YouTubeKey)) { if (ytModuleCompatibility.enabled) { ytModuleCompatibility.HandleYouTubeKeyPress(); } else { sPEAKerUI.Instance.ShowToast("YouTube Radio is not available"); } } YouTubeUI instance = YouTubeUI.Instance; if ((Object)(object)instance != (Object)null && instance.IsVisible() && Input.GetKeyDown((KeyCode)32) && ytModuleCompatibility.enabled) { ytModuleCompatibility.HandleYouTubeSpaceKey(); } if (Input.GetKeyDown(HostOnlyKey)) { if (!HostOnly.Instance.IsActive) { HostOnly.Instance.EnableHostMode(); } else { HostOnly.Instance.DisableHostMode(); } } } private static IEnumerator DelayedManualMode(Controller controller) { yield return null; if (controller.droneBehaviour.IsFloating && controller.droneBehaviour.CurrentBehaviourMode != DroneBehaviourMode.Manual && controller.droneBehaviour.SwitchBehaviour(DroneBehaviourMode.Manual)) { sPEAKerUI.Instance.ShowToast("Drone Mode: Manual"); } } private void OnDestroy() { Log("[Plugin] Plugin destroying..."); SceneManager.sceneLoaded -= OnSceneLoaded; Instance = null; LogObj = null; } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { Log("[Plugin] Scene loaded: " + ((Scene)(ref scene)).name); if (TheObserver.Instance.DoessPEAKerExist()) { TheObserver.Instance.KillsPEAKer(); } TheObserver.Instance.ResetState(); if (((Scene)(ref scene)).name == "Airport") { if ((Object)(object)PeakFont == (Object)null) { LoadFont(); } string path = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "data"); string airportMediaPath = Path.Combine(path, "airport"); AirportEditor.Initialize(airportMediaPath); QueueUI.Instance.PreWarmUI(); MilestoneUI.Instance.LoadCategoryIcons(); if (SpawnAtAirport) { Log("[Plugin] Spawning at airport."); ((MonoBehaviour)this).StartCoroutine(LobbySpawn()); } } if (((Scene)(ref scene)).name.StartsWith("Level_") && SpawnAtGameStart && PhotonNetwork.IsConnected && PhotonNetwork.IsMasterClient) { ((MonoBehaviour)this).StartCoroutine(BeachSpawn()); } } private void ApplyPatches() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown Harmony val = new Harmony("onlystar.sPEAKer"); val.PatchAll(Assembly.GetExecutingAssembly()); } private static IEnumerator LobbySpawn() { yield return (object)new WaitForSeconds(6f); Vector3 seatPosition = new Vector3(-6.89f, 1.7f, 87.27f); Quaternion seatRotation = Quaternion.Euler(0f, 90f, 0f); PhotonNetwork.InstantiateItemRoom(((Object)_prefab).name, seatPosition, seatRotation); Log("[Plugin] Spawned sPEAKer at Airport seat."); if (MilestonesEnabled && AudioMaster.IsInitialized) { MilestoneTracker.Instance.CheckSoundtrack2MyLife(); } } private static IEnumerator BeachSpawn() { yield return (object)new WaitForSeconds(5f); Player masterClient = PhotonNetwork.MasterClient; foreach (Character character in Character.AllCharacters) { if (!object.Equals((character != null) ? ((MonoBehaviourPun)character).photonView.Owner : null, masterClient)) { continue; } Vector3 spawnPos = new Vector3(character.Center.x + Random.insideUnitSphere.z * 1.5f, character.Center.y + 5f, character.Center.z + Random.insideUnitSphere.z * 1.5f); PhotonNetwork.InstantiateItemRoom(((Object)_prefab).name, spawnPos, Quaternion.identity); break; } if (MilestonesEnabled && AudioMaster.IsInitialized) { MilestoneTracker.Instance.CheckSoundtrack2MyLife(); } } public void SpawnAtHand() { //IL_001a: 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_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) PhotonNetwork.Instantiate("0_Items/" + ((Object)_prefab).name, Character.localCharacter.Center + Vector3.up * 3f, Quaternion.identity, (byte)0, (object[])null).GetComponent().Interact(Character.localCharacter); } public static void Log(string message, string type = "info") { if (DebugMode) { switch (type) { case "info": LogObj.LogInfo((object)message); break; case "warning": LogObj.LogWarning((object)message); break; case "error": LogObj.LogError((object)message); break; default: LogObj.LogInfo((object)message); break; } } } } } namespace sPEAKer.Scripts { public class AudioMaster : MonoBehaviourPun { private Transform _targetTransform; private Character _targetCharacter; private bool _isFollowing; private const int HASH_ALGORITHM_VERSION = 1; private static FileHashCache _fileHashCache = new FileHashCache(); private static string _cachePath = ""; private static readonly List _processedFilePaths = new List(); private static int _cacheHits = 0; private static int _cacheMisses = 0; private static AudioMaster _instance; private static int NonOggFiles; private Coroutine _autoSkipCoroutine; public static bool IsInitialized { get; private set; } private static bool IsLoading { get; set; } public static AudioSource MixtapeAudioSource { get; set; } = null; private static AudioSource EffectsAudioSource { get; set; } = null; public static AudioSource YouTubeAudioSource { get; private set; } = null; public static AudioSource ActiveSource { get { if (IsYouTubeMode) { return YouTubeAudioSource; } return MixtapeAudioSource; } } public static List LoadedAudioClips { get; private set; } = new List(); public static List AvailableMixtapes { get; set; } = new List(); public static Dictionary SongHashToIndex { get; private set; } = new Dictionary(); public static Dictionary IndexToSongHash { get; private set; } = new Dictionary(); public static Dictionary IndexToFilePath { get; private set; } = new Dictionary(); public static PlaybackState CurrentPlaybackState { get; private set; } = PlaybackState.Stopped; public static int CurrentSongIndex { get; private set; } public static float CurrentPausedTime { get; private set; } private static MixtapeInfo CurrentMixtape { get; set; } public static bool IsYouTubeMode { get; private set; } public static float NetworkVolume { get; private set; } = 0.35f; public static float LocalVolumeMultiplier { get; set; } = 0.5f; public static LoopState CurrentLoopState { get; private set; } = LoopState.None; public static List SongQueue { get; private set; } = new List(); public static AudioMaster Instance { get { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown if ((Object)(object)_instance == (Object)null) { _instance = Object.FindAnyObjectByType(); if ((Object)(object)_instance == (Object)null) { GameObject val = new GameObject("sPEAKerAudioMaster"); _instance = val.AddComponent(); Object.DontDestroyOnLoad((Object)(object)val); Plugin.Log("[AudioMaster] Created new instance and marked as DontDestroyOnLoad."); } } return _instance; } } public static event Action OnSongChanged; public static event Action OnQueueSynced; public static event Action OnPlaybackAction; public static event Action OnPlaybackStateChanged; public static event Action OnLoopStateChanged; public static event Action OnYouTubeModeChanged; public static void EnsureInitialized() { Plugin.Log("[AudioMaster] EnsureInitialized called."); AudioMaster instance = Instance; if (!IsInitialized && !IsLoading) { Plugin.Log("[AudioMaster] Not initialized, starting initialization..."); instance.InitializeAsync(); } } private void Awake() { Plugin.Log("[AudioMaster] Awake called."); if ((Object)(object)_instance != (Object)null && (Object)(object)_instance != (Object)(object)this) { Plugin.Log("[AudioMaster] Duplicate instance detected, destroying."); Object.Destroy((Object)(object)((Component)this).gameObject); return; } _instance = this; Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); Plugin.Log("[AudioMaster] Instance set and marked as DontDestroyOnLoad."); InitializeAudioSources(); SceneManager.sceneLoaded += OnSceneLoaded; if (!IsInitialized && !IsLoading) { InitializeAsync(); } } private void InitializeAudioSources() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown AnimationCurve val = new AnimationCurve(); val.AddKey(0f, 1f); val.AddKey(0.25f, 0.85f); val.AddKey(0.6f, 0.2f); val.AddKey(1f, 0f); for (int i = 0; i < val.length; i++) { val.SmoothTangents(i, 0f); } if ((Object)(object)MixtapeAudioSource == (Object)null) { MixtapeAudioSource = ((Component)this).gameObject.AddComponent(); MixtapeAudioSource.loop = false; MixtapeAudioSource.playOnAwake = false; MixtapeAudioSource.volume = NetworkVolume * LocalVolumeMultiplier; if (Plugin.UseSpatialAudio) { MixtapeAudioSource.minDistance = 2f; MixtapeAudioSource.maxDistance = 50f; MixtapeAudioSource.spatialBlend = 1f; MixtapeAudioSource.dopplerLevel = 0.1f; MixtapeAudioSource.spatialize = true; MixtapeAudioSource.rolloffMode = (AudioRolloffMode)2; MixtapeAudioSource.SetCustomCurve((AudioSourceCurveType)0, val); } else { MixtapeAudioSource.spatialBlend = 0f; } Plugin.Log("[AudioMaster] Created persistent audio source."); } if ((Object)(object)EffectsAudioSource == (Object)null) { EffectsAudioSource = ((Component)this).gameObject.AddComponent(); EffectsAudioSource.playOnAwake = false; EffectsAudioSource.loop = false; if (Plugin.UseSpatialAudio) { EffectsAudioSource.spatialBlend = 1f; EffectsAudioSource.volume = 1f; EffectsAudioSource.minDistance = 2f; EffectsAudioSource.maxDistance = 25f; EffectsAudioSource.rolloffMode = (AudioRolloffMode)0; } else { EffectsAudioSource.spatialBlend = 0f; EffectsAudioSource.volume = 1f; } Plugin.Log("[AudioMaster] Created effects audio source."); } if ((Object)(object)YouTubeAudioSource == (Object)null) { YouTubeAudioSource = ((Component)this).gameObject.AddComponent(); YouTubeAudioSource.playOnAwake = false; YouTubeAudioSource.loop = false; YouTubeAudioSource.volume = NetworkVolume * LocalVolumeMultiplier; if (Plugin.UseSpatialAudio) { YouTubeAudioSource.minDistance = 2f; YouTubeAudioSource.maxDistance = 50f; YouTubeAudioSource.spatialBlend = 1f; YouTubeAudioSource.dopplerLevel = 0.1f; YouTubeAudioSource.spatialize = true; YouTubeAudioSource.rolloffMode = (AudioRolloffMode)2; YouTubeAudioSource.SetCustomCurve((AudioSourceCurveType)0, val); } else { YouTubeAudioSource.spatialBlend = 0f; } Plugin.Log("[AudioMaster] Created YouTube audio source."); } } private async Task InitializeAsync() { IsLoading = true; Plugin.Log("[AudioMaster] Starting initialization... "); LoadOrCreateHashCache(); sPEAKerUI.Instance.ShowLoading("Initializing sPEAKer..."); await LoadFolderAudio(); sPEAKerUI.Instance.ShowLoading("Loading bundle audio..."); await LoadBundleAudio(); if (LoadedAudioClips.Count > 0) { SongQueue = IndexToSongHash.Values.ToList(); Plugin.Log($"[AudioMaster] Initial song queue populated with {SongQueue.Count} tracks."); if (Plugin.ShuffleOnStart) { Master_ShuffleQueue(broadcast: false); Plugin.Log("[AudioMaster] Shuffled on start."); } } sPEAKerUI.Instance.ShowLoadingComplete(); SaveChangesToCache(); IsInitialized = true; IsLoading = false; Plugin.Log("[AudioMaster] Initialization complete. Total clips loaded: " + LoadedAudioClips.Count + ", Mixtapes loaded: " + AvailableMixtapes.Count); if (Plugin.MilestonesEnabled) { if (LoadedAudioClips.Count > 1 && NonOggFiles == 0) { MilestoneTracker.Instance.ReportProgress("CrystalClear"); } MilestoneTracker.Instance.SetAbsoluteProgress("ElectricRelaxation", LoadedAudioClips.Count); MilestoneTracker.Instance.SetAbsoluteProgress("DontStop", AvailableMixtapes.Count); } } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { if (((Scene)(ref scene)).name == "Airport") { StopAllPlayback(); sPEAKerUI.Instance.HideDuration(); sPEAKerUI.Instance.HideAuthorInfo(); Plugin.Log("[AudioMaster] Airport scene detected - stopped audio playback"); } } private void OnDestroy() { Plugin.Log("[AudioMaster] OnDestroy called."); SceneManager.sceneLoaded -= OnSceneLoaded; if ((Object)(object)_instance == (Object)(object)this) { IsInitialized = false; IsLoading = false; } } private async Task LoadBundleAudio() { Plugin.Log("[AudioMaster] Starting bundle audio loading..."); int waitCount = 0; while (Plugin.Bundle == null && waitCount < 100) { await Task.Delay(100); waitCount++; } if (Plugin.Bundle == null) { Plugin.Log("[AudioMaster] Bundle not available after 10 seconds - skipping bundle audio loading", "error"); return; } Plugin.Log("[AudioMaster] Bundle is now available, proceeding with loading..."); string[] allAssets = Plugin.Bundle.GetAllAssetNames(); Plugin.Log("[AudioMaster] Total assets in bundle: " + allAssets.Length); string[] jblFX = allAssets.Where((string asset) => asset.Contains("jbl_fx") && asset.EndsWith(".ogg")).ToArray(); string[] array = jblFX; foreach (string fx in array) { AudioClip clip = Plugin.Bundle.LoadAsset(fx); string filename = Path.GetFileNameWithoutExtension(fx); SoundEffects.SetClip(filename, clip); } string[] laserFX = allAssets.Where((string asset) => asset.Contains("laser") && asset.EndsWith(".ogg")).ToArray(); string[] array2 = laserFX; foreach (string fx2 in array2) { AudioClip clip2 = Plugin.Bundle.LoadAsset(fx2); string filename2 = Path.GetFileNameWithoutExtension(fx2); SoundEffects.SetClip(filename2, clip2); } string whiteNoise = allAssets.FirstOrDefault((string asset) => asset.Contains("whitenoise")); if (!string.IsNullOrEmpty(whiteNoise)) { AudioClip whiteNoiseClip = Plugin.Bundle.LoadAsset(whiteNoise); SoundEffects.SetClip("whitenoise", whiteNoiseClip); } if (Plugin.LoadDemoSong) { AudioClip demoClip = Plugin.Bundle.LoadAsset("assets/speaker audio/portal.ogg"); if ((Object)(object)demoClip != (Object)null) { demoClip.LoadAudioData(); while ((int)demoClip.loadState == 1) { await Task.Yield(); } if ((int)demoClip.loadState == 2) { LoadedAudioClips.Add(demoClip); string songHash = "portalAsset"; int index = LoadedAudioClips.Count - 1; SongHashToIndex[songHash] = index; IndexToSongHash[index] = songHash; IndexToFilePath[index] = null; } else { Plugin.Log("[AudioMaster] Failed to load demo song audio data", "error"); } } } Plugin.Log("[AudioMaster] Bundle loading complete."); } private async Task LoadFolderAudio() { List foldersToProcess = new List(); string finalUserPath; try { string configuredPath = Plugin.ExternalAudioFolderPath; if (Directory.Exists(configuredPath) || (!Directory.Exists(configuredPath) && Directory.GetParent(configuredPath).Exists)) { finalUserPath = configuredPath; } else { Plugin.Log("[AudioMaster] Configured path '" + configuredPath + "' is invalid. Falling back to the default path.", "warning"); finalUserPath = Plugin.DefaultMusicPath; } } catch (Exception ex) { Exception e = ex; Plugin.Log("[AudioMaster] Error validating configured path '" + Plugin.ExternalAudioFolderPath + "'. Falling back to default. Error: " + e.Message, "error"); finalUserPath = Plugin.DefaultMusicPath; } try { if (!Directory.Exists(finalUserPath)) { Directory.CreateDirectory(finalUserPath); Plugin.Log("[AudioMaster] Created audio folder: " + finalUserPath); } foldersToProcess.Add(finalUserPath); } catch (Exception ex) { Exception e2 = ex; Plugin.Log("[AudioMaster] Failed to create or access the final user path '" + finalUserPath + "'. Error: " + e2.Message, "error"); } string pluginsPath = Paths.PluginPath; if (Directory.Exists(pluginsPath)) { Plugin.Log("[AudioMaster] Scanning for mixtape mods in: " + pluginsPath); string[] subDirectories = Directory.GetDirectories(pluginsPath, "*", SearchOption.AllDirectories); string[] array = subDirectories; foreach (string subDir in array) { if (File.Exists(Path.Combine(subDir, "sPEAKer.json")) && !foldersToProcess.Contains(subDir)) { foldersToProcess.Add(subDir); Plugin.Log("[AudioMaster] Found mixtape mod folder: " + subDir); } } } Plugin.Log("[AudioMaster] Total unique folders to process: " + foldersToProcess.Count); foreach (string folderPath in foldersToProcess) { await ProcessFolderForAudio(folderPath); } } private async Task ProcessAudioFolder(string folderPath) { Plugin.Log("[AudioMaster] Processing loose audio files in: " + folderPath); List searchPatterns = new List { "*.ogg" }; if (Plugin.ExtendedFormatSupport) { Plugin.Log("[AudioMaster] Extended format support is ON. Searching for .mp3 and .wav files."); searchPatterns.Add("*.mp3"); searchPatterns.Add("*.wav"); } List audioFiles = searchPatterns.SelectMany((string pattern) => Directory.GetFiles(folderPath, pattern, SearchOption.TopDirectoryOnly)).ToList(); Plugin.Log("[AudioMaster] Found " + audioFiles.Count + " loose audio files in " + folderPath); if (audioFiles.Count == 0) { Plugin.Log("[AudioMaster] No loose audio files found, skipping: " + folderPath); return; } _processedFilePaths.AddRange(audioFiles.Select(NormalizePath)); int successCount = 0; int failCount = 0; foreach (string file in audioFiles) { string fileName = Path.GetFileName(file); if (!fileName.Contains(".ogg")) { NonOggFiles++; } Plugin.Log("[AudioMaster] Loading file: " + fileName); string url = "file://" + file.Replace('\\', '/').Replace("+", "%2B").Replace("#", "%23"); AudioType audioType = GetAudioTypeFromExtension(file); if ((int)audioType == 0) { Plugin.Log("[AudioMaster] Skipping unsupported file type: " + fileName, "warning"); continue; } UnityWebRequest www = UnityWebRequestMultimedia.GetAudioClip(url, audioType); try { ((DownloadHandlerAudioClip)www.downloadHandler).streamAudio = Plugin.FastLoad; await (AsyncOperation)(object)www.SendWebRequest(); if ((int)www.result == 2 || (int)www.result == 3) { Plugin.Log("[AudioMaster] Failed to load audio: " + fileName + " - Error: " + www.error, "error"); failCount++; goto IL_05e6; } AudioClip clip = DownloadHandlerAudioClip.GetContent(www); if ((Object)(object)clip != (Object)null) { ((Object)clip).name = Path.GetFileNameWithoutExtension(file); string songHash = await GenerateFileHashAsync(file); if (songHash == null) { continue; } clip.LoadAudioData(); while ((int)clip.loadState == 1) { await Task.Yield(); } if ((int)clip.loadState == 2) { LoadedAudioClips.Add(clip); int index = LoadedAudioClips.Count - 1; SongHashToIndex[songHash] = index; IndexToSongHash[index] = songHash; IndexToFilePath[index] = file; successCount++; } else { Plugin.Log("[AudioMaster] Failed to preload audio data for: " + file, "error"); } goto IL_05e6; } goto IL_05e6; } finally { ((IDisposable)www)?.Dispose(); } IL_05e6: await Task.Yield(); } Plugin.Log("[AudioMaster] Loose file processing complete for " + folderPath + ". Success: " + successCount + ", Failed: " + failCount); } private async Task ProcessFolderForAudio(string folderPath) { List searchPatterns = new List { "*.ogg" }; if (Plugin.ExtendedFormatSupport) { searchPatterns.Add("*.mp3"); searchPatterns.Add("*.wav"); } List looseAudioFiles = searchPatterns.SelectMany((string pattern) => Directory.GetFiles(folderPath, pattern, SearchOption.TopDirectoryOnly)).ToList(); string[] subDirectories = Directory.GetDirectories(folderPath); bool hasSpeakerJson = File.Exists(Path.Combine(folderPath, "sPEAKer.json")); bool hasLooseAudio = looseAudioFiles.Count > 0; if (hasSpeakerJson && hasLooseAudio) { await LoadLooseFilesAsMixtape(folderPath, looseAudioFiles); } else if (hasLooseAudio) { await ProcessAudioFolder(folderPath); } string[] array = subDirectories; foreach (string mixtapeDir in array) { await LoadMixtapeAsync(mixtapeDir); } } private async Task LoadMixtapeAsync(string mixtapePath) { string mixtapeName = new DirectoryInfo(mixtapePath).Name; Plugin.Log("[AudioMaster] Loading mixtape: " + mixtapeName); Plugin.Log("[AudioMaster] From path: " + mixtapePath); MixtapeInfo mixtapeInfo = new MixtapeInfo { Name = mixtapeName, Author = "Unknown", FirstSongIndex = LoadedAudioClips.Count, Path = mixtapePath }; string metadataPath = Path.Combine(mixtapePath, "mixtape.json"); if (File.Exists(metadataPath)) { try { string jsonContent = File.ReadAllText(metadataPath); MixtapeMetadata metadata = JsonUtility.FromJson(jsonContent); if (!string.IsNullOrEmpty(metadata.name)) { mixtapeInfo.Name = metadata.name; sPEAKerUI.Instance.UpdateLoadingPhase("Loading " + mixtapeInfo.Name + "..."); } else { sPEAKerUI.Instance.UpdateLoadingPhase("Loading " + mixtapeName + "..."); } if (!string.IsNullOrEmpty(metadata.author)) { mixtapeInfo.Author = metadata.author; } Plugin.Log("[AudioMaster] Loaded mixtape metadata - Name: " + mixtapeInfo.Name + ", Author: " + mixtapeInfo.Author); } catch (Exception ex) { Exception e = ex; Plugin.Log("[AudioMaster] Failed to parse mixtape metadata for " + mixtapeName + ": " + e.Message, "warning"); } } string iconPath = Path.Combine(mixtapePath, "..", "icon.png"); Plugin.Log("[AudioMaster] Checking for icon at path: " + iconPath); string normalizedIconPath = Path.GetFullPath(iconPath); Plugin.Log("[AudioMaster] Normalized icon path: " + normalizedIconPath); Plugin.Log($"[AudioMaster] Icon file exists: {File.Exists(normalizedIconPath)}"); if (File.Exists(normalizedIconPath)) { try { FileInfo fileInfo = new FileInfo(normalizedIconPath); Plugin.Log($"[AudioMaster] Icon file size: {fileInfo.Length} bytes"); string iconUrl = "file://" + normalizedIconPath.Replace('\\', '/').Replace("+", "%2B").Replace("#", "%23"); Plugin.Log("[AudioMaster] Converted icon URL: " + iconUrl); UnityWebRequest www = UnityWebRequest.Get(iconUrl); try { Plugin.Log("[AudioMaster] Starting web request for icon: " + mixtapeName); await (AsyncOperation)(object)www.SendWebRequest(); Plugin.Log($"[AudioMaster] Web request result: {www.result}"); Plugin.Log($"[AudioMaster] Response code: {www.responseCode}"); if ((int)www.result == 2 || (int)www.result == 3) { Plugin.Log("[AudioMaster] Failed to load icon for mixtape " + mixtapeName + ": " + www.error, "error"); } else { byte[] iconBytes = www.downloadHandler.data; Texture2D iconTexture = new Texture2D(2, 2); if (ImageConversion.LoadImage(iconTexture, iconBytes)) { Plugin.Log($"[AudioMaster] Icon texture dimensions: {((Texture)iconTexture).width}x{((Texture)iconTexture).height}"); Plugin.Log($"[AudioMaster] Icon texture is readable: {((Texture)iconTexture).isReadable}"); mixtapeInfo.IconTexture = iconTexture; Plugin.Log("[AudioMaster] Successfully loaded and assigned icon for mixtape: " + mixtapeName); } else { Plugin.Log("[AudioMaster] Failed to load image data from bytes for: " + mixtapeName, "error"); } } } finally { ((IDisposable)www)?.Dispose(); } } catch (Exception ex) { Exception e2 = ex; Plugin.Log("[AudioMaster] Exception loading icon for mixtape " + mixtapeName + ": " + e2.Message, "error"); } } List searchPatterns = new List { "*.ogg" }; if (Plugin.ExtendedFormatSupport) { searchPatterns.Add("*.mp3"); searchPatterns.Add("*.wav"); } List audioFiles = searchPatterns.SelectMany((string pattern) => Directory.GetFiles(mixtapePath, pattern, SearchOption.TopDirectoryOnly)).ToList(); _processedFilePaths.AddRange(audioFiles.Select(NormalizePath)); Plugin.Log("[AudioMaster] Found " + audioFiles.Count + " audio files in mixtape."); int songSuccessCount = 0; float totalDuration = 0f; foreach (string audioFile in audioFiles) { string fileName = Path.GetFileName(audioFile); if (!fileName.Contains(".ogg")) { NonOggFiles++; } Plugin.Log("[AudioMaster] Loading mixtape song: " + fileName); string url = "file://" + audioFile.Replace('\\', '/').Replace("+", "%2B").Replace("#", "%23"); AudioType audioType = GetAudioTypeFromExtension(audioFile); if ((int)audioType == 0) { Plugin.Log("[AudioMaster] Skipping unsupported file type in mixtape: " + fileName, "warning"); continue; } UnityWebRequest www2 = UnityWebRequestMultimedia.GetAudioClip(url, audioType); try { ((DownloadHandlerAudioClip)www2.downloadHandler).streamAudio = Plugin.FastLoad; await (AsyncOperation)(object)www2.SendWebRequest(); if ((int)www2.result == 2 || (int)www2.result == 3) { Plugin.Log("[AudioMaster] Failed to load mixtape audio: " + fileName + " - Error: " + www2.error, "error"); goto IL_0b7f; } AudioClip clip = DownloadHandlerAudioClip.GetContent(www2); if ((Object)(object)clip != (Object)null) { ((Object)clip).name = Path.GetFileNameWithoutExtension(audioFile); string songHash = await GenerateFileHashAsync(audioFile); if (songHash == null) { continue; } clip.LoadAudioData(); while ((int)clip.loadState == 1) { await Task.Yield(); } if ((int)clip.loadState == 2) { LoadedAudioClips.Add(clip); int index = LoadedAudioClips.Count - 1; SongHashToIndex[songHash] = index; IndexToSongHash[index] = songHash; IndexToFilePath[index] = audioFile; totalDuration += clip.length; songSuccessCount++; } else { Plugin.Log("[AudioMaster] Failed to preload audio data for mixtape song: " + audioFile, "error"); } goto IL_0b7f; } goto IL_0b7f; } finally { ((IDisposable)www2)?.Dispose(); } IL_0b7f: await Task.Yield(); } mixtapeInfo.SongCount = songSuccessCount; mixtapeInfo.LastSongIndex = mixtapeInfo.FirstSongIndex + songSuccessCount - 1; mixtapeInfo.TotalLength = FormatDuration(totalDuration); if (songSuccessCount > 0) { AvailableMixtapes.Add(mixtapeInfo); Plugin.Log("[AudioMaster] Successfully loaded mixtape '" + mixtapeInfo.Name + "' by " + mixtapeInfo.Author + " - " + songSuccessCount + " songs, " + mixtapeInfo.TotalLength + " total"); return true; } Plugin.Log("[AudioMaster] Mixtape folder '" + mixtapeInfo.Name + "' contains no valid audio files.", "warning"); return false; } private async Task LoadLooseFilesAsMixtape(string folderPath, List audioFiles) { string folderName = new DirectoryInfo(folderPath).Name; Plugin.Log("[AudioMaster] Loading loose files as mixtape: " + folderName); MixtapeInfo mixtapeInfo = new MixtapeInfo { Name = folderName, Author = "Unknown", FirstSongIndex = LoadedAudioClips.Count, Path = folderPath }; string metadataPath = Path.Combine(folderPath, "mixtape.json"); if (File.Exists(metadataPath)) { try { string jsonContent = File.ReadAllText(metadataPath); Plugin.Log("[AudioMaster] Raw JSON content: " + jsonContent); MixtapeMetadata metadata = JsonUtility.FromJson(jsonContent); Plugin.Log("[AudioMaster] Parsed metadata - name: '" + metadata?.name + "', author: '" + metadata?.author + "'"); if (metadata != null) { if (!string.IsNullOrEmpty(metadata.name)) { mixtapeInfo.Name = metadata.name; sPEAKerUI.Instance.UpdateLoadingPhase("Loading " + mixtapeInfo.Name + "..."); } else { sPEAKerUI.Instance.UpdateLoadingPhase("Loading " + folderName + "..."); } if (!string.IsNullOrEmpty(metadata.author)) { mixtapeInfo.Author = metadata.author; } } else { Plugin.Log("[AudioMaster] JsonUtility.FromJson returned null for mixtape metadata", "warning"); } Plugin.Log("[AudioMaster] Final mixtape info - Name: " + mixtapeInfo.Name + ", Author: " + mixtapeInfo.Author); } catch (Exception ex) { Exception e = ex; Plugin.Log("[AudioMaster] Failed to parse mixtape metadata for " + folderName + ": " + e.Message, "warning"); string[] parts = folderName.Split('-', 2); if (parts.Length == 2) { mixtapeInfo.Author = parts[0].Trim(); mixtapeInfo.Name = parts[1].Trim(); Plugin.Log("[AudioMaster] Used fallback parsing - Author: " + mixtapeInfo.Author + ", Name: " + mixtapeInfo.Name); } } } else { string[] parts2 = folderName.Split('-', 2); if (parts2.Length == 2) { mixtapeInfo.Author = parts2[0].Trim(); mixtapeInfo.Name = parts2[1].Trim(); Plugin.Log("[AudioMaster] Parsed folder name - Author: " + mixtapeInfo.Author + ", Name: " + mixtapeInfo.Name); } sPEAKerUI.Instance.UpdateLoadingPhase("Loading " + mixtapeInfo.Name + "..."); } string iconPath = Path.Combine(folderPath, "icon.png"); if (File.Exists(iconPath)) { try { string iconUrl = "file://" + iconPath.Replace('\\', '/').Replace("+", "%2B").Replace("#", "%23"); UnityWebRequest www = UnityWebRequest.Get(iconUrl); try { await (AsyncOperation)(object)www.SendWebRequest(); if ((int)www.result == 2 || (int)www.result == 3) { Plugin.Log("[AudioMaster] Failed to load icon for mixtape " + folderName + ": " + www.error, "error"); } else { byte[] iconBytes = www.downloadHandler.data; Texture2D iconTexture = new Texture2D(2, 2); if (ImageConversion.LoadImage(iconTexture, iconBytes)) { mixtapeInfo.IconTexture = iconTexture; Plugin.Log("[AudioMaster] Successfully loaded icon for loose files mixtape: " + folderName); } } } finally { ((IDisposable)www)?.Dispose(); } } catch (Exception ex) { Exception e2 = ex; Plugin.Log("[AudioMaster] Exception loading icon for loose files mixtape " + folderName + ": " + e2.Message, "error"); } } _processedFilePaths.AddRange(audioFiles.Select(NormalizePath)); int songSuccessCount = 0; float totalDuration = 0f; foreach (string audioFile in audioFiles) { string fileName = Path.GetFileName(audioFile); if (!fileName.Contains(".ogg")) { NonOggFiles++; } Plugin.Log("[AudioMaster] Loading loose file as mixtape song: " + fileName); string url = "file://" + audioFile.Replace('\\', '/').Replace("+", "%2B").Replace("#", "%23"); AudioType audioType = GetAudioTypeFromExtension(audioFile); if ((int)audioType == 0) { Plugin.Log("[AudioMaster] Skipping unsupported file type: " + fileName, "warning"); continue; } UnityWebRequest www2 = UnityWebRequestMultimedia.GetAudioClip(url, audioType); try { ((DownloadHandlerAudioClip)www2.downloadHandler).streamAudio = Plugin.FastLoad; await (AsyncOperation)(object)www2.SendWebRequest(); if ((int)www2.result == 2 || (int)www2.result == 3) { Plugin.Log("[AudioMaster] Failed to load loose audio: " + fileName + " - Error: " + www2.error, "error"); goto IL_0aca; } AudioClip clip = DownloadHandlerAudioClip.GetContent(www2); if ((Object)(object)clip != (Object)null) { ((Object)clip).name = Path.GetFileNameWithoutExtension(audioFile); string songHash = await GenerateFileHashAsync(audioFile); if (songHash == null) { continue; } clip.LoadAudioData(); while ((int)clip.loadState == 1) { await Task.Yield(); } if ((int)clip.loadState == 2) { LoadedAudioClips.Add(clip); int index = LoadedAudioClips.Count - 1; SongHashToIndex[songHash] = index; IndexToSongHash[index] = songHash; IndexToFilePath[index] = audioFile; totalDuration += clip.length; songSuccessCount++; } else { Plugin.Log("[AudioMaster] Failed to preload audio data for loose file: " + audioFile, "error"); } goto IL_0aca; } goto IL_0aca; } finally { ((IDisposable)www2)?.Dispose(); } IL_0aca: await Task.Yield(); } mixtapeInfo.SongCount = songSuccessCount; mixtapeInfo.LastSongIndex = mixtapeInfo.FirstSongIndex + songSuccessCount - 1; mixtapeInfo.TotalLength = FormatDuration(totalDuration); if (songSuccessCount > 0) { AvailableMixtapes.Add(mixtapeInfo); Plugin.Log("[AudioMaster] Successfully loaded loose files as mixtape '" + mixtapeInfo.Name + "' by " + mixtapeInfo.Author + " - " + songSuccessCount + " songs, " + mixtapeInfo.TotalLength + " total"); } else { Plugin.Log("[AudioMaster] Loose files in '" + folderName + "' could not be loaded as mixtape.", "warning"); } } private async Task GenerateFileHashAsync(string filePath) { string normalizedPath = NormalizePath(filePath); try { FileInfo fileInfo = new FileInfo(filePath); long size = fileInfo.Length; DateTime lastModified = fileInfo.LastWriteTimeUtc; if (_fileHashCache.Hashes.TryGetValue(normalizedPath, out var cachedEntry) && cachedEntry.Size == size && cachedEntry.LastModified == lastModified) { _cacheHits++; return cachedEntry.Hash; } _cacheMisses++; string fileName = Path.GetFileName(filePath); string uniqueId = $"{fileName}|{size}"; using SHA256 sha256 = SHA256.Create(); byte[] hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(uniqueId)); string newHash = Convert.ToBase64String(hashBytes).Substring(0, 12); _fileHashCache.Hashes[normalizedPath] = new CacheEntry { Hash = newHash, LastModified = lastModified, Size = size }; return await Task.FromResult(newHash); } catch (Exception ex) { Plugin.Log("[AudioMaster] Failed to generate metadata hash for " + filePath + ": " + ex.Message, "error"); return await Task.FromResult(null); } } private void LoadOrCreateHashCache() { try { string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); string text = Path.Combine(directoryName, "data"); _cachePath = Path.Combine(text, "file_hashes.json"); if (!Directory.Exists(text)) { Directory.CreateDirectory(text); Plugin.Log("[Cache] Created cache data folder at: " + text); } if (File.Exists(_cachePath)) { string text2 = File.ReadAllText(_cachePath); FileHashCache fileHashCache = JsonConvert.DeserializeObject(text2); if (fileHashCache != null && fileHashCache.Version == 1 && fileHashCache.AlgorithmVersion == 1) { _fileHashCache = fileHashCache; Plugin.Log($"[Cache] Successfully loaded {_fileHashCache.Hashes.Count} hashes from cache (Algo v{1})."); } else { Plugin.Log("[Cache] Cache is from an incompatible algorithm version. Regenerating.", "warning"); _fileHashCache = new FileHashCache { AlgorithmVersion = 1 }; } } else { Plugin.Log("[Cache] No cache file found. A new one will be created."); _fileHashCache = new FileHashCache { AlgorithmVersion = 1 }; } } catch (Exception ex) { Plugin.Log("[Cache] Error loading hash cache, regenerating. Error: " + ex.Message, "error"); _fileHashCache = new FileHashCache { AlgorithmVersion = 1 }; } } private void SaveChangesToCache() { try { List list = _fileHashCache.Hashes.Keys.Except(_processedFilePaths).ToList(); foreach (string item in list) { _fileHashCache.Hashes.Remove(item); } Plugin.Log($"[Cache] Cleanup removed {list.Count} stale entries."); Plugin.Log($"[Cache] Stats: {_cacheHits} hits, {_cacheMisses} misses."); string contents = JsonConvert.SerializeObject((object)_fileHashCache, (Formatting)1); string text = _cachePath + ".tmp"; File.WriteAllText(text, contents); if (File.Exists(_cachePath)) { File.Replace(text, _cachePath, null); } else { File.Move(text, _cachePath); } Plugin.Log($"[Cache] Saved {_fileHashCache.Hashes.Count} hashes to cache at {_cachePath}"); } catch (Exception ex) { Plugin.Log("[Cache] Failed to save hash cache: " + ex.Message, "error"); } } public static void FirePlaybackAction(PlaybackActionType action, AudioClip clip, float time) { AudioMaster.OnPlaybackAction?.Invoke(action, clip, time); Plugin.Log(string.Format("[AudioMaster] PlaybackAction fired: {0}, clip={1}, time={2}", action, ((clip != null) ? ((Object)clip).name : null) ?? "null", time)); } public async Task GetNonStreamedClipAsync(int index) { if (index < 0 || index >= LoadedAudioClips.Count) { return null; } if (!Plugin.FastLoad) { return LoadedAudioClips[index]; } if (!IndexToFilePath.TryGetValue(index, out var filePath) || string.IsNullOrEmpty(filePath)) { return LoadedAudioClips[index]; } Plugin.Log($"[AudioMaster] Loading non-streamed clip for index {index} from: {Path.GetFileName(filePath)}"); string url = "file://" + filePath.Replace('\\', '/').Replace("+", "%2B").Replace("#", "%23"); AudioType audioType = GetAudioTypeFromExtension(filePath); UnityWebRequest www = UnityWebRequestMultimedia.GetAudioClip(url, audioType); try { ((DownloadHandlerAudioClip)www.downloadHandler).streamAudio = false; await (AsyncOperation)(object)www.SendWebRequest(); if ((int)www.result != 1) { Plugin.Log("[AudioMaster] Failed to reload clip non-streamed: " + www.error, "error"); return null; } AudioClip clip = DownloadHandlerAudioClip.GetContent(www); if ((Object)(object)clip == (Object)null) { return null; } ((Object)clip).name = ((Object)LoadedAudioClips[index]).name + " (HostOnly)"; clip.LoadAudioData(); while ((int)clip.loadState == 1) { await Task.Yield(); } if ((int)clip.loadState != 2) { Plugin.Log("[AudioMaster] Failed to load audio data for non-streamed clip.", "error"); return null; } Plugin.Log("[AudioMaster] Non-streamed clip ready: " + ((Object)clip).name); return clip; } finally { ((IDisposable)www)?.Dispose(); } } public static void UpdatePlaybackState(PlaybackState newState, int songIndex = -1, float pausedTime = 0f) { CurrentPlaybackState = newState; if (songIndex >= 0) { CurrentSongIndex = songIndex; } CurrentPausedTime = pausedTime; AudioMaster.OnPlaybackStateChanged?.Invoke(newState); Plugin.Log($"[AudioMaster] Playback state updated - State: {newState}, Song: {CurrentSongIndex}, PausedTime: {pausedTime}"); } private static void ResetPlaybackState() { CurrentPlaybackState = PlaybackState.Stopped; CurrentSongIndex = 0; CurrentPausedTime = 0f; Plugin.Log("[AudioMaster] Playback state reset to defaults"); } private void PlaySongAtIndex(int index) { if (index < 0 || index >= LoadedAudioClips.Count || (Object)(object)MixtapeAudioSource == (Object)null) { Plugin.Log($"[AudioMaster] PlaySongAtIndex called with invalid index: {index}. Aborting.", "error"); return; } string text = IndexToSongHash[index]; Plugin.Log($"[AudioMaster] PlaySongAtIndex: Playing index {index} with hash '{text}'."); AudioMaster.OnSongChanged?.Invoke(text); if (Plugin.MilestonesEnabled) { MilestoneTracker.Instance.RecordSongPlayed(text); } Plugin.Log("[AudioMaster] OnSongChanged event has been invoked for hash '" + text + "'."); AudioClip val = LoadedAudioClips[index]; MixtapeAudioSource.clip = val; MixtapeAudioSource.Play(); FirePlaybackAction(PlaybackActionType.Play, val, 0f); if (Plugin.MilestonesEnabled) { MilestoneTracker.Instance.ReportProgress("UnstoppableForce"); } CurrentMixtape = GetMixtapeForSongIndex(index); if (!string.IsNullOrEmpty(CurrentMixtape.Name)) { sPEAKerUI.Instance.ShowToast(CurrentMixtape.Name + " - " + ((Object)val).name); sPEAKerUI.Instance.ShowAuthorInfo(CurrentMixtape.Author, IsYouTubeMode); } else { sPEAKerUI.Instance.HideAuthorInfo(); sPEAKerUI.Instance.ShowToast(((Object)val).name); } sPEAKerUI.Instance.ShowDuration(val.length); } private IEnumerator MonitorForAutoSkip() { Plugin.Log("[AudioMaster] Auto-skip monitor started."); yield return (object)new WaitUntil((Func)(() => (Object)(object)MixtapeAudioSource != (Object)null && MixtapeAudioSource.isPlaying)); while (CurrentPlaybackState == PlaybackState.Playing && MixtapeAudioSource.isPlaying) { if (IsYouTubeMode) { Plugin.Log("[AudioMaster] Auto-skip monitor ended - YouTube mode active."); yield break; } yield return (object)new WaitForSeconds(0.5f); } if (CurrentPlaybackState == PlaybackState.Playing && !IsYouTubeMode && PhotonNetwork.IsMasterClient) { Plugin.Log("[AudioMaster] Song finished. Automatically playing next track."); AutoSkipToNext(); } Plugin.Log("[AudioMaster] Auto-skip monitor ended."); } private void AutoSkipToNext() { List loadedAudioClips = LoadedAudioClips; if (loadedAudioClips != null && loadedAudioClips.Count <= 0) { Plugin.Log("[AudioMaster] Not enough songs to auto-skip"); return; } int num2; switch (CurrentLoopState) { case LoopState.One: num2 = CurrentSongIndex; break; case LoopState.All: num2 = GetNextSongIndex(CurrentSongIndex); break; default: { string item = IndexToSongHash[CurrentSongIndex]; int num = SongQueue.IndexOf(item); if (num == SongQueue.Count - 1) { Plugin.Log("[AudioMaster] Reached end of queue. Stopping."); Netcode.Instance.SendStopPlayback(); return; } num2 = GetNextSongIndex(CurrentSongIndex); break; } } string songHash = IndexToSongHash[num2]; string name = ((Object)LoadedAudioClips[num2]).name; Netcode.Instance.SendNextSong(songHash, name); } public void StartAutoSkipMonitoring() { if (_autoSkipCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_autoSkipCoroutine); } _autoSkipCoroutine = ((MonoBehaviour)this).StartCoroutine(MonitorForAutoSkip()); } public void StopAutoSkipMonitoring() { if (_autoSkipCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_autoSkipCoroutine); _autoSkipCoroutine = null; } } public void StopAllPlayback() { ResetPlaybackState(); FirePlaybackAction(PlaybackActionType.Stop, null, 0f); if ((Object)(object)MixtapeAudioSource != (Object)null && MixtapeAudioSource.isPlaying) { MixtapeAudioSource.Stop(); } if ((Object)(object)YouTubeAudioSource != (Object)null && YouTubeAudioSource.isPlaying) { YouTubeAudioSource.Stop(); } } private IEnumerator FadeOut(float playDuration, float fadeDuration) { yield return (object)new WaitForSeconds(playDuration); float startVolume = MixtapeAudioSource.volume; float elapsed = 0f; while (elapsed < fadeDuration) { elapsed += Time.deltaTime; MixtapeAudioSource.volume = Mathf.Lerp(startVolume, 0f, elapsed / fadeDuration); yield return null; } MixtapeAudioSource.volume = startVolume; } public static void PauseLocalAudio() { if ((Object)(object)MixtapeAudioSource != (Object)null && MixtapeAudioSource.isPlaying) { MixtapeAudioSource.Pause(); Plugin.Log("[AudioMaster] Local audio paused for YouTube mode"); } } public static void ResumeLocalAudio() { if (!((Object)(object)MixtapeAudioSource != (Object)null) || !((Object)(object)MixtapeAudioSource.clip != (Object)null)) { return; } MixtapeAudioSource.UnPause(); if (MixtapeAudioSource.isPlaying) { CurrentPlaybackState = PlaybackState.Playing; Controller controllerInstance = TheObserver.Instance.GetControllerInstance(); if ((Object)(object)controllerInstance != (Object)null) { controllerInstance.UpdateMainPrompt(PlaybackState.Playing); } } AirportEditor.PlayAllVisualizers(ActiveSource); Plugin.Log("[AudioMaster] Local audio resumed from YouTube mode"); } public static void ToggleLoop() { CurrentLoopState = (LoopState)((int)(CurrentLoopState + 1) % 3); switch (CurrentLoopState) { case LoopState.None: sPEAKerUI.Instance.ShowToast("Loop is OFF"); break; case LoopState.All: sPEAKerUI.Instance.ShowToast("Looping ALL songs"); break; case LoopState.One: sPEAKerUI.Instance.ShowToast("Looping CURRENT song"); if (Plugin.MilestonesEnabled) { MilestoneTracker.Instance.ReportProgress("OneMoreTime"); } break; } if (PhotonNetwork.IsMasterClient) { Netcode.Instance.SendLoopStateChange((int)CurrentLoopState); } AudioMaster.OnLoopStateChanged?.Invoke(CurrentLoopState); Plugin.Log($"[AudioMaster] Loop state toggled: {CurrentLoopState}"); } public void SetVolume(float newVolume) { float num = Mathf.Clamp(newVolume, 0f, 1f); if (num >= 1f && NetworkVolume < 1f) { PlayFX(SoundEffects.VolMax); if (Plugin.MilestonesEnabled) { MilestoneTracker.Instance.ReportProgress("SubeElBajo"); } } if (num <= 0f && NetworkVolume > 0f && Plugin.MilestonesEnabled) { MilestoneTracker.Instance.ReportProgress("Mute"); } NetworkVolume = num; ApplyVolumeMultiplier(); Plugin.Log("[AudioMaster] Network volume set to " + NetworkVolume + " with local multiplier " + LocalVolumeMultiplier + " = final volume " + NetworkVolume * LocalVolumeMultiplier); } public static void SetLocalVolumeMultiplier(float volume) { float localVolumeMultiplier = LocalVolumeMultiplier; LocalVolumeMultiplier = Mathf.Clamp01(volume); if (Plugin.MilestonesEnabled && Mathf.Abs(localVolumeMultiplier - LocalVolumeMultiplier) > 0.01f) { MilestoneTracker.Instance.ReportProgress("MyWay"); } ApplyVolumeMultiplier(); } private static void ApplyVolumeMultiplier() { if ((Object)(object)MixtapeAudioSource != (Object)null) { MixtapeAudioSource.volume = NetworkVolume * LocalVolumeMultiplier; } if ((Object)(object)YouTubeAudioSource != (Object)null) { YouTubeAudioSource.volume = NetworkVolume * LocalVolumeMultiplier; } } public static void UpdateSpatialAudioSetting(bool useSpatial) { float spatialBlend = (useSpatial ? 1f : 0f); if ((Object)(object)MixtapeAudioSource != (Object)null) { MixtapeAudioSource.spatialBlend = spatialBlend; } if ((Object)(object)EffectsAudioSource != (Object)null) { EffectsAudioSource.spatialBlend = spatialBlend; } if ((Object)(object)YouTubeAudioSource != (Object)null) { YouTubeAudioSource.spatialBlend = spatialBlend; } Plugin.Log("[AudioMaster] Spatial audio updated: " + (useSpatial ? "enabled" : "disabled")); } public static void PlayFX(AudioClip clip) { if (Plugin.SoundEffectsEnabled && !((Object)(object)EffectsAudioSource == (Object)null) && !((Object)(object)clip == (Object)null)) { EffectsAudioSource.clip = clip; EffectsAudioSource.Play(); Plugin.Log("[AudioMaster] Playing sound effect: " + ((Object)clip).name); } } public static void PlayLaserFX() { if (Plugin.SoundEffectsEnabled && !((Object)(object)EffectsAudioSource == (Object)null)) { List list = new List(3) { SoundEffects.Laser1, SoundEffects.Laser2, SoundEffects.Laser3 }; int index = Random.Range(0, 2); EffectsAudioSource.volume = 0.2f; EffectsAudioSource.clip = list[index]; EffectsAudioSource.Play(); Plugin.Log("[AudioMaster] Playing laser sound effect: " + ((Object)EffectsAudioSource.clip).name); } } public static int GetNextSongIndex(int currentIndex) { if (SongQueue.Count < 2) { return currentIndex; } string valueOrDefault = IndexToSongHash.GetValueOrDefault(currentIndex, ""); if (string.IsNullOrEmpty(valueOrDefault)) { return (currentIndex + 1) % LoadedAudioClips.Count; } int num = SongQueue.IndexOf(valueOrDefault); if (num == -1) { return (currentIndex + 1) % LoadedAudioClips.Count; } int index = (num + 1) % SongQueue.Count; string key = SongQueue[index]; return SongHashToIndex.GetValueOrDefault(key, 0); } public static int GetPreviousSongIndex(int currentIndex) { if (SongQueue.Count < 2) { return currentIndex; } string valueOrDefault = IndexToSongHash.GetValueOrDefault(currentIndex, ""); if (string.IsNullOrEmpty(valueOrDefault)) { return (currentIndex - 1 + LoadedAudioClips.Count) % LoadedAudioClips.Count; } int num = SongQueue.IndexOf(valueOrDefault); if (num == -1) { return (currentIndex - 1 + LoadedAudioClips.Count) % LoadedAudioClips.Count; } int index = (num - 1 + SongQueue.Count) % SongQueue.Count; string key = SongQueue[index]; return SongHashToIndex.GetValueOrDefault(key, 0); } public static void SetYouTubeMode(bool enabled) { IsYouTubeMode = enabled; Plugin.Log($"[AudioMaster] YouTube mode set to {enabled}"); AudioMaster.OnYouTubeModeChanged?.Invoke(enabled); } public void HandleNextSong(string songHash, string songName) { if (Plugin.MilestonesEnabled) { MilestoneTracker.Instance.ReportProgress("ThankUNext"); } if (CurrentPlaybackState == PlaybackState.Stopped) { return; } if (SongHashToIndex.TryGetValue(songHash, out var value)) { CurrentSongIndex = value; PlaySongAtIndex(value); } else { Plugin.Log("[AudioMaster] Song not available for next song, playing white noise", "warning"); MixtapeAudioSource.clip = SoundEffects.WhiteNoise; MixtapeAudioSource.Play(); if (Plugin.MilestonesEnabled) { MilestoneTracker.Instance.ReportProgress("WhiteNoise"); } sPEAKerUI.Instance.ShowToast(songName); float length = SoundEffects.WhiteNoise.length; ((MonoBehaviour)this).StartCoroutine(FadeOut(length / 2f, length / 4f)); } UpdatePlaybackState(PlaybackState.Playing, CurrentSongIndex); if (PhotonNetwork.IsMasterClient && !IsYouTubeMode) { StartAutoSkipMonitoring(); } } public void HandleNetworkLoopStateChange(int loopState) { CurrentLoopState = (LoopState)loopState; if (!PhotonNetwork.IsMasterClient) { switch (CurrentLoopState) { case LoopState.None: sPEAKerUI.Instance.ShowToast("Loop is OFF"); break; case LoopState.All: sPEAKerUI.Instance.ShowToast("Looping ALL songs"); break; case LoopState.One: sPEAKerUI.Instance.ShowToast("Looping CURRENT song"); break; } } AudioMaster.OnLoopStateChanged?.Invoke(CurrentLoopState); Plugin.Log($"[AudioMaster] Network updated loop state: {CurrentLoopState}"); } public void HandleNetworkQueueSync(string[] newQueue) { SongQueue = newQueue.ToList(); Plugin.Log($"[AudioMaster] Synced queue from network. {SongQueue.Count} tracks."); AudioMaster.OnQueueSynced?.Invoke(); } public void Master_PlaySongByHash(string songHash) { if (PhotonNetwork.IsMasterClient && SongHashToIndex.TryGetValue(songHash, out var value) && value >= 0 && value < LoadedAudioClips.Count) { string name = ((Object)LoadedAudioClips[value]).name; Netcode.Instance.SendNextSong(songHash, name); } } public void Master_UpdateQueue(List newQueueOrder, bool broadcast = true) { if (PhotonNetwork.IsMasterClient) { SongQueue = new List(newQueueOrder); Plugin.Log($"[AudioMaster] Master updated queue. New count: {SongQueue.Count}"); if (broadcast) { Netcode.Instance.BroadcastSongQueue(SongQueue.ToArray()); } } } public void Master_ShuffleQueue(bool broadcast = true) { if (PhotonNetwork.IsMasterClient) { Random random = new Random(); SongQueue = SongQueue.OrderBy((string x) => random.Next()).ToList(); Plugin.Log("[AudioMaster] Master shuffled the queue."); sPEAKerUI.Instance.ShowToast("Queue has been shuffled!"); if (broadcast) { Netcode.Instance.BroadcastSongQueue(SongQueue.ToArray()); } } } public void SetFollow(Transform target) { _targetTransform = target; _targetCharacter = null; _isFollowing = (Object)(object)target != (Object)null; if (_isFollowing) { Plugin.Log("[AudioMaster] Set to follow transform: " + ((Object)target).name); } else { Plugin.Log("[AudioMaster] Stopped following."); } } public void SetFollow(Character target) { _targetCharacter = target; _targetTransform = null; _isFollowing = (Object)(object)target != (Object)null; if (_isFollowing) { Plugin.Log("[AudioMaster] Set to follow character: " + ((target != null) ? ((Object)target).name : null)); } else { Plugin.Log("[AudioMaster] Stopped following."); } } public static MixtapeInfo GetMixtapeForSongIndex(int songIndex) { return AvailableMixtapes.FirstOrDefault((MixtapeInfo m) => m.ContainsSongIndex(songIndex)) ?? new MixtapeInfo { Author = "unknown" }; } public Texture2D GetIconForSongHash(string songHash) { if (SongHashToIndex.TryGetValue(songHash, out var songIndex)) { MixtapeInfo mixtapeInfo = AvailableMixtapes.FirstOrDefault((MixtapeInfo m) => m.ContainsSongIndex(songIndex)); if (mixtapeInfo != null) { return mixtapeInfo.IconTexture; } } return null; } public static Texture2D GetCurrentMixtapeIcon() { if (LoadedAudioClips.Count == 0) { return null; } return GetMixtapeForSongIndex(CurrentSongIndex)?.IconTexture; } public string GetCurrentMixtapeAuthor() { if (LoadedAudioClips.Count == 0) { return null; } return GetMixtapeForSongIndex(CurrentSongIndex)?.Author; } private void LateUpdate() { //IL_0013: 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_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0050: 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_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_008d: 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_00b1: 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_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_0109: Unknown result type (might be due to invalid IL or missing references) if (_isFollowing) { Vector3 val = Vector3.zero; if ((Object)(object)_targetTransform != (Object)null) { val = _targetTransform.position; } else if ((Object)(object)_targetCharacter != (Object)null) { val = _targetCharacter.Center; } if ((Object)(object)MixtapeAudioSource != (Object)null && ((Component)MixtapeAudioSource).transform.position != val) { ((Component)MixtapeAudioSource).transform.position = val; } if ((Object)(object)EffectsAudioSource != (Object)null && ((Component)EffectsAudioSource).transform.position != val) { ((Component)EffectsAudioSource).transform.position = val; } if ((Object)(object)YouTubeAudioSource != (Object)null && ((Component)YouTubeAudioSource).transform.position != val) { ((Component)YouTubeAudioSource).transform.position = val; } } } private void OnGUI() { if (!((Object)(object)GUIManager.instance != (Object)null)) { return; } if (GUIManager.instance.pauseMenu.activeSelf) { if (CurrentPlaybackState != PlaybackState.Stopped) { sPEAKerUI.Instance.HideDuration(); sPEAKerUI.Instance.HideAuthorInfo(); } } else { if (CurrentPlaybackState == PlaybackState.Stopped || !((Object)(object)ActiveSource != (Object)null) || !((Object)(object)ActiveSource.clip != (Object)null)) { return; } sPEAKerUI.Instance.ShowDuration(ActiveSource.clip.length); if (IsYouTubeMode) { ytTrack ytTrack2 = YouTubePlayer.Instance?.currentTrack; if (ytTrack2 != null && !string.IsNullOrEmpty(ytTrack2.queuedBy)) { sPEAKerUI.Instance.ShowAuthorInfo(ytTrack2.queuedBy, IsYTMode: true); } else { sPEAKerUI.Instance.HideAuthorInfo(); } } else { MixtapeInfo mixtapeForSongIndex = GetMixtapeForSongIndex(CurrentSongIndex); if (!string.IsNullOrEmpty(mixtapeForSongIndex.Name)) { sPEAKerUI.Instance.ShowAuthorInfo(mixtapeForSongIndex.Author, IsYTMode: false); } else { sPEAKerUI.Instance.HideAuthorInfo(); } } } } private string FormatDuration(float totalSeconds) { int num = Mathf.FloorToInt(totalSeconds / 60f); int num2 = Mathf.FloorToInt(totalSeconds % 60f); return $"{num}:{num2:00}"; } private string NormalizePath(string path) { return path.Replace('\\', '/'); } private AudioType GetAudioTypeFromExtension(string filePath) { //IL_003c: 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_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) //IL_004a: Unknown result type (might be due to invalid IL or missing references) return (AudioType)(Path.GetExtension(filePath).ToLowerInvariant() switch { ".mp3" => 13, ".wav" => 20, ".ogg" => 14, _ => 0, }); } } public class FileHashCache { public const int CurrentVersion = 1; public int Version { get; set; } = 1; public int AlgorithmVersion { get; set; } public Dictionary Hashes { get; set; } = new Dictionary(); } public class CacheEntry { [JsonProperty("hash")] public string Hash { get; set; } [JsonProperty("lastModified")] public DateTime LastModified { get; set; } [JsonProperty("size")] public long Size { get; set; } } [Serializable] public class MixtapeMetadata { public string name; public string author; } public class MixtapeInfo { public string Name; public string Author; public string TotalLength; public int FirstSongIndex; public int LastSongIndex; public Texture2D IconTexture; public int SongCount; public string Path; public bool ContainsSongIndex(int index) { return index >= FirstSongIndex && index <= LastSongIndex; } } public enum PlaybackState { Stopped, Playing, Paused } public enum LoopState { None, All, One } public static class SoundEffects { public static AudioClip Paired { get; private set; } public static AudioClip Pairing { get; private set; } public static AudioClip Party { get; private set; } public static AudioClip PartyOff { get; private set; } public static AudioClip TurnOn { get; private set; } public static AudioClip VolMax { get; private set; } public static AudioClip WhiteNoise { get; private set; } public static AudioClip Laser1 { get; private set; } public static AudioClip Laser2 { get; private set; } public static AudioClip Laser3 { get; private set; } public static AudioClip Respawn { get; private set; } public static void SetClip(string filename, AudioClip clip) { switch (filename.ToLower().Replace(" ", "")) { case "connected": Paired = clip; break; case "connecting": Pairing = clip; break; case "party": Party = clip; break; case "partyoff": PartyOff = clip; break; case "turnon": TurnOn = clip; break; case "volmax": VolMax = clip; break; case "whitenoise": WhiteNoise = clip; break; case "laser1": Laser1 = clip; break; case "laser2": Laser2 = clip; break; case "laser3": Laser3 = clip; break; case "respawn": Respawn = clip; break; } } } public static class TaskExtensions { public static TaskAwaiter GetAwaiter(this AsyncOperation asyncOp) { TaskCompletionSource tcs = new TaskCompletionSource(); asyncOp.completed += delegate { tcs.TrySetResult(null); }; return ((Task)tcs.Task).GetAwaiter(); } } public enum PlaybackActionType { Play, Pause, Resume, Stop } public class Controller : MonoBehaviour { public Item item; private static ItemState _previousItemState; private bool _isTurningOn; public bool IsTurnedOn; private bool _wasUsingPrimary; private bool _wasUsingSecondary; private int _currentSongIndex; private PlaybackState _playbackState = PlaybackState.Stopped; private AudioClip[] _currentAudioClips; private const float VolumeStep = 0.05f; private bool _wasPocketed; public bool _audioIsFollowingCharacter; private Character _pocketedByCharacter; private int _followTargetPhotonViewID = -1; public DroneBehaviour droneBehaviour; private bool _waitingForSyncBeforeTurnOn = false; public static float SuppressFXWindow; public static void Static_PrimaryUse() { TheObserver.Instance.GetControllerInstance()?.PrimaryUse(); } public static void Static_NextSong() { TheObserver.Instance.GetControllerInstance()?.NextSong(); } public static void Static_PreviousSong() { TheObserver.Instance.GetControllerInstance()?.PreviousSong(); } public static void Static_ToggleLoop() { TheObserver.Instance.GetControllerInstance()?.ToggleLoop(); } private void Awake() { droneBehaviour = ((Component)this).GetComponent(); if ((Object)(object)droneBehaviour == (Object)null) { droneBehaviour = ((Component)this).gameObject.AddComponent(); } } private void Start() { Plugin.Log("[Controller] Start called for " + ((Object)((Component)this).gameObject).name); item = ((Component)this).GetComponent(); if ((Object)(object)item == (Object)null) { Plugin.Log("[Controller] No Item component found on " + ((Object)((Component)this).gameObject).name, "error"); return; } AudioMaster.EnsureInitialized(); Netcode.EnsureInitialized(); ((MonoBehaviour)this).StartCoroutine(InitializeWhenReady()); } private IEnumerator InitializeWhenReady() { Plugin.Log("[Controller] Waiting for AudioMaster to initialize..."); int waitCount = 0; while (!AudioMaster.IsInitialized && waitCount < 50) { yield return (object)new WaitForSeconds(0.1f); waitCount++; } if (!AudioMaster.IsInitialized) { Plugin.Log("[Controller] AudioMaster failed to initialize after 5 seconds!", "error"); yield break; } Item obj = item; obj.OnScrolled = (Action)Delegate.Combine(obj.OnScrolled, new Action(HandleScroll)); _currentAudioClips = AudioMaster.LoadedAudioClips.ToArray(); Plugin.Log("[Controller] Loaded " + _currentAudioClips.Length + " audio clips"); AudioMaster.Instance.SetFollow(((Component)this).transform); SyncWithPersistentAudio(); droneBehaviour.CreateSearchRadiusIndicator(); TheObserver.Instance.OnsPEAKerSpawned(this); } private void Update() { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) if (Plugin.MilestonesEnabled && AudioMaster.CurrentPlaybackState == PlaybackState.Playing) { Character localCharacter = Character.localCharacter; if ((Object)(object)localCharacter != (Object)null) { float num = Vector3.Distance(((Component)this).transform.position, localCharacter.Center); if (num >= 60f) { MilestoneTracker.Instance.ReportProgress("EnjoyTheSilence"); } } } if (AudioMaster.IsYouTubeMode) { PlaybackState currentPlaybackState = YouTubePlayer.Instance.CurrentPlaybackState; LoadingState currentLoadingState = YouTubePlayer.Instance.CurrentLoadingState; if (_playbackState != currentPlaybackState) { UpdateMainPrompt(currentPlaybackState); } else if (_playbackState == PlaybackState.Stopped) { string mainInteractPrompt = ((currentLoadingState == LoadingState.Loading) ? "BUFFERING" : "START RADIO"); item.UIData.mainInteractPrompt = mainInteractPrompt; } } bool isUsingPrimary = item.isUsingPrimary; if (isUsingPrimary && !_wasUsingPrimary) { PrimaryUse(); } _wasUsingPrimary = isUsingPrimary; bool isUsingSecondary = item.isUsingSecondary; if (isUsingSecondary && !_wasUsingSecondary) { HandleSecondaryUse(); } _wasUsingSecondary = isUsingSecondary; UpdateSecondaryPrompt(); } private void HandleSecondaryUse() { if (_isTurningOn) { return; } bool flag = Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305); bool flag2 = Input.GetKey((KeyCode)304) || Input.GetKey((KeyCode)303); bool flag3 = Input.GetKey((KeyCode)308) || Input.GetKey((KeyCode)307); if (AudioMaster.IsYouTubeMode) { if (flag2) { Netcode.Instance.SendYouTubePreviousRequest(); } else if (flag3) { ToggleYouTubeLoop(); } else { Netcode.Instance.SendYouTubeSkipRequest(); } } else if (flag) { ToggleShuffle(); } else if (flag2) { PreviousSong(); } else if (flag3) { ToggleLoop(); } else { NextSong(); } } public void UpdateMainPrompt(PlaybackState newState) { _playbackState = newState; IsTurnedOn = newState != PlaybackState.Stopped; if ((Object)(object)item == (Object)null) { return; } if (AudioMaster.IsYouTubeMode && newState == PlaybackState.Stopped) { LoadingState currentLoadingState = ytModuleCompatibility.GetCurrentLoadingState(); if (currentLoadingState == LoadingState.Loading) { item.UIData.mainInteractPrompt = "BUFFERING"; } else { item.UIData.mainInteractPrompt = "START RADIO"; } } else { switch (newState) { case PlaybackState.Playing: item.UIData.mainInteractPrompt = "PAUSE"; break; case PlaybackState.Paused: item.UIData.mainInteractPrompt = "PLAY"; break; case PlaybackState.Stopped: item.UIData.mainInteractPrompt = "TURN ON"; break; } } Plugin.Log("[Controller] Main prompt updated to: " + item.UIData.mainInteractPrompt); } private void UpdateSecondaryPrompt() { bool flag = Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305); bool flag2 = Input.GetKey((KeyCode)304) || Input.GetKey((KeyCode)303); bool flag3 = Input.GetKey((KeyCode)308) || Input.GetKey((KeyCode)307); string text = "NEXT"; if (AudioMaster.IsYouTubeMode) { if (flag2) { text = "PREVIOUS"; } else if (flag3) { text = "LOOP"; } } else if (flag) { text = "SHUFFLE"; } else if (flag2) { text = "PREVIOUS"; } else if (flag3) { text = "LOOP"; } if (item.UIData.secondaryInteractPrompt != text) { item.UIData.secondaryInteractPrompt = text; } } public void PrimaryUse() { if (_isTurningOn) { return; } if (AudioMaster.IsYouTubeMode && item.UIData.mainInteractPrompt == "START RADIO") { QueueUI.Instance.OpenAddTrackScreen(); return; } if (AudioMaster.IsYouTubeMode) { Plugin.Log("[Controller] Primary pressed in YouTube Mode. Requesting Toggle Pause."); Netcode.Instance.SendYouTubeTogglePauseRequest(); return; } Plugin.Log($"[Controller] Primary pressed. Current state: {_playbackState}"); switch (_playbackState) { case PlaybackState.Stopped: if (!IsTurnedOn) { ((MonoBehaviour)this).StartCoroutine(TurnOnSequence()); break; } item.UIData.mainInteractPrompt = "PAUSE"; StartPlayback(); break; case PlaybackState.Playing: item.UIData.mainInteractPrompt = "PLAY"; PausePlayback(); break; case PlaybackState.Paused: item.UIData.mainInteractPrompt = "PAUSE"; ResumePlayback(); break; } } private void HandleScroll(float scrollAmount) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Invalid comparison between Unknown and I4 if (!((Object)(object)AudioMaster.MixtapeAudioSource == (Object)null)) { InputScheme currentUsedInputScheme = InputHandler.GetCurrentUsedInputScheme(); float num = (((int)currentUsedInputScheme == 1) ? 0.0050000004f : 0.05f); float networkVolume = AudioMaster.NetworkVolume; float num2 = networkVolume; if (scrollAmount > 0f) { num2 += num; } else if (scrollAmount < 0f) { num2 -= num; } num2 = Mathf.Clamp(num2, 0f, 1f); Netcode.Instance.SendVolumeChange(num2); } } private IEnumerator TurnOnSequence() { item.UIData.mainInteractPrompt = "..."; if (!PhotonNetwork.IsMasterClient) { Plugin.Log("[Controller] Non-host turning on sPEAKer. Requesting sync first to prevent state contamination."); _waitingForSyncBeforeTurnOn = true; Netcode.Instance.RequestResyncFromHost(); float syncTimeout = 5f; float syncWaitTime = 0f; while (_waitingForSyncBeforeTurnOn && syncWaitTime < syncTimeout) { syncWaitTime += Time.deltaTime; yield return null; } if (_waitingForSyncBeforeTurnOn) { Plugin.Log("[Controller] Sync timeout before turn-on. Proceeding anyway.", "warning"); _waitingForSyncBeforeTurnOn = false; } if (AudioMaster.CurrentPlaybackState != PlaybackState.Stopped || _playbackState != PlaybackState.Stopped) { Plugin.Log($"[Controller] After sync: sPEAKer is already turned on (AudioMaster: {AudioMaster.CurrentPlaybackState}, Controller: {_playbackState}). Updating UI and aborting turn-on sequence."); _isTurningOn = false; IsTurnedOn = true; _playbackState = AudioMaster.CurrentPlaybackState; UpdateMainPrompt(AudioMaster.CurrentPlaybackState); yield break; } Plugin.Log("[Controller] After sync: sPEAKer is indeed off. Continuing with turn-on sequence."); } _isTurningOn = true; Netcode.Instance.SendPlayTurnOn(); if (Plugin.MilestonesEnabled) { MilestoneTracker.Instance.ReportProgress("Genesis"); } float waitTime = (((Object)(object)SoundEffects.TurnOn != (Object)null) ? SoundEffects.TurnOn.length : 0.5f); yield return (object)new WaitForSeconds(waitTime); IsTurnedOn = true; _isTurningOn = false; if (Plugin.TurnOnAndPlay && !AudioMaster.IsYouTubeMode) { _playbackState = PlaybackState.Playing; item.UIData.mainInteractPrompt = "PAUSE"; StartPlayback(); } else { _playbackState = PlaybackState.Paused; item.UIData.mainInteractPrompt = "PLAY"; AudioMaster.UpdatePlaybackState(PlaybackState.Paused, _currentSongIndex); } } private void ToggleShuffle() { if (!_isTurningOn) { if (PhotonNetwork.IsMasterClient) { AudioMaster.Instance.Master_ShuffleQueue(); } else { Netcode.Instance.SendRequestShuffleToggle(); } } } public void ToggleLoop() { if (!_isTurningOn) { if (AudioMaster.IsYouTubeMode && ytModuleCompatibility.enabled) { ToggleYouTubeLoop(); } else if (PhotonNetwork.IsMasterClient) { AudioMaster.ToggleLoop(); } else { Netcode.Instance.SendRequestLoopToggle(); } } } private void ToggleYouTubeLoop() { YouTubePlayer.YouTubeLoopState = ((YouTubePlayer.YouTubeLoopState == LoopState.None) ? LoopState.One : LoopState.None); if (PhotonNetwork.IsMasterClient) { Netcode.Instance.SendYouTubeLoopStateChange((int)YouTubePlayer.YouTubeLoopState); } string text = ((YouTubePlayer.YouTubeLoopState == LoopState.One) ? "Looping CURRENT song" : "Loop is OFF"); sPEAKerUI.Instance.ShowToast(text); Plugin.Log($"[Controller] YouTube loop toggled: {YouTubePlayer.YouTubeLoopState}"); } public void ToggleFloatMode() { if (droneBehaviour.IsFloating) { if ((Object)(object)droneBehaviour.FollowTarget == (Object)null || (Object)(object)droneBehaviour.FollowTarget == (Object)(object)Character.localCharacter) { Plugin.Log("[Controller] Toggling float mode OFF."); Netcode.Instance.SendFloatModeToggle(isFloating: false, -1); } else { Plugin.Log("[Controller] Request to disable float mode denied. Only the target player (" + ((Object)droneBehaviour.FollowTarget).name + ") can do this.", "warning"); } } else { Plugin.Log("[Controller] Toggling float mode ON. Hover and search engaged."); Netcode.Instance.SendFloatModeToggle(isFloating: true, -1); } } public void CycleDroneBehaviour() { if (!droneBehaviour.IsFloating) { Plugin.Log("[Controller] Cannot cycle drone behavior - not floating"); } else { droneBehaviour.CycleBehaviour(); } } public void RecallToPlayer(Character recallingPlayer) { if ((Object)(object)recallingPlayer == (Object)null) { Plugin.Log("[Controller] RecallToPlayer called with a null player.", "warning"); return; } Plugin.Log("[Controller] Recalling to player: " + ((Object)recallingPlayer).name); sPEAKerUI.Instance.ShowToast("Recalling sPEAKer"); int viewID = ((MonoBehaviourPun)recallingPlayer).photonView.ViewID; Netcode.Instance.SendFloatModeToggle(isFloating: true, viewID, isRecall: true); } private void StartPlayback() { if (_currentAudioClips == null || _currentAudioClips.Length == 0 || (Object)(object)AudioMaster.MixtapeAudioSource == (Object)null) { Plugin.Log("[Controller] Cannot start playback: no audio clips available", "warning"); return; } Plugin.Log("[Controller] Starting playback..."); if (PhotonNetwork.IsMasterClient) { Netcode.Instance.BroadcastStartPlayback(_currentSongIndex); } else { Netcode.Instance.SendRequestStartPlayback(_currentSongIndex); } } private void PausePlayback() { if (_playbackState == PlaybackState.Playing) { float num = (((Object)(object)AudioMaster.MixtapeAudioSource != (Object)null) ? AudioMaster.MixtapeAudioSource.time : 0f); Plugin.Log($"[Controller] Pausing at {num}s"); if (PhotonNetwork.IsMasterClient) { Netcode.Instance.BroadcastPausePlayback(num); } else { Netcode.Instance.SendRequestPausePlayback(num); } } } private void ResumePlayback() { if (_playbackState == PlaybackState.Paused) { float num = (((Object)(object)AudioMaster.ActiveSource != (Object)null) ? AudioMaster.ActiveSource.time : AudioMaster.CurrentPausedTime); Plugin.Log($"[Controller] Resuming from {num}s"); if (PhotonNetwork.IsMasterClient) { Netcode.Instance.BroadcastResumePlayback(num); } else { Netcode.Instance.SendRequestResumePlayback(num); } } } public void NextSong() { if (PhotonNetwork.IsMasterClient) { AudioClip[] currentAudioClips = _currentAudioClips; if (currentAudioClips == null || currentAudioClips.Length <= 1) { Plugin.Log("[Controller] Not enough songs to switch"); return; } _currentSongIndex = AudioMaster.GetNextSongIndex(_currentSongIndex); Plugin.Log("[Controller] Switching to song " + (_currentSongIndex + 1)); string songHash = AudioMaster.IndexToSongHash[_currentSongIndex]; string name = ((Object)_currentAudioClips[_currentSongIndex]).name; Netcode.Instance.SendNextSong(songHash, name); } else { Netcode.Instance.SendRequestNextSong(); } } public void PreviousSong() { if (Plugin.MilestonesEnabled) { MilestoneTracker.Instance.ReportProgress("SingItBack"); } if (PhotonNetwork.IsMasterClient) { AudioClip[] currentAudioClips = _currentAudioClips; if (currentAudioClips == null || currentAudioClips.Length <= 1) { Plugin.Log("[Controller] Not enough songs to switch"); return; } _currentSongIndex = AudioMaster.GetPreviousSongIndex(_currentSongIndex); Plugin.Log("[Controller] Switching to previous song " + (_currentSongIndex + 1)); string songHash = AudioMaster.IndexToSongHash[_currentSongIndex]; string name = ((Object)_currentAudioClips[_currentSongIndex]).name; Netcode.Instance.SendNextSong(songHash, name); } else { Netcode.Instance.SendRequestPreviousSong(); } } private void PlayCurrentSound() { if (_currentSongIndex < 0 || _currentSongIndex >= _currentAudioClips.Length) { Plugin.Log("[Controller] Invalid sound index: " + _currentSongIndex, "error"); return; } if ((Object)(object)AudioMaster.MixtapeAudioSource == (Object)null) { Plugin.Log("[Controller] Persistent audio source is null!", "error"); return; } AudioClip val = _currentAudioClips[_currentSongIndex]; if ((Object)(object)val == (Object)null) { Plugin.Log("[Controller] Clip at index " + _currentSongIndex + " is null!", "error"); return; } AudioMaster.MixtapeAudioSource.clip = val; AudioMaster.MixtapeAudioSource.Play(); AudioMaster.FirePlaybackAction(PlaybackActionType.Play, AudioMaster.MixtapeAudioSource.clip, AudioMaster.MixtapeAudioSource.time); Plugin.Log("[Controller] Playing sound " + (_currentSongIndex + 1) + ": " + ((Object)val).name); MixtapeInfo mixtapeForSongIndex = AudioMaster.GetMixtapeForSongIndex(_currentSongIndex); if (!string.IsNullOrEmpty(mixtapeForSongIndex.Name) && !AudioMaster.IsYouTubeMode) { sPEAKerUI.Instance.ShowToast(mixtapeForSongIndex.Name + " - " + ((Object)val).name); sPEAKerUI.Instance.ShowAuthorInfo(mixtapeForSongIndex.Author, AudioMaster.IsYouTubeMode); } else { sPEAKerUI.Instance.ShowToast(((Object)val).name); sPEAKerUI.Instance.HideAuthorInfo(); } sPEAKerUI.Instance.ShowDuration(val.length); } public void HandleNetworkStartPlayback(string songHash, string songName) { Plugin.Log("[Controller] CLIENT RECEIVED: HandleNetworkStartPlayback - songHash=" + songHash); _playbackState = PlaybackState.Playing; if (AudioMaster.SongHashToIndex.TryGetValue(songHash, out var value)) { _currentSongIndex = value; PlayCurrentSound(); if (Plugin.MilestonesEnabled) { MilestoneTracker.Instance.ReportProgress("UnstoppableForce"); } } else { Plugin.Log("[Controller] Song not available locally, playing white noise", "warning"); AudioMaster.MixtapeAudioSource.clip = SoundEffects.WhiteNoise; AudioMaster.MixtapeAudioSource.Play(); sPEAKerUI.Instance.ShowToast(songName); } AudioMaster.UpdatePlaybackState(PlaybackState.Playing, _currentSongIndex); if (PhotonNetwork.IsMasterClient && !AudioMaster.IsYouTubeMode) { AudioMaster.Instance.StartAutoSkipMonitoring(); } } public void HandleNetworkPausePlayback(float pauseTime) { Plugin.Log($"[Controller] HandleNetworkPausePlayback at {pauseTime}s"); _playbackState = PlaybackState.Paused; AudioMaster.UpdatePlaybackState(PlaybackState.Paused, _currentSongIndex, pauseTime); if ((Object)(object)AudioMaster.MixtapeAudioSource != (Object)null && AudioMaster.MixtapeAudioSource.isPlaying) { AudioMaster.MixtapeAudioSource.Pause(); AudioMaster.FirePlaybackAction(PlaybackActionType.Pause, AudioMaster.MixtapeAudioSource.clip, AudioMaster.MixtapeAudioSource.time); } if (PhotonNetwork.IsMasterClient) { AudioMaster.Instance.StopAutoSkipMonitoring(); } } public void HandleNetworkResumePlayback(float resumeTime) { Plugin.Log($"[Controller] HandleNetworkResumePlayback from {resumeTime}s"); _playbackState = PlaybackState.Playing; AudioMaster.UpdatePlaybackState(PlaybackState.Playing, _currentSongIndex, resumeTime); if ((Object)(object)AudioMaster.MixtapeAudioSource != (Object)null) { if ((Object)(object)AudioMaster.MixtapeAudioSource.clip == (Object)null) { PlayCurrentSound(); } AudioMaster.MixtapeAudioSource.time = resumeTime; AudioMaster.MixtapeAudioSource.UnPause(); AudioMaster.FirePlaybackAction(PlaybackActionType.Resume, AudioMaster.MixtapeAudioSource.clip, AudioMaster.MixtapeAudioSource.time); } if (PhotonNetwork.IsMasterClient && !AudioMaster.IsYouTubeMode) { AudioMaster.Instance.StartAutoSkipMonitoring(); } } public void HandleNetworkStopPlayback() { Plugin.Log("[Controller] HandleNetworkStopPlayback called"); _playbackState = PlaybackState.Stopped; IsTurnedOn = false; item.UIData.mainInteractPrompt = "TURN ON"; AudioMaster.UpdatePlaybackState(PlaybackState.Stopped, 0); if ((Object)(object)AudioMaster.MixtapeAudioSource != (Object)null && AudioMaster.MixtapeAudioSource.isPlaying) { AudioMaster.MixtapeAudioSource.Stop(); AudioMaster.MixtapeAudioSource.clip = null; } if (PhotonNetwork.IsMasterClient) { AudioMaster.Instance.StopAutoSkipMonitoring(); } sPEAKerUI.Instance.HideDuration(); sPEAKerUI.Instance.HideAuthorInfo(); } public void HandleNetworkNextSong(string songHash, string songName) { Plugin.Log("[Controller] HandleNetworkNextSong called. songHash=" + songHash); if ((Object)(object)AudioMaster.Instance != (Object)null) { AudioMaster.Instance.HandleNextSong(songHash, songName); if (!string.IsNullOrEmpty(songHash) && AudioMaster.SongHashToIndex.TryGetValue(songHash, out var value)) { _currentSongIndex = value; } } _playbackState = PlaybackState.Playing; UpdateMainPrompt(PlaybackState.Playing); } public void HandleNetworkVolumeChange(float newVolume) { Plugin.Log("[Controller] HandleNetworkVolumeChange called with value " + newVolume); AudioMaster.Instance.SetVolume(newVolume); } public void HandleRequestNextSong() { Plugin.Log("[Controller] HandleRequestNextSong called"); NextSong(); } public void HandleRequestPreviousSong() { Plugin.Log("[Controller] HandleRequestPreviousSong called"); PreviousSong(); } public void HandleNetworkFloatModeToggle(bool isFloating, int targetViewId, bool isRecall) { bool isFloating2 = droneBehaviour.IsFloating; bool flag = droneBehaviour.IsFollowingTarget(); droneBehaviour.IsFloating = isFloating; if (droneBehaviour.IsFloating) { PhotonView val = PhotonView.Find(targetViewId); Character val2 = ((val != null) ? ((Component)val).GetComponent() : null); if (isRecall && (Object)(object)val2 != (Object)null) { droneBehaviour.BeginRecall(val2); return; } droneBehaviour.FollowTarget = val2; bool flag2 = droneBehaviour.IsFollowingTarget(); if (!isRecall && Time.time > SuppressFXWindow) { if (!isFloating2 && !flag2) { AudioMaster.PlayFX(SoundEffects.Pairing); } else if (isFloating2 && !flag && flag2) { AudioMaster.PlayFX(SoundEffects.Paired); } } droneBehaviour.SetRigidbodyState(isFloating: true); } else { droneBehaviour.EndRecall(); droneBehaviour.FollowTarget = null; droneBehaviour.shouldLogNextSearch = true; droneBehaviour.SetRigidbodyState(isFloating: false); } } public void HandleSyncStateUpdate(PlaybackState networkState, string songHash) { Plugin.Log($"[Controller] HandleSyncStateUpdate: State={networkState}, Hash={songHash}"); _playbackState = networkState; IsTurnedOn = networkState != PlaybackState.Stopped; if (!string.IsNullOrEmpty(songHash) && AudioMaster.SongHashToIndex.TryGetValue(songHash, out var value)) { _currentSongIndex = value; } UpdateMainPrompt(networkState); Plugin.Log($"[Controller] Sync state update complete. State={_playbackState}, Index={_currentSongIndex}, TurnedOn={IsTurnedOn}"); } private void OnDestroy() { Plugin.Log("[Controller] OnDestroy called"); if ((Object)(object)item != (Object)null) { Item obj = item; obj.OnScrolled = (Action)Delegate.Remove(obj.OnScrolled, new Action(HandleScroll)); } } private void OnDisable() { //IL_00b1: 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) Plugin.Log("[Controller] OnDisable called."); if ((Object)(object)QueueUI.Instance != (Object)null && ((MenuWindow)QueueUI.Instance).isOpen) { ((MenuWindow)QueueUI.Instance).Close(); } if (!((Object)(object)item == (Object)null) && !((Object)(object)item.lastHolderCharacter == (Object)null)) { _wasPocketed = true; _pocketedByCharacter = item.lastHolderCharacter; AudioMaster.Instance.SetFollow(_pocketedByCharacter); _followTargetPhotonViewID = ((MonoBehaviourPun)_pocketedByCharacter).photonView.ViewID; _audioIsFollowingCharacter = true; _previousItemState = item.itemState; Netcode.Instance.CachedFollowViewID = _followTargetPhotonViewID; Netcode.Instance.CachedIsFollowingCharacter = _audioIsFollowingCharacter; TheObserver.Instance.OnsPEAKerPocketed(_pocketedByCharacter); } } private void OnEnable() { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Invalid comparison between Unknown and I4 Plugin.Log($"[Controller] OnEnable called. sPEAKer being unpocketed. {_wasPocketed} {(Object)(object)AudioMaster.Instance == (Object)null}"); if (_wasPocketed) { TheObserver.Instance.OnsPEAKerUnpocketed(this); } if (Plugin.MilestonesEnabled && (int)_previousItemState == 1) { ((MonoBehaviour)this).StartCoroutine(CheckHitTheFloor()); } MilestoneTracker.Instance.FlushBufferedUnlocks(); Netcode.Instance.CachedFollowViewID = _followTargetPhotonViewID; Netcode.Instance.CachedIsFollowingCharacter = _audioIsFollowingCharacter; ((MonoBehaviour)this).StartCoroutine(DelayedUIUpdate()); } private IEnumerator DelayedUIUpdate() { yield return null; PlaybackState currentState = ((!AudioMaster.IsYouTubeMode) ? AudioMaster.CurrentPlaybackState : YouTubePlayer.Instance.CurrentPlaybackState); UpdateMainPrompt(currentState); _currentSongIndex = AudioMaster.CurrentSongIndex; Plugin.Log($"[Controller] Delayed UI update complete. Final state: {_playbackState}"); } public void OnSyncCompleted() { if (_waitingForSyncBeforeTurnOn) { Plugin.Log("[Controller] Sync completed before turn-on. Continuing with turn-on sequence."); _waitingForSyncBeforeTurnOn = false; } } private void SyncWithPersistentAudio() { if ((Object)(object)AudioMaster.MixtapeAudioSource == (Object)null || !AudioMaster.MixtapeAudioSource.isPlaying) { Plugin.Log("[Controller] No active playback to sync with."); return; } _playbackState = PlaybackState.Playing; IsTurnedOn = true; _currentSongIndex = AudioMaster.CurrentSongIndex; if (_currentSongIndex >= 0 && _currentSongIndex < _currentAudioClips.Length) { AudioClip clip = AudioMaster.MixtapeAudioSource.clip; AudioClip val = _currentAudioClips[_currentSongIndex]; if ((Object)(object)clip != (Object)null && (Object)(object)val != (Object)null && ((Object)clip).name == ((Object)val).name) { Plugin.Log("[Controller] Synced with existing playback. song " + (_currentSongIndex + 1) + ": " + ((Object)val).name); } else { Plugin.Log("[Controller] Song mismatch detected during sync. client might be playing white noise"); } } else { Plugin.Log("[Controller] Invalid song index during sync: " + _currentSongIndex, "warning"); _currentSongIndex = 0; } if (PhotonNetwork.IsMasterClient) { AudioMaster.Instance.StartAutoSkipMonitoring(); } } public void KillYourself() { PhotonNetwork.Destroy(((Component)this).gameObject); } private IEnumerator CheckHitTheFloor() { yield return (object)new WaitForSeconds(1f); if (!((Object)(object)item == (Object)null) && !((Object)(object)AudioMaster.ActiveSource == (Object)null) && AudioMaster.ActiveSource.isPlaying && !((Object)(object)Character.localCharacter == (Object)null) && !((Object)(object)Character.localCharacter != (Object)(object)item.lastHolderCharacter)) { MilestoneTracker.Instance.ReportProgress("HitTheFloor"); } } } public class HostOnly : MonoBehaviour { private const bool DISABLED = true; private static HostOnly _instance; public bool IsActive; private Recorder _hostRecorder; private AudioClip _originalClip; private AudioClip _subClip; private AudioClip _nonStreamedClip; public static HostOnly Instance { get { bool flag = true; return null; } } 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)((Component)this).gameObject); } private void OnEnable() { AudioMaster.OnPlaybackAction += HandlePlaybackAction; } private void OnDisable() { AudioMaster.OnPlaybackAction -= HandlePlaybackAction; } public void EnableHostMode() { ((MonoBehaviour)this).StartCoroutine(EnableHostModeCoroutine()); } private IEnumerator EnableHostModeCoroutine() { if (!PhotonNetwork.IsMasterClient) { Plugin.Log("[HostOnly] Only the host can enable host mode.", "error"); yield break; } if (IsActive) { Plugin.Log("[HostOnly] Host mode is already active."); yield break; } if ((Object)(object)AudioMaster.ActiveSource.clip == (Object)null) { Plugin.Log("[HostOnly] No active clip. Start playback first!", "error"); sPEAKerUI.Instance.ShowToast("No music is playing!"); yield break; } Character localChar = Character.localCharacter; if ((Object)(object)localChar == (Object)null) { Plugin.Log("[HostOnly] No local character found.", "error"); yield break; } _hostRecorder = ((Component)localChar).GetComponentInChildren(); if ((Object)(object)_hostRecorder == (Object)null) { Plugin.Log("[HostOnly] No Recorder found on local character.", "error"); yield break; } if ((Object)(object)AudioMaster.ActiveSource.clip != (Object)null && AudioMaster.CurrentPlaybackState == PlaybackState.Playing) { float currentTime = AudioMaster.ActiveSource.time; if (AudioMaster.IsYouTubeMode) { yield return ((MonoBehaviour)this).StartCoroutine(LoadNonStreamedYouTubeClipCoroutine()); } else { yield return ((MonoBehaviour)this).StartCoroutine(LoadNonStreamedClipCoroutine(AudioMaster.CurrentSongIndex)); } AudioClip clipToFeed = (_originalClip = _nonStreamedClip ?? AudioMaster.ActiveSource.clip); AudioClip clipToPlay = ((currentTime > 0f) ? CreateSubClip(clipToFeed, currentTime) : null); if ((Object)(object)clipToPlay != (Object)null) { _subClip = clipToPlay; } else { clipToPlay = clipToFeed; } ConfigureRecorderForMusic(_hostRecorder, clipToPlay); IsActive = true; } else { ConfigureRecorderForMusic(_hostRecorder, null); IsActive = true; } Netcode.Instance.SendHostModeState(active: true); sPEAKerUI.Instance.ShowToast("Streaming music. Voice disabled"); Plugin.Log("[HostOnly] Host mode enabled."); } public void DisableHostMode() { if (IsActive && !((Object)(object)_hostRecorder == (Object)null)) { Plugin.Log("[HostOnly] Disabling host mode..."); _hostRecorder.SourceType = (InputSourceType)0; _hostRecorder.AudioClip = null; _hostRecorder.RecordingEnabled = true; DestroySubClip(); DestroyNonStreamedClip(); _originalClip = null; _hostRecorder = null; IsActive = false; Netcode.Instance.SendHostModeState(active: false); sPEAKerUI.Instance.ShowToast("Restored voice. Music disabled"); Plugin.Log("[HostOnly] Host mode disabled. Voice chat restored."); } } private void HandlePlaybackAction(PlaybackActionType action, AudioClip clip, float time) { if (!IsActive || (Object)(object)_hostRecorder == (Object)null) { return; } switch (action) { case PlaybackActionType.Play: ((MonoBehaviour)this).StartCoroutine(HandlePlayCoroutine()); break; case PlaybackActionType.Pause: _hostRecorder.RecordingEnabled = false; Plugin.Log($"[HostOnly] Pause: stopped recording at {time}s"); break; case PlaybackActionType.Resume: if ((Object)(object)_originalClip == (Object)null) { Plugin.Log("[HostOnly] Resume: no original clip to chop from.", "error"); break; } DestroySubClip(); _subClip = CreateSubClip(_originalClip, time); if ((Object)(object)_subClip != (Object)null) { _hostRecorder.AudioClip = _subClip; _hostRecorder.RecordingEnabled = true; Plugin.Log($"[HostOnly] Resume: feeding sub-clip from {time}s ({_subClip.samples} samples)"); } else { Plugin.Log("[HostOnly] Resume: sub-clip creation failed.", "error"); } break; case PlaybackActionType.Stop: _hostRecorder.RecordingEnabled = false; DestroySubClip(); DestroyNonStreamedClip(); _originalClip = null; Plugin.Log("[HostOnly] Stop: recording stopped, clip references cleared."); break; } } private IEnumerator HandlePlayCoroutine() { DestroySubClip(); DestroyNonStreamedClip(); if (AudioMaster.IsYouTubeMode) { yield return ((MonoBehaviour)this).StartCoroutine(LoadNonStreamedYouTubeClipCoroutine()); } else { yield return ((MonoBehaviour)this).StartCoroutine(LoadNonStreamedClipCoroutine(AudioMaster.CurrentSongIndex)); } AudioClip clipToFeed = (_originalClip = _nonStreamedClip ?? AudioMaster.ActiveSource.clip); FeedFullClip(clipToFeed); Plugin.Log("[HostOnly] Play: feeding clip '" + ((clipToFeed != null) ? ((Object)clipToFeed).name : null) + "'"); } private IEnumerator LoadNonStreamedClipCoroutine(int songIndex) { DestroyNonStreamedClip(); Task task = AudioMaster.Instance.GetNonStreamedClipAsync(songIndex); while (!task.IsCompleted) { yield return null; } if (task.IsFaulted) { Plugin.Log("[HostOnly] Failed to load non-streamed clip: " + task.Exception?.InnerException?.Message, "error"); } else { _nonStreamedClip = task.Result; } } private IEnumerator LoadNonStreamedYouTubeClipCoroutine() { DestroyNonStreamedClip(); ytTrack track = YouTubeQueue.Instance?.CurrentTrack; if (track == null) { Plugin.Log("[HostOnly] YouTube mode but no current track.", "error"); yield break; } string filePath = YouTubePlayer.Instance?.GetAudioFilePath(track.url); if (string.IsNullOrEmpty(filePath)) { Plugin.Log("[HostOnly] YouTube file not on disk for URL: " + track.url, "error"); yield break; } Plugin.Log("[HostOnly] Loading non-streamed YouTube clip from: " + Path.GetFileName(filePath)); string fileUrl = "file://" + filePath.Replace('\\', '/').Replace("+", "%2B").Replace("#", "%23"); Task task = LoadNonStreamedFromPathAsync(fileUrl, track.title ?? track.url); while (!task.IsCompleted) { yield return null; } if (task.IsFaulted) { Plugin.Log("[HostOnly] Failed to load non-streamed YouTube clip: " + task.Exception?.InnerException?.Message, "error"); } else { _nonStreamedClip = task.Result; } } private async Task LoadNonStreamedFromPathAsync(string fileUrl, string clipName) { UnityWebRequest www = UnityWebRequestMultimedia.GetAudioClip(fileUrl, (AudioType)14); try { ((DownloadHandlerAudioClip)www.downloadHandler).streamAudio = false; await (AsyncOperation)(object)www.SendWebRequest(); if ((int)www.result != 1) { Plugin.Log("[HostOnly] UnityWebRequest failed: " + www.error, "error"); return null; } AudioClip clip = DownloadHandlerAudioClip.GetContent(www); if ((Object)(object)clip == (Object)null) { return null; } ((Object)clip).name = clipName + " (HostOnly)"; clip.LoadAudioData(); while ((int)clip.loadState == 1) { await Task.Yield(); } if ((int)clip.loadState != 2) { Plugin.Log("[HostOnly] Audio data failed to load for YouTube clip.", "error"); return null; } Plugin.Log("[HostOnly] Non-streamed YouTube clip ready: " + ((Object)clip).name); return clip; } finally { ((IDisposable)www)?.Dispose(); } } private void FeedFullClip(AudioClip clip) { if (!((Object)(object)_hostRecorder == (Object)null)) { if ((Object)(object)clip != (Object)null) { _hostRecorder.AudioClip = clip; } _hostRecorder.RecordingEnabled = true; } } private AudioClip CreateSubClip(AudioClip original, float startTime) { if ((Object)(object)original == (Object)null || startTime < 0f) { return null; } int num = (int)(startTime * (float)original.frequency); if (num >= original.samples) { Plugin.Log($"[HostOnly] Sub-clip start ({num}) >= total samples ({original.samples}). Nothing to play.", "warning"); return null; } int num2 = original.samples - num; float[] array = new float[num2 * original.channels]; original.GetData(array, num); AudioClip val = AudioClip.Create("HostOnly_SubClip", num2, original.channels, original.frequency, false); val.SetData(array, 0); return val; } private void DestroySubClip() { if ((Object)(object)_subClip != (Object)null) { Object.Destroy((Object)(object)_subClip); _subClip = null; } } private void DestroyNonStreamedClip() { if ((Object)(object)_nonStreamedClip != (Object)null) { Object.Destroy((Object)(object)_nonStreamedClip); _nonStreamedClip = null; } } private void ConfigureRecorderForMusic(Recorder recorder, AudioClip clip) { WebRtcAudioDsp component = ((Component)recorder).GetComponent(); if ((Object)(object)component != (Object)null) { component.AGC = false; component.AEC = false; component.NoiseSuppression = false; component.HighPass = false; component.Bypass = true; Plugin.Log("[HostOnly] Disabled WebRTC voice processing."); } recorder.SamplingRate = (SamplingRate)48000; recorder.Bitrate = 510000; recorder.FrameDuration = (FrameDuration)20000; recorder.LoopAudioClip = false; recorder.TransmitEnabled = true; recorder.VoiceDetection = false; if ((Object)(object)clip != (Object)null) { recorder.AudioClip = clip; } recorder.SourceType = (InputSourceType)1; if ((Object)(object)clip != (Object)null) { recorder.RecordingEnabled = true; } } } internal static class Localization { internal static void Apply() { LocalizedText.mainTable["NAME_SPEAKER"] = new List(15) { "sPEAKer", "sPEAKer", "sPEAKer", "sPEAKer", "sPEAKer", "sPEAKer", "sPEAKer", "sPEAKer", "sPEAKer", "sPEAKer", "sPEAKer", "sPEAKer", "sPEAKer", "sPEAKer", "sPEAKer" }; LocalizedText.mainTable["..."] = new List(15) { "...", "...", "...", "...", "...", "...", "...", "...", "...", "...", "...", "...", "...", "...", "..." }; LocalizedText.mainTable["VOLUME"] = new List(15) { "Volume", "Volume", "Volume", "Lautstärke", "Volumen", "Volumen", "Volume", "Громкость", "Гучність", "音量", "音量", "音量", "볼륨", "Głośność", "Ses" }; LocalizedText.mainTable["TURN ON"] = new List(15) { "Turn On", "Allumer", "Accendere", "Einschalten", "Encender", "Encender", "Ligar", "Включить", "Увімкнути", "打开", "打開", "オンにする", "켜기", "Włącz", "Aç" }; LocalizedText.mainTable["PAUSE"] = new List(15) { "Pause", "Pause", "Pausa", "Pause", "Pausa", "Pausa", "Pausar", "Пауза", "Пауза", "暂停", "暫停", "一時停止", "일시정지", "Pauza", "Duraklat" }; LocalizedText.mainTable["SHUFFLE"] = new List(15) { "Shuffle", "Aléatoire", "Casuale", "Zufallswiedergabe", "Aleatorio", "Aleatorio", "Aleatório", "Перемешать", "Перемішати", "随机播放", "隨機播放", "シャッフル", "셔플", "Losowo", "Karıştır" }; LocalizedText.mainTable["PREVIOUS"] = new List(15) { "Previous", "Précédent", "Precedente", "Vorheriger", "Anterior", "Anterior", "Anterior", "Предыдущий", "Попередній", "上一首", "上一首", "前へ", "이전", "Poprzedni", "Önceki" }; LocalizedText.mainTable["LOOP"] = new List(15) { "Loop", "Répéter", "Ripeti", "Wiederholen", "Repetir", "Repetir", "Repetir", "Повторить", "Повторити", "循环", "循環", "リピート", "반복", "Pętla", "Döngü" }; LocalizedText.mainTable["START RADIO"] = new List(15) { "Start Radio", "Lancer la Radio", "Avvia Radio", "Radio starten", "Iniciar Radio", "Iniciar Radio", "Iniciar Rádio", "Запустить радио", "Запустити радіо", "播放电台", "播放電台", "ラジオを開始", "라디오 시작", "Uruchom Radio", "Radyo Başlat" }; LocalizedText.mainTable["BUFFERING"] = new List(15) { "Buffering...", "Chargement...", "Caricamento...", "Wird geladen...", "Cargando...", "Cargando...", "Carregando...", "Загрузка...", "Завантаження...", "缓冲中...", "正在緩衝...", "バッファリング中...", "버퍼링...", "Buforowanie...", "Arabelleğe alınıyor..." }; LocalizedText.mainTable["SHUFFLE_ON"] = new List(15) { "Shuffle is ON", "Lecture aléatoire activée", "Riproduzione casuale ON", "Zufallswiedergabe EIN", "Aleatorio activado", "Aleatorio activado", "Ordem aleatória LIGADA", "Вперемешку ВКЛ", "Перемішування УВІМК", "随机播放已开启", "隨機播放已開啟", "シャッフルオン", "셔플 켜짐", "Losowanie WŁĄCZONE", "Karıştırma AÇIK" }; LocalizedText.mainTable["SHUFFLE_OFF"] = new List(15) { "Shuffle is OFF", "Lecture aléatoire désactivée", "Riproduzione casuale OFF", "Zufallswiedergabe AUS", "Aleatorio desactivado", "Aleatorio desactivado", "Ordem aleatória DESLIGADA", "Вперемешку ВЫКЛ", "Перемішування ВИМК", "随机播放已关闭", "隨機播放已關閉", "シャッフルオフ", "셔플 꺼짐", "Losowanie WYŁĄCZONE", "Karıştırma KAPALI" }; LocalizedText.mainTable["LOOP_OFF"] = new List(15) { "Loop is OFF", "Répétition désactivée", "Ripetizione OFF", "Wiederholung AUS", "Repetir desactivado", "Repetir desactivado", "Repetição DESLIGADA", "Повтор ВЫКЛ", "Повтор ВИМК", "循环已关闭", "循環已關閉", "ループオフ", "반복 꺼짐", "Pętla WYŁĄCZONA", "Döngü KAPALI" }; LocalizedText.mainTable["LOOPING_ALL"] = new List(15) { "Looping ALL songs", "Répétition de TOUS les titres", "Ripetizione di TUTTI i brani", "Wiederhole ALLE Titel", "Repitiendo TODAS las canciones", "Repitiendo TODAS las canciones", "Repetindo TODAS as músicas", "Повтор ВСЕХ песен", "Повтор УСІХ пісень", "循环播放全部歌曲", "循環播放全部歌曲", "全曲をループ再生", "모든 곡 반복", "Pętla WSZYSTKICH piosenek", "TÜM şarkıları döngüye al" }; LocalizedText.mainTable["LOOPING_CURRENT"] = new List(15) { "Looping CURRENT song", "Répétition du titre ACTUEL", "Ripetizione del brano ATTUALE", "Wiederhole AKTUELLEN Titel", "Repitiendo canción ACTUAL", "Repitiendo canción ACTUAL", "Repetindo música ATUAL", "Повтор ТЕКУЩЕЙ песни", "Повтор ПОТОЧНОЇ пісні", "单曲循环", "單曲循環", "この曲をループ再生", "현재 곡 반복", "Pętla BIEŻĄCEJ piosenki", "MEVCUT şarkıyı döngüye al" }; LocalizedText.mainTable["NO_PREVIOUS_SONGS"] = new List(15) { "No previous songs available", "Aucun titre précédent disponible", "Nessun brano precedente disponibile", "Keine vorherigen Titel verfügbar", "No hay canciones anteriores", "No hay canciones anteriores", "Nenhuma música anterior disponível", "Нет предыдущих песен", "Немає попередніх пісень", "没有上一首歌曲", "沒有上一首歌曲", "前の曲はありません", "이전 곡 없음", "Brak poprzednich piosenek", "Önceki şarkı yok" }; LocalizedText.mainTable["PREVIOUS_SONG_NOT_CACHED"] = new List(15) { "Previous song no longer cached", "Le titre précédent n'est plus en cache", "Brano precedente non più in cache", "Vorheriger Titel nicht mehr zwischengespeichert", "La canción anterior ya no está en caché", "La canción anterior ya no está en caché", "Música anterior não está mais em cache", "Предыдущая песня удалена из кэша", "Попередню пісню видалено з кешу", "上一首歌曲已不在缓存中", "上一首歌曲已不在快取中", "前の曲はキャッシュされていません", "이전 곡이 더 이상 캐시되지 않음", "Poprzednia piosenka nie jest już w pamięci", "Önceki şarkı artık önbellekte değil" }; LocalizedText.mainTable["MIXTAPE_BY"] = new List(15) { "Mixtape by", "Mixtape par", "Mixtape di", "Mixtape von", "Mixtape de", "Mixtape de", "Mixtape por", "Микстейп от", "Мікстейп від", "音乐合集,创作者:", "音樂合輯,創作者:", "ミックステープ作成者:", "믹스테이프 작성자:", "Mixtape autorstwa", "Mixtape yapan:" }; LocalizedText.mainTable["QUEUE"] = new List(15) { "Queue", "File", "Coda", "Warteschlange", "Cola", "Cola", "Fila", "Очередь", "Черга", "队列", "佇列", "キュー", "대기열", "Kolejka", "Sıra" }; LocalizedText.mainTable["MILESTONES"] = new List(15) { "Milestones", "Succès", "Traguardi", "Erfolge", "Logros", "Logros", "Conquistas", "Достижения", "Досягнення", "成就", "成就", "実績", "업적", "Osiągnięcia", "Başarımlar" }; LocalizedText.mainTable["PLAY"] = new List(15) { "Play", "Lecture", "Riproduci", "Abspielen", "Reproducir", "Reproducir", "Reproduzir", "Воспроизвести", "Відтворити", "播放", "播放", "再生", "재생", "Odtwórz", "Oynat" }; LocalizedText.mainTable["NEXT"] = new List(15) { "Next", "Suivant", "Successivo", "Weiter", "Siguiente", "Siguiente", "Próximo", "Следующий", "Наступний", "下一首", "下一首", "次へ", "다음", "Następny", "Sonraki" }; LocalizedText.mainTable["RESET"] = new List(15) { "Reset", "Réinitialiser", "Ripristina", "Zurücksetzen", "Restablecer", "Restablecer", "Redefinir", "Сбросить", "Скинути", "重置", "重設", "リセット", "초기화", "Resetuj", "Sıfırla" }; LocalizedText.mainTable["LOOP_ALL"] = new List(15) { "Loop All", "Tout répéter", "Ripeti tutto", "Alle wiederholen", "Repetir todo", "Repetir todo", "Repetir tudo", "Повтор всего", "Повтор усього", "全部循环", "全部循環", "全て繰り返す", "전체 반복", "Zapętl wszystko", "Tümünü Döngüye Al" }; LocalizedText.mainTable["LOOP_ONE"] = new List(15) { "Loop One", "Répéter un", "Ripeti uno", "Eines wiederholen", "Repetir uno", "Repetir uno", "Repetir um", "Повтор одного", "Повтор одного", "单曲循环", "單曲循環", "一曲繰り返す", "한 곡 반복", "Zapętl jedno", "Birini Döngüye Al" }; LocalizedText.mainTable["SPEAKER_OFF"] = new List(15) { "sPEAKer is turned off!", "Le sPEAKer est éteint !", "sPEAKer è spento!", "sPEAKer ist ausgeschaltet!", "¡sPEAKer está apagado!", "¡sPEAKer está apagado!", "sPEAKer está desligado!", "sPEAKer выключен!", "sPEAKer вимкнено!", "sPEAKer已关闭!", "sPEAKer已關閉!", "sPEAKerはオフです!", "sPEAKer가 꺼져 있습니다!", "sPEAKer jest wyłączony!", "sPEAKer kapalı!" }; LocalizedText.mainTable["HOLD_TO_TURNON"] = new List(15) { "Hold sPEAKer in your hands to Turn On.", "Tenez sPEAKer en main pour l'allumer.", "Tieni sPEAKer in mano per accendere.", "Halten Sie sPEAKer in der Hand, um einzuschalten.", "Sostén sPEAKer en la mano para encender.", "Sostén sPEAKer en la mano para encender.", "Segure sPEAKer nas mãos para ligar.", "Держите sPEAKer в руке для включения.", "Тримайте sPEAKer в руці для включення.", "手持sPEAKer来打开。", "手持sPEAKer來打開。", "sPEAKerを手に持って電源を入れます。", "sPEAKer를 손에 들고 켜십시오.", "Trzymaj sPEAKer w ręku, aby włączyć.", "sPEAKer'ı açmak için elinizde tutun." }; LocalizedText.mainTable["THEME"] = new List(15) { "Theme", "Thème", "Tema", "Thema", "Tema", "Tema", "Tema", "Тема", "Тема", "主题", "主題", "テーマ", "테마", "Motyw", "Tema" }; LocalizedText.mainTable["SHOW_TRACK_DURATION"] = new List(15) { "Show Track Duration", "Durée de la piste", "Mostra durata traccia", "Titeldauer anzeigen", "Mostrar duración", "Mostrar duración", "Mostrar duração", "Показывать длительность", "Показати тривалість", "显示曲目时长", "顯示曲目時長", "曲の長さを表示", "트랙 길이 표시", "Pokaż długość utworu", "Parça Süresini Göster" }; LocalizedText.mainTable["DISPLAY_AUTHOR_INFO"] = new List(15) { "Display Author Info", "Infos sur l'auteur", "Mostra info autore", "Autoreninfo anzeigen", "Mostrar info autor", "Mostrar info autor", "Mostrar info do autor", "Показывать автора", "Показати автора", "显示作者信息", "顯示作者資訊", "作者情報を表示", "작성자 정보 표시", "Pokaż autora", "Yazar Bilgisini Göster" }; LocalizedText.mainTable["USE_SPATIAL_AUDIO"] = new List(15) { "Use Spatial Audio", "Audio spatial", "Audio spaziale", "Raumklang verwenden", "Audio espacial", "Audio espacial", "Áudio espacial", "Пространственный звук", "Просторовий звук", "使用空间音频", "使用空間音訊", "空間オーディオを使用", "공간 오디오 사용", "Użyj dźwięku przestrz.", "Uzamsal Ses Kullan" }; LocalizedText.mainTable["MAX_CACHED_SONGS"] = new List(15) { "Max Cached Songs", "Chansons en cache max", "Canzoni in cache max", "Max. Cache-Songs", "Máx. Canciones en Caché", "Máx. Canciones en Caché", "Máx. músicas em cache", "Макс. кэш песен", "Макс. кеш пісень", "最大缓存曲目数", "最大快取曲目數", "最大キャッシュ曲数", "최대 캐시 곡 수", "Maks. piosenek w cache", "Maks. Önbellekteki Şarkı" }; LocalizedText.mainTable["PREDOWNLOAD_COUNT"] = new List(15) { "Predownload Count", "Téléchargements préalables", "Precaricamenti", "Vorladungen", "Cantidad de Precargas", "Cantidad de Precargas", "Pré-downloads", "Предзагрузка", "Попереднє завантаження", "预加载数量", "預載入數量", "事前ダウンロード数", "사전 다운로드 수", "Liczba pobrań wstępnych", "Ön İndirme Sayısı" }; LocalizedText.mainTable["CACHE_SIZE"] = new List(15) { "Cache Size", "Taille du cache", "Dimensione cache", "Cache-Größe", "Tamaño de Caché", "Tamaño de Caché", "Tamanho do cache", "Размер кэша", "Розмір кешу", "缓存大小", "快取大小", "キャッシュサイズ", "캐시 크기", "Rozmiar pamięci cache", "Önbellek Boyutu" }; LocalizedText.mainTable["CLEAR"] = new List(15) { "Clear", "Effacer", "Cancella", "Leeren", "Limpiar", "Limpiar", "Limpar", "Очистить", "Очистити", "清除", "清除", "クリア", "지우기", "Wyczyść", "Temizle" }; LocalizedText.mainTable["TOOLTIP_MAX_CACHED_SONGS"] = new List(15) { "Maximum songs YouTube Radio keeps cached on disk. Lowering this immediately removes older files.", "Nombre maximum de chansons que YouTube Radio garde en cache sur le disque dur. Réduire ce paramètre supprime immédiatement les fichiers plus anciens.", "Numero massimo di brani che YouTube Radio mantiene in cache sul disco. Ridurre questo valore rimuove immediatamente i file più vecchi.", "Maximale Anzahl von Songs, die YouTube Radio auf der Festplatte speichert. Ein Verringern dieses Wertes löscht sofort ältere Dateien.", "Número máximo de canciones que YouTube Radio almacena en caché en el disco duro. Reducir este valor elimina inmediatamente los archivos más antiguos.", "Número máximo de canciones que YouTube Radio guarda en caché en el disco duro. Bajar este valor elimina inmediatamente los archivos más antiguos.", "Número máximo de músicas que o YouTube Radio armazena em cache no disco rígido. Reduzir esse valor remove imediatamente os arquivos mais antigos.", "Максимальное количество песен, которые YouTube Radio кэширует на диске. Уменьшение этого значения немедленно удаляет старые файлы.", "Максимальна кількість пісень, які YouTube Radio зберігає в кеші на диску. Зменшення цього значення одразу видаляє старі файли.", "YouTube Radio 在磁盘上缓存的最大歌曲数量。降低此值会立即删除较旧的文件。", "YouTube Radio 在磁碟中快取的最大歌曲數量。降低此值會立即刪除較舊的檔案。", "YouTube Radioがディスク上にキャッシュする曲の最大数。この値を下げるとすぐに古いファイルが削除されます。", "YouTube Radio가 디스크에 캐시하는 최대 곡 수. 이 값을 낮추면 오래된 파일이 즉시 삭제됩니다.", "Maksymalna liczba utworów przechowywanych w pamięci na dysku. Obniżenie tej wartości natychmiast usuwa starsze pliki.", "YouTube Radio'nun disk üzerinde önbelleğe aldığı maksimum şarkı sayısı. Bu değeri düşürmek eski dosyaları anında siler." }; LocalizedText.mainTable["TOOLTIP_PREDOWNLOAD_COUNT"] = new List(15) { "Number of upcoming songs YouTube Radio downloads in the background.", "Nombre de chansons à venir que YouTube Radio télécharge en arrière-plan.", "Numero di brani successivi che YouTube Radio scarica in background.", "Anzahl der kommenden Songs, die YouTube Radio im Hintergrund herunterlädt.", "Número de canciones próximas que YouTube Radio descarga en segundo plano.", "Número de canciones por venir que YouTube Radio descarga en segundo plano.", "Número de músicas futuras que o YouTube Radio baixa em segundo plano.", "Количество предстоящих песен, которые YouTube Radio загружает в фоновом режиме.", "Кількість наступних пісень, які YouTube Radio завантажує у фоновому режимі.", "YouTube Radio 在后台预先下载的后续歌曲数量。", "YouTube Radio 在背景預先下載的後續歌曲數量。", "YouTube Radioがバックグラウンドでダウンロードする、次に再生される曲の数。", "YouTube Radio가 백그라운드에서 미리 다운로드할 다음 곡의 수.", "Liczba nadchodzących utworów pobieranych w tle przez YouTube Radio.", "YouTube Radio'nun arka planda indirdiği sıradaki şarkı sayısı." }; LocalizedText.mainTable["AUTO_PLAY"] = new List(15) { "Auto Play", "Lecture auto", "Riproduzione auto", "Auto-Wiedergabe", "Reproducción automática", "Reproducción automática", "Reprodução automática", "Автовоспроизведение", "Автовідтворення", "自动播放", "自動播放", "自動再生", "자동 재생", "Autoodtwarzanie", "Otomatik Oynat" }; LocalizedText.mainTable["MS_CAT_TUTORIAL"] = new List(15) { "TUTORIAL", "TUTORIEL", "TUTORIAL", "TUTORIAL", "TUTORIAL", "TUTORIAL", "TUTORIAL", "ОБУЧЕНИЕ", "НАВЧАННЯ", "教程", "教程", "チュートリアル", "튜토리얼", "PORADNIK", "ÖĞRETİCİ" }; LocalizedText.mainTable["MS_CAT_STANDARD"] = new List(15) { "STANDARD", "STANDARD", "STANDARD", "STANDARD", "ESTÁNDAR", "ESTÁNDAR", "PADRÃO", "СТАНДАРТНОЕ", "СТАНДАРТНЕ", "标准", "標準", "スタンダード", "표준", "STANDARDOWY", "STANDART" }; LocalizedText.mainTable["MS_CAT_PROGRESS"] = new List(15) { "PROGRESS", "PROGRÈS", "PROGRESSO", "FORTSCHRITT", "PROGRESO", "PROGRESO", "PROGRESSO", "ПРОГРЕСС", "ПРОГРЕС", "进度", "進度", "進行状況", "진행", "POSTĘP", "İLERLEME" }; LocalizedText.mainTable["MS_CAT_SECRET"] = new List(15) { "SECRET", "SECRET", "SEGRETO", "GEHEIM", "SECRETO", "SECRETO", "SECRETO", "СЕКРЕТНОЕ", "ТАЄМНИЧЕ", "秘密", "秘密", "シークレット", "비밀", "TAJNE", "GİZLİ" }; LocalizedText.mainTable["MS_LEVELING_PROMPT"] = new List(15) { "Install Atomic's Leveling to claim {0} pending XP!", "Installez Atomic's Leveling pour réclamer {0} XP en attente !", "Installa Atomic's Leveling per reclamare {0} XP in sospeso!", "Installieren Sie Atomic's Leveling um {0} ausstehende XP zu beanspruchen!", "¡Instale Atomic's Leveling para reclamar {0} XP pendientes!", "¡Instale Atomic's Leveling para reclamar {0} XP pendientes!", "Instale Atomic's Leveling para reclamar {0} XP pendentes!", "Установите Atomic's Leveling чтобы получить {0} ожидающих XP!", "Встановіть Atomic's Leveling щоб отримати {0} очікування XP!", "安装 Atomic's Leveling 以领取 {0} 待处理的 XP!", "安裝 Atomic's Leveling 以領取 {0} 待處理的 XP!", "Atomic's Leveling をインストールして、{0} 保留中 XP を要求してください!", "Atomic's Leveling을 설치하여 {0} 보류 중인 XP를 받으세요!", "Zainstaluj Atomic's Leveling aby odebrać {0} oczekujące XP!", "Atomic's Leveling'i yükleyerek {0} bekleyen XP'yi talep edin!" }; LocalizedText.mainTable["MS_CLAIMED_XP"] = new List(15) { "Claimed {0} XP for Leveling!", "Réclamé {0} XP pour Leveling !", "Reclamato {0} XP per Leveling!", "Beansprucht {0} XP für Leveling!", "¡Reclamado {0} XP para Leveling!", "¡Reclamado {0} XP para Leveling!", "Reclamado {0} XP para Leveling!", "Получено {0} XP для Leveling!", "Отримано {0} XP для Leveling!", "为升级获得了 {0} XP!", "為升級獲得了 {0} XP!", "Leveling に {0} XP を要求しました!", "Leveling을 위해 {0} XP를 얻었습니다!", "Zebrano {0} XP dla Leveling!", "Leveling için {0} XP talep edildi!" }; LocalizedText.mainTable["MS_DESC_GENESIS"] = new List(15) { "Turn on sPEAKer for the first time", "Allumer sPEAKer pour la première fois", "Accendi sPEAKer per la prima volta", "sPEAKer zum ersten Mal einschalten", "Encender sPEAKer por primera vez", "Encender sPEAKer por primera vez", "Ligar sPEAKer pela primeira vez", "Впервые включить sPEAKer", "Вперше включити sPEAKer", "首次打开 sPEAKer", "首次開啟 sPEAKer", "sPEAKerを初めて起動する", "sPEAKer를 처음 켜기", "Włącz sPEAKer po raz pierwszy", "sPEAKer'ı ilk kez aç" }; LocalizedText.mainTable["MS_DESC_HITTHEFLOOR"] = new List(15) { "Drop sPEAKer while it's playing", "Jeter sPEAKer pendant qu'il joue", "Getta sPEAKer mentre riproduce", "sPEAKer während der Wiedergabe fallen lassen", "Suelta sPEAKer mientras reproduce", "Suelta sPEAKer mientras reproduce", "Deixa cair sPEAKer enquanto toca", "Бросить sPEAKer во время воспроизведения", "Кинути sPEAKer під час відтворення", "在播放时摔落 sPEAKer", "在播放時摔落 sPEAKer", "再生中にsPEAKerを落とす", "재생 중 sPEAKer 떨어뜨리기", "Upuść sPEAKer podczas odtwarzania", "Oynatırken sPEAKer'ı düşür" }; LocalizedText.mainTable["MS_DESC_THANKUNEXT"] = new List(15) { "Skip to the next song", "Passer à la chanson suivante", "Vai alla canzone successiva", "Zum nächsten Lied überspringen", "Saltar a la siguiente canción", "Saltar a la siguiente canción", "Pule para a próxima música", "Перейти к следующей песне", "Перейти на наступну пісню", "跳到下一首歌", "跳到下一首歌", "次の曲にスキップ", "다음 곡으로 건너뛰기", "Przejdź do następnej piosenki", "Sonraki şarkıya atla" }; LocalizedText.mainTable["MS_DESC_SUBEELBAJO"] = new List(15) { "Hit maximum volume", "Atteindre le volume maximum", "Raggiungere il volume massimo", "Maximale Lautstärke erreichen", "Alcanzar el volumen máximo", "Alcanzar el volumen máximo", "Atingir o volume máximo", "Достичь максимальной громкости", "Досягти максимальної гучності", "达到最大音量", "達到最大音量", "最大音量に達する", "최대 볼륨에 도달", "Osiągnij maksymalną głośność", "Maksimum ses seviyesine ulaş" }; LocalizedText.mainTable["MS_DESC_MUTE"] = new List(15) { "Set volume to minimum", "Réduire le volume au minimum", "Imposta il volume al minimo", "Lautstärke auf Minimum stellen", "Establecer volumen en mínimo", "Establecer volumen en mínimo", "Definir volume como mínimo", "Установить громкость на минимум", "Встановити гучність на мінімум", "将音量设置为最小", "將音量設置為最小", "音量を最小に設定", "볼륨을 최소로 설정", "Ustaw głośność na minimum", "Ses seviyesini minimuma ayarla" }; LocalizedText.mainTable["MS_DESC_MYWAY"] = new List(15) { "Adjust your Local Volume in pause menu", "Ajuster votre volume local dans le menu pause", "Regola il tuo volume locale dal menu pausa", "Passen Sie Ihre lokale Lautstärke im Pausemenü an", "Ajusta tu volumen local en el menú de pausa", "Ajusta tu volumen local en el menú de pausa", "Ajuste o volume local no menu de pausa", "Отрегулируйте громкость в меню паузы", "Відрегулюйте гучність у меню паузи", "在暂停菜单中调整本地音量", "在暫停菜單中調整本地音量", "一時停止メニューでローカル音量を調整", "일시정지 메뉴에서 로컬 볼륨 조정", "Dostosuj swoją głośność lokalną w menu pauzy", "Duraklat menüsünde yerel ses seviyesini ayarla" }; LocalizedText.mainTable["MS_DESC_ENJOYTHESILENCE"] = new List(15) { "Abandon sPEAKer while it's playing", "Abandonner sPEAKer pendant qu'il joue", "Abbandona sPEAKer mentre riproduce", "sPEAKer während der Wiedergabe verlassen", "Abandona sPEAKer mientras reproduce", "Abandona sPEAKer mientras reproduce", "Abandona sPEAKer enquanto toca", "Покинуть sPEAKer во время воспроизведения", "Залишити sPEAKer під час відтворення", "在播放时放弃 sPEAKer", "在播放時放棄 sPEAKer", "再生中にsPEAKerを放棄する", "재생 중 sPEAKer 포기", "Opuść sPEAKer podczas odtwarzania", "Oynatırken sPEAKer'ı terk et" }; LocalizedText.mainTable["MS_DESC_SINGITBACK"] = new List(15) { "Go back to previous song", "Revenir à la chanson précédente", "Torna alla canzone precedente", "Zurück zum vorherigen Lied", "Volver a la canción anterior", "Volver a la canción anterior", "Volte para a música anterior", "Вернуться к предыдущей песне", "Повернутися на попередню пісню", "返回上一首歌", "返回上一首歌", "前の曲に戻る", "이전 곡으로 돌아가기", "Wróć do poprzedniej piosenki", "Önceki şarkıya geri dön" }; LocalizedText.mainTable["MS_DESC_GETLUCKY"] = new List(15) { "Enable shuffle mode", "Activer le mode aléatoire", "Abilita modalità casuale", "Shuffle-Modus aktivieren", "Activar modo aleatorio", "Activar modo aleatorio", "Ativar modo aleatório", "Включить режим перемешивания", "Увімкнути режим перемішування", "启用随机播放模式", "啟用隨機播放模式", "シャッフルモードを有効にする", "셔플 모드 활성화", "Włącz tryb losowy", "Karıştırma modunu etkinleştir" }; LocalizedText.mainTable["MS_DESC_NOSURPRISES"] = new List(15) { "Restore the queue's original order", "Restaurer l'ordre original de la file", "Ripristina l'ordine originale della coda", "Wiederherstellen der ursprünglichen Reihenfolge", "Restaurar el orden original de la cola", "Restaurar el orden original de la cola", "Restaurar a ordem original da fila", "Восстановить исходный порядок очереди", "Відновити первісний порядок черги", "恢复队列的原始顺序", "恢復隊列的原始順序", "キューの元の順序を復元", "큐의 원래 순서 복원", "Przywróć pierwotną kolejność kolejki", "Kuyruk sırasını orijinal düzene getir" }; LocalizedText.mainTable["MS_DESC_ONEMORETIME"] = new List(15) { "Try out all loop modes", "Essayer tous les modes de boucle", "Prova tutte le modalità di loop", "Probiere alle Loop-Modi", "Prueba todos los modos de repetición", "Prueba todos los modos de repetición", "Tente todos os modos de loop", "Попробуйте все режимы повтора", "Спробуйте всі режими повтору", "尝试所有循环模式", "嘗試所有循環模式", "すべてのループモードを試す", "모든 루프 모드 시도", "Spróbuj wszystkie tryby pętli", "Tüm döngü modlarını dene" }; LocalizedText.mainTable["MS_DESC_WINDOWLICKER"] = new List(15) { "Open Queue UI", "Ouvrir l'interface de file", "Apri interfaccia coda", "Warteschlangen-Benutzeroberfläche öffnen", "Abrir interfaz de cola", "Abrir interfaz de cola", "Abrir interface de fila", "Открыть интерфейс очереди", "Відкрити інтерфейс черги", "打开队列界面", "打開隊列界面", "キューUIを開く", "대기열 UI 열기", "Otwórz interfejs kolejki", "Sıra UI'sını aç" }; LocalizedText.mainTable["MS_DESC_BORNSLIPPY"] = new List(15) { "Change the order of your queue", "Modifier l'ordre de votre file", "Cambia l'ordine della tua coda", "Ändern Sie die Reihenfolge Ihrer Warteschlange", "Cambia el orden de tu cola", "Cambia el orden de tu cola", "Altere a ordem da sua fila", "Измените порядок своей очереди", "Змініть порядок вашої черги", "改变队列的顺序", "改變隊列的順序", "キューの順序を変更", "큐의 순서 변경", "Zmień kolejność swojej kolejki", "Kuyruğun sırasını değiştir" }; LocalizedText.mainTable["MS_DESC_FREEBIRD"] = new List(15) { "Engage Drone Mode", "Activer le mode drone", "Attiva modalità drone", "Drohnenmodus aktivieren", "Activar modo dron", "Activar modo dron", "Ativar modo drone", "Включить режим дрона", "Увімкнути режим дрона", "启用无人机模式", "啟用無人機模式", "ドローンモードを有効にする", "드론 모드 활성화", "Włącz tryb drona", "Drone modunu etkinleştir" }; LocalizedText.mainTable["MS_DESC_BABYCOMEBACK"] = new List(15) { "Use Drone Recall", "Utiliser le rappel drone", "Usa richiamo drone", "Drohnen-Rückruf verwenden", "Usar llamada de dron", "Usar llamada de dron", "Usar recall do drone", "Использовать отзыв дрона", "Використовувати виклик дрона", "使用无人机召回", "使用無人機召回", "ドローンリコールを使用", "드론 리콜 사용", "Użyj odwołania drona", "Drone geri çağırmasını kullan" }; LocalizedText.mainTable["MS_DESC_WATCHINGMEDANCE"] = new List(15) { "Make sPEAKer dance", "Faire danser sPEAKer", "Fai ballare sPEAKer", "sPEAKer tanzen lassen", "Hacer bailar sPEAKer", "Hacer bailar sPEAKer", "Fazer sPEAKer dançar", "Заставить sPEAKer танцевать", "Змусити sPEAKer танцювати", "让 sPEAKer 跳舞", "讓 sPEAKer 跳舞", "sPEAKerを踊らせる", "sPEAKer를 춤추게 하기", "Poddaj sPEAKera do tańca", "sPEAKer'ı dans ettir" }; LocalizedText.mainTable["MS_DESC_RADIOVIDEO"] = new List(15) { "Enter YouTube Radio", "Entrer dans YouTube Radio", "Entra in YouTube Radio", "YouTube Radio betreten", "Ingresar a YouTube Radio", "Ingresar a YouTube Radio", "Entra no YouTube Radio", "Войти в YouTube Radio", "Увійти в YouTube Radio", "进入 YouTube 电台", "進入 YouTube 電台", "YouTube Radioに入る", "YouTube Radio 입장", "Wejdź na YouTube Radio", "YouTube Radio'ya gir" }; LocalizedText.mainTable["MS_DESC_BACKTOBASICS"] = new List(15) { "Switch from YouTube Radio back to Mixtapes", "Retour à la Radio YouTube vers les mixtapes", "Torna a YouTube Radio dai Mixtape", "Von YouTube Radio zurück zu Mixtapes wechseln", "Cambiar de YouTube Radio a Mixtapes", "Cambiar de YouTube Radio a Mixtapes", "Volte do YouTube Radio para Mixtapes", "Переключиться с YouTube Radio на Mixtape", "Перейдіть з YouTube Radio на Mixtape", "从 YouTube 电台切换回混音带", "從 YouTube 電台切換回混音帶", "YouTube RadioからMixtapeに戻す", "YouTube Radio에서 Mixtape로 전환", "Przełącz się z YouTube Radio na Mixtape", "YouTube Radio'dan Mixtape'lere geri dön" }; LocalizedText.mainTable["MS_DESC_SESSION"] = new List(15) { "Enter a YouTube playlist URL", "Entrez une URL de liste de lecture YouTube", "Inserisci URL playlist YouTube", "Geben Sie eine YouTube-Playlist-URL ein", "Ingrese una URL de lista de reproducción de YouTube", "Ingrese una URL de lista de reproducción de YouTube", "Digite uma URL de playlist do YouTube", "Введите URL плейлиста YouTube", "Введіть URL-адресу плейлисту YouTube", "输入 YouTube 播放列表 URL", "輸入 YouTube 播放列表 URL", "YouTubeプレイリスト URL を入力する", "YouTube 재생목록 URL 입력", "Wpisz URL playlisty YouTube", "YouTube oynatma listesi URL'si gir" }; LocalizedText.mainTable["MS_DESC_LIGHTSWITCH"] = new List(15) { "Visualize your music at the airport", "Visualisez votre musique à l'aéroport", "Visualizza la tua musica in aeroporto", "Visualisieren Sie Ihre Musik am Flughafen", "Visualiza tu música en el aeropuerto", "Visualiza tu música en el aeropuerto", "Visualize sua música no aeroporto", "Визуализируйте музыку в аэропорту", "Візуалізуйте музику в аеропорту", "在机场可视化您的音乐", "在機場可視化您的音樂", "空港で音楽を視覚化する", "공항에서 음악 시각화", "Wizualizuj muzykę na lotnisku", "Müziklerini havaalanında görselleştir" }; LocalizedText.mainTable["MS_DESC_SOUNDTRACK2MYLIFE"] = new List(15) { "Load a mixtape with your name as author", "Charger une mixtape avec votre nom en tant qu'auteur", "Carica un mixtape con il tuo nome come autore", "Laden Sie einen Mixtape mit Ihrem Namen als Autor", "Carga un mixtape con tu nombre como autor", "Carga un mixtape con tu nombre como autor", "Carregue um mixtape com seu nome como autor", "Загрузите микстейп с вашим именем в качестве автора", "Завантажте мікс-стрічку з вашим іменем як автором", "加载以您的名字作为作者的混音带", "加載以您的名字作為作者的混音帶", "作者として自分の名前を持つミックステープを読み込む", "작성자로서 이름이 있는 믹스테이프 로드", "Załaduj mixtape ze swoją nazwą jako autorem", "Adı yazar olarak olan bir mixtape yükle" }; LocalizedText.mainTable["MS_DESC_CRYSTALCLEAR"] = new List(15) { "Load only .ogg files", "Charger uniquement les fichiers .ogg", "Carica solo file .ogg", "Laden Sie nur .ogg-Dateien", "Carga solo archivos .ogg", "Carga solo archivos .ogg", "Carregue apenas arquivos .ogg", "Загрузить только файлы .ogg", "Завантажте лише файли .ogg", "仅加载 .ogg 文件", "僅加載 .ogg 文件", ".oggファイルのみを読み込む", ".ogg 파일만 로드", "Załaduj tylko pliki .ogg", "Yalnızca .ogg dosyalarını yükle" }; LocalizedText.mainTable["MS_DESC_DEJAVU"] = new List(15) { "Play a YouTube song that was already cached", "Jouer une chanson YouTube déjà en cache", "Riproduci una canzone YouTube già in cache", "Spielen Sie ein YouTube-Lied ab, das bereits gecacht ist", "Reproduce una canción de YouTube que ya estaba en caché", "Reproduce una canción de YouTube que ya estaba en caché", "Reproduza uma música do YouTube que já foi armazenada", "Воспроизведите песню YouTube, которая уже была в кэше", "Відтворіть пісню YouTube, яка вже була в кешу", "播放已缓存的 YouTube 歌曲", "播放已快取的 YouTube 歌曲", "既にキャッシュされているYouTube曲を再生", "이미 캐시된 YouTube 노래 재생", "Odtwórz piosenkę YouTube, która była już w pamięci", "Zaten önbelleğe alınmış bir YouTube şarkısını oynat" }; LocalizedText.mainTable["MS_DESC_THESICKNESS"] = new List(15) { "Pass out while carrying sPEAKer", "S'évanouir en portant sPEAKer", "Svenire mentre porti sPEAKer", "Ohnmächtig werden während man sPEAKer trägt", "Desmayarse mientras llevas sPEAKer", "Desmayarse mientras llevas sPEAKer", "Desmaiar enquanto carrega sPEAKer", "Потерять сознание во время ношения sPEAKer", "Втратити свідомість під час перенесення sPEAKer", "携带 sPEAKer 时昏迷", "攜帶 sPEAKer 時昏迷", "sPEAKerを携帯中に失神する", "sPEAKer를 들고 가는 동안 기절", "Zemdleć podczas noszenia sPEAKer", "sPEAKer taşırken bayıl" }; LocalizedText.mainTable["MS_DESC_AROUNDTHEWORLD"] = new List(15) { "Play every song in a mixtape", "Jouer toutes les chansons d'une mixtape", "Riproduci tutte le canzoni di un mixtape", "Alle Lieder in einem Mixtape abspielen", "Reproduce todas las canciones de un mixtape", "Reproduce todas las canciones de un mixtape", "Reproduza todas as músicas de um mixtape", "Воспроизведите все песни в микстейпе", "Відтворіть усі пісні в мікс-стрічці", "播放混音带中的每首歌曲", "播放混音帶中的每首歌曲", "ミックステープのすべての曲を再生", "믹스테이프의 모든 곡 재생", "Odtwórz wszystkie piosenki w mixtape", "Mixtape'teki tüm şarkıları oynat" }; LocalizedText.mainTable["MS_DESC_UNSTOPPABLEFORCE"] = new List(15) { "Play 30 songs in a single game", "Jouer 30 chansons en un seul jeu", "Riproduci 30 canzoni in un singolo gioco", "Spielen Sie 30 Lieder in einem einzigen Spiel", "Reproduce 30 canciones en un único juego", "Reproduce 30 canciones en una única sesión de juego", "Reproduza 30 músicas em um único jogo", "Воспроизведите 30 песен в одной игре", "Відтворіть 30 пісень в одній грі", "在一个游戏中播放 30 首歌曲", "在一個遊戲中播放 30 首歌曲", "1つのゲームで30曲を再生", "한 게임에서 30곡 재생", "Odtwórz 30 piosenek w jednej grze", "Tek oyunda 30 şarkı oynat" }; LocalizedText.mainTable["MS_DESC_SHAKEIT"] = new List(15) { "Perform all 9 different dance emotes", "Effectuer les 9 émojis de danse différents", "Esegui tutti i 9 diversi emote di danza", "Führe alle 9 verschiedenen Tanzbewegungen auf", "Ejecuta los 9 diferentes emotes de baile", "Ejecuta los 9 diferentes emotes de baile", "Execute todos os 9 emotes de dança diferentes", "Выполните все 9 различных танцевальных эмотиконов", "Виконайте всі 9 різних емотиконів танцю", "执行所有 9 种不同的舞蹈表情", "執行所有 9 種不同的舞蹈表情", "9種類のダンスエモートをすべて実行", "9가지 다른 춤 이모트 모두 수행", "Wykonaj wszystkie 9 różnych emote'ów tanecznych", "Tüm 9 farklı dans emote'sini gerçekleştir" }; LocalizedText.mainTable["MS_DESC_WEAREFAMILY"] = new List(15) { "Use sPEAKer in a full squad", "Utiliser sPEAKer dans une équipe complète", "Usa sPEAKer in una squadra completa", "Verwenden Sie sPEAKer in einem vollständigen Team", "Usa sPEAKer en un equipo completo", "Usa sPEAKer en un equipo completo", "Use sPEAKer em um time completo", "Используйте sPEAKer в полной команде", "Використовуйте sPEAKer в повній команді", "在完整小队中使用 sPEAKer", "在完整小隊中使用 sPEAKer", "フルスクワッドでsPEAKerを使用", "완전한 분대에서 sPEAKer 사용", "Użyj sPEAKer w pełnym zespole", "Tam bir ekipte sPEAKer kullan" }; LocalizedText.mainTable["MS_DESC_DONTSTOP"] = new List(15) { "Load 5 different Thunderstore mixtapes", "Charger 5 mixtapes Thunderstore différentes", "Carica 5 mixtape Thunderstore diversi", "Laden Sie 5 verschiedene Thunderstore-Mixtapes", "Carga 5 mixtapes diferentes de Thunderstore", "Carga 5 mixtapes diferentes de Thunderstore", "Carregue 5 mixtapes diferentes da Thunderstore", "Загрузите 5 различных микстейпов Thunderstore", "Завантажте 5 різних мікс-стрічок Thunderstore", "加载 5 个不同的 Thunderstore 混音带", "加載 5 個不同的 Thunderstore 混音帶", "5つの異なるThunderstore Mixtapeを読み込む", "5개의 다른 Thunderstore 믹스테이프 로드", "Załaduj 5 różnych mixtape'ów Thunderstore", "5 farklı Thunderstore mixtape yükle" }; LocalizedText.mainTable["MS_DESC_ELECTRICRELAXATION"] = new List(15) { "Load 200+ songs", "Charger 200+ chansons", "Carica 200+ canzoni", "Laden Sie 200+ Lieder", "Carga 200+ canciones", "Carga 200+ canciones", "Carregue 200+ músicas", "Загрузить 200+ песен", "Завантажте 200+ пісень", "加载 200+ 首歌曲", "加載 200+ 首歌曲", "200+曲を読み込む", "200+ 곡 로드", "Załaduj 200+ piosenek", "200+ şarkı yükle" }; LocalizedText.mainTable["MS_DESC_1000ROUNDS"] = new List(15) { "Play music for a total of 1000 minutes", "Jouer de la musique pendant un total de 1000 minutes", "Riproduci musica per un totale di 1000 minuti", "Spielen Sie Musik für insgesamt 1000 Minuten", "Reproduce música durante un total de 1000 minutos", "Reproduce música durante un total de 1000 minutos", "Reproduza música por um total de 1000 minutos", "Воспроизводите музыку в течение 1000 минут", "Відтворюйте музику протягом 1000 хвилин", "播放总共 1000 分钟的音乐", "播放總共 1000 分鐘的音樂", "合計1000分間音楽を再生", "총 1000분 동안 음악 재생", "Odtwórz muzykę przez łącznie 1000 minut", "Toplam 1000 dakika müzik oynat" }; LocalizedText.mainTable["MS_DESC_WHITENOISE"] = new List(15) { "Have the host play a song that you don't have", "Faire jouer l'hôte une chanson que vous n'avez pas", "Fai suonare all'host una canzone che non hai", "Lassen Sie den Host ein Lied spielen, das Sie nicht haben", "Haz que el host reproduzca una canción que no tienes", "Haz que el host reproduzca una canción que no tienes", "Deixe o anfitrião tocar uma música que você não tem", "Заставить хоста воспроизвести песню, которой у вас нет", "Змусіть хоста відтворити пісню, яку у вас немає", "让主机播放您没有的歌曲", "讓主機播放您沒有的歌曲", "ホストが持っていない曲を再生させる", "호스트가 가지지 않은 곡 재생", "Zmień hosta na grę w piosenką, której nie masz", "Ev sahibinin sahip olmadığı bir şarkı çalmasını sağla" }; LocalizedText.mainTable["MS_DESC_LARGERTHANLIFE"] = new List(15) { "Spam dance emotes until sPEAKer is big enough", "Spammer les émotes de danse jusqu'à ce que sPEAKer soit assez grand", "Spam emote di danza finché sPEAKer non è abbastanza grande", "Tanzbewegungen spammen, bis sPEAKer groß genug ist", "Haz que sPEAKer baile repetidamente hasta que sea lo suficientemente grande", "Haz que sPEAKer baile repetidamente hasta que sea lo suficientemente grande", "Envie spam de emotes de dança até que sPEAKer fique grande o suficiente", "Спамьте танцевальными эмотиконами, пока sPEAKer не будет достаточно большим", "Спамте емотиконами танцю, поки sPEAKer не буде досить великим", "垃圾邮件舞蹈表情,直到 sPEAKer 足够大", "垃圾郵件舞蹈表情,直到 sPEAKer 足夠大", "sPEAKerが十分に大きくなるまでダンスエモートをスパムします", "sPEAKer가 충분할 때까지 춤 이모트를 스팸", "Spam emote'ami tanczymi aż sPEAKer będzie wystarczająco duży", "sPEAKer yeterince büyük olana kadar dans emote'lerini spam yap" }; LocalizedText.mainTable["MS_DESC_BACKFROMTHEDEAD"] = new List(15) { "Witness sPEAKer respawning", "Témoin sPEAKer réapparaître", "Testimone sPEAKer che riappare", "sPEAKer beim Wiedererscheinen beobachten", "Presencia el renacimiento de sPEAKer", "Presencia a sPEAKer reapareciendo", "Testemunhe sPEAKer reaparecendo", "Свидетель sPEAKer возрождается", "Спостерігайте sPEAKer, що знову з'являється", "见证 sPEAKer 重生", "見證 sPEAKer 重生", "sPEAKerの復活を目撃する", "sPEAKer 부활 목격", "Obserwuj odrodzenie sPEAKer", "sPEAKer'ın yeniden doğuşuna tanık ol" }; LocalizedText.mainTable["MS_DESC_SHOOTINGSTARS"] = new List(15) { "Enable manual drone mode", "Activer le mode drone manuel", "Abilita modalità drone manuale", "Manuellen Drohnenmodus aktivieren", "Activar modo dron manual", "Activar modo dron manual", "Ativar modo drone manual", "Включить ручной режим дрона", "Увімкнути ручний режим дрона", "启用手动无人机模式", "啟用手動無人機模式", "手動ドローンモードを有効にする", "수동 드론 모드 활성화", "Włącz ręczny tryb drona", "Manuel drone modunu etkinleştir" }; LocalizedText.mainTable["MS_DESC_MURDERONTHEDANCEFLOOR"] = new List(15) { "Kill someone using the drone's lasers", "Tuer quelqu'un avec les lasers du drone", "Uccidi qualcuno usando i laser del drone", "Töten Sie jemanden mit den Lasern der Drohne", "Mata a alguien con los láseres del dron", "Mata a alguien con los láseres del dron", "Mate alguém com os lasers do drone", "Убить кого-то с помощью лазеров дрона", "Вб'йте когось, використовуючи лазери дрона", "使用无人机的激光杀死某人", "使用無人機的激光殺死某人", "ドローンのレーザーを使って誰かを殺す", "드론의 레이저로 누군가를 죽이기", "Zabij kogoś za pomocą laserów drona", "Drone'un lazerlerini kullanarak birini öldür" }; LocalizedText.mainTable["MS_DESC_GHOSTTOWN"] = new List(15) { "Possess sPEAKer", "Posséder sPEAKer", "Possedere sPEAKer", "sPEAKer besitzen", "Poseer sPEAKer", "Poseer sPEAKer", "Possuir sPEAKer", "Завладеть sPEAKer", "Заволодіти sPEAKer", "占据 sPEAKer", "占據 sPEAKer", "sPEAKerに乗り移る", "sPEAKer 빙의하기", "Opętaj sPEAKer", "sPEAKer'ı ele geçir" }; LocalizedText.mainTable["MS_DESC_THEHUB"] = new List(15) { "Search for a song in YouTube Radio", "Rechercher une chanson dans YouTube Radio", "Cerca una canzone in YouTube Radio", "Suche einen Song im YouTube Radio", "Busca una canción en YouTube Radio", "Busca una canción en YouTube Radio", "Pesquise uma música no YouTube Radio", "Найдите песню в YouTube Radio", "Знайдіть пісню в YouTube Radio", "在 YouTube Radio 中搜索歌曲", "在 YouTube Radio 中搜尋歌曲", "YouTube Radioで曲を検索する", "YouTube Radio에서 노래 검색하기", "Wyszukaj utwór w YouTube Radio", "YouTube Radio'da bir şarkı ara" }; LocalizedText.mainTable["MS_HINT_WHITENOISE"] = new List(15) { "something missing...", "quelque chose manque...", "qualcosa manca...", "etwas fehlt...", "falta algo...", "falta algo...", "falta algo...", "чего-то не хватает...", "чогось не вистачає...", "缺少什么...", "缺少什麼...", "何かが足りない...", "뭔가 빠졌어...", "czegoś brakuje...", "bir şey eksik..." }; LocalizedText.mainTable["MS_HINT_LARGERTHANLIFE"] = new List(15) { "feels like dancing a LOT", "l'impression de danser BEAUCOUP", "sembra di ballare MOLTO", "fühlt sich an wie VIEL tanzen", "se siente como bailar MUCHO", "es como bailar MUCHO", "parece estar dançando MUITO", "похоже на МНОГО танцев", "схоже на БАГАТО танців", "感觉很像在跳舞", "感覺很像在跳舞", "たくさん踊っているような感じ", "많이 춤을 추는 것 같은 느낌", "wygląda jak DUŻO tańczenia", "çok dans etmek gibi geliyor" }; LocalizedText.mainTable["MS_HINT_BACKFROMTHEDEAD"] = new List(15) { "there's a 'technique' for this", "il y a une 'technique' pour cela", "c'è una 'tecnica' per questo", "es gibt eine 'Technik' dafür", "hay una 'técnica' para esto", "hay una 'técnica' para esto", "há uma 'técnica' para isso", "есть 'техника' для этого", "є 'техніка' для цього", "有一个'技巧'来做这个", "有一個'技巧'來做這個", "これにはコツがあります", "이를 위한 '기술'이 있습니다", "jest 'technika' na to", "bunun için bir 'teknik' var" }; LocalizedText.mainTable["MS_HINT_SHOOTINGSTARS"] = new List(15) { "a strange combination", "une combinaison étrange", "una combinazione strana", "eine seltsame Kombination", "una combinación extraña", "una combinación extraña", "uma combinação estranha", "странная комбинация", "дивна комбінація", "一个奇怪的组合", "一個奇怪的組合", "奇妙な組み合わせ", "이상한 조합", "dziwna kombinacja", "garip bir kombinasyon" }; LocalizedText.mainTable["INVALID_URL"] = new List(15) { "Invalid URL", "URL invalide", "URL non valido", "Ungültige URL", "URL inválida", "URL inválida", "URL inválida", "Неверный URL", "Невірний URL", "无效链接", "無效連結", "無効なURL", "잘못된 URL", "Nieprawidłowy URL", "Geçersiz URL" }; LocalizedText.mainTable["ADD_TRACK_TITLE"] = new List(15) { "Add Track", "Ajouter une piste", "Aggiungi brano", "Titel hinzufügen", "Añadir pista", "Añadir pista", "Adicionar faixa", "Добавить трек", "Додати трек", "添加曲目", "新增曲目", "トラックを追加", "트랙 추가", "Dodaj utwór", "Parça Ekle" }; LocalizedText.mainTable["PLAY_NEXT"] = new List(15) { "Play Next", "Jouer ensuite", "Riproduci dopo", "Als nächstes spielen", "Reproducir siguiente", "Reproducir siguiente", "Tocar a seguir", "Следующим", "Наступним", "下一首播放", "下一首播放", "次に再生", "다음에 재생", "Odtwórz następny", "Sıradaki Oynat" }; LocalizedText.mainTable["ADD_TO_END"] = new List(15) { "Add to End", "Ajouter à la fin", "Aggiungi in fondo", "Am Ende hinzufügen", "Añadir al final", "Añadir al final", "Adicionar ao final", "В конец", "В кінець", "添加到末尾", "新增至末尾", "末尾に追加", "맨 뒤에 추가", "Dodaj na koniec", "Sona Ekle" }; LocalizedText.mainTable["RESTART_RADIO"] = new List(15) { "Start Radio", "Démarrer la radio", "Avvia la radio", "Radio starten", "Iniciar radio", "Iniciar radio", "Iniciar rádio", "Запустить радио", "Запустити радіо", "开始电台", "開始電台", "ラジオを開始", "라디오 시작", "Uruchom radio", "Radyoyu Başlat" }; LocalizedText.mainTable["PLAYLIST_STARTED"] = new List(15) { "Playlist started.", "Liste de lecture lancée.", "Playlist avviata.", "Playlist gestartet.", "Lista de reproducción iniciada.", "Lista de reproducción iniciada.", "Playlist iniciada.", "Плейлист запущен.", "Плейлист запущено.", "播放列表已开始。", "播放列表已開始。", "プレイリストを開始しました。", "플레이리스트가 시작됐습니다.", "Playlista uruchomiona.", "Oynatma listesi başladı." }; LocalizedText.mainTable["PLAYLIST_QUEUED"] = new List(15) { "Playlist added to queue.", "Liste de lecture ajoutée à la file.", "Playlist aggiunta alla coda.", "Playlist zur Warteschlange hinzugefügt.", "Lista añadida a la cola.", "Lista agregada a la cola.", "Playlist adicionada à fila.", "Плейлист добавлен в очередь.", "Плейлист додано до черги.", "播放列表已加入队列。", "播放列表已加入佇列。", "プレイリストをキューに追加しました。", "플레이리스트를 대기열에 추가했습니다.", "Playlista dodana do kolejki.", "Oynatma listesi sıraya eklendi." }; LocalizedText.mainTable["MS_PLAYLIST_PROMPT"] = new List(15) { "Play the tracks milestone names are based off:", "Écoutez les morceaux qui inspirent les noms des hauts faits :", "Ascolta i brani da cui prendono nome i traguardi:", "Höre die Titel, nach denen die Meilensteine benannt sind:", "Escucha las canciones en las que se basan los nombres de los logros:", "Escuchá las canciones en las que se basan los nombres de los logros:", "Ouça as faixas que dão nome às conquistas:", "Послушайте треки, по которым названы достижения:", "Послухайте треки, на честь яких названо досягнення:", "里程碑名称所参考的曲目:", "里程碑名稱所參考的曲目:", "マイルストーン名の元になった曲を再生:", "마일스톤 이름의 바탕이 된 곡을 재생하세요:", "Posłuchaj utworów, od których pochodzą nazwy osiągnięć:", "Kilometre taşı adlarının dayandığı parçaları dinleyin:" }; LocalizedText.mainTable["MS_PLAYLIST_BUTTON"] = new List(15) { "Queue Milestones playlist", "Ajouter la playlist des hauts faits", "Aggiungi playlist traguardi", "Meilenstein-Playlist anhängen", "Añadir playlist de logros", "Agregar playlist de logros", "Enfileirar playlist de conquistas", "Добавить плейлист достижений", "Додати плейлист досягнень", "添加里程碑播放列表", "新增里程碑播放列表", "マイルストーンのプレイリストを追加", "마일스톤 플레이리스트 추가", "Dodaj playlistę osiągnięć", "Kilometre taşı listesini ekle" }; LocalizedText.mainTable["TRACK_ADDED"] = new List(15) { "Track added!", "Piste ajoutée !", "Brano aggiunto!", "Titel hinzugefügt!", "¡Pista añadida!", "¡Pista añadida!", "Faixa adicionada!", "Трек добавлен!", "Трек додано!", "曲目已添加!", "曲目已新增!", "トラックを追加しました!", "트랙이 추가되었습니다!", "Utwór dodany!", "Parça Eklendi!" }; LocalizedText.mainTable["ADD_TRACK_PLACEHOLDER"] = new List(15) { "Enter your query or supported URL...", "Entrez votre requête ou URL prise en charge...", "Inserisci la tua ricerca o URL supportato...", "Suchbegriff oder unterstützte URL eingeben...", "Escribe tu búsqueda o URL compatible...", "Escribe tu búsqueda o URL compatible...", "Digite sua busca ou URL suportada...", "Введите запрос или поддерживаемый URL...", "Введіть запит або підтримуваний URL...", "输入搜索内容或支持的链接...", "輸入搜尋內容或支援的連結...", "クエリまたは対応URLを入力...", "검색어 또는 지원되는 URL 입력...", "Wpisz zapytanie lub obsługiwany URL...", "Sorgunuzu veya desteklenen URL'yi girin..." }; LocalizedText.mainTable["SEARCH_PRESS_ENTER"] = new List(15) { "Press ENTER to search", "Appuyez sur ENTRÉE pour chercher", "Premi INVIO per cercare", "ENTER drücken zum Suchen", "Pulsa ENTER para buscar", "Presiona ENTER para buscar", "Pressione ENTER para buscar", "Нажмите ENTER для поиска", "Натисніть ENTER для пошуку", "按 ENTER 搜索", "按 ENTER 搜尋", "ENTERで検索", "ENTER를 눌러 검색", "Naciśnij ENTER, aby wyszukać", "Aramak için ENTER'a basın" }; LocalizedText.mainTable["UNSUPPORTED_URL"] = new List(15) { "Unsupported URL", "URL non prise en charge", "URL non supportato", "URL nicht unterstützt", "URL no compatible", "URL no compatible", "URL não suportada", "URL не поддерживается", "URL не підтримується", "不支持的链接", "不支援的連結", "サポートされていないURL", "지원되지 않는 URL", "Nieobsługiwany URL", "Desteklenmeyen URL" }; LocalizedText.mainTable["SEARCHING"] = new List(15) { "Searching", "Recherche", "Ricerca", "Suche", "Buscando", "Buscando", "Buscando", "Поиск", "Пошук", "搜索中", "搜尋中", "検索中", "검색 중", "Wyszukiwanie", "Aranıyor" }; LocalizedText.mainTable["NO_SEARCH_RESULTS"] = new List(15) { "No results", "Aucun résultat", "Nessun risultato", "Keine Ergebnisse", "Sin resultados", "Sin resultados", "Sem resultados", "Нет результатов", "Немає результатів", "无结果", "無結果", "結果なし", "결과 없음", "Brak wyników", "Sonuç yok" }; LocalizedText.mainTable["HIDE_BUTTON"] = new List(13) { "Hide", "Masquer", "Nascondi", "Ausblenden", "Ocultar", "Ocultar", "Ocultar", "Скрыть", "Приховати", "隐藏", "隱藏", "非表示", "숨기기" }; LocalizedText.mainTable["ADD_TRACK_BUTTON"] = new List(13) { "Add Track", "Ajouter une piste", "Aggiungi brano", "Titel hinzufügen", "Añadir pista", "Añadir pista", "Adicionar faixa", "Добавить трек", "Додати трек", "添加曲目", "新增曲目", "トラックを追加", "트랙 추가" }; LocalizedText.mainTable["ADD_TRACK_SUBMIT"] = new List(13) { "Add", "Ajouter", "Aggiungi", "Hinzufügen", "Añadir", "Añadir", "Adicionar", "Добавить", "Додати", "添加", "新增", "追加", "추가" }; LocalizedText.mainTable["START_PLAYLIST"] = new List(13) { "Start Playlist", "Lancer la playlist", "Avvia playlist", "Playlist starten", "Iniciar lista", "Iniciar lista", "Iniciar playlist", "Запустить плейлист", "Запустити плейлист", "开始播放列表", "開始播放清單", "プレイリストを開始", "플레이리스트 시작" }; LocalizedText.mainTable["RL_TITLE"] = new List(15) { "RATE LIMITED", "LIMITE ATTEINTE", "LIMITE RAGGIUNTO", "RATE-LIMIT ERREICHT", "LÍMITE ALCANZADO", "LÍMITE ALCANZADO", "LIMITE ATINGIDO", "ЛИМИТ ЗАПРОСОВ", "ЛІМІТ ЗАПИТІВ", "请求已限速", "請求已限速", "レート制限", "요청 제한됨", "LIMIT ZAPYTAŃ", "İSTEK LİMİTİ" }; LocalizedText.mainTable["RL_BODY"] = new List(15) { "It seems YouTube is restricting you from downloading songs.\nYou can fix this by providing your cookies to ytModule.", "Il semble que YouTube vous empêche de télécharger des chansons.\nVous pouvez résoudre ce problème en fournissant vos cookies à ytModule.", "Sembra che YouTube stia limitando il download dei brani.\nPuoi risolvere fornendo i tuoi cookie a ytModule.", "Anscheinend schränkt YouTube das Herunterladen von Songs ein.\nDu kannst dies beheben, indem du deine Cookies an ytModule übergibst.", "Parece que YouTube está limitando la descarga de canciones.\nPuedes solucionarlo proporcionando tus cookies a ytModule.", "Parece que YouTube está limitando la descarga de canciones.\nPuedes solucionarlo proporcionando tus cookies a ytModule.", "Parece que o YouTube está limitando o download de músicas.\nVocê pode corrigir isso fornecendo seus cookies ao ytModule.", "Похоже, YouTube ограничивает загрузку песен.\nВы можете исправить это, передав свои cookies в ytModule.", "Схоже, YouTube обмежує завантаження пісень.\nВи можете виправити це, надавши свої cookies у ytModule.", "YouTube 似乎正在限制您下载歌曲。\n您可以通过向 ytModule 提供 Cookie 来解决此问题。", "YouTube 似乎正在限制您下載歌曲。\n您可以透過向 ytModule 提供 Cookie 來解決此問題。", "YouTubeが楽曲のダウンロードを制限しているようです。\nytModuleにCookieを提供することで解決できます。", "YouTube가 노래 다운로드를 제한하고 있는 것 같습니다.\nytModule에 쿠키를 제공하면 해결할 수 있습니다.", "Wygląda na to, że YouTube ogranicza pobieranie piosenek.\nMożesz to naprawić, przekazując swoje pliki cookie do ytModule.", "YouTube'un şarkı indirmeyi kısıtladığı görünüyor.\nBunu, ytModule'e çerezlerinizi sağlayarak düzeltebilirsiniz." }; LocalizedText.mainTable["RL_OPEN_INSTRUCTIONS"] = new List(15) { "Open Instructions", "Ouvrir les instructions", "Apri istruzioni", "Anleitung öffnen", "Abrir instrucciones", "Abrir instrucciones", "Abrir instruções", "Открыть инструкцию", "Відкрити інструкцію", "打开说明", "打開說明", "手順を開く", "안내 열기", "Otwórz instrukcję", "Talimatları Aç" }; LocalizedText.mainTable["RL_OPEN_FOLDER"] = new List(15) { "Open ytModule folder", "Ouvrir le dossier ytModule", "Apri cartella ytModule", "ytModule-Ordner öffnen", "Abrir carpeta ytModule", "Abrir carpeta ytModule", "Abrir pasta ytModule", "Открыть папку ytModule", "Відкрити папку ytModule", "打开 ytModule 文件夹", "打開 ytModule 資料夾", "ytModuleフォルダを開く", "ytModule 폴더 열기", "Otwórz folder ytModule", "ytModule Klasörünü Aç" }; LocalizedText.mainTable["DISMISS"] = new List(15) { "Dismiss", "Ignorer", "Ignora", "Schließen", "Cerrar", "Cerrar", "Fechar", "Закрыть", "Закрити", "关闭", "關閉", "閉じる", "닫기", "Zamknij", "Kapat" }; LocalizedText.mainTable["MS_HINT_MURDERONTHEDANCEFLOOR"] = new List(15) { "✨ murder ✨", "✨ meurtre ✨", "✨ omicidio ✨", "✨ Mord ✨", "✨ asesinato ✨", "✨ asesinato ✨", "✨ assassinato ✨", "✨ убийство ✨", "✨ вбивство ✨", "✨ 谋杀 ✨", "✨ 謀殺 ✨", "✨ 殺人 ✨", "✨ 살인 ✨", "✨ morderstwo ✨", "✨ cinayet ✨" }; LocalizedText.mainTable["MS_HINT_GHOSTTOWN"] = new List(15) { "it's a poltergeist!", "c'est un poltergeist !", "è un poltergeist!", "es ist ein Poltergeist!", "¡es un poltergeist!", "¡es un poltergeist!", "é um poltergeist!", "это полтергейст!", "це полтергейст!", "这是闹鬼!", "這是鬧鬼!", "ポルターガイストだ!", "폴터가이스트야!", "to poltergeist!", "bu bir poltergeist!" }; } } public class Netcode : MonoBehaviourPunCallbacks { private static Netcode _instance; private PhotonView _photonView; private float _lastRotationSyncTime = 0f; private Dictionary _hasRetriedSync = new Dictionary(); private float? _savedHostVolume; public static Netcode Instance { get { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown if ((Object)(object)_instance == (Object)null) { _instance = Object.FindAnyObjectByType(); if ((Object)(object)_instance == (Object)null) { GameObject val = new GameObject("sPEAKerNetcode"); _instance = val.AddComponent(); Object.DontDestroyOnLoad((Object)(object)val); Plugin.Log("[Netcode] Created new instance and marked as DontDestroyOnLoad."); } } return _instance; } } public int CachedPlaybackState { get; set; } = 0; public string CachedSongHash { get; set; } = ""; public string CachedSongName { get; set; } = ""; public int CachedFollowViewID { get; set; } = -1; public bool CachedIsFollowingCharacter { get; set; } = false; public bool IsHostModeActive { get; private set; } public static void EnsureInitialized() { Plugin.Log("[Netcode] EnsureInitialized called."); _ = Instance; } private void Awake() { Plugin.Log("[Netcode] Awake called."); if ((Object)(object)_instance != (Object)null && (Object)(object)_instance != (Object)(object)this) { Plugin.Log("[Netcode] Duplicate instance detected, destroying."); Object.Destroy((Object)(object)((Component)this).gameObject); return; } _instance = this; _photonView = ((Component)this).GetComponent(); if ((Object)(object)_photonView == (Object)null) { _photonView = ((Component)this).gameObject.AddComponent(); byte[] value = MD5.Create().ComputeHash("onlystar.sPEAKer.Netcode"u8.ToArray()); _photonView.ViewID = BitConverter.ToInt32(value, 0); } Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); Plugin.Log("[Netcode] Instance set and marked as DontDestroyOnLoad."); } public void SendRequestDanceEmote() { _photonView.RPC("RPC_RequestDanceEmote", (RpcTarget)2, Array.Empty()); } [PunRPC] private void RPC_RequestDanceEmote() { if (!PhotonNetwork.IsMasterClient) { return; } Plugin.Log("[Netcode] Master received Dance Emote Request"); Controller controllerInstance = TheObserver.Instance.GetControllerInstance(); if ((Object)(object)controllerInstance != (Object)null) { DroneBehaviour component = ((Component)controllerInstance).GetComponent(); if ((Object)(object)component != (Object)null && component.IsFloating) { Func func = component.SelectWeightedRandomMoveForNetwork(); string name = func.Method.Name; Plugin.Log("[Netcode] Master selected dance move: " + name); _photonView.RPC("RPC_DanceEmote", (RpcTarget)0, new object[1] { name }); } } } [PunRPC] private void RPC_DanceEmote(string moveName) { Plugin.Log("[Netcode] RPC_DanceEmote called with move: " + moveName); Controller controllerInstance = TheObserver.Instance.GetControllerInstance(); if ((Object)(object)controllerInstance != (Object)null) { DroneBehaviour component = ((Component)controllerInstance).GetComponent(); if ((Object)(object)component != (Object)null) { component.StartNetworkDance(moveName); } } } public void BroadcastStartPlayback(int soundIndex) { if (PhotonNetwork.IsMasterClient) { string text = AudioMaster.IndexToSongHash[soundIndex]; string name = ((Object)AudioMaster.LoadedAudioClips[soundIndex]).name; _photonView.RPC("RPC_StartPlayback", (RpcTarget)0, new object[2] { text, name }); } } public void BroadcastPausePlayback(float pauseTime) { if (PhotonNetwork.IsMasterClient) { _photonView.RPC("RPC_PausePlayback", (RpcTarget)0, new object[1] { pauseTime }); } } public void BroadcastResumePlayback(float resumeTime) { if (PhotonNetwork.IsMasterClient) { _photonView.RPC("RPC_ResumePlayback", (RpcTarget)0, new object[1] { resumeTime }); } } public void BroadcastSongQueue(string[] songHashes) { if (PhotonNetwork.IsMasterClient) { _photonView.RPC("RPC_SyncQueue", (RpcTarget)0, new object[1] { songHashes }); } } public void SendRequestStartPlayback(int soundIndex) { _photonView.RPC("RPC_RequestStartPlayback", (RpcTarget)2, new object[1] { soundIndex }); } public void SendRequestPausePlayback(float pauseTime) { _photonView.RPC("RPC_RequestPausePlayback", (RpcTarget)2, new object[1] { pauseTime }); } public void SendRequestResumePlayback(float resumeTime) { _photonView.RPC("RPC_RequestResumePlayback", (RpcTarget)2, new object[1] { resumeTime }); } public void SendRequestSeek(float seekTime) { if (PhotonNetwork.IsMasterClient) { BroadcastSeek(seekTime); return; } _photonView.RPC("RPC_RequestSeek", (RpcTarget)2, new object[1] { seekTime }); } public void BroadcastSeek(float seekTime) { _photonView.RPC("RPC_Seek", (RpcTarget)0, new object[1] { seekTime }); } public void SendStopPlayback() { if (PhotonNetwork.IsMasterClient) { _photonView.RPC("RPC_StopPlayback", (RpcTarget)0, Array.Empty()); } } public void SendVolumeChange(float newVolume) { _photonView.RPC("RPC_SetVolume", (RpcTarget)0, new object[1] { newVolume }); } public void SendNextSong(string songHash, string songName) { if (PhotonNetwork.IsMasterClient) { _photonView.RPC("RPC_NextSong", (RpcTarget)0, new object[2] { songHash, songName }); } } public void SendRequestNextSong() { _photonView.RPC("RPC_RequestNextSong", (RpcTarget)2, Array.Empty()); } public void SendRequestPreviousSong() { _photonView.RPC("RPC_RequestPreviousSong", (RpcTarget)2, Array.Empty()); } public void SendPlayTurnOn() { _photonView.RPC("RPC_PlayTurnOn", (RpcTarget)0, Array.Empty()); } public void SendLoopStateChange(int loopState) { if (PhotonNetwork.IsMasterClient) { _photonView.RPC("RPC_SetLoopState", (RpcTarget)0, new object[1] { loopState }); } } public void SendShuffleStateChange(bool isEnabled) { if (PhotonNetwork.IsMasterClient) { _photonView.RPC("RPC_BroadcastShuffleState", (RpcTarget)0, new object[1] { isEnabled }); } } public void SendRequestShuffleToggle() { _photonView.RPC("RPC_RequestShuffleToggle", (RpcTarget)2, Array.Empty()); } public void SendRequestShuffleQueue() { _photonView.RPC("RPC_RequestShuffleQueue", (RpcTarget)2, Array.Empty()); } public void SendRequestPlaySongByHash(string songHash) { _photonView.RPC("RPC_RequestPlaySongByHash", (RpcTarget)2, new object[1] { songHash }); } public void SendRequestUpdateQueue(string[] newQueue) { _photonView.RPC("RPC_RequestUpdateQueue", (RpcTarget)2, new object[1] { newQueue }); } public void SendRequestQueueState() { _photonView.RPC("RPC_RequestQueueState", (RpcTarget)2, Array.Empty()); } public void SendRequestLoopToggle() { _photonView.RPC("RPC_RequestLoopToggle", (RpcTarget)2, Array.Empty()); } public void SendFloatModeToggle(bool isFloating, int targetViewId, bool isRecall = false) { Plugin.Log($"[Netcode] Sending Float Mode Toggle RPC: Floating={isFloating}, Target={targetViewId}, IsRecall={isRecall}"); _photonView.RPC("RPC_FloatModeToggle", (RpcTarget)0, new object[3] { isFloating, targetViewId, isRecall }); } public void SendDroneMode(int modeInt, int driverViewID) { Plugin.Log($"[Netcode] Sending Drone Mode RPC: Mode={modeInt}, Driver={driverViewID}"); _photonView.RPC("RPC_DroneMode", (RpcTarget)0, new object[2] { modeInt, driverViewID }); } public void SendLaserFire(Vector3 leftPos, Quaternion leftRot, Vector3 rightPos, Quaternion rightRot, int effectType) { //IL_0031: 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_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) Plugin.Log($"[Netcode] Sending Laser Fire RPC: EffectType={effectType}"); _photonView.RPC("RPC_LaserFire", (RpcTarget)0, new object[5] { leftPos, leftRot, rightPos, rightRot, effectType }); } public void SendDroneRotation(Quaternion rotation) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) if (!(Time.time - _lastRotationSyncTime < 0.05f)) { _lastRotationSyncTime = Time.time; _photonView.RPC("RPC_DroneRotation", (RpcTarget)1, new object[1] { rotation }); } } public override void OnPlayerEnteredRoom(Player newPlayer) { if (Plugin.MilestonesEnabled) { MilestoneTracker.Instance.SetAbsoluteProgress("WeAreFamily", PhotonNetwork.CurrentRoom.PlayerCount); } if (PhotonNetwork.IsMasterClient) { Plugin.Log("[Netcode] Player " + newPlayer.NickName + " entered room. Checking if sync is needed."); if (TheObserver.Instance.DoessPEAKerExist()) { Plugin.Log("[Netcode] sPEAKer instance exists. Initiating sync with " + newPlayer.NickName + "."); NetworkSyncData networkSyncData = PrepareCurrentSyncData(); Plugin.Log($"[Netcode] HOST SENDING SYNC DATA to {newPlayer.NickName}: State={networkSyncData.PlaybackStateInt}, Song={networkSyncData.SongHash}, Time={networkSyncData.PlaybackTime}, Volume={networkSyncData.Volume}"); SendSyncToPlayer(newPlayer, networkSyncData); } else { Plugin.Log("[Netcode] No sPEAKer instance found. No sync needed for " + newPlayer.NickName + "."); } } } [PunRPC] private void RPC_ResyncRequest(PhotonMessageInfo info) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) if (PhotonNetwork.IsMasterClient) { Player sender = info.Sender; Plugin.Log("[Netcode] Received resync request from " + sender.NickName + ". Sending current state."); NetworkSyncData networkSyncData = PrepareCurrentSyncData(); Plugin.Log($"[Netcode] HOST SENDING RESYNC DATA to {sender.NickName}: State={networkSyncData.PlaybackStateInt}, Song={networkSyncData.SongHash}, Time={networkSyncData.PlaybackTime}, Volume={networkSyncData.Volume}"); SendSyncToPlayer(sender, networkSyncData); } } [PunRPC] private void RPC_SyncConfirmation(string syncDataJson, PhotonMessageInfo info) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) if (!PhotonNetwork.IsMasterClient) { return; } Player sender = info.Sender; NetworkSyncData networkSyncData = JsonConvert.DeserializeObject(syncDataJson); if (networkSyncData == null) { Plugin.Log("[Netcode] Failed to deserialize sync confirmation from " + sender.NickName, "error"); return; } Plugin.Log($"[Netcode] HOST RECEIVED CONFIRMATION from {sender.NickName}: State={networkSyncData.PlaybackStateInt}, Song={networkSyncData.SongHash}, Time={networkSyncData.PlaybackTime}, Volume={networkSyncData.Volume}"); NetworkSyncData networkSyncData2 = PrepareCurrentSyncData(); Plugin.Log($"[Netcode] HOST CURRENT DATA for comparison: State={networkSyncData2.PlaybackStateInt}, Song={networkSyncData2.SongHash}, Time={networkSyncData2.PlaybackTime}, Volume={networkSyncData2.Volume}"); if (IsSyncDataMatching(networkSyncData2, networkSyncData)) { Plugin.Log("[Netcode] Sync successful with " + sender.NickName + "."); _hasRetriedSync.Remove(sender.UserId); _photonView.RPC("RPC_SyncSuccess", sender, Array.Empty()); } else if (!_hasRetriedSync.ContainsKey(sender.UserId)) { Plugin.Log("[Netcode] SYNC MISMATCH with " + sender.NickName + ". RETRYING SYNC (first attempt)."); Plugin.Log($"[Netcode] HOST SENDING RETRY DATA to {sender.NickName}: State={networkSyncData2.PlaybackStateInt}, Song={networkSyncData2.SongHash}, Time={networkSyncData2.PlaybackTime}, Volume={networkSyncData2.Volume}"); _hasRetriedSync[sender.UserId] = true; SendSyncToPlayer(sender, networkSyncData2); } else { Plugin.Log("[Netcode] SYNC MISMATCH with " + sender.NickName + ". Already retried once - accepting sync as-is."); _hasRetriedSync.Remove(sender.UserId); _photonView.RPC("RPC_SyncSuccess", sender, Array.Empty()); } } [PunRPC] private void RPC_InitiateSync(string syncDataJson) { if (!PhotonNetwork.IsMasterClient) { Plugin.Log($"[Netcode] CLIENT RECEIVED SYNC DATA (JSON length: {syncDataJson?.Length ?? 0})"); NetworkSyncData networkSyncData = JsonConvert.DeserializeObject(syncDataJson); if (networkSyncData == null) { Plugin.Log("[Netcode] Failed to deserialize sync data from host!", "error"); return; } Plugin.Log($"[Netcode] CLIENT RECEIVED: State={networkSyncData.PlaybackStateInt}, Song={networkSyncData.SongHash}, Time={networkSyncData.PlaybackTime}, Volume={networkSyncData.Volume}"); SyncWithNetwork(networkSyncData); Plugin.Log("[Netcode] CLIENT SENDING CONFIRMATION to host"); SendSyncConfirmation(networkSyncData); } } [PunRPC] private void RPC_SyncSuccess() { Plugin.Log("[Netcode] CLIENT RECEIVED: Sync handshake completed successfully."); Controller controllerInstance = TheObserver.Instance.GetControllerInstance(); if ((Object)(object)controllerInstance != (Object)null) { controllerInstance.OnSyncCompleted(); } } public void RequestResyncFromHost() { if (!PhotonNetwork.IsMasterClient) { Plugin.Log("[Netcode] CLIENT REQUESTING: Manual resync from host."); _photonView.RPC("RPC_ResyncRequest", (RpcTarget)2, Array.Empty()); } } private void SendSyncToPlayer(Player player, NetworkSyncData data) { string text = JsonConvert.SerializeObject((object)data); Plugin.Log($"[Netcode] Sending sync to {player.NickName}. JSON length: {text.Length}"); _photonView.RPC("RPC_InitiateSync", player, new object[1] { text }); } private void SendSyncConfirmation(NetworkSyncData syncData) { string text = JsonConvert.SerializeObject((object)syncData); _photonView.RPC("RPC_SyncConfirmation", (RpcTarget)2, new object[1] { text }); } private NetworkSyncData PrepareCurrentSyncData() { Controller controllerInstance = TheObserver.Instance.GetControllerInstance(); DroneBehaviour droneBehaviour = null; if ((Object)(object)controllerInstance != (Object)null) { droneBehaviour = ((Component)controllerInstance).GetComponent(); } float num = 0f; float num2 = 0.35f; if ((Object)(object)AudioMaster.MixtapeAudioSource != (Object)null) { num = AudioMaster.MixtapeAudioSource.time; num2 = AudioMaster.MixtapeAudioSource.volume; Plugin.Log($"[Netcode] PrepareCurrentSyncData - AudioSource.time={num}, Volume={num2}, IsPlaying={AudioMaster.MixtapeAudioSource.isPlaying}"); } float num3 = ((AudioMaster.CurrentPlaybackState == PlaybackState.Paused) ? AudioMaster.CurrentPausedTime : ((AudioMaster.CurrentPlaybackState == PlaybackState.Playing) ? num : 0f)); Plugin.Log($"[Netcode] PrepareCurrentSyncData - Final PlaybackTime={num3}, CurrentPlaybackState={AudioMaster.CurrentPlaybackState}, CurrentPausedTime={AudioMaster.CurrentPausedTime}"); NetworkSyncData networkSyncData = new NetworkSyncData { PlaybackStateInt = (int)AudioMaster.CurrentPlaybackState, SongHash = ((AudioMaster.CurrentPlaybackState != PlaybackState.Stopped) ? AudioMaster.IndexToSongHash.GetValueOrDefault(AudioMaster.CurrentSongIndex, "") : ""), SongName = ((AudioMaster.CurrentPlaybackState != PlaybackState.Stopped && AudioMaster.CurrentSongIndex < AudioMaster.LoadedAudioClips.Count) ? ((Object)AudioMaster.LoadedAudioClips[AudioMaster.CurrentSongIndex]).name : ""), PlaybackTime = num3, Volume = num2, FollowViewID = CachedFollowViewID, IsFollowingCharacter = CachedIsFollowingCharacter, LoopStateInt = (int)AudioMaster.CurrentLoopState, YouTubeLoopStateInt = (ytModuleCompatibility.enabled ? ytModuleCompatibility.GetYouTubeLoopState() : 0), IsYouTubeMode = AudioMaster.IsYouTubeMode, IsFloating = ((Object)(object)droneBehaviour != (Object)null && droneBehaviour.IsFloating), FollowTargetViewID = (((Object)(object)droneBehaviour != (Object)null && (Object)(object)droneBehaviour.FollowTarget != (Object)null) ? ((MonoBehaviourPun)droneBehaviour.FollowTarget).photonView.ViewID : (-1)), SongQueue = AudioMaster.SongQueue.ToArray(), IsHostModeActive = ((Object)(object)HostOnly.Instance != (Object)null && HostOnly.Instance.IsActive) }; if (AudioMaster.IsYouTubeMode && YouTubePlayer.Instance.currentTrack != null) { networkSyncData.CurrentYouTubeTitle = YouTubePlayer.Instance.currentTrack.title; networkSyncData.CurrentYouTubeUrl = YouTubePlayer.Instance.currentTrack.url; networkSyncData.YouTubeQueueUrls = YouTubeQueue.Instance.Tracks.Select((ytTrack track) => track.url).ToArray(); } return networkSyncData; } private bool IsSyncDataMatching(NetworkSyncData expected, NetworkSyncData received) { bool flag = expected.PlaybackStateInt == received.PlaybackStateInt && expected.SongHash == received.SongHash && expected.SongName == received.SongName && Mathf.Abs(expected.Volume - received.Volume) < 0.01f && expected.FollowViewID == received.FollowViewID && expected.IsFollowingCharacter == received.IsFollowingCharacter && expected.LoopStateInt == received.LoopStateInt && expected.YouTubeLoopStateInt == received.YouTubeLoopStateInt && expected.IsYouTubeMode == received.IsYouTubeMode && expected.IsFloating == received.IsFloating && expected.FollowTargetViewID == received.FollowTargetViewID; bool flag2 = Mathf.Abs(expected.PlaybackTime - received.PlaybackTime) <= 1f; bool flag3 = expected.SongQueue.Length == received.SongQueue.Length; if (flag3) { for (int i = 0; i < expected.SongQueue.Length; i++) { if (expected.SongQueue[i] != received.SongQueue[i]) { flag3 = false; break; } } } Plugin.Log($"[Netcode] IsSyncDataMatching - BasicMatch={flag}, PlaybackTimeMatch={flag2} (diff={Mathf.Abs(expected.PlaybackTime - received.PlaybackTime)}), QueueMatch={flag3}"); return flag && flag2 && flag3; } private void SyncWithNetwork(NetworkSyncData syncData) { if (Plugin.MilestonesEnabled) { MilestoneTracker.Instance.SetAbsoluteProgress("WeAreFamily", PhotonNetwork.CurrentRoom.PlayerCount); } Plugin.Log($"[Netcode] SyncWithNetwork started. - State={syncData.PlaybackStateInt}, Song={syncData.SongHash}, Time={syncData.PlaybackTime}, Volume={syncData.Volume}, IsYouTubeMode={syncData.IsYouTubeMode}"); Plugin.Log($"[Netcode] Setting volume to {syncData.Volume}"); AudioMaster.Instance.SetVolume(syncData.Volume); Plugin.Log($"[Netcode] Setting loop state to {(LoopState)syncData.LoopStateInt}"); AudioMaster.Instance.HandleNetworkLoopStateChange(syncData.LoopStateInt); Controller controllerInstance = TheObserver.Instance.GetControllerInstance(); if ((Object)(object)controllerInstance != (Object)null) { controllerInstance.HandleNetworkFloatModeToggle(syncData.IsFloating, syncData.FollowTargetViewID, isRecall: false); } string[] songQueue = syncData.SongQueue; if (songQueue != null && songQueue.Length > 0) { AudioMaster.Instance.HandleNetworkQueueSync(syncData.SongQueue); } if (syncData.FollowViewID > 0) { PhotonView val = PhotonView.Find(syncData.FollowViewID); if ((Object)(object)val != (Object)null) { Plugin.Log($"[Netcode] Setting follow target to PhotonView {syncData.FollowViewID}"); if (syncData.IsFollowingCharacter) { Character component = ((Component)val).GetComponent(); if ((Object)(object)component != (Object)null) { AudioMaster.Instance.SetFollow(component); } } else { AudioMaster.Instance.SetFollow(((Component)val).transform); } } else { Plugin.Log($"[Netcode] PhotonView {syncData.FollowViewID} not found for follow target", "warning"); } } else { Plugin.Log("[Netcode] Setting follow target to speaker transform"); if ((Object)(object)controllerInstance != (Object)null) { AudioMaster.Instance.SetFollow(((Component)controllerInstance).transform); } } if (syncData.IsYouTubeMode && ytModuleCompatibility.enabled) { Plugin.Log("[Netcode] Syncing YouTube mode state"); if (!AudioMaster.IsYouTubeMode) { Plugin.Log("[Netcode] Entering YouTube mode for late joiner"); ytModuleCompatibility.HandleNetworkEnterYouTubeMode(); } ytTrack ytTrack2 = new ytTrack(syncData.CurrentYouTubeUrl, "Sync"); ytTrack2.title = syncData.CurrentYouTubeTitle; YouTubePlayer.Instance.HandleNetworkPlay(ytTrack2); } else if (!syncData.IsYouTubeMode) { AudioMaster.UpdatePlaybackState((PlaybackState)syncData.PlaybackStateInt, AudioMaster.SongHashToIndex.GetValueOrDefault(syncData.SongHash ?? "", 0), (syncData.PlaybackStateInt == 2) ? syncData.PlaybackTime : 0f); if (!string.IsNullOrEmpty(syncData.SongHash) && AudioMaster.SongHashToIndex.TryGetValue(syncData.SongHash, out var value)) { AudioClip val2 = AudioMaster.LoadedAudioClips[value]; AudioMaster.MixtapeAudioSource.clip = val2; AudioMaster.MixtapeAudioSource.time = syncData.PlaybackTime; if (syncData.PlaybackStateInt == 1) { AudioMaster.MixtapeAudioSource.Play(); if (Plugin.MilestonesEnabled) { MilestoneTracker.Instance.ReportProgress("UnstoppableForce"); } sPEAKerUI.Instance.ShowToast(syncData.SongName); sPEAKerUI.Instance.ShowDuration(val2.length); } else { AudioMaster.MixtapeAudioSource.Pause(); } } } if (!PhotonNetwork.IsMasterClient) { if (syncData.IsHostModeActive && !IsHostModeActive) { string userId = PhotonNetwork.MasterClient.UserId; _savedHostVolume = AudioLevels.GetPlayerLevel(userId); AudioLevels.SetPlayerLevel(userId, 0f); IsHostModeActive = true; Plugin.Log("[Netcode] SyncWithNetwork: applied HostMode mute for late joiner."); } else if (!syncData.IsHostModeActive && IsHostModeActive) { if (_savedHostVolume.HasValue) { AudioLevels.SetPlayerLevel(PhotonNetwork.MasterClient.UserId, _savedHostVolume.Value); _savedHostVolume = null; } IsHostModeActive = false; Plugin.Log("[Netcode] SyncWithNetwork: cleared HostMode mute for late joiner."); } } if ((Object)(object)controllerInstance != (Object)null) { controllerInstance.HandleSyncStateUpdate((PlaybackState)syncData.PlaybackStateInt, syncData.SongHash); } } public void ResetCachedState() { CachedPlaybackState = (int)AudioMaster.CurrentPlaybackState; CachedSongHash = ((AudioMaster.CurrentPlaybackState != PlaybackState.Stopped) ? AudioMaster.IndexToSongHash.GetValueOrDefault(AudioMaster.CurrentSongIndex, "") : ""); CachedSongName = ((AudioMaster.CurrentPlaybackState != PlaybackState.Stopped && AudioMaster.CurrentSongIndex < AudioMaster.LoadedAudioClips.Count) ? ((Object)AudioMaster.LoadedAudioClips[AudioMaster.CurrentSongIndex]).name : ""); CachedFollowViewID = -1; CachedIsFollowingCharacter = false; Plugin.Log($"[Netcode] Reset cached state from AudioMaster - State={CachedPlaybackState}, Song={CachedSongHash}"); } [PunRPC] private void RPC_StartPlayback(string songHash, string songName) { Plugin.Log("[Netcode] RPC_StartPlayback called - hash=" + songHash + ", name=" + songName); Controller controllerInstance = TheObserver.Instance.GetControllerInstance(); if ((Object)(object)controllerInstance != (Object)null) { controllerInstance.HandleNetworkStartPlayback(songHash, songName); } AirportEditor.PlayAllVisualizers(AudioMaster.ActiveSource); CachedPlaybackState = 1; CachedSongHash = songHash; CachedSongName = songName; } [PunRPC] private void RPC_PausePlayback(float pauseTime) { Plugin.Log($"[Netcode] RPC_PausePlayback at {pauseTime}s"); Controller controllerInstance = TheObserver.Instance.GetControllerInstance(); if ((Object)(object)controllerInstance != (Object)null) { controllerInstance.HandleNetworkPausePlayback(pauseTime); } CachedPlaybackState = 2; } [PunRPC] private void RPC_ResumePlayback(float resumeTime) { Plugin.Log($"[Netcode] RPC_ResumePlayback from {resumeTime}s"); Controller controllerInstance = TheObserver.Instance.GetControllerInstance(); if ((Object)(object)controllerInstance != (Object)null) { controllerInstance.HandleNetworkResumePlayback(resumeTime); } CachedPlaybackState = 1; } [PunRPC] private void RPC_RequestStartPlayback(int soundIndex) { if (PhotonNetwork.IsMasterClient) { Plugin.Log("[Netcode] Master received Start Playback Request"); BroadcastStartPlayback(soundIndex); } } [PunRPC] private void RPC_RequestPausePlayback(float pauseTime) { if (PhotonNetwork.IsMasterClient) { Plugin.Log("[Netcode] Master received Pause Playback Request"); BroadcastPausePlayback(pauseTime); } } [PunRPC] private void RPC_RequestResumePlayback(float resumeTime) { if (PhotonNetwork.IsMasterClient) { Plugin.Log("[Netcode] Master received Resume Playback Request"); BroadcastResumePlayback(resumeTime); } } [PunRPC] private void RPC_RequestSeek(float seekTime) { if (PhotonNetwork.IsMasterClient) { BroadcastSeek(seekTime); } } [PunRPC] private void RPC_Seek(float seekTime) { Plugin.Log($"[Netcode] RPC_Seek to {seekTime}s"); AudioSource mixtapeAudioSource = AudioMaster.MixtapeAudioSource; if ((Object)(object)mixtapeAudioSource != (Object)null && (Object)(object)mixtapeAudioSource.clip != (Object)null) { mixtapeAudioSource.time = Mathf.Clamp(seekTime, 0f, mixtapeAudioSource.clip.length - 0.01f); } } [PunRPC] private void RPC_SyncQueue(string[] songHashes) { Plugin.Log($"[Netcode] Received queue sync RPC with {songHashes.Length} songs."); AudioMaster.Instance.HandleNetworkQueueSync(songHashes); } [PunRPC] private void RPC_StopPlayback() { Plugin.Log("[Netcode] RPC_StopPlayback called"); Controller controllerInstance = TheObserver.Instance.GetControllerInstance(); if ((Object)(object)controllerInstance != (Object)null) { controllerInstance.HandleNetworkStopPlayback(); } CachedPlaybackState = 0; } [PunRPC] private void RPC_NextSong(string songHash, string songName) { Plugin.Log("[Netcode] RPC_NextSong called - hash=" + songHash + ", name=" + songName); Controller controllerInstance = TheObserver.Instance.GetControllerInstance(); if ((Object)(object)controllerInstance != (Object)null) { controllerInstance.HandleNetworkNextSong(songHash, songName); } else { AudioMaster.Instance.HandleNextSong(songHash, songName); } CachedSongHash = songHash; CachedSongName = songName; } [PunRPC] private void RPC_RequestNextSong() { Plugin.Log("[Netcode] RPC_RequestNextSong called"); if (PhotonNetwork.IsMasterClient) { Controller controllerInstance = TheObserver.Instance.GetControllerInstance(); if ((Object)(object)controllerInstance != (Object)null) { ((object)controllerInstance).GetType().GetMethod("NextSong", BindingFlags.Instance | BindingFlags.NonPublic)?.Invoke(controllerInstance, null); } } } [PunRPC] private void RPC_RequestPreviousSong() { Plugin.Log("[Netcode] RPC_RequestPreviousSong called"); if (PhotonNetwork.IsMasterClient) { Controller controllerInstance = TheObserver.Instance.GetControllerInstance(); if ((Object)(object)controllerInstance != (Object)null) { ((object)controllerInstance).GetType().GetMethod("PreviousSong", BindingFlags.Instance | BindingFlags.NonPublic)?.Invoke(controllerInstance, null); } } } [PunRPC] private void RPC_SetVolume(float newVolume) { Plugin.Log($"[Netcode] RPC_SetVolume called with value {newVolume}"); Controller controllerInstance = TheObserver.Instance.GetControllerInstance(); if ((Object)(object)controllerInstance != (Object)null) { controllerInstance.HandleNetworkVolumeChange(newVolume); } } [PunRPC] private void RPC_PlayTurnOn() { Plugin.Log("[Netcode] RPC_PlayTurnOn called"); AudioMaster.PlayFX(SoundEffects.TurnOn); } [PunRPC] private void RPC_SetLoopState(int loopState) { Plugin.Log($"[Netcode] RPC_SetLoopState called with value {loopState}"); AudioMaster.Instance.HandleNetworkLoopStateChange(loopState); } [PunRPC] private void RPC_RequestLoopToggle() { Plugin.Log("[Netcode] Master received Loop Toggle Request"); if (PhotonNetwork.IsMasterClient) { AudioMaster.ToggleLoop(); } } [PunRPC] private void RPC_FloatModeToggle(bool isFloating, int targetViewId, bool isRecall) { Plugin.Log($"[Netcode] Received Float Mode Toggle RPC: Floating={isFloating}, Target={targetViewId}, IsRecall={isRecall}"); Controller controllerInstance = TheObserver.Instance.GetControllerInstance(); if ((Object)(object)controllerInstance != (Object)null) { controllerInstance.HandleNetworkFloatModeToggle(isFloating, targetViewId, isRecall); } } [PunRPC] private void RPC_DroneMode(int modeInt, int driverViewID) { Plugin.Log($"[Netcode] Received Drone Mode RPC: Mode={modeInt}, Driver={driverViewID}"); Controller controllerInstance = TheObserver.Instance.GetControllerInstance(); if ((Object)(object)controllerInstance != (Object)null) { DroneBehaviour component = ((Component)controllerInstance).GetComponent(); if ((Object)(object)component != (Object)null) { bool isDriver = (Object)(object)Character.localCharacter != (Object)null && ((MonoBehaviourPun)Character.localCharacter).photonView.ViewID == driverViewID; component.NetworkSwitchBehaviour(modeInt, isDriver); } } } [PunRPC] private void RPC_LaserFire(Vector3 leftPos, Quaternion leftRot, Vector3 rightPos, Quaternion rightRot, int effectType) { //IL_0048: 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_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) Plugin.Log($"[Netcode] Received Laser Fire RPC: EffectType={effectType}"); Controller controllerInstance = TheObserver.Instance.GetControllerInstance(); if ((Object)(object)controllerInstance != (Object)null) { DroneBehaviour component = ((Component)controllerInstance).GetComponent(); if ((Object)(object)component != (Object)null) { component.NetworkSpawnLasers(leftPos, leftRot, rightPos, rightRot, effectType); } } } [PunRPC] private void RPC_DroneRotation(Quaternion rotation) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) Controller controllerInstance = TheObserver.Instance.GetControllerInstance(); if ((Object)(object)controllerInstance != (Object)null) { DroneBehaviour component = ((Component)controllerInstance).GetComponent(); if ((Object)(object)component != (Object)null && (Object)(object)component.DroneRigidbody != (Object)null) { component.DroneRigidbody.MoveRotation(rotation); } } } [PunRPC] private void RPC_RequestShuffleQueue() { if (PhotonNetwork.IsMasterClient) { Plugin.Log("[Netcode] Master received Shuffle Queue Request"); AudioMaster.Instance.Master_ShuffleQueue(); } } [PunRPC] private void RPC_RequestPlaySongByHash(string songHash) { if (PhotonNetwork.IsMasterClient) { Plugin.Log("[Netcode] Master received Play Song by Hash Request: " + songHash); AudioMaster.Instance.Master_PlaySongByHash(songHash); } } [PunRPC] private void RPC_RequestUpdateQueue(string[] newQueue) { if (PhotonNetwork.IsMasterClient) { Plugin.Log($"[Netcode] Master received Update Queue Request with {newQueue.Length} songs."); AudioMaster.Instance.Master_UpdateQueue(newQueue.ToList()); } } [PunRPC] private void RPC_RequestQueueState(PhotonMessageInfo info) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) if (PhotonNetwork.IsMasterClient) { Plugin.Log("[Netcode] Master received Queue State Request from " + info.Sender.NickName + "."); _photonView.RPC("RPC_SyncQueue", info.Sender, new object[1] { AudioMaster.SongQueue.ToArray() }); } } public void SendEnterYouTubeMode() { if (PhotonNetwork.IsConnectedAndReady) { _photonView.RPC("RPC_EnterYouTubeMode", (RpcTarget)0, Array.Empty()); } } public void SendExitYouTubeMode() { if (PhotonNetwork.IsConnectedAndReady) { _photonView.RPC("RPC_ExitYouTubeMode", (RpcTarget)0, Array.Empty()); } } public void SendYouTubePlayRequest(string url, string titleHint = "") { if (PhotonNetwork.IsConnectedAndReady) { _photonView.RPC("RPC_YouTubePlayRequest", (RpcTarget)2, new object[2] { url, titleHint ?? "" }); } } public void SendYouTubeTogglePauseRequest() { if (PhotonNetwork.IsConnectedAndReady) { _photonView.RPC("RPC_YouTubeTogglePauseRequest", (RpcTarget)2, Array.Empty()); } } public void SendYouTubeSkipRequest() { if (PhotonNetwork.IsConnectedAndReady) { _photonView.RPC("RPC_YouTubeSkipRequest", (RpcTarget)2, Array.Empty()); } } public void SendYouTubeSeekRequest(float seekTime) { if (PhotonNetwork.IsMasterClient) { YouTubePlayer.Instance?.Master_Seek(seekTime); return; } _photonView.RPC("RPC_YouTubeSeekRequest", (RpcTarget)2, new object[1] { seekTime }); } public void BroadcastYouTubePlay(ytTrack track, Player target = null) { if (PhotonNetwork.IsMasterClient) { string text = JsonUtility.ToJson((object)track); if (target != null) { _photonView.RPC("RPC_YouTubePlay", target, new object[1] { text }); } else { _photonView.RPC("RPC_YouTubePlay", (RpcTarget)1, new object[1] { text }); } } } public void BroadcastYouTubePause(float pauseTime) { if (PhotonNetwork.IsMasterClient) { _photonView.RPC("RPC_YouTubePause", (RpcTarget)0, new object[1] { pauseTime }); } } public void BroadcastYouTubeResume() { if (PhotonNetwork.IsMasterClient) { _photonView.RPC("RPC_YouTubeResume", (RpcTarget)0, Array.Empty()); } } public void BroadcastYouTubeStop() { if (PhotonNetwork.IsMasterClient) { _photonView.RPC("RPC_YouTubeStop", (RpcTarget)0, Array.Empty()); } } public void SendYouTubeSync(string songHash, bool isPlaying, float currentTime) { if (PhotonNetwork.IsMasterClient) { _photonView.RPC("RPC_YouTubeSync", (RpcTarget)1, new object[3] { songHash, isPlaying, currentTime }); } } public void SendYouTubeQueueSync(ytTrack[] queueTracks, Player target = null) { if (PhotonNetwork.IsMasterClient) { string text = JsonConvert.SerializeObject((object)queueTracks); int currentIndex = ytModuleCompatibility.GetCurrentIndex(); Plugin.Log($"[Netcode] Sending YouTube queue sync. Tracks: {((queueTracks != null) ? queueTracks.Length : 0)}, Index: {currentIndex}"); if (target != null) { _photonView.RPC("RPC_YouTubeQueueSync", target, new object[2] { text, currentIndex }); } else { _photonView.RPC("RPC_YouTubeQueueSync", (RpcTarget)0, new object[2] { text, currentIndex }); } } } public void SendYouTubeReadyConfirmation() { if (PhotonNetwork.IsConnectedAndReady) { int actorNumber = PhotonNetwork.LocalPlayer.ActorNumber; _photonView.RPC("RPC_YouTubeClientReady", (RpcTarget)0, new object[1] { actorNumber }); } } public void SendYouTubePreviousRequest() { _photonView.RPC("RPC_YouTubePreviousRequest", (RpcTarget)2, Array.Empty()); } public void SendYouTubeLoopStateChange(int loopState) { if (PhotonNetwork.IsMasterClient) { _photonView.RPC("RPC_YouTubeLoopState", (RpcTarget)0, new object[1] { loopState }); } } public void RequestYouTubeSyncTime() { if (!PhotonNetwork.IsMasterClient && PhotonNetwork.IsConnectedAndReady) { _photonView.RPC("RPC_RequestYouTubeSyncTime", (RpcTarget)2, Array.Empty()); } } public void SendYouTubeSyncRequest() { if (!PhotonNetwork.IsMasterClient) { _photonView.RPC("RPC_YouTubeSyncRequest", (RpcTarget)2, new object[1] { PhotonNetwork.LocalPlayer.ActorNumber }); } } public void SendYouTubeJumpToIndexRequest(int index) { _photonView.RPC("RPC_YouTubeJumpToIndexRequest", (RpcTarget)2, new object[1] { index }); } public void SendYouTubeMoveTrackRequest(int fromIndex, int toIndex) { _photonView.RPC("RPC_YouTubeMoveTrackRequest", (RpcTarget)2, new object[2] { fromIndex, toIndex }); } public void SendYouTubeShuffleRequest() { _photonView.RPC("RPC_YouTubeShuffleRequest", (RpcTarget)2, Array.Empty()); } public void SendYouTubeResetRequest() { _photonView.RPC("RPC_YouTubeResetRequest", (RpcTarget)2, Array.Empty()); } public void SendYouTubeAddTrackNextRequest(string url, string titleHint = "") { if (PhotonNetwork.IsMasterClient) { YouTubePlayer.Instance?.Master_AddTrackNext(url, titleHint ?? ""); return; } _photonView.RPC("RPC_YouTubeAddTrackNextRequest", (RpcTarget)2, new object[2] { url, titleHint ?? "" }); } public void SendYouTubeAddTrackEndRequest(string url, string titleHint = "") { if (PhotonNetwork.IsMasterClient) { YouTubePlayer.Instance?.Master_AddTrackEnd(url, titleHint ?? ""); return; } _photonView.RPC("RPC_YouTubeAddTrackEndRequest", (RpcTarget)2, new object[2] { url, titleHint ?? "" }); } public void SendYouTubeAddPlaylistEndRequest(string url) { if (PhotonNetwork.IsMasterClient) { YouTubePlayer.Instance?.Master_AddPlaylistEnd(url); return; } _photonView.RPC("RPC_YouTubeAddPlaylistEndRequest", (RpcTarget)2, new object[1] { url }); } [PunRPC] private void RPC_YouTubeAddPlaylistEndRequest(string url) { if (PhotonNetwork.IsMasterClient && ytModuleCompatibility.enabled) { Plugin.Log("[Netcode] Master received AddPlaylistEnd request for url=" + url); ytModuleCompatibility.Master_AddPlaylistEnd(url); } } [PunRPC] private void RPC_YouTubeAddTrackNextRequest(string url, string titleHint) { if (PhotonNetwork.IsMasterClient && ytModuleCompatibility.enabled) { Plugin.Log("[Netcode] Master received AddTrackNext request for url=" + url + " title='" + titleHint + "'"); ytModuleCompatibility.Master_AddTrackNext(url, titleHint); } } [PunRPC] private void RPC_YouTubeAddTrackEndRequest(string url, string titleHint) { if (PhotonNetwork.IsMasterClient && ytModuleCompatibility.enabled) { Plugin.Log("[Netcode] Master received AddTrackEnd request for url=" + url + " title='" + titleHint + "'"); ytModuleCompatibility.Master_AddTrackEnd(url, titleHint); } } [PunRPC] private void RPC_EnterYouTubeMode() { Plugin.Log("[Netcode] RPC_EnterYouTubeMode called"); if (ytModuleCompatibility.enabled) { if (Plugin.MilestonesEnabled) { MilestoneTracker.Instance.ReportProgress("RadioVideo"); } ytModuleCompatibility.HandleNetworkEnterYouTubeMode(); } } [PunRPC] private void RPC_ExitYouTubeMode() { Plugin.Log("[Netcode] RPC_ExitYouTubeMode called"); if (ytModuleCompatibility.enabled) { if (Plugin.MilestonesEnabled) { MilestoneTracker.Instance.ReportProgress("BackToBasics"); } ytModuleCompatibility.HandleNetworkExitYouTubeMode(); } } [PunRPC] private void RPC_YouTubePlayRequest(string url, string titleHint) { if (PhotonNetwork.IsMasterClient && ytModuleCompatibility.enabled) { Plugin.Log("[Netcode] Master received YouTube Play Request for url=" + url + " title='" + titleHint + "'"); ytModuleCompatibility.Master_StartSession(url, titleHint); } } [PunRPC] private void RPC_YouTubeTogglePauseRequest() { if (PhotonNetwork.IsMasterClient && ytModuleCompatibility.enabled) { Plugin.Log("[Netcode] Master received YouTube Toggle Pause Request"); ytModuleCompatibility.Master_TogglePause(); } } [PunRPC] private void RPC_YouTubeSkipRequest() { if (PhotonNetwork.IsMasterClient && ytModuleCompatibility.enabled) { Plugin.Log("[Netcode] Master received YouTube Skip Request"); ytModuleCompatibility.Master_SkipToNext(); } } [PunRPC] private void RPC_YouTubeSeekRequest(float seekTime) { if (PhotonNetwork.IsMasterClient) { YouTubePlayer.Instance?.Master_Seek(seekTime); } } [PunRPC] private void RPC_YouTubePlay(string trackJson) { Plugin.Log("[Netcode] RPC_YouTubePlay called - trackJson=" + trackJson); if (ytModuleCompatibility.enabled) { ytTrack track = JsonUtility.FromJson(trackJson); ytModuleCompatibility.HandleNetworkPlay(track); } } [PunRPC] private void RPC_YouTubePause(float pauseTime) { Plugin.Log($"[Netcode] RPC_YouTubePause called - time={pauseTime}"); if (ytModuleCompatibility.enabled) { ytModuleCompatibility.HandleNetworkPause(pauseTime); } } [PunRPC] private void RPC_YouTubeResume() { Plugin.Log("[Netcode] RPC_YouTubeResume called"); if (ytModuleCompatibility.enabled) { ytModuleCompatibility.HandleNetworkResume(); } } [PunRPC] private void RPC_YouTubeStop() { Plugin.Log("[Netcode] RPC_YouTubeStop called"); if (ytModuleCompatibility.enabled) { ytModuleCompatibility.HandleNetworkStop(); } } [PunRPC] private void RPC_YouTubeSync(string songHash, bool isPlaying, float currentTime) { Plugin.Log($"[Netcode] RPC_YouTubeSync called - hash={songHash}, playing={isPlaying}, time={currentTime}"); if (ytModuleCompatibility.enabled) { ytModuleCompatibility.HandleNetworkYouTubeSync(songHash, isPlaying, currentTime); } } [PunRPC] private void RPC_YouTubeQueueSync(string jsonQueue, int currentIndex) { Plugin.Log($"[Netcode] RPC_YouTubeQueueSync received - Index: {currentIndex}"); ytTrack[] array = JsonConvert.DeserializeObject(jsonQueue); if (array == null) { Plugin.Log("[Netcode] Failed to deserialize ytTrack[]!", "error"); return; } Plugin.Log($"[Netcode] RPC_YouTubeQueueSync called - {array.Length} songs, currentIndex: {currentIndex}"); QueueUI.Instance?.SetPlaylistLoading(loading: false, rerender: false); if (ytModuleCompatibility.enabled) { ytModuleCompatibility.HandleNetworkYouTubeQueueSync(array, currentIndex); } } public void SendPlaylistLoading(bool loading) { if (PhotonNetwork.IsMasterClient) { _photonView.RPC("RPC_PlaylistLoading", (RpcTarget)0, new object[1] { loading }); } } [PunRPC] private void RPC_PlaylistLoading(bool loading) { QueueUI.Instance?.SetPlaylistLoading(loading); } [PunRPC] private void RPC_YouTubeClientReady(int clientViewID) { if (PhotonNetwork.IsMasterClient && ytModuleCompatibility.enabled) { ytModuleCompatibility.HandleClientReady(clientViewID); } } [PunRPC] private void RPC_YouTubePreviousRequest() { if (PhotonNetwork.IsMasterClient && ytModuleCompatibility.enabled) { ytModuleCompatibility.Master_PlayPrevious(); } } [PunRPC] private void RPC_YouTubeJumpToIndexRequest(int index) { if (PhotonNetwork.IsMasterClient && ytModuleCompatibility.enabled) { Plugin.Log($"[Netcode] Master received YouTube JumpToIndex Request: {index}"); ytModuleCompatibility.Master_JumpToIndex(index); } } [PunRPC] private void RPC_YouTubeMoveTrackRequest(int fromIndex, int toIndex) { if (PhotonNetwork.IsMasterClient && ytModuleCompatibility.enabled) { Plugin.Log($"[Netcode] Master received YouTube MoveTrack Request: {fromIndex} -> {toIndex}"); ytModuleCompatibility.Master_MoveTrack(fromIndex, toIndex); } } [PunRPC] private void RPC_YouTubeShuffleRequest() { if (PhotonNetwork.IsMasterClient && ytModuleCompatibility.enabled) { Plugin.Log("[Netcode] Master received YouTube Shuffle Request"); ytModuleCompatibility.Master_Shuffle(); } } [PunRPC] private void RPC_YouTubeResetRequest() { if (PhotonNetwork.IsMasterClient && ytModuleCompatibility.enabled) { Plugin.Log("[Netcode] Master received YouTube Reset Request"); ytModuleCompatibility.Master_Reset(); } } [PunRPC] private void RPC_YouTubeLoopState(int loopState) { if (ytModuleCompatibility.enabled) { ytModuleCompatibility.HandleNetworkYouTubeLoopStateChange(loopState); Plugin.Log($"[Netcode] YouTube loop state updated: {(LoopState)loopState}"); } } [PunRPC] private void RPC_RequestYouTubeSyncTime(PhotonMessageInfo info) { //IL_0062: 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) if (PhotonNetwork.IsMasterClient && ytModuleCompatibility.enabled) { float num = 0f; if ((Object)(object)AudioMaster.YouTubeAudioSource != (Object)null && (Object)(object)AudioMaster.YouTubeAudioSource.clip != (Object)null) { num = AudioMaster.YouTubeAudioSource.time; } bool flag = YouTubePlayer.Instance.CurrentPlaybackState == PlaybackState.Playing; Plugin.Log($"[Netcode] Sending sync time to {info.Sender.NickName}: {num}s, playing={flag}"); _photonView.RPC("RPC_ReceiveYouTubeSyncTime", info.Sender, new object[2] { num, flag }); } } [PunRPC] private void RPC_ReceiveYouTubeSyncTime(float hostTime, bool isPlaying) { if (!PhotonNetwork.IsMasterClient && ytModuleCompatibility.enabled) { Plugin.Log($"[Netcode] Received sync time from host: {hostTime}s, playing={isPlaying}"); ytModuleCompatibility.ApplySyncTime(hostTime, isPlaying); } } [PunRPC] private void RPC_YouTubeSyncRequest(int requestingClientActorNumber) { YouTubePlayer.Instance.Master_HandleSyncRequest(requestingClientActorNumber); } public void SendLaserHit(int targetViewID, int effectType, Vector3 knockbackForce) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) _photonView.RPC("RPC_LaserHit", (RpcTarget)0, new object[3] { targetViewID, effectType, knockbackForce }); } [PunRPC] private void RPC_LaserHit(int targetViewID, int effectType, Vector3 knockbackForce) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) PhotonView val = PhotonView.Find(targetViewID); if ((Object)(object)val == (Object)null) { return; } Character component = ((Component)val).GetComponent(); if (!((Object)(object)component == (Object)null) && ((MonoBehaviourPun)component).photonView.IsMine) { Plugin.Log($"[Netcode] Applying laser effect {effectType} to {component.characterName}"); component.AddForce(knockbackForce, 1f, 1f); switch (effectType) { case 0: component.refs.afflictions.AddStatus((STATUSTYPE)0, 0.0125f, false, true, true); break; case 1: component.refs.afflictions.SubtractStatus((STATUSTYPE)0, 0.0125f, false, false); break; } Plugin.Log($"[Netcode] FirePlayerLaserHit remote — viewID={targetViewID} effect={effectType}"); Plugin.FirePlayerLaserHit(targetViewID, effectType); } } public void SendMilestoneUnlockAll(string milestoneId) { _photonView.RPC("RPC_MilestoneUnlockAll", (RpcTarget)0, new object[1] { milestoneId }); } [PunRPC] private void RPC_MilestoneUnlockAll(string milestoneId) { Plugin.Log("[Netcode] RPC_MilestoneUnlockAll: " + milestoneId); if (Plugin.MilestonesEnabled) { MilestoneTracker.Instance.ReportProgress(milestoneId); } } public void SendHostModeState(bool active) { if (PhotonNetwork.IsMasterClient) { _photonView.RPC("RPC_HostModeState", (RpcTarget)0, new object[1] { active }); } } [PunRPC] private void RPC_HostModeState(bool active) { Plugin.Log($"[Netcode] RPC_HostModeState: {active}"); IsHostModeActive = active; if (!PhotonNetwork.IsMasterClient) { string userId = PhotonNetwork.MasterClient.UserId; if (active) { _savedHostVolume = AudioLevels.GetPlayerLevel(userId); AudioLevels.SetPlayerLevel(userId, 0f); Plugin.Log($"[Netcode] Muted host voice. Saved volume: {_savedHostVolume}"); } else if (_savedHostVolume.HasValue) { AudioLevels.SetPlayerLevel(userId, _savedHostVolume.Value); Plugin.Log($"[Netcode] Restored host voice to {_savedHostVolume.Value}"); _savedHostVolume = null; } } } private void Update() { if (_savedHostVolume.HasValue && !PhotonNetwork.IsMasterClient) { float playerLevel = AudioLevels.GetPlayerLevel(PhotonNetwork.MasterClient.UserId); if (playerLevel != 0f) { Plugin.Log("[Netcode] User manually adjusted host volume. Clearing saved value."); _savedHostVolume = null; } } } public override void OnLeftRoom() { ((MonoBehaviourPunCallbacks)this).OnLeftRoom(); sPEAKerUI.Instance.HideDuration(); sPEAKerUI.Instance.HideAuthorInfo(); AudioMaster.Instance.StopAllPlayback(); } } public class NetworkSyncData { public int PlaybackStateInt; public string SongHash; public string SongName; public float PlaybackTime; public float Volume; public int FollowViewID; public bool IsFollowingCharacter; public int LoopStateInt; public int YouTubeLoopStateInt; public bool IsFloating; public int FollowTargetViewID; public string[] SongQueue; public bool IsYouTubeMode; public string CurrentYouTubeUrl; public string CurrentYouTubeTitle; public string[] YouTubeQueueUrls; public int DroneBehaviourModeInt; public bool IsHostModeActive; } public class TheObserver : MonoBehaviourPunCallbacks { private static TheObserver _instance = null; private PhotonView _photonView; private ushort _sPEAKerItemID; private bool _sPEAKerExists; private bool _isPocketed; private Character _pocketedByCharacter; private Controller _lastKnownController; private int _lastKnownViewID = -1; private float _suspectedMissingStartTime; private ObserverState _currentState = ObserverState.Healthy; private const float VALIDATION_INTERVAL = 2f; private const float MISSING_CONFIRMATION_TIMEOUT = 5f; private bool _isRecoveringDrone; private static readonly int Mode = Shader.PropertyToID("_Mode"); private static readonly int SrcBlend = Shader.PropertyToID("_SrcBlend"); private static readonly int DstBlend = Shader.PropertyToID("_DstBlend"); private static readonly int ZWrite = Shader.PropertyToID("_ZWrite"); private static readonly int Color1 = Shader.PropertyToID("_Color"); private static readonly int EmissionColor = Shader.PropertyToID("_EmissionColor"); public static TheObserver Instance { get { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown if ((Object)(object)_instance == (Object)null) { _instance = Object.FindAnyObjectByType(); if ((Object)(object)_instance == (Object)null) { GameObject val = new GameObject("sPEAKerObserver"); _instance = val.AddComponent(); Object.DontDestroyOnLoad((Object)(object)val); Plugin.Log("[The Observer] Born."); } } return _instance; } } public static void EnsureInitialized() { Plugin.Log("[The Observer] Ensuring initialization."); _ = Instance; } private void Awake() { Plugin.Log("[The Observer] I'm awake."); if ((Object)(object)_instance != (Object)null && (Object)(object)_instance != (Object)(object)this) { Plugin.Log("[The Observer] Duplicate instance found. Destroying."); Object.Destroy((Object)(object)((Component)this).gameObject); return; } _instance = this; _photonView = ((Component)this).GetComponent(); if ((Object)(object)_photonView == (Object)null) { _photonView = ((Component)this).gameObject.AddComponent(); byte[] value = MD5.Create().ComputeHash("onlystar.sPEAKer.Observer"u8.ToArray()); _photonView.ViewID = BitConverter.ToInt32(value, 0); } Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); Plugin.Log("[The Observer] I now have network power. Commencing observation..."); ((MonoBehaviour)this).StartCoroutine(PeriodicStateValidation()); } public bool DoessPEAKerExist() { return _sPEAKerExists; } public ushort GetsPEAKerItemID() { return _sPEAKerItemID; } public bool IsPocketed() { return _isPocketed; } public bool CanSpawnsPEAKer() { if (!PhotonNetwork.IsMasterClient) { Plugin.Log("[The Observer] Spawn DENIED. You're not MasterClient."); return false; } if (_sPEAKerExists) { Plugin.Log("[The Observer] Spawn DENIED. sPEAKer already exists."); return false; } Controller controller = FindsPEAKerController(); if ((Object)(object)controller != (Object)null) { Plugin.Log("[The Observer] Faulty state detected: Controller exists, but sPEAKerExists flag is still false. Correcting state."); _sPEAKerExists = true; _lastKnownController = controller; BroadcastExistenceState(exists: true); return false; } return true; } public Controller GetControllerInstance() { if ((Object)(object)_lastKnownController != (Object)null && (Object)(object)((Component)_lastKnownController).gameObject != (Object)null) { return _lastKnownController; } _lastKnownController = FindsPEAKerController(); return _lastKnownController; } public bool CanRecallsPEAKer() { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Invalid comparison between Unknown and I4 //IL_0070: Unknown result type (might be due to invalid IL or missing references) if (!_sPEAKerExists) { Plugin.Log("[The Observer] Recall DENIED. sPEAKer does not exist."); return false; } Controller controllerInstance = GetControllerInstance(); if ((Object)(object)controllerInstance == (Object)null) { Plugin.Log("[The Observer] Recall DENIED. sPEAKer may be pocketed."); return false; } if ((int)controllerInstance.item.itemState > 0) { Plugin.Log($"[The Observer] Recall DENIED. sPEAKer not in the ground (state: {controllerInstance.item.itemState})"); return false; } if (controllerInstance.droneBehaviour.IsFollowingTarget() && (Object)(object)controllerInstance.droneBehaviour.FollowTarget != (Object)(object)Character.localCharacter) { Plugin.Log("[The Observer] Recall DENIED. sPEAKer is following someone else."); return false; } return true; } public void OnsPEAKerSpawned(Controller controller) { Plugin.Log("[The Observer] OnsPEAKerSpawned called."); _sPEAKerExists = true; _lastKnownController = controller; _isPocketed = false; _pocketedByCharacter = null; _currentState = ObserverState.Healthy; if ((Object)(object)controller.item != (Object)null && (Object)(object)((MonoBehaviourPun)controller.item).photonView != (Object)null) { _lastKnownViewID = ((MonoBehaviourPun)controller.item).photonView.ViewID; } ushort itemID = Plugin.ItemID; ushort num = (ushort)(((Object)(object)controller.item != (Object)null) ? controller.item.itemID : 0); if (num != 0) { _sPEAKerItemID = num; if (itemID != num) { Plugin.Log("[The Observer] Our Controller has a non-zero value. Hoo-ray. We will trust it for its efforts.", "warning"); } else { Plugin.Log($"[The Observer] ItemID confirmed: {_sPEAKerItemID}"); } } else if (itemID != 0) { _sPEAKerItemID = itemID; if ((Object)(object)controller.item != (Object)null) { controller.item.itemID = itemID; Plugin.Log("[The Observer] Controller's item was lacking an identity. It's okay, happens to the best of us. A new identity has been provided.", "warning"); } else { Plugin.Log($"[The Observer] Using hardcoded itemID: {_sPEAKerItemID} (Controller item is null)", "warning"); } } else { _sPEAKerItemID = 0; Plugin.Log("[The Observer] CRITICAL: ItemID is irrecoverable.", "error"); } if (PhotonNetwork.IsMasterClient) { BroadcastExistenceState(exists: true); } Plugin.Log($"[The Observer] sPEAKer spawned. ViewID: {_lastKnownViewID}, ItemID: {_sPEAKerItemID}, State: {_currentState}"); } public void OnsPEAKerDestroyed() { Plugin.Log("[The Observer] OnsPEAKerDestroyed called."); _sPEAKerExists = false; _lastKnownController = null; _isPocketed = false; _pocketedByCharacter = null; _lastKnownViewID = -1; _currentState = ObserverState.Healthy; if (PhotonNetwork.IsMasterClient) { BroadcastExistenceState(exists: false); } Plugin.Log("[The Observer] sPEAKer destroyed. State reset."); } public void OnsPEAKerPocketed(Character pocketedBy) { Plugin.Log("[The Observer] OnsPEAKerPocketed by " + ((MonoBehaviourPun)pocketedBy).photonView.Owner.NickName + "."); _isPocketed = true; _pocketedByCharacter = pocketedBy; _lastKnownController = null; Plugin.Log($"[The Observer] sPEAKer pocketed. Controller cleared, sPEAKerExists: {_sPEAKerExists}"); } public void OnsPEAKerUnpocketed(Controller controller) { Plugin.Log("[The Observer] OnsPEAKerUnpocketed called."); _isPocketed = false; _pocketedByCharacter = null; _lastKnownController = controller; if ((Object)(object)controller.item != (Object)null && (Object)(object)((MonoBehaviourPun)controller.item).photonView != (Object)null) { _lastKnownViewID = ((MonoBehaviourPun)controller.item).photonView.ViewID; } Plugin.Log($"[The Observer] sPEAKer unpocketed. ViewID: {_lastKnownViewID}."); } public void ResetState() { Plugin.Log("[The Observer] We're resetting state."); _sPEAKerExists = false; _lastKnownController = null; _isPocketed = false; _pocketedByCharacter = null; _lastKnownViewID = -1; _currentState = ObserverState.Healthy; _suspectedMissingStartTime = 0f; if (Plugin.MilestonesEnabled) { MilestoneTracker.Instance.SetAbsoluteProgress("UnstoppableForce", 0); } Plugin.Log("[The Observer] State reset."); } private IEnumerator PeriodicStateValidation() { while (true) { yield return (object)new WaitForSeconds(2f); if (PhotonNetwork.InRoom) { ValidateState(); } } } private void ValidateState() { List list = FindAllsPEAKerControllers(); if (list.Count > 1) { Plugin.Log($"[The Observer] Duplicate sPEAKer instances detected ({list.Count}). Triggering recovery.", "error"); _sPEAKerExists = true; _currentState = ObserverState.ConfirmedMissing; TriggerRecovery(); return; } if (!_sPEAKerExists) { _currentState = ObserverState.Healthy; return; } Controller controller = FindsPEAKerController(); if ((Object)(object)controller != (Object)null) { _lastKnownController = controller; _currentState = ObserverState.Healthy; _suspectedMissingStartTime = 0f; return; } if (_isPocketed && (Object)(object)_pocketedByCharacter != (Object)null) { if (IssPEAKerInInventory(_pocketedByCharacter)) { _currentState = ObserverState.Healthy; _suspectedMissingStartTime = 0f; } else { Plugin.Log("[The Observer] sPEAKer should be in " + ((MonoBehaviourPun)_pocketedByCharacter).photonView.Owner.NickName + "'s pocket, but it's not. They have been robbed.", "error"); DetectDesyncState(); } return; } Character val = FindPlayerWithsPEAKerInInventory(); if ((Object)(object)val != (Object)null) { Plugin.Log("[The Observer] Found sPEAKer in " + ((MonoBehaviourPun)val).photonView.Owner.NickName + "'s pocket. Shame on them. Correcting state.", "warning"); _isPocketed = true; _pocketedByCharacter = val; _currentState = ObserverState.Healthy; _suspectedMissingStartTime = 0f; } else { DetectDesyncState(); } } private void DetectDesyncState() { if (_currentState == ObserverState.Healthy) { _currentState = ObserverState.SuspectedMissing; _suspectedMissingStartTime = Time.time; Plugin.Log("[The Observer] Controller missing but should exist. Suspicious.", "warning"); } if (_currentState == ObserverState.SuspectedMissing) { float num = Time.time - _suspectedMissingStartTime; if (num >= 5f) { _currentState = ObserverState.ConfirmedMissing; Plugin.Log($"[The Observer] Controller has been missing for {num}s. We must recover.", "error"); TriggerRecovery(); } } } private void TriggerRecovery() { if (_currentState == ObserverState.ConfirmedMissing) { _currentState = ObserverState.Recovering; if (PhotonNetwork.IsMasterClient) { Plugin.Log("[The Observer] You are the host, and somehow have been desynced. Disgraceful.", "error"); Plugin.Log("[The Observer] We must respawn sPEAKer.", "warning"); AttemptDroneRecovery(); } else if (_isPocketed && (Object)(object)_pocketedByCharacter != (Object)null) { Plugin.Log("[The Observer] Requesting to respawn sPEAKer.", "warning"); int actorNumber = ((MonoBehaviourPun)_pocketedByCharacter).photonView.Owner.ActorNumber; _photonView.RPC("RPC_RequestDroneRecovery", (RpcTarget)2, new object[1] { actorNumber }); } else { Plugin.Log("[The Observer] Hold on, I will ask Master what's real.", "warning"); _photonView.RPC("RPC_RequestAuthoritativeState", (RpcTarget)2, Array.Empty()); } } } public bool IssPEAKerInInventory(Character character) { if ((Object)(object)character == (Object)null || _sPEAKerItemID == 0) { return false; } try { if (character.player.HasInAnySlot(_sPEAKerItemID)) { return true; } if (character.player.backpackSlot.hasBackpack) { BackpackData val2 = default(BackpackData); BackpackData val = (((ItemSlot)character.player.backpackSlot).data.TryGetDataEntry((DataEntryKey)7, ref val2) ? val2 : null); if (val != null) { ItemSlot[] itemSlots = val.itemSlots; foreach (ItemSlot val3 in itemSlots) { if (!val3.IsEmpty() && val3.prefab.itemID == _sPEAKerItemID) { return true; } } } } return false; } catch (Exception ex) { Plugin.Log("[The Observer] Error checking inventory for " + ((MonoBehaviourPun)character).photonView.Owner.NickName + ": " + ex.Message, "error"); return false; } } private Character FindPlayerWithsPEAKerInInventory() { if (_sPEAKerItemID == 0) { return null; } try { foreach (Character allCharacter in Character.AllCharacters) { if ((Object)(object)allCharacter != (Object)null && allCharacter.player.HasInAnySlot(_sPEAKerItemID)) { Plugin.Log("[The Observer] Found sPEAKer in " + ((MonoBehaviourPun)allCharacter).photonView.Owner.NickName + "'s inventory"); return allCharacter; } } } catch (Exception ex) { Plugin.Log("[The Observer] Error scanning inventories: " + ex.Message, "error"); } return null; } public void KillsPEAKer() { if (PhotonNetwork.IsMasterClient) { _photonView.RPC("RPC_KillsPEAKer", (RpcTarget)0, Array.Empty()); } } public void AttemptDroneRecovery() { if (!PhotonNetwork.IsMasterClient) { return; } if (_isRecoveringDrone) { Plugin.Log("[The Observer] Drone recovery already in progress, ignoring."); return; } Character val = _pocketedByCharacter; if ((Object)(object)val == (Object)null) { Plugin.Log("[The Observer] No last known holder. Falling back to master client.", "warning"); Player masterClient = PhotonNetwork.MasterClient; foreach (Character allCharacter in Character.AllCharacters) { if ((Object)(object)allCharacter != (Object)null && object.Equals(((MonoBehaviourPun)allCharacter).photonView.Owner, masterClient)) { val = allCharacter; Plugin.Log("[The Observer] Using master client as recovery target."); break; } } } if ((Object)(object)val == (Object)null) { Plugin.Log("[The Observer] CRITICAL: Cannot find any valid character for recovery. Will retry on next validation cycle.", "error"); _currentState = ObserverState.ConfirmedMissing; return; } _isRecoveringDrone = true; _pocketedByCharacter = val; Plugin.Log("[The Observer] Initiating drone recovery for " + ((MonoBehaviourPun)val).photonView.Owner.NickName + "."); _photonView.RPC("RPC_KillsPEAKer", (RpcTarget)0, Array.Empty()); ((MonoBehaviour)this).StartCoroutine(DelayedRespawn(val)); } private IEnumerator DelayedRespawn(Character targetCharacter) { yield return (object)new WaitForSeconds(0.5f); try { Vector3 spawnPosition = targetCharacter.Center + Vector3.up * 2f; GameObject spawnedObject = PhotonNetwork.InstantiateItemRoom(((Object)Plugin.Bundle.LoadAsset("assets/sPEAKer.prefab")).name, spawnPosition, Quaternion.identity); CreateSpawnFlash(spawnPosition); AudioMaster.PlayFX(SoundEffects.Respawn); ((MonoBehaviour)this).StartCoroutine(DelayedDroneMode(spawnedObject)); Plugin.Log("[The Observer] sPEAKer successfully respawned as a drone. Hello world!", "warning"); _currentState = ObserverState.Healthy; } catch (Exception ex) { Exception e = ex; Plugin.Log("[The Observer] Exception during respawn: " + e.Message + ". Will retry on next validation cycle.", "error"); _currentState = ObserverState.ConfirmedMissing; } finally { _isRecoveringDrone = false; } } private IEnumerator DelayedDroneMode(GameObject spawnedObject) { CreateSpawnFlash(spawnedObject.transform.position); yield return (object)new WaitForSeconds(0.5f); Controller controller = spawnedObject.GetComponent(); if ((Object)(object)controller == (Object)null) { Plugin.Log("[The Observer] Failed to get Controller for drone mode.", "error"); yield break; } Controller.SuppressFXWindow = Time.time + 2f; Netcode.Instance.SendFloatModeToggle(isFloating: true, -1, isRecall: true); Plugin.Log("[The Observer] Drone mode engaged."); } private void CreateSpawnFlash(Vector3 position) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0012: 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_0073: 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_008e: 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_00bd: Expected O, but got Unknown //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_015b: 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) GameObject val = new GameObject("sPEAKer_SpawnFlash"); val.transform.position = position; Light val2 = val.AddComponent(); val2.type = (LightType)2; val2.color = Color.white; val2.intensity = 20f; val2.range = 25f; val2.renderMode = (LightRenderMode)1; GameObject val3 = GameObject.CreatePrimitive((PrimitiveType)0); val3.transform.SetParent(val.transform); val3.transform.localPosition = Vector3.zero; val3.transform.localScale = Vector3.one * 2f; Object.Destroy((Object)(object)val3.GetComponent()); Renderer component = val3.GetComponent(); Material val4 = new Material(Shader.Find("Standard")); val4.SetFloat(Mode, 3f); val4.SetInt(SrcBlend, 1); val4.SetInt(DstBlend, 10); val4.SetInt(ZWrite, 0); val4.DisableKeyword("_ALPHATEST_ON"); val4.DisableKeyword("_ALPHABLEND_ON"); val4.EnableKeyword("_ALPHAPREMULTIPLY_ON"); val4.renderQueue = 3000; val4.SetColor(Color1, new Color(1f, 1f, 1f, 0.8f)); val4.SetColor(EmissionColor, Color.white * 3f); val4.EnableKeyword("_EMISSION"); component.material = val4; LensFlare val5 = val.AddComponent(); val5.brightness = 2f; val5.fadeSpeed = 4f; ((MonoBehaviour)this).StartCoroutine(FadeAndDestroyFlash(val, val2, component, val4)); } private IEnumerator FadeAndDestroyFlash(GameObject flashObject, Light mainLight, Renderer glowRenderer, Material glowMat) { float duration = 0.8f; float elapsed = 0f; float startIntensity = mainLight.intensity; Color startEmission = glowMat.GetColor(EmissionColor); Color startColor = glowMat.GetColor(Color1); Vector3 startScale = ((Component)glowRenderer).transform.localScale; while (elapsed < duration) { elapsed += Time.deltaTime; float t = elapsed / duration; mainLight.intensity = startIntensity * (1f - t * t); glowMat.SetColor(EmissionColor, startEmission * (1f - t)); Color color = startColor; color.a = 1f - t; glowMat.SetColor(Color1, color); ((Component)glowRenderer).transform.localScale = startScale * (1f + t * 0.5f); yield return null; } Object.Destroy((Object)(object)flashObject); } private void BroadcastExistenceState(bool exists) { if (PhotonNetwork.IsMasterClient) { Plugin.Log($"[The Observer] Broadcasting existence state: {exists}"); _photonView.RPC("RPC_SyncExistenceState", (RpcTarget)1, new object[1] { exists }); } } [PunRPC] private void RPC_SyncExistenceState(bool exists) { if (!PhotonNetwork.IsMasterClient) { Plugin.Log($"[The Observer] Received existence state from host: {exists}"); _sPEAKerExists = exists; if (exists && (Object)(object)FindsPEAKerController() == (Object)null && !_isPocketed) { Plugin.Log("[The Observer] Host says sPEAKer exists, but we don't see it. Are they lying? Triggering validation."); _currentState = ObserverState.SuspectedMissing; _suspectedMissingStartTime = Time.time; } } } [PunRPC] private void RPC_RequestAuthoritativeState(PhotonMessageInfo info) { //IL_0014: 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_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) if (PhotonNetwork.IsMasterClient) { Player sender = info.Sender; Plugin.Log("[The Observer] " + sender.NickName + " requested authoritative state."); Controller controller = FindsPEAKerController(); bool flag = (Object)(object)controller != (Object)null || _isPocketed; if (_sPEAKerExists != flag) { Plugin.Log($"[The Observer] State mismatch detected. Our state says '{_sPEAKerExists}', but should be {flag}. Correcting.", "warning"); _sPEAKerExists = flag; } ObserverRecoveryData observerRecoveryData = new ObserverRecoveryData { Exists = _sPEAKerExists, IsPocketed = _isPocketed, ViewID = _lastKnownViewID, Position = (((Object)(object)controller != (Object)null) ? ((Component)controller).transform.position : Vector3.zero), ItemState = (((Object)(object)controller != (Object)null) ? ((int)controller.item.itemState) : 0) }; string text = JsonUtility.ToJson((object)observerRecoveryData); _photonView.RPC("RPC_ReceiveAuthoritativeState", sender, new object[1] { text }); } } [PunRPC] private void RPC_ReceiveAuthoritativeState(string jsonData) { if (!PhotonNetwork.IsMasterClient) { ObserverRecoveryData observerRecoveryData = JsonUtility.FromJson(jsonData); Plugin.Log($"[The Observer] Received authoritative state. Exists={observerRecoveryData.Exists}, Pocketed={observerRecoveryData.IsPocketed}, ViewID={observerRecoveryData.ViewID}, ItemState={observerRecoveryData.ItemState}"); _sPEAKerExists = observerRecoveryData.Exists; _isPocketed = observerRecoveryData.IsPocketed; _lastKnownViewID = observerRecoveryData.ViewID; Controller controller = FindsPEAKerController(); if (observerRecoveryData.Exists && (Object)(object)controller == (Object)null && !observerRecoveryData.IsPocketed) { Plugin.Log("[The Observer] Confirmed desync after receiving authoritative state. This will not do. Requesting hard resync.", "error"); ((MonoBehaviour)this).StartCoroutine(DelayedHardResyncRequest()); } else { _currentState = ObserverState.Healthy; _suspectedMissingStartTime = 0f; Plugin.Log("[The Observer] State reconciled successfully. I am happy."); } } } [PunRPC] private void RPC_RequestHardResync(PhotonMessageInfo info) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) if (!PhotonNetwork.IsMasterClient) { return; } Player sender = info.Sender; Plugin.Log("[The Observer] " + sender.NickName + " requested a hard resync.", "warning"); Character val = _pocketedByCharacter; if ((Object)(object)val == (Object)null) { Player masterClient = PhotonNetwork.MasterClient; foreach (Character allCharacter in Character.AllCharacters) { if ((Object)(object)allCharacter != (Object)null && object.Equals(((MonoBehaviourPun)allCharacter).photonView.Owner, masterClient)) { val = allCharacter; Plugin.Log("[The Observer] Using master client as fallback target for hard resync."); break; } } } if ((Object)(object)val == (Object)null) { Plugin.Log("[The Observer] Cannot find valid target for hard resync.", "error"); _currentState = ObserverState.ConfirmedMissing; } else { _pocketedByCharacter = val; _isPocketed = false; AttemptDroneRecovery(); } } [PunRPC] private void RPC_RequestDroneRecovery(int targetPlayerActorNumber, PhotonMessageInfo info) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) if (!PhotonNetwork.IsMasterClient) { return; } Player sender = info.Sender; Plugin.Log($"[The Observer] {sender.NickName} requested drone recovery for player {targetPlayerActorNumber}.", "warning"); Player player = PhotonNetwork.CurrentRoom.GetPlayer(targetPlayerActorNumber, false); if (player == null) { Plugin.Log($"[The Observer] Cannot find Photon player with actor number {targetPlayerActorNumber}.", "error"); return; } Character val = null; foreach (Character allCharacter in Character.AllCharacters) { if ((Object)(object)allCharacter != (Object)null && ((MonoBehaviourPun)allCharacter).photonView.Owner.ActorNumber == targetPlayerActorNumber) { val = allCharacter; break; } } if ((Object)(object)val == (Object)null) { Plugin.Log("[The Observer] Cannot find Character for player " + player.NickName + ".", "error"); return; } _pocketedByCharacter = val; _isPocketed = true; AttemptDroneRecovery(); } [PunRPC] private void RPC_KillsPEAKer() { Plugin.Log("[The Observer] KillsPEAKer called - purging all instances."); if (Plugin.MilestonesEnabled) { MilestoneTracker.Instance.ReportProgress("BackFromTheDead"); } Controller controller = FindsPEAKerController(); if ((Object)(object)controller != (Object)null) { Plugin.Log("[The Observer] Destroying sPEAKer mesh."); if (PhotonNetwork.IsMasterClient) { PhotonNetwork.Destroy(((Component)controller).gameObject); } else { Object.Destroy((Object)(object)((Component)controller).gameObject); } } foreach (Character allCharacter in Character.AllCharacters) { if ((Object)(object)allCharacter == (Object)null || (Object)(object)allCharacter.player == (Object)null) { continue; } Player player = allCharacter.player; for (byte b = 0; b < player.itemSlots.Length; b++) { ItemSlot val = player.itemSlots[b]; if (!val.IsEmpty() && val.prefab.itemID == _sPEAKerItemID) { Plugin.Log($"[The Observer] Removing sPEAKer from {((MonoBehaviourPun)allCharacter).photonView.Owner.NickName}'s inventory slot {b}."); val.EmptyOut(); } } if (!player.tempFullSlot.IsEmpty() && player.tempFullSlot.prefab.itemID == _sPEAKerItemID) { Plugin.Log("[The Observer] Removing sPEAKer from " + ((MonoBehaviourPun)allCharacter).photonView.Owner.NickName + "'s temp slot."); player.tempFullSlot.EmptyOut(); } } BackpackData val3 = default(BackpackData); foreach (Character allCharacter2 in Character.AllCharacters) { if ((Object)(object)allCharacter2 == (Object)null || (Object)(object)allCharacter2.player == (Object)null) { continue; } Player player2 = allCharacter2.player; if (!player2.backpackSlot.hasBackpack) { continue; } BackpackData val2 = (((ItemSlot)player2.backpackSlot).data.TryGetDataEntry((DataEntryKey)7, ref val3) ? val3 : null); if (val2 == null) { continue; } for (byte b2 = 0; b2 < val2.itemSlots.Length; b2++) { ItemSlot val4 = val2.itemSlots[b2]; if (!val4.IsEmpty() && val4.prefab.itemID == _sPEAKerItemID) { Plugin.Log($"[The Observer] Removing sPEAKer from {((MonoBehaviourPun)allCharacter2).photonView.Owner.NickName}'s equipped backpack slot {b2}."); val4.EmptyOut(); } } } Backpack[] array = Object.FindObjectsByType((FindObjectsSortMode)0); Backpack[] array2 = array; foreach (Backpack val5 in array2) { BackpackData data = ((Item)val5).GetData((DataEntryKey)7); if (data == null) { continue; } for (byte b3 = 0; b3 < data.itemSlots.Length; b3++) { ItemSlot val6 = data.itemSlots[b3]; if (!val6.IsEmpty() && val6.prefab.itemID == _sPEAKerItemID) { Plugin.Log($"[The Observer] Removing sPEAKer from ground backpack slot {b3}."); val6.EmptyOut(); } } } Plugin.Log("[The Observer] KillsPEAKer complete. All instances purged."); } private IEnumerator DelayedHardResyncRequest() { yield return (object)new WaitForSeconds(0.5f); Controller controller = FindsPEAKerController(); if ((Object)(object)controller == (Object)null && !_isPocketed && _sPEAKerExists) { Plugin.Log("[The Observer] Still missing after delay. Requesting hard resync from host.", "error"); _photonView.RPC("RPC_RequestHardResync", (RpcTarget)2, Array.Empty()); yield break; } _currentState = ObserverState.Healthy; _suspectedMissingStartTime = 0f; _lastKnownController = controller; Plugin.Log("[The Observer] Controller decided to appear during delay. Recovery successful."); } private Controller FindsPEAKerController() { Controller[] array = Object.FindObjectsByType((FindObjectsSortMode)0); Controller[] array2 = array; foreach (Controller controller in array2) { if (((object)controller).GetType().Namespace == "sPEAKer.Scripts") { return controller; } } return null; } private List FindAllsPEAKerControllers() { List list = new List(); Controller[] array = Object.FindObjectsByType((FindObjectsSortMode)0); Controller[] array2 = array; foreach (Controller controller in array2) { if (((object)controller).GetType().Namespace == "sPEAKer.Scripts") { list.Add(controller); } } return list; } } public enum ObserverState { Healthy, SuspectedMissing, ConfirmedMissing, Recovering } public class ObserverRecoveryData { public bool Exists; public bool IsPocketed; public int ViewID; public Vector3 Position; public int ItemState; } [RequireComponent(typeof(Item))] public class DroneBehaviour : MonoBehaviour { private static readonly int EmissionColor = Shader.PropertyToID("_EmissionColor"); private static readonly int Tint = Shader.PropertyToID("_Tint"); private Item _item; private ItemPhysicsSyncer _physicsSyncer; public bool IsFloating; public Character FollowTarget; public IDroneBehaviour CurrentBehaviour; private Dictionary _behaviors; public float FollowForce = 15f; public float MaxSpeed = 5f; private float _recallTimer; private Vector3 _recallEscapeDirection; private float _recallEscapeTimer; private const float RecallEscapeRecheckInterval = 0.5f; public const float PlayerSearchRadius = 5f; public bool IsHoverPositionSet; public GameObject SearchRadiusIndicator; public Vector3 HoverPosition; public float HoverOrbitRadius = 0.5f; public float HoverOrbitSpeed = 30f; public float OrbitSpeed = 12f; public float OrbitRadius = 2f; public float OrbitAngle = 0f; public Vector3 LastTargetPosition; public float StillThreshold = 0.1f; private Vector3 _originalScale; public bool IsDancing; private Coroutine _danceCoroutine; private List> _danceMoves = new List>(); private Dictionary, float> _danceWeights; private int _danceCountSinceReset; private const int DanceCountResetThreshold = 15; public Transform LeftFirePoint; public Transform RightFirePoint; public float LaserSpeed = 40f; public bool shouldLogNextSearch = true; private static readonly string[] playedDances = Array.Empty(); public bool IsBeingRecalled { get; private set; } = false; public DroneBehaviourMode CurrentBehaviourMode { get; private set; } = DroneBehaviourMode.Orbiting; public bool IsGhostDriver => (CurrentBehaviour as ManualBehaviour)?.IsGhostDriver ?? false; public Rigidbody DroneRigidbody => _item.rig; private void Awake() { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0074: 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) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) _item = ((Component)this).GetComponent(); if ((Object)(object)_item == (Object)null) { Plugin.Log("[DroneBehaviour] Item component is missing!", "error"); } Vector3 defaultLocalPosition = default(Vector3); ((Vector3)(ref defaultLocalPosition))..ctor(-0.3f, 0f, 0.5f); Vector3 defaultLocalPosition2 = default(Vector3); ((Vector3)(ref defaultLocalPosition2))..ctor(0.3f, 0f, 0.5f); LeftFirePoint = SetupFirePoint("LeftFirePoint", defaultLocalPosition); RightFirePoint = SetupFirePoint("RightFirePoint", defaultLocalPosition2); Plugin.Log("[DroneBehaviour] Fire points setup complete."); InitializeBehaviours(); _physicsSyncer = ((Component)this).GetComponent(); _originalScale = ((Component)this).transform.localScale; } private void InitializeBehaviours() { _behaviors = new Dictionary { { DroneBehaviourMode.Orbiting, new OrbitingBehaviour() }, { DroneBehaviourMode.Manual, new ManualBehaviour() }, { DroneBehaviourMode.Hovering, new HoveringBehaviour() } }; CurrentBehaviour = _behaviors[DroneBehaviourMode.Orbiting]; CurrentBehaviour.OnEnter(this); } private Transform SetupFirePoint(string pointName, Vector3 defaultLocalPosition) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown //IL_0039: 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) Transform val = ((Component)this).transform.Find(pointName); if ((Object)(object)val == (Object)null) { GameObject val2 = new GameObject(pointName); val2.transform.SetParent(((Component)this).transform); val2.transform.localPosition = defaultLocalPosition; val2.transform.localRotation = Quaternion.identity; val = val2.transform; } return val; } private void FixedUpdate() { //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) bool flag = IsFloating && (Object)(object)FollowTarget == (Object)null && CurrentBehaviourMode == DroneBehaviourMode.Orbiting; if ((Object)(object)SearchRadiusIndicator != (Object)null && SearchRadiusIndicator.activeSelf != flag) { SearchRadiusIndicator.SetActive(flag); } if (!IsFloating || (Object)(object)_item?.rig == (Object)null) { return; } if (IsBeingRecalled) { UpdateRecall(); } else if (IsDancing) { Vector3 linearVelocity = _item.rig.linearVelocity; if (((Vector3)(ref linearVelocity)).sqrMagnitude > 0.01f) { _item.rig.linearVelocity = Vector3.Lerp(_item.rig.linearVelocity, Vector3.zero, Time.fixedDeltaTime * 5f); } } else { CurrentBehaviour.UpdateBehaviour(this); } } private void LateUpdate() { //IL_002d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)SearchRadiusIndicator != (Object)null && SearchRadiusIndicator.activeSelf) { SearchRadiusIndicator.transform.rotation = Quaternion.identity; } } public void CycleBehaviour() { if (!IsFloating) { Plugin.Log("[DroneBehaviour] Cannot cycle behavior - not floating"); } else if (SwitchBehaviour(CurrentBehaviourMode switch { DroneBehaviourMode.Orbiting => DroneBehaviourMode.Manual, DroneBehaviourMode.Manual => DroneBehaviourMode.Orbiting, DroneBehaviourMode.Hovering => DroneBehaviourMode.Orbiting, _ => DroneBehaviourMode.Orbiting, })) { sPEAKerUI.Instance.ShowToast("Drone Mode: " + GetCurrentBehaviourName()); } } public bool SwitchBehaviour(DroneBehaviourMode newMode, bool isDriver = true) { if (newMode == CurrentBehaviourMode) { Plugin.Log($"[DroneBehaviour] Already in {newMode} mode"); return false; } if (!_behaviors.ContainsKey(newMode)) { Plugin.Log($"[DroneBehaviour] Behaviour {newMode} not implemented", "error"); return false; } IDroneBehaviour droneBehaviour = _behaviors[newMode]; if (!droneBehaviour.CanEnterBehaviour(this)) { Plugin.Log($"[DroneBehaviour] Cannot enter {newMode} mode at this time"); return false; } CurrentBehaviour.OnExit(this); DroneBehaviourMode currentBehaviourMode = CurrentBehaviourMode; CurrentBehaviourMode = newMode; CurrentBehaviour = droneBehaviour; CurrentBehaviour.OnEnter(this, isDriver); if ((Object)(object)_item != (Object)null) { _item.blockInteraction = newMode == DroneBehaviourMode.Manual; } Plugin.Log($"[DroneBehaviour] Switched from {currentBehaviourMode} to {newMode}"); if (isDriver && (Object)(object)Character.localCharacter != (Object)null) { Netcode.Instance.SendDroneMode((int)newMode, ((MonoBehaviourPun)Character.localCharacter).photonView.ViewID); } return true; } public void NetworkSwitchBehaviour(int modeInt, bool isDriver) { SwitchBehaviour((DroneBehaviourMode)modeInt, isDriver); } public bool SwitchBehaviourAsGhost() { if (!IsFloating) { Plugin.Log("[DroneBehaviour] SwitchBehaviourAsGhost: drone is not floating"); return false; } if (CurrentBehaviourMode == DroneBehaviourMode.Manual) { Plugin.Log("[DroneBehaviour] SwitchBehaviourAsGhost: already in Manual mode"); return false; } if (!(_behaviors[DroneBehaviourMode.Manual] is ManualBehaviour manualBehaviour)) { Plugin.Log("[DroneBehaviour] SwitchBehaviourAsGhost: ManualBehaviour not found", "error"); return false; } if (!manualBehaviour.CanEnterBehaviour(this)) { Plugin.Log("[DroneBehaviour] SwitchBehaviourAsGhost: CanEnterBehaviour returned false"); return false; } CurrentBehaviour.OnExit(this); DroneBehaviourMode currentBehaviourMode = CurrentBehaviourMode; CurrentBehaviourMode = DroneBehaviourMode.Manual; CurrentBehaviour = manualBehaviour; manualBehaviour.OnEnterAsGhost(this); if ((Object)(object)_item != (Object)null) { _item.blockInteraction = true; } Plugin.Log($"[DroneBehaviour] Ghost switched drone from {currentBehaviourMode} to Manual"); Netcode.Instance.SendDroneMode(1, -1); return true; } public void ExitManualMode() { SwitchBehaviour(DroneBehaviourMode.Orbiting); } public void InjectManualInput(float moveX, float moveY, float moveZ, float lookDeltaX, float lookDeltaY, bool fire, bool heal) { if (CurrentBehaviour is ManualBehaviour manualBehaviour) { manualBehaviour.InjectInput(moveX, moveY, moveZ, lookDeltaX, lookDeltaY, fire, heal); } } private string GetCurrentBehaviourName() { return CurrentBehaviour.GetBehaviourName(); } public void SetSyncState(bool shouldSync) { if ((Object)(object)_physicsSyncer != (Object)null) { _physicsSyncer.shouldSync = shouldSync; } } public void SetRigidbodyState(bool isFloating) { if (!((Object)(object)_item?.rig == (Object)null)) { if (isFloating) { _item.rig.useGravity = false; _item.rig.isKinematic = false; IsHoverPositionSet = false; _item.rig.linearDamping = 2f; _item.rig.angularDamping = 5f; } else { SetSyncState(shouldSync: true); _item.rig.useGravity = true; _item.rig.linearDamping = 0f; _item.rig.angularDamping = 0.5f; } } } public Vector3 AvoidObstacles(Vector3 desiredDirection) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0012: 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_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: 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_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0054: 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_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0079: 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) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0081: 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_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_00db: 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_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: 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_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_011c: 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_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0190: 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_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0173: 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) Vector3 worldCenterOfMass = _item.rig.worldCenterOfMass; RaycastHit val = default(RaycastHit); if (!Physics.SphereCast(worldCenterOfMass, 0.4f, desiredDirection, ref val, 5f)) { return desiredDirection; } Vector3 normalized = ((Vector3)(ref desiredDirection)).normalized; Vector3 val2 = Vector3.Cross(normalized, Vector3.up); Vector3 normalized2 = ((Vector3)(ref val2)).normalized; if (normalized2 == Vector3.zero) { val2 = Vector3.Cross(normalized, Vector3.right); normalized2 = ((Vector3)(ref val2)).normalized; } Vector3 val3 = Vector3.Cross(normalized2, normalized); Vector3 result = Vector3.up; float num = float.MinValue; float num2 = MathF.PI * (3f - Mathf.Sqrt(5f)); RaycastHit val5 = default(RaycastHit); for (int i = 0; i < 16; i++) { float num3 = (float)i / 15f; float num4 = Mathf.Acos(1f - num3); float num5 = num2 * (float)i; float num6 = Mathf.Sin(num4); Vector3 val4 = normalized * Mathf.Cos(num4) + normalized2 * (num6 * Mathf.Cos(num5)) + val3 * (num6 * Mathf.Sin(num5)); ((Vector3)(ref val4)).Normalize(); float num7 = (Physics.SphereCast(worldCenterOfMass, 0.4f, val4, ref val5, 5f) ? ((RaycastHit)(ref val5)).distance : 5f); float num8 = Vector3.Dot(val4, desiredDirection); float num9 = num7 + num8 * 3f; if (num9 > num) { num = num9; result = val4; } } return result; } public void FindAndFollowNearestPlayer() { //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) Character val = null; float num = float.MaxValue; IEnumerable allPlayers = PlayerHandler.GetAllPlayers(); if (shouldLogNextSearch) { Plugin.Log($"[DroneBehaviour] Searching for players. Found {allPlayers.Count()} total players."); shouldLogNextSearch = false; } foreach (Player item in allPlayers) { if ((Object)(object)item != (Object)null && (Object)(object)item.character != (Object)null) { Character character = item.character; Vector3 val2 = character.Center - _item.rig.worldCenterOfMass; float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude; if (sqrMagnitude < num) { num = sqrMagnitude; val = character; } } } if ((Object)(object)val != (Object)null && num <= 25f) { Plugin.Log("[DroneBehaviour] Found nearest player: " + ((Object)val).name + ". Following."); shouldLogNextSearch = true; int viewID = ((MonoBehaviourPun)val).photonView.ViewID; Netcode.Instance.SendFloatModeToggle(isFloating: true, viewID); } } public void CreateSearchRadiusIndicator() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Expected O, but got Unknown //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: 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_011d: Unknown result type (might be due to invalid IL or missing references) SearchRadiusIndicator = new GameObject("SearchRadiusIndicator"); SearchRadiusIndicator.transform.SetParent(((Component)this).transform, false); LineRenderer val = SearchRadiusIndicator.AddComponent(); val.useWorldSpace = false; val.loop = true; val.startWidth = 0.1f; val.endWidth = 0.1f; val.alignment = (LineAlignment)1; Color val2 = default(Color); ((Color)(ref val2))..ctor(0.37f, 1f, 0.79f, 0.6f); Material val3 = new Material(Shader.Find("W/Peak_Glass")); val3.SetColor(Tint, val2); val3.SetColor(EmissionColor, val2 * 1.5f); ((Renderer)val).material = val3; val.startColor = val2; val.endColor = val2; int num = (val.positionCount = 50); for (int i = 0; i < num; i++) { float num3 = (float)i / (float)(num - 1) * 2f * MathF.PI; float num4 = Mathf.Cos(num3) * 5f; float num5 = Mathf.Sin(num3) * 5f; val.SetPosition(i, new Vector3(num4, 0f, num5)); } SearchRadiusIndicator.SetActive(false); } public GameObject CreateLaserProjectile(LaserProjectile.LaserEffect effectType) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: 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_00e0: Expected O, but got Unknown //IL_00e7: 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_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("LaserProjectile"); val.transform.localScale = new Vector3(0.1f, 0.1f, 3f); GameObject val2 = val; GameObject val3 = GameObject.CreatePrimitive((PrimitiveType)2); Mesh mesh = val3.GetComponent().mesh; Object.Destroy((Object)(object)val3); MeshFilter val4 = val2.AddComponent(); val4.mesh = mesh; MeshRenderer val5 = val2.AddComponent(); if (1 == 0) { } Color val6 = (Color)(effectType switch { LaserProjectile.LaserEffect.Damage => new Color(1f, 0f, 0f, 0.8f), LaserProjectile.LaserEffect.Heal => new Color(0f, 1f, 0f, 0.8f), _ => new Color(1f, 0f, 0f, 0.8f), }); if (1 == 0) { } Color val7 = val6; Shader val8 = Shader.Find("W/Peak_Glass"); Material val9 = new Material(val8); val9.SetColor(Tint, val7); val9.SetColor(EmissionColor, val7 * 3f); ((Renderer)val5).material = val9; CapsuleCollider val10 = val2.AddComponent(); ((Collider)val10).isTrigger = true; val10.radius = 0.1f; val10.height = 3f; val10.direction = 1; Rigidbody val11 = val2.AddComponent(); LaserProjectile laserProjectile = val2.AddComponent(); laserProjectile.Speed = LaserSpeed; laserProjectile.EffectType = effectType; int layer = LayerMask.NameToLayer("Default"); val2.layer = layer; Collider[] componentsInChildren = ((Component)this).GetComponentsInChildren(); Collider[] array = componentsInChildren; foreach (Collider val12 in array) { if ((Object)(object)val12 != (Object)null) { Physics.IgnoreCollision((Collider)(object)val10, val12, true); } } return val2; } public void NetworkSpawnLasers(Vector3 leftPos, Quaternion leftRot, Vector3 rightPos, Quaternion rightRot, int effectType) { //IL_0016: 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_002d: 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) AudioMaster.PlayLaserFX(); GameObject val = CreateLaserProjectile((LaserProjectile.LaserEffect)effectType); val.transform.SetPositionAndRotation(leftPos, leftRot); GameObject val2 = CreateLaserProjectile((LaserProjectile.LaserEffect)effectType); val2.transform.SetPositionAndRotation(rightPos, rightRot); } public void BeginRecall(Character target) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) FollowTarget = target; IsBeingRecalled = true; _recallTimer = 0f; _recallEscapeDirection = Vector3.zero; _recallEscapeTimer = 0f; if ((Object)(object)_item?.rig != (Object)null) { _item.rig.useGravity = false; _item.rig.isKinematic = true; } SetSyncState(shouldSync: false); Plugin.Log("[DroneBehaviour] Recall started toward " + ((Object)target).name); } public void EndRecall() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) if (IsBeingRecalled) { IsBeingRecalled = false; _recallTimer = 0f; _recallEscapeDirection = Vector3.zero; if (Plugin.MilestonesEnabled) { MilestoneTracker.Instance.ReportProgress("BabyComeBack"); } SetRigidbodyState(isFloating: true); Plugin.Log("[DroneBehaviour] Recall ended, returning to physics follow"); } } private void UpdateRecall() { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: 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_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: 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_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: 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_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)FollowTarget == (Object)null) { EndRecall(); return; } _recallTimer += Time.fixedDeltaTime; if (_recallTimer >= 5f) { Plugin.Log("[DroneBehaviour] Recall timeout after 5s, recovering drone"); EndRecall(); TheObserver.Instance.AttemptDroneRecovery(); return; } Vector3 position = ((Component)this).transform.position; Vector3 center = FollowTarget.Center; float num = Vector3.Distance(position, center); if (num < 2.5f) { AudioMaster.PlayFX(SoundEffects.Paired); EndRecall(); return; } Vector3 val = center - position; Vector3 normalized = ((Vector3)(ref val)).normalized; float num2 = 30f * Time.fixedDeltaTime; RaycastHit val2 = default(RaycastHit); Vector3 val3; if (!Physics.SphereCast(position, 0.3f, normalized, ref val2, num - 2f)) { val3 = normalized; _recallEscapeDirection = Vector3.zero; _recallEscapeTimer = 0f; } else { Vector3 val4 = AvoidObstacles(normalized); if (val4 != Vector3.up) { val3 = val4; _recallEscapeDirection = Vector3.zero; _recallEscapeTimer = 0f; } else { _recallEscapeTimer -= Time.fixedDeltaTime; if (_recallEscapeTimer <= 0f || _recallEscapeDirection == Vector3.zero) { _recallEscapeDirection = FindRecallEscapeDirection(position, normalized); _recallEscapeTimer = 0.5f; } val3 = _recallEscapeDirection; } } _item.rig.MovePosition(position + val3 * num2); if (((Vector3)(ref val3)).sqrMagnitude > 0.001f) { Quaternion val5 = Quaternion.LookRotation(val3); _item.rig.MoveRotation(Quaternion.Slerp(((Component)this).transform.rotation, val5, Time.fixedDeltaTime * 15f)); } } private Vector3 FindRecallEscapeDirection(Vector3 position, Vector3 dirToTarget) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0012: 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) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_007e: 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_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) Vector3 result = Vector3.up; float num = float.MinValue; RaycastHit val = default(RaycastHit); for (int i = 0; i < 20; i++) { Vector3 onUnitSphere = Random.onUnitSphere; float num2 = (Physics.SphereCast(position, 0.3f, onUnitSphere, ref val, 8f) ? ((RaycastHit)(ref val)).distance : 8f); float num3 = Vector3.Dot(onUnitSphere, dirToTarget); float num4 = num2 + num3 * 3f; if (num4 > num) { num = num4; result = onUnitSphere; } } return result; } private void InitializeDanceMoves() { _danceMoves = new List>(9) { Headbang, Infinity, SideToSide, SpiralDrop, TheWave, TheGlitch, TheNod, TheHeartbeat, TheAtom }; _danceWeights = new Dictionary, float>(); ResetDanceWeights(logCurrentWeights: false); } private Func SelectWeightedRandomMove() { float num = _danceWeights.Values.Sum(); float num2 = Random.Range(0f, num); foreach (Func danceMove in _danceMoves) { if (num2 < _danceWeights[danceMove]) { return danceMove; } num2 -= _danceWeights[danceMove]; } return _danceMoves[Random.Range(0, _danceMoves.Count)]; } public void StartDance() { if (!IsFloating) { Plugin.Log("[DroneBehaviour] Cannot dance while not in float mode."); return; } ((MonoBehaviour)this).StopCoroutine(_danceCoroutine); if (_danceMoves.Count == 0) { InitializeDanceMoves(); } if (_danceCountSinceReset >= 15) { Plugin.Log($"[DroneBehaviour] Dance weight reset triggered. Count: {_danceCountSinceReset}"); ResetDanceWeights(logCurrentWeights: true); } _danceCountSinceReset++; Func func = SelectWeightedRandomMove(); Plugin.Log("[DroneBehaviour] Starting weighted random dance: " + func.Method.Name); _danceCoroutine = ((MonoBehaviour)this).StartCoroutine(ExecuteRandomDance(func)); } private void ResetDanceWeights(bool logCurrentWeights) { if (logCurrentWeights) { string text = string.Join(", ", _danceWeights.Select((KeyValuePair, float> kvp) => $"{kvp.Key.Method.Name}: {kvp.Value:F1}")); Plugin.Log("[DroneBehaviour] Pre-reset weights: " + text); } _danceWeights = new Dictionary, float>(); foreach (Func danceMove in _danceMoves) { _danceWeights[danceMove] = 1f; } _danceCountSinceReset = 0; Plugin.Log("[DroneBehaviour] Dance weights have been reset to initial values."); } private IEnumerator ExecuteRandomDance(Func danceMove) { if (!playedDances.Contains(danceMove.Method.Name)) { CollectionExtensions.AddItem((IEnumerable)playedDances, danceMove.Method.Name); if (Plugin.MilestonesEnabled) { MilestoneTracker.Instance.ReportProgress("ShakeIt"); } } if (_danceWeights.ContainsKey(danceMove)) { _danceWeights[danceMove] = 0f; } foreach (Func key in _danceWeights.Keys.ToList()) { if (key != danceMove) { _danceWeights[key] += 1f; } } IsDancing = true; _item.rig.linearVelocity = Vector3.zero; Vector3 startPosition = ((Component)this).transform.position; Quaternion startRotation = ((Component)this).transform.rotation; yield return ((MonoBehaviour)this).StartCoroutine(danceMove(startPosition, startRotation)); if (Plugin.MilestonesEnabled && ((Component)this).transform.localScale.x >= _originalScale.x * 10f) { MilestoneTracker.Instance.ReportProgress("LargerThanLife"); } IsDancing = false; _danceCoroutine = null; Plugin.Log("[DroneBehaviour] Dance finished. Resuming normal behavior."); } private IEnumerator MoveTo(Vector3 targetPos, Quaternion targetRot, float duration) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) Vector3 initialPos = ((Component)this).transform.position; Quaternion initialRot = ((Component)this).transform.rotation; float time = 0f; while (time < duration) { ((Component)this).transform.position = Vector3.Lerp(initialPos, targetPos, time / duration); ((Component)this).transform.rotation = Quaternion.Slerp(initialRot, targetRot, time / duration); time += Time.deltaTime; yield return null; } ((Component)this).transform.position = targetPos; ((Component)this).transform.rotation = targetRot; } private IEnumerator ScaleTo(Vector3 targetScale, float duration) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) Vector3 initialScale = ((Component)this).transform.localScale; float time = 0f; while (time < duration) { ((Component)this).transform.localScale = Vector3.Lerp(initialScale, targetScale, time / duration); time += Time.deltaTime; yield return null; } ((Component)this).transform.localScale = targetScale; } private IEnumerator Headbang(Vector3 startPosition, Quaternion startRotation) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < 4; i++) { Vector3 downPos = startPosition - ((Component)this).transform.up * 0.6f + ((Component)this).transform.forward * 0.4f; Quaternion downRot = startRotation * Quaternion.Euler(85f, 0f, 0f); yield return ((MonoBehaviour)this).StartCoroutine(MoveTo(downPos, downRot, 0.125f)); Vector3 upPos = startPosition + ((Component)this).transform.up * 0.3f; Quaternion upRot = startRotation * Quaternion.Euler(-40f, 0f, 0f); yield return ((MonoBehaviour)this).StartCoroutine(MoveTo(upPos, upRot, 0.375f)); } yield return ((MonoBehaviour)this).StartCoroutine(MoveTo(startPosition, startRotation, 0.2f)); } private IEnumerator Infinity(Vector3 startPosition, Quaternion startRotation) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) float infinityDuration = 2f; float infinityTimer = 0f; float infinityScaleX = 1f; float infinityScaleY = 0.5f; while (infinityTimer < infinityDuration) { infinityTimer += Time.deltaTime; float t = infinityTimer / infinityDuration; float angle = t * 2f * MathF.PI * 2f; float x = infinityScaleX * Mathf.Sin(angle); float y = infinityScaleY * Mathf.Sin(angle) * Mathf.Cos(angle); Vector3 offset = ((Component)this).transform.right * x + ((Component)this).transform.up * y; ((Component)this).transform.position = startPosition + offset; yield return null; } ((Component)this).transform.position = startPosition; } private IEnumerator Jitterbug(Vector3 startPosition, Quaternion startRotation) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) float jitterTimer = 0f; while (jitterTimer < 2f) { jitterTimer += Time.deltaTime; Vector3 randomPosOffset = Random.insideUnitSphere * 0.15f; Quaternion randomRotOffset = Quaternion.Euler(Random.Range(-10f, 10f), Random.Range(-10f, 10f), Random.Range(-10f, 10f)); ((Component)this).transform.position = startPosition + randomPosOffset; ((Component)this).transform.rotation = startRotation * randomRotOffset; yield return null; } yield return ((MonoBehaviour)this).StartCoroutine(MoveTo(startPosition, startRotation, 0.2f)); } private IEnumerator SideToSide(Vector3 startPosition, Quaternion startRotation) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) Vector3 right = (((Object)(object)FollowTarget != (Object)null) ? ((Component)FollowTarget).transform.right : ((Component)this).transform.right); yield return ((MonoBehaviour)this).StartCoroutine(MoveTo(startPosition - right * 0.6f + Vector3.up * 0.2f, startRotation * Quaternion.Euler(0f, 0f, 35f), 0.25f)); yield return ((MonoBehaviour)this).StartCoroutine(MoveTo(startPosition - right * 0.5f, startRotation * Quaternion.Euler(0f, 0f, 25f), 0.25f)); yield return ((MonoBehaviour)this).StartCoroutine(MoveTo(startPosition - right * 0.8f + Vector3.up * 0.25f, startRotation * Quaternion.Euler(0f, 0f, 45f), 0.25f)); yield return ((MonoBehaviour)this).StartCoroutine(MoveTo(startPosition - right * 0.7f, startRotation * Quaternion.Euler(0f, 0f, 35f), 0.25f)); yield return ((MonoBehaviour)this).StartCoroutine(MoveTo(startPosition + right * 0.6f + Vector3.up * 0.2f, startRotation * Quaternion.Euler(0f, 0f, -35f), 0.25f)); yield return ((MonoBehaviour)this).StartCoroutine(MoveTo(startPosition + right * 0.5f, startRotation * Quaternion.Euler(0f, 0f, -25f), 0.25f)); yield return ((MonoBehaviour)this).StartCoroutine(MoveTo(startPosition + right * 0.8f + Vector3.up * 0.25f, startRotation * Quaternion.Euler(0f, 0f, -45f), 0.25f)); yield return ((MonoBehaviour)this).StartCoroutine(MoveTo(startPosition + right * 0.7f, startRotation * Quaternion.Euler(0f, 0f, -35f), 0.25f)); yield return ((MonoBehaviour)this).StartCoroutine(MoveTo(startPosition, startRotation, 0.25f)); } private IEnumerator SpiralDrop(Vector3 startPosition, Quaternion startRotation) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) float spiralTimer = 0f; Vector3 spiralStartPosition = ((Component)this).transform.position; while (spiralTimer < 2f) { spiralTimer += Time.deltaTime; float t = spiralTimer / 2f; float currentAngle = t * 360f * 4f; float baselineHeight = Mathf.Lerp(0f, 1.5f, t); float pulseOffset = Mathf.Sin(currentAngle * (MathF.PI / 180f)) * 0.1f; float currentHeight = baselineHeight + pulseOffset; float x = Mathf.Cos(currentAngle * (MathF.PI / 180f)) * 0.5f; float z = Mathf.Sin(currentAngle * (MathF.PI / 180f)) * 0.5f; ((Component)this).transform.position = spiralStartPosition + new Vector3(x, currentHeight, z); ((Component)this).transform.rotation = startRotation * Quaternion.Euler(0f, currentAngle, 0f); yield return null; } yield return (object)new WaitForSeconds(0.2f); yield return ((MonoBehaviour)this).StartCoroutine(MoveTo(startPosition, startRotation, 0.25f)); } private IEnumerator TheGlitch(Vector3 startPosition, Quaternion startRotation) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) float timer = 0f; float nextGlitchTime = 0f; while (timer < 3f) { timer += Time.deltaTime; if (timer > nextGlitchTime) { Vector3 randomPos = startPosition + Random.insideUnitSphere * 0.4f; Quaternion randomRot = startRotation * Quaternion.Euler((float)Random.Range(-60, 60), (float)Random.Range(-60, 60), (float)Random.Range(-60, 60)); ((Component)this).transform.SetPositionAndRotation(randomPos, randomRot); nextGlitchTime = timer + 0.12f; } yield return null; } yield return ((MonoBehaviour)this).StartCoroutine(MoveTo(startPosition, startRotation, 0.1f)); } private IEnumerator TheHeartbeat(Vector3 startPosition, Quaternion startRotation) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) Vector3 initialScale = ((Component)this).transform.localScale; Vector3 peakScale = initialScale * 2f; ((Component)this).transform.position = startPosition; ((Component)this).transform.rotation = startRotation; for (int i = 0; i < 4; i++) { yield return ((MonoBehaviour)this).StartCoroutine(ScaleTo(peakScale, 0.1f)); yield return ((MonoBehaviour)this).StartCoroutine(ScaleTo(initialScale, 0.15f)); yield return ((MonoBehaviour)this).StartCoroutine(ScaleTo(peakScale, 0.1f)); yield return ((MonoBehaviour)this).StartCoroutine(ScaleTo(initialScale, 0.15f)); yield return (object)new WaitForSeconds(0.25f); } } private IEnumerator TheNod(Vector3 startPosition, Quaternion startRotation) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < 4; i++) { float currentAngle = ((i % 2 == 0) ? 35f : (-35f)); Vector3 dipPos = startPosition - Vector3.up * 0.1f; Quaternion tiltRot = startRotation * Quaternion.Euler(0f, 0f, currentAngle); yield return ((MonoBehaviour)this).StartCoroutine(MoveTo(dipPos, tiltRot, 0.25f)); yield return ((MonoBehaviour)this).StartCoroutine(MoveTo(startPosition, startRotation, 0.25f)); } } private IEnumerator TheAtom(Vector3 startPosition, Quaternion startRotation) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) float timer = 0f; Quaternion randomTilt = Random.rotation; while (timer < 3.5f) { timer += Time.deltaTime; float t = timer / 3.5f; float theta = t * MathF.PI * 2f * 5f; float phi = (Mathf.Sin(t * MathF.PI * 2f * 2f) + 1f) / 2f * MathF.PI; float x = 2f * Mathf.Sin(phi) * Mathf.Cos(theta); float y = 2f * Mathf.Cos(phi); float z = 2f * Mathf.Sin(phi) * Mathf.Sin(theta); Vector3 offset = new Vector3(x, y, z); ((Component)this).transform.position = startPosition + randomTilt * offset; Vector3 val = startPosition - ((Component)this).transform.position; Vector3 lookDirection = ((Vector3)(ref val)).normalized; if (lookDirection != Vector3.zero) { ((Component)this).transform.rotation = Quaternion.LookRotation(lookDirection); } yield return null; } yield return ((MonoBehaviour)this).StartCoroutine(MoveTo(startPosition, startRotation, 0.3f)); } private IEnumerator TheWave(Vector3 startPosition, Quaternion startRotation) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) float waveTimer = 0f; while (waveTimer < 2f) { waveTimer += Time.deltaTime; float t = waveTimer / 2f; float angle = t * MathF.PI * 4f; float zOffset = Mathf.Sin(angle) * 0.6f; float xRot = Mathf.Cos(angle) * 40f; ((Component)this).transform.position = startPosition + ((Component)this).transform.forward * zOffset; ((Component)this).transform.rotation = startRotation * Quaternion.Euler(xRot, 0f, 0f); yield return null; } yield return ((MonoBehaviour)this).StartCoroutine(MoveTo(startPosition, startRotation, 0.1f)); } public bool IsFollowingTarget() { return (Object)(object)FollowTarget != (Object)null; } public Func SelectWeightedRandomMoveForNetwork() { if (_danceMoves.Count == 0) { InitializeDanceMoves(); } if (_danceCountSinceReset >= 15) { Plugin.Log($"[DroneBehaviour] Network dance weight reset triggered. Count: {_danceCountSinceReset}"); ResetDanceWeights(logCurrentWeights: true); } _danceCountSinceReset++; return SelectWeightedRandomMove(); } public void StartNetworkDance(string moveName) { if (!IsFloating) { Plugin.Log("[DroneBehaviour] Cannot dance while not in float mode."); return; } if (_danceCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_danceCoroutine); } if (_danceMoves.Count == 0) { InitializeDanceMoves(); } Func func = _danceMoves.FirstOrDefault((Func move) => move.Method.Name == moveName); if (func != null) { if (Plugin.MilestonesEnabled) { MilestoneTracker.Instance.ReportProgress("WatchingMeDance"); } Plugin.Log("[DroneBehaviour] Starting network dance: " + moveName); _danceCoroutine = ((MonoBehaviour)this).StartCoroutine(ExecuteRandomDance(func)); } else { Plugin.Log("[DroneBehaviour] Dance move '" + moveName + "' not found, falling back to random selection", "warning"); StartDance(); } } } public enum DroneBehaviourMode { Orbiting, Manual, Hovering } public interface IDroneBehaviour { void OnEnter(DroneBehaviour drone, bool isDriver = true); void OnExit(DroneBehaviour drone); void UpdateBehaviour(DroneBehaviour drone); bool CanEnterBehaviour(DroneBehaviour drone); string GetBehaviourName(); } public class OrbitingBehaviour : IDroneBehaviour { private float _followStuckTimer = 0f; private const float FollowStuckTimeout = 5f; private const float FollowStuckDistanceMultiplier = 8f; public void OnEnter(DroneBehaviour drone, bool isDriver = true) { Plugin.Log("[OrbitingBehaviour] Entered orbiting mode"); _followStuckTimer = 0f; } public void OnExit(DroneBehaviour drone) { drone.SetSyncState(shouldSync: true); Plugin.Log("[OrbitingBehaviour] Exited orbiting mode"); } public void UpdateBehaviour(DroneBehaviour drone) { if ((Object)(object)drone.FollowTarget != (Object)null) { drone.SetSyncState(shouldSync: false); if (Plugin.MilestonesEnabled) { MilestoneTracker.Instance.ReportProgress("FreeBird"); } HandleFollowTarget(drone); } else { drone.SetSyncState(shouldSync: true); HandleHoverAndSearch(drone); } } public bool CanEnterBehaviour(DroneBehaviour drone) { return drone.IsFloating && !drone.IsDancing; } public string GetBehaviourName() { return "Orbiting"; } private void HandleFollowTarget(DroneBehaviour drone) { //IL_005c: 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_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_006e: 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) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0135: 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_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0203: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Unknown result type (might be due to invalid IL or missing references) if (PhotonNetwork.IsMasterClient && drone.FollowTarget.data.dead) { Plugin.Log("[OrbitingBehavior] Target " + ((Object)drone.FollowTarget).name + " has died. Returning to hover mode."); Netcode.Instance.SendFloatModeToggle(isFloating: true, -1); return; } Vector3 val = CalculateTargetPosition(drone); Vector3 worldCenterOfMass = drone.DroneRigidbody.worldCenterOfMass; float num = Vector3.Distance(worldCenterOfMass, val); float num2 = drone.OrbitRadius * 8f; if (num > num2) { _followStuckTimer += Time.fixedDeltaTime; if (_followStuckTimer >= 5f) { Plugin.Log($"[OrbitingBehaviour] Drone stuck while following (distance: {num:F1}, threshold: {num2:F1}), recovering"); _followStuckTimer = 0f; TheObserver.Instance.AttemptDroneRecovery(); return; } } else { _followStuckTimer = 0f; } if (num < 0.1f) { Rigidbody droneRigidbody = drone.DroneRigidbody; droneRigidbody.linearVelocity *= 0.95f; return; } Vector3 val2 = val - worldCenterOfMass; Vector3 normalized = ((Vector3)(ref val2)).normalized; normalized = drone.AvoidObstacles(normalized); float num3 = drone.MaxSpeed; if (num > 6f) { num3 *= Mathf.Lerp(1f, 6f, Mathf.InverseLerp(6f, 30f, num)); } Vector3 val3 = normalized * num3; Vector3 val4 = (val3 - drone.DroneRigidbody.linearVelocity) * drone.FollowForce; drone.DroneRigidbody.AddForce(val4); Vector3 val5 = drone.FollowTarget.Center - worldCenterOfMass; if (((Vector3)(ref val5)).magnitude > 0.5f) { Quaternion val6 = Quaternion.LookRotation(val5); drone.DroneRigidbody.MoveRotation(Quaternion.Slerp(((Component)drone).transform.rotation, val6, Time.fixedDeltaTime * 8f)); } } private void HandleHoverAndSearch(DroneBehaviour drone) { //IL_0076: 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_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_008d: 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_0099: 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_00a2: 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_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: 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_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: 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_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: 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) if (!drone.IsHoverPositionSet) { drone.HoverPosition = drone.DroneRigidbody.worldCenterOfMass; drone.IsHoverPositionSet = true; } drone.OrbitAngle += drone.HoverOrbitSpeed * Time.fixedDeltaTime; float num = Mathf.Cos(drone.OrbitAngle * (MathF.PI / 180f)) * drone.HoverOrbitRadius; float num2 = Mathf.Sin(drone.OrbitAngle * (MathF.PI / 180f)) * drone.HoverOrbitRadius; Vector3 val = drone.HoverPosition + new Vector3(num, 0f, num2); Vector3 val2 = val - drone.DroneRigidbody.worldCenterOfMass; Vector3 normalized = ((Vector3)(ref val2)).normalized; Vector3 val3 = normalized * (drone.MaxSpeed * 0.25f); Vector3 val4 = (val3 - drone.DroneRigidbody.linearVelocity) * (drone.FollowForce * 0.5f); drone.DroneRigidbody.AddForce(val4); if (PhotonNetwork.IsMasterClient) { drone.FindAndFollowNearestPlayer(); } } private Vector3 CalculateTargetPosition(DroneBehaviour drone) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00de: 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_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: 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_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00af: 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_010b: Unknown result type (might be due to invalid IL or missing references) if (Vector3.Distance(drone.FollowTarget.Center, drone.LastTargetPosition) < drone.StillThreshold) { drone.OrbitAngle += drone.OrbitSpeed * Time.fixedDeltaTime; float num = Mathf.Cos(drone.OrbitAngle * (MathF.PI / 180f)) * drone.OrbitRadius; float num2 = Mathf.Sin(drone.OrbitAngle * (MathF.PI / 180f)) * drone.OrbitRadius; Vector3 val = ((Component)drone.FollowTarget).transform.TransformDirection(new Vector3(num, 0f, num2)); return drone.FollowTarget.Center + Vector3.up * 0.5f + val; } drone.LastTargetPosition = drone.FollowTarget.Center; return drone.FollowTarget.Center + Vector3.up * 1.5f - ((Component)drone.FollowTarget).transform.forward * 2f; } } public class ManualBehaviour : IDroneBehaviour { private float _manualYaw; private float _manualPitch; private readonly float _manualMoveSpeed = 10f; private readonly float _manualRotationSpeed = 100f; private CameraOverride _cameraOverride; private bool _wasKinematic; private bool _wasUsingGravity; private float _originalDrag; private float _originalAngularDrag; private bool _shouldFire = false; private bool _shouldHeal = false; private Vector3 _externalMoveInput; private Vector2 _externalLookDelta; private bool _hasExternalInput; private bool _externalFire; private bool _externalHeal; private float _lastFireTime = 0f; public float FireCooldown = 0.3f; private DroneBehaviour _currentDrone; private Dictionary _originalGravityStates; private Dictionary _originalDragValues; private bool _isDriver = false; private bool _isGhostDriver = false; private Player _originalOwner = null; public bool IsGhostDriver => _isGhostDriver; public void InjectInput(float moveX, float moveY, float moveZ, float lookDeltaX, float lookDeltaY, bool fire, bool heal) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) _externalMoveInput = new Vector3(moveX, moveY, moveZ); _externalLookDelta = new Vector2(lookDeltaX, lookDeltaY); _hasExternalInput = true; if (fire) { _externalFire = true; } if (heal) { _externalHeal = true; } } public void OnEnter(DroneBehaviour drone, bool isDriver = true) { OnEnter(drone, isDriver, isGhostDriver: false); } public void OnEnterAsGhost(DroneBehaviour drone) { OnEnter(drone, isDriver: true, isGhostDriver: true); } private void OnEnter(DroneBehaviour drone, bool isDriver, bool isGhostDriver) { //IL_0342: Unknown result type (might be due to invalid IL or missing references) //IL_02df: Unknown result type (might be due to invalid IL or missing references) //IL_02ea: Unknown result type (might be due to invalid IL or missing references) //IL_02ef: Unknown result type (might be due to invalid IL or missing references) //IL_02f4: Unknown result type (might be due to invalid IL or missing references) //IL_02f8: Unknown result type (might be due to invalid IL or missing references) //IL_02fd: Unknown result type (might be due to invalid IL or missing references) //IL_0300: Unknown result type (might be due to invalid IL or missing references) //IL_0307: Unknown result type (might be due to invalid IL or missing references) //IL_031f: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: 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) _currentDrone = drone; _isDriver = isDriver; _isGhostDriver = isGhostDriver; Plugin.Log($"[ManualBehaviour] Entered manual mode. isDriver={isDriver} isGhostDriver={isGhostDriver}"); drone.FollowTarget = null; if (isDriver) { if (Plugin.MilestonesEnabled) { MilestoneTracker.Instance.ReportProgress("ShootingStars"); if (isGhostDriver) { MilestoneTracker.Instance.ReportProgress("GhostTown"); } } Item component = ((Component)drone).GetComponent(); if ((Object)(object)component != (Object)null && (Object)(object)((MonoBehaviourPun)component).photonView != (Object)null) { _originalOwner = ((MonoBehaviourPun)component).photonView.Owner; if (!((MonoBehaviourPun)component).photonView.IsMine) { ((MonoBehaviourPun)component).photonView.TransferOwnership(PhotonNetwork.LocalPlayer); } } } if (!isDriver) { return; } if (!isGhostDriver && (Object)(object)Character.localCharacter != (Object)null && (Object)(object)Character.localCharacter.refs.movement != (Object)null) { _originalGravityStates = new Dictionary(); _originalDragValues = new Dictionary(); foreach (Bodypart part in Character.localCharacter.refs.ragdoll.partList) { if ((Object)(object)part.Rig != (Object)null) { _originalGravityStates[part.Rig] = part.Rig.useGravity; _originalDragValues[part.Rig] = part.Rig.linearDamping; part.Rig.useGravity = false; part.Rig.linearDamping = 10f; part.Rig.linearVelocity = Vector3.zero; part.Rig.angularVelocity = Vector3.zero; } } ((Behaviour)Character.localCharacter.refs.movement).enabled = false; } if ((Object)(object)drone.DroneRigidbody != (Object)null) { _wasKinematic = drone.DroneRigidbody.isKinematic; _wasUsingGravity = drone.DroneRigidbody.useGravity; _originalDrag = drone.DroneRigidbody.linearDamping; _originalAngularDrag = drone.DroneRigidbody.angularDamping; drone.DroneRigidbody.isKinematic = false; drone.DroneRigidbody.useGravity = false; drone.DroneRigidbody.linearDamping = 10f; drone.DroneRigidbody.angularDamping = 10f; } if (!isGhostDriver && (Object)(object)Character.localCharacter != (Object)null) { Vector3 val = Character.localCharacter.Center - ((Component)drone).transform.position; Vector3 normalized = ((Vector3)(ref val)).normalized; _manualYaw = Mathf.Atan2(normalized.x, normalized.z) * 57.29578f; _manualPitch = (0f - Mathf.Asin(normalized.y)) * 57.29578f; } else { _manualYaw = ((Component)drone).transform.eulerAngles.y; _manualPitch = 0f; } if (!isGhostDriver && (Object)(object)MainCamera.instance != (Object)null) { _cameraOverride = ((Component)drone).gameObject.GetComponent(); if ((Object)(object)_cameraOverride == (Object)null) { _cameraOverride = ((Component)drone).gameObject.AddComponent(); } _cameraOverride.fov = 70f; _cameraOverride.DoOverride(); } ((MonoBehaviour)drone).StartCoroutine(MonitorInputs()); } public void OnExit(DroneBehaviour drone) { Plugin.Log($"[ManualBehaviour] Exiting manual mode. IsDriver: {_isDriver} IsGhostDriver: {_isGhostDriver}"); if (_isDriver && _originalOwner != null) { Item component = ((Component)drone).GetComponent(); if ((Object)(object)component != (Object)null && (Object)(object)((MonoBehaviourPun)component).photonView != (Object)null && ((MonoBehaviourPun)component).photonView.IsMine) { ((MonoBehaviourPun)component).photonView.TransferOwnership(_originalOwner); } _originalOwner = null; } if (!_isDriver) { _currentDrone = null; return; } if (!_isGhostDriver && (Object)(object)Character.localCharacter != (Object)null && (Object)(object)Character.localCharacter.refs.movement != (Object)null) { ((Behaviour)Character.localCharacter.refs.movement).enabled = true; if (_originalGravityStates != null && _originalDragValues != null) { foreach (Bodypart part in Character.localCharacter.refs.ragdoll.partList) { if ((Object)(object)part.Rig != (Object)null) { if (_originalGravityStates.ContainsKey(part.Rig)) { part.Rig.useGravity = _originalGravityStates[part.Rig]; } if (_originalDragValues.ContainsKey(part.Rig)) { part.Rig.linearDamping = _originalDragValues[part.Rig]; } } } } } if ((Object)(object)drone.DroneRigidbody != (Object)null) { drone.DroneRigidbody.isKinematic = _wasKinematic; drone.DroneRigidbody.useGravity = _wasUsingGravity; drone.DroneRigidbody.linearDamping = _originalDrag; drone.DroneRigidbody.angularDamping = _originalAngularDrag; } if (!_isGhostDriver && (Object)(object)MainCamera.instance != (Object)null) { MainCamera.instance.SetCameraOverride((CameraOverride)null); } if ((Object)(object)_cameraOverride != (Object)null) { Object.Destroy((Object)(object)_cameraOverride); _cameraOverride = null; } _currentDrone = null; _isDriver = false; _isGhostDriver = false; } public void UpdateBehaviour(DroneBehaviour drone) { HandleManualControl(drone); } public bool CanEnterBehaviour(DroneBehaviour drone) { return drone.IsFloating && !drone.IsDancing; } public string GetBehaviourName() { return "Manual"; } private IEnumerator MonitorInputs() { while ((Object)(object)_currentDrone != (Object)null && _currentDrone.CurrentBehaviourMode == DroneBehaviourMode.Manual) { Mouse mouse = Mouse.current; bool uiOpen = (Object)(object)QueueUI.Instance != (Object)null && ((MenuWindow)QueueUI.Instance).isOpen; if (mouse != null && !uiOpen) { if (mouse.leftButton.wasPressedThisFrame) { _shouldFire = true; Plugin.Log("[ManualBehaviour] Input detected: Left mouse button pressed"); } if (mouse.rightButton.wasPressedThisFrame) { _shouldHeal = true; Plugin.Log("[ManualBehaviour] Input detected: Right mouse button pressed"); } } yield return null; } } private void HandleManualControl(DroneBehaviour drone) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: 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) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0275: Unknown result type (might be due to invalid IL or missing references) //IL_027d: Unknown result type (might be due to invalid IL or missing references) //IL_0282: 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_0292: Unknown result type (might be due to invalid IL or missing references) //IL_0297: Unknown result type (might be due to invalid IL or missing references) //IL_0299: Unknown result type (might be due to invalid IL or missing references) //IL_02a1: Unknown result type (might be due to invalid IL or missing references) //IL_02a6: Unknown result type (might be due to invalid IL or missing references) //IL_02a8: Unknown result type (might be due to invalid IL or missing references) //IL_02aa: Unknown result type (might be due to invalid IL or missing references) //IL_02ac: Unknown result type (might be due to invalid IL or missing references) //IL_02ae: Unknown result type (might be due to invalid IL or missing references) //IL_02b0: Unknown result type (might be due to invalid IL or missing references) //IL_02b2: Unknown result type (might be due to invalid IL or missing references) //IL_02d1: Unknown result type (might be due to invalid IL or missing references) //IL_0317: Unknown result type (might be due to invalid IL or missing references) //IL_02f1: Unknown result type (might be due to invalid IL or missing references) //IL_02f6: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0386: Unknown result type (might be due to invalid IL or missing references) //IL_0388: Unknown result type (might be due to invalid IL or missing references) //IL_038a: Unknown result type (might be due to invalid IL or missing references) //IL_038f: 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_0396: Unknown result type (might be due to invalid IL or missing references) //IL_035d: Unknown result type (might be due to invalid IL or missing references) //IL_0337: Unknown result type (might be due to invalid IL or missing references) //IL_033c: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_03e7: Unknown result type (might be due to invalid IL or missing references) //IL_03b3: Unknown result type (might be due to invalid IL or missing references) //IL_03b8: Unknown result type (might be due to invalid IL or missing references) //IL_03ba: Unknown result type (might be due to invalid IL or missing references) //IL_03bf: Unknown result type (might be due to invalid IL or missing references) //IL_03c7: Unknown result type (might be due to invalid IL or missing references) //IL_037d: Unknown result type (might be due to invalid IL or missing references) //IL_0382: Unknown result type (might be due to invalid IL or missing references) //IL_0464: 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_047a: Unknown result type (might be due to invalid IL or missing references) //IL_0485: Unknown result type (might be due to invalid IL or missing references) //IL_04fd: Unknown result type (might be due to invalid IL or missing references) //IL_0508: Unknown result type (might be due to invalid IL or missing references) //IL_0513: Unknown result type (might be due to invalid IL or missing references) //IL_051e: Unknown result type (might be due to invalid IL or missing references) if (!_isDriver) { return; } float num; float num2; float num3; if (_hasExternalInput) { _hasExternalInput = false; Vector2 val = _externalLookDelta * 0.1f; _manualYaw += val.x * _manualRotationSpeed * Time.fixedDeltaTime; _manualPitch -= val.y * _manualRotationSpeed * Time.fixedDeltaTime; _manualPitch = Mathf.Clamp(_manualPitch, -89f, 89f); num = _externalMoveInput.x; num2 = _externalMoveInput.y; num3 = _externalMoveInput.z; if (_externalFire) { _shouldFire = true; _externalFire = false; } if (_externalHeal) { _shouldHeal = true; _externalHeal = false; } } else { Keyboard current = Keyboard.current; Mouse current2 = Mouse.current; if (current == null || current2 == null) { return; } Vector2 val2 = (((Object)(object)QueueUI.Instance != (Object)null && ((MenuWindow)QueueUI.Instance).isOpen) ? Vector2.zero : (((InputControl)(object)((Pointer)current2).delta).ReadValue() * 0.1f)); _manualYaw += val2.x * _manualRotationSpeed * Time.fixedDeltaTime; _manualPitch -= val2.y * _manualRotationSpeed * Time.fixedDeltaTime; _manualPitch = Mathf.Clamp(_manualPitch, -89f, 89f); num = 0f; num2 = 0f; num3 = 0f; if (((ButtonControl)current.wKey).isPressed) { num3 = 1f; } if (((ButtonControl)current.sKey).isPressed) { num3 = -1f; } if (((ButtonControl)current.dKey).isPressed) { num = 1f; } if (((ButtonControl)current.aKey).isPressed) { num = -1f; } if (((ButtonControl)current.spaceKey).isPressed) { num2 = 1f; } if (((ButtonControl)current.leftCtrlKey).isPressed) { num2 = -1f; } } float num4 = _manualMoveSpeed * Time.fixedDeltaTime; Vector3 val3 = ((Component)drone).transform.forward * (num3 * num4); Vector3 val4 = ((Component)drone).transform.right * (num * num4); Vector3 val5 = Vector3.up * (num2 * num4); Vector3 val6 = val3; Vector3 val7 = val4; Vector3 val8 = val5; RaycastHit val9 = default(RaycastHit); if (((Vector3)(ref val6)).sqrMagnitude > 0f && drone.DroneRigidbody.SweepTest(((Vector3)(ref val6)).normalized, ref val9, ((Vector3)(ref val6)).magnitude + 0.05f)) { val6 = Vector3.zero; } if (((Vector3)(ref val7)).sqrMagnitude > 0f && drone.DroneRigidbody.SweepTest(((Vector3)(ref val7)).normalized, ref val9, ((Vector3)(ref val7)).magnitude + 0.05f)) { val7 = Vector3.zero; } if (((Vector3)(ref val8)).sqrMagnitude > 0f && drone.DroneRigidbody.SweepTest(((Vector3)(ref val8)).normalized, ref val9, ((Vector3)(ref val8)).magnitude + 0.05f)) { val8 = Vector3.zero; } Vector3 val10 = val6 + val7 + val8; if (((Vector3)(ref val10)).sqrMagnitude > 0f) { Vector3 val11 = ((Component)drone).transform.position + val10; drone.DroneRigidbody.MovePosition(val11); } drone.DroneRigidbody.MoveRotation(Quaternion.Euler(_manualPitch, _manualYaw, 0f)); if (!(Time.time >= _lastFireTime + FireCooldown)) { return; } if (_shouldFire) { _shouldFire = false; _lastFireTime = Time.time; if ((Object)(object)drone.LeftFirePoint != (Object)null && (Object)(object)drone.RightFirePoint != (Object)null) { Netcode.Instance.SendLaserFire(drone.LeftFirePoint.position, drone.LeftFirePoint.rotation, drone.RightFirePoint.position, drone.RightFirePoint.rotation, 0); } else { Plugin.Log("[ManualBehaviour] Firing failed: LeftFirePoint or RightFirePoint is not assigned in the inspector.", "warning"); } } else if (_shouldHeal) { _shouldHeal = false; _lastFireTime = Time.time; if ((Object)(object)drone.LeftFirePoint != (Object)null && (Object)(object)drone.RightFirePoint != (Object)null) { Netcode.Instance.SendLaserFire(drone.LeftFirePoint.position, drone.LeftFirePoint.rotation, drone.RightFirePoint.position, drone.RightFirePoint.rotation, 1); } else { Plugin.Log("[ManualBehaviour] Healing failed: LeftFirePoint or RightFirePoint is not assigned in the inspector.", "warning"); } } } } public class HoveringBehaviour : IDroneBehaviour { public void OnEnter(DroneBehaviour drone, bool isDriver = true) { Plugin.Log("[HoveringBehaviour] Entered hovering mode (not implemented)"); } public void OnExit(DroneBehaviour drone) { Plugin.Log("[HoveringBehaviour] Exited hovering mode"); } public void UpdateBehaviour(DroneBehaviour drone) { } public bool CanEnterBehaviour(DroneBehaviour drone) { return drone.IsFloating && !drone.IsDancing; } public string GetBehaviourName() { return "Hovering"; } } [RequireComponent(typeof(Rigidbody))] [RequireComponent(typeof(Collider))] public class LaserProjectile : MonoBehaviour { public enum LaserEffect { Damage, Heal } private Rigidbody _rb; public float Speed = 70f; public float Lifetime = 3f; public LaserEffect EffectType; private bool _hasHit = false; private void Awake() { _rb = ((Component)this).GetComponent(); _rb.useGravity = false; _rb.interpolation = (RigidbodyInterpolation)1; _rb.collisionDetectionMode = (CollisionDetectionMode)2; ((Component)this).GetComponent().isTrigger = true; } private void Start() { //IL_001d: 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) if ((Object)(object)_rb != (Object)null) { _rb.linearVelocity = ((Component)this).transform.forward * Speed; } Object.Destroy((Object)(object)((Component)this).gameObject, Lifetime); } private void OnTriggerEnter(Collider other) { if (_hasHit || (Object)(object)((Component)other).GetComponent() != (Object)null) { return; } Transform val = ((Component)other).transform; GameObject val2 = null; while ((Object)(object)val != (Object)null) { if (((Object)val).name.StartsWith("Character")) { val2 = ((Component)val).gameObject; break; } val = val.parent; } if ((Object)(object)val2 != (Object)null) { Character component = val2.GetComponent(); if ((Object)(object)component != (Object)null) { ApplyLaserEffects(component); _hasHit = true; } else { Plugin.Log("[LaserProjectile] Found Character GameObject but no Character component: " + ((Object)val2).name, "warning"); } } else { _hasHit = true; } Object.Destroy((Object)(object)((Component)this).gameObject); } private void ApplyLaserEffects(Character character) { //IL_0007: 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) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) Vector3 val = ((Component)this).transform.forward * 10f; if (((MonoBehaviourPun)character).photonView.IsMine) { character.AddForce(val, 1f, 1f); switch (EffectType) { case LaserEffect.Damage: character.refs.afflictions.AddStatus((STATUSTYPE)0, 0.0125f, false, true, true); break; case LaserEffect.Heal: character.refs.afflictions.SubtractStatus((STATUSTYPE)0, 0.0125f, false, false); break; } Plugin.Log($"[LaserProjectile] FirePlayerLaserHit local — viewID={((MonoBehaviourPun)character).photonView.ViewID} effect={EffectType}"); Plugin.FirePlayerLaserHit(((MonoBehaviourPun)character).photonView.ViewID, (int)EffectType); } else { if (Plugin.MilestonesEnabled && EffectType == LaserEffect.Damage) { MilestoneTracker.Instance.RecordLaserHit(((MonoBehaviourPun)character).photonView.ViewID); } Netcode.Instance.SendLaserHit(((MonoBehaviourPun)character).photonView.ViewID, (int)EffectType, val); } } } public class MilestoneTracker : MonoBehaviour { private static MilestoneTracker _instance; public const string GENESIS = "Genesis"; public const string HIT_THE_FLOOR = "HitTheFloor"; public const string THANK_U_NEXT = "ThankUNext"; public const string SUBE_EL_BAJO = "SubeElBajo"; public const string MUTE = "Mute"; public const string MY_WAY = "MyWay"; public const string ENJOY_THE_SILENCE = "EnjoyTheSilence"; public const string SING_IT_BACK = "SingItBack"; public const string GET_LUCKY = "GetLucky"; public const string NO_SURPRISES = "NoSurprises"; public const string ONE_MORE_TIME = "OneMoreTime"; public const string WINDOWLICKER = "Windowlicker"; public const string BORN_SLIPPY = "BornSlippy"; public const string FREE_BIRD = "FreeBird"; public const string BABY_COME_BACK = "BabyComeBack"; public const string WATCHING_ME_DANCE = "WatchingMeDance"; public const string RADIO_VIDEO = "RadioVideo"; public const string THE_HUB = "TheHub"; public const string BACK_TO_BASICS = "BackToBasics"; public const string SESSION = "Session"; public const string LIGHT_SWITCH = "LightSwitch"; private const string SOUNDTRACK_2_MY_LIFE = "Soundtrack2MyLife"; public const string CRYSTAL_CLEAR = "CrystalClear"; public const string DEJA_VU = "DejaVu"; private const string THE_SICKNESS = "TheSickness"; public const string AROUND_THE_WORLD = "AroundTheWorld"; public const string UNSTOPPABLE_FORCE = "UnstoppableForce"; public const string SHAKE_IT = "ShakeIt"; public const string WE_ARE_FAMILY = "WeAreFamily"; public const string DONT_STOP = "DontStop"; public const string ELECTRIC_RELAXATION = "ElectricRelaxation"; private const string THOUSAND_ROUNDS = "1000Rounds"; public const string WHITE_NOISE = "WhiteNoise"; public const string LARGER_THAN_LIFE = "LargerThanLife"; public const string BACK_FROM_THE_DEAD = "BackFromTheDead"; public const string SHOOTING_STARS = "ShootingStars"; private const string MURDER_ON_THE_DANCEFLOOR = "MurderOnTheDancefloor"; public const string GHOST_TOWN = "GhostTown"; private readonly Dictionary _registry = new Dictionary(); private const string PENDING_XP_KEY = "sPEAKer_PendingXP"; private bool _bufferRewards = true; private readonly List _bufferedUnlocks = new List(); private const string PLAYED_HASHES_KEY = "sPEAKer_ATW_PlayedHashes"; private HashSet _playedSongHashes; private float _thousandRoundsAccumulator; private bool _isMusicPlaying; private readonly Dictionary _laserHitTimestamps = new Dictionary(); public static MilestoneTracker Instance { get { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown if ((Object)(object)_instance == (Object)null) { _instance = Object.FindAnyObjectByType(); if ((Object)(object)_instance == (Object)null) { GameObject val = new GameObject("sPEAKerMilestones"); _instance = val.AddComponent(); Object.DontDestroyOnLoad((Object)(object)val); Plugin.Log("[MilestoneTracker] Created new instance and marked as DontDestroyOnLoad."); } } return _instance; } } public static void EnsureInitialized() { Plugin.Log("[MilestoneTracker] EnsureInitialized called."); _ = Instance; } private void Awake() { Plugin.Log("[MilestoneTracker] Awake called."); if ((Object)(object)_instance != (Object)null && (Object)(object)_instance != (Object)(object)this) { Plugin.Log("[MilestoneTracker] Duplicate instance detected, destroying."); Object.Destroy((Object)(object)((Component)this).gameObject); return; } _instance = this; Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); Plugin.Log("[MilestoneTracker] Instance set and marked as DontDestroyOnLoad."); RegisterAllMilestones(); } private void Register(string id, string displayName, string description, int xp, MilestoneCategory category, int threshold = 1, string hint = "") { _registry[id] = new MilestoneDefinition(id, displayName, description, xp, category, threshold, hint); } private void RegisterAllMilestones() { Register("Genesis", "Genesis", "Turn on sPEAKer for the first time", 5, MilestoneCategory.Tutorial); Register("HitTheFloor", "Hit the Floor", "Drop sPEAKer while it's playing", 5, MilestoneCategory.Tutorial); Register("ThankUNext", "thank u, next", "Skip to the next song", 5, MilestoneCategory.Tutorial); Register("SubeElBajo", "SUBE EL BAJO", "Hit maximum volume", 10, MilestoneCategory.Tutorial); Register("Mute", "Mute", "Set volume to minimum", 5, MilestoneCategory.Tutorial); Register("MyWay", "My Way", "Adjust your Local Volume in pause menu", 15, MilestoneCategory.Tutorial); Register("EnjoyTheSilence", "Enjoy the Silence", "Abandon sPEAKer while it's playing", 10, MilestoneCategory.Tutorial); Register("SingItBack", "Sing It Back", "Go back to previous song", 10, MilestoneCategory.Tutorial); Register("GetLucky", "Get Lucky", "Enable shuffle mode", 10, MilestoneCategory.Tutorial); Register("NoSurprises", "No Surprises", "Restore the queue's original order", 10, MilestoneCategory.Tutorial); Register("OneMoreTime", "One More Time", "Try out all loop modes", 10, MilestoneCategory.Tutorial); Register("Windowlicker", "Windowlicker", "Open Queue UI", 10, MilestoneCategory.Tutorial, 1, "[U]"); Register("BornSlippy", "Born Slippy", "Change the order of your queue", 15, MilestoneCategory.Tutorial); Register("FreeBird", "Free Bird", "Engage Drone Mode", 15, MilestoneCategory.Standard, 1, "[P]"); Register("BabyComeBack", "Baby Come Back", "Use Drone Recall", 15, MilestoneCategory.Standard, 1, "[Ctrl + P]"); Register("WatchingMeDance", "Watching Me Dance", "Make sPEAKer dance", 10, MilestoneCategory.Standard, 1, "[O]"); Register("RadioVideo", "Radio/Video", "Enter YouTube Radio", 15, MilestoneCategory.Standard, 1, "[Y]"); Register("TheHub", "The Hub", "Search for a song in YouTube Radio", 20, MilestoneCategory.Standard); Register("BackToBasics", "Back to Basics", "Switch from YouTube Radio back to Mixtapes", 15, MilestoneCategory.Standard); Register("Session", "Session", "Enter a YouTube playlist URL", 35, MilestoneCategory.Standard); Register("LightSwitch", "Light Switch", "Visualize your music at the airport", 20, MilestoneCategory.Standard); Register("Soundtrack2MyLife", "Soundtrack 2 My Life", "Load a mixtape with your name as author", 150, MilestoneCategory.Standard); Register("CrystalClear", "Crystal Clear", "Load only .ogg files", 30, MilestoneCategory.Standard); Register("DejaVu", "deja vu", "Play a YouTube song that was already cached", 20, MilestoneCategory.Standard, 1, "Back to Basics"); Register("TheSickness", "The Sickness", "Pass out while carrying sPEAKer", 50, MilestoneCategory.Standard); Register("AroundTheWorld", "Around The World", "Play every song in a mixtape", 30, MilestoneCategory.Progress); Register("UnstoppableForce", "Unstoppable Force", "Play 30 songs in a single game", 80, MilestoneCategory.Progress, 30); Register("ShakeIt", "Shake It", "Perform all 9 different dance emotes", 35, MilestoneCategory.Progress, 9, "they're random btw"); Register("WeAreFamily", "We Are Family", "Use sPEAKer in a full squad", 40, MilestoneCategory.Progress, 4); Register("DontStop", "Don't Stop", "Load 5 different Thunderstore mixtapes", 45, MilestoneCategory.Progress, 5); Register("ElectricRelaxation", "Electric Relaxation", "Load 200+ songs", 50, MilestoneCategory.Progress, 200); Register("1000Rounds", "1000 Rounds", "Play music for a total of 1000 minutes", 200, MilestoneCategory.Progress, 1000); Register("WhiteNoise", "White Noise", "Have the host play a song that you don't have", 60, MilestoneCategory.Hidden, 1, "something missing..."); Register("LargerThanLife", "Larger Than Life", "Spam dance emotes until sPEAKer is big enough", 60, MilestoneCategory.Hidden, 1, "feels like dancing a LOT"); Register("BackFromTheDead", "Back From The Dead", "Witness sPEAKer respawning", 60, MilestoneCategory.Hidden, 1, "there's a 'technique' for this"); Register("ShootingStars", "Shooting Stars", "Enable manual drone mode", 100, MilestoneCategory.Hidden, 1, "a strange combination"); Register("MurderOnTheDancefloor", "Murder On The Dancefloor", "Kill someone using the drone's lasers", 150, MilestoneCategory.Hidden, 1, "✨ murder ✨"); Register("GhostTown", "Ghost Town", "Possess sPEAKer", 100, MilestoneCategory.Hidden, 1, "it's a poltergeist!"); Plugin.Log($"[MilestoneTracker] Registered {_registry.Count} milestones."); } private bool HasUnlocked(string milestoneId) { return PlayerPrefs.GetInt("sPEAKer_Milestone_" + milestoneId, 0) == 1; } private void MarkAsUnlocked(string milestoneId) { PlayerPrefs.SetInt("sPEAKer_Milestone_" + milestoneId, 1); PlayerPrefs.Save(); QueueUI.Instance.RefreshMilestones(); } private int GetProgress(string milestoneId) { return PlayerPrefs.GetInt("sPEAKer_Progress_" + milestoneId, 0); } public void SetProgress(string milestoneId, int value) { PlayerPrefs.SetInt("sPEAKer_Progress_" + milestoneId, value); PlayerPrefs.Save(); } public int GetPendingXP() { return PlayerPrefs.GetInt("sPEAKer_PendingXP", 0); } private void AddPendingXP(int xp) { PlayerPrefs.SetInt("sPEAKer_PendingXP", GetPendingXP() + xp); PlayerPrefs.Save(); } private void ClearPendingXP() { PlayerPrefs.DeleteKey("sPEAKer_PendingXP"); PlayerPrefs.Save(); } private void FlushPendingXP() { int pendingXP = GetPendingXP(); if (pendingXP > 0) { LevelingBridge.AddExperience(pendingXP); sPEAKerUI.Instance.ShowToast(string.Format(LocalizedText.GetText("MS_CLAIMED_XP", true), pendingXP)); ClearPendingXP(); Plugin.Log($"[MilestoneTracker] Flushed {pendingXP} pending XP to Leveling."); } } public void ReportProgress(string milestoneId, int amount = 1) { if (!_registry.TryGetValue(milestoneId, out var value)) { Plugin.Log("[MilestoneTracker] Unknown milestone ID: " + milestoneId, "warning"); } else { if (HasUnlocked(milestoneId)) { return; } int num = GetProgress(milestoneId) + amount; SetProgress(milestoneId, num); if (num < value.Threshold) { return; } MarkAsUnlocked(milestoneId); if (_bufferRewards) { _bufferedUnlocks.Add(value); Plugin.Log($"[MilestoneTracker] {value.DisplayName} buffered for later delivery. +{value.XP} XP"); return; } if (Plugin.LevelingAvailable) { LevelingBridge.AddExperience(value.XP); } else { AddPendingXP(value.XP); } MilestoneUI.Instance.ShowMilestone(value); Plugin.Log($"[MilestoneTracker] {value.DisplayName} unlocked! +{value.XP} XP"); } } public void SetAbsoluteProgress(string milestoneId, int absoluteValue) { if (!_registry.TryGetValue(milestoneId, out var value)) { Plugin.Log("[MilestoneTracker] Unknown milestone ID: " + milestoneId, "warning"); } else { if (HasUnlocked(milestoneId)) { return; } SetProgress(milestoneId, absoluteValue); if (absoluteValue < value.Threshold) { return; } MarkAsUnlocked(milestoneId); if (_bufferRewards) { _bufferedUnlocks.Add(value); Plugin.Log($"[MilestoneTracker] {value.DisplayName} buffered for later delivery. +{value.XP} XP"); return; } if (Plugin.LevelingAvailable) { LevelingBridge.AddExperience(value.XP); } else { AddPendingXP(value.XP); } MilestoneUI.Instance.ShowMilestone(value); Plugin.Log($"[MilestoneTracker] {value.DisplayName} unlocked! +{value.XP} XP"); } } public List GetAllMilestones() { return _registry.Values.Select((MilestoneDefinition def) => new MilestoneStatus(def, GetProgress(def.Id), HasUnlocked(def.Id))).ToList(); } public MilestoneStatus GetMilestone(string milestoneId) { if (!_registry.TryGetValue(milestoneId, out var value)) { return null; } return new MilestoneStatus(value, GetProgress(milestoneId), HasUnlocked(milestoneId)); } public int GetTotalXPEarned() { return _registry.Values.Where((MilestoneDefinition def) => HasUnlocked(def.Id)).Sum((MilestoneDefinition def) => def.XP); } public int GetTotalXPAvailable() { return _registry.Values.Sum((MilestoneDefinition def) => def.XP); } public void FlushBufferedUnlocks() { _bufferRewards = false; foreach (MilestoneDefinition bufferedUnlock in _bufferedUnlocks) { if (Plugin.LevelingAvailable) { LevelingBridge.AddExperience(bufferedUnlock.XP); } else { AddPendingXP(bufferedUnlock.XP); } MilestoneUI.Instance.ShowMilestone(bufferedUnlock); } _bufferedUnlocks.Clear(); if (Plugin.LevelingAvailable) { FlushPendingXP(); } } public void UnlockForAll(string milestoneId) { Netcode.Instance.SendMilestoneUnlockAll(milestoneId); } public void ClearAllMilestones() { Plugin.Log("[MilestoneTracker] Clearing all milestones..."); foreach (MilestoneDefinition value in _registry.Values) { PlayerPrefs.DeleteKey("sPEAKer_Milestone_" + value.Id); PlayerPrefs.DeleteKey("sPEAKer_Progress_" + value.Id); } PlayerPrefs.DeleteKey("sPEAKer_ATW_PlayedHashes"); PlayerPrefs.Save(); Plugin.Log("[MilestoneTracker] All milestones cleared!"); } private void OnEnable() { AudioMaster.OnPlaybackAction += HandlePlaybackAction; GlobalEvents.OnCharacterPassedOut = (Action)Delegate.Combine(GlobalEvents.OnCharacterPassedOut, new Action(HandleCharacterPassedOut)); } private void OnDisable() { AudioMaster.OnPlaybackAction -= HandlePlaybackAction; GlobalEvents.OnCharacterPassedOut = (Action)Delegate.Remove(GlobalEvents.OnCharacterPassedOut, new Action(HandleCharacterPassedOut)); } private void HandlePlaybackAction(PlaybackActionType action, AudioClip clip, float time) { switch (action) { case PlaybackActionType.Play: case PlaybackActionType.Resume: _isMusicPlaying = true; break; case PlaybackActionType.Pause: case PlaybackActionType.Stop: _isMusicPlaying = false; break; } } private void SavePlayedHashes() { string text = JsonConvert.SerializeObject((object)_playedSongHashes.ToList()); PlayerPrefs.SetString("sPEAKer_ATW_PlayedHashes", text); PlayerPrefs.Save(); } public void RecordSongPlayed(string songHash) { if (HasUnlocked("AroundTheWorld")) { return; } if (_playedSongHashes == null) { _playedSongHashes = LoadPlayedHashes(); } if (!_playedSongHashes.Add(songHash)) { return; } SavePlayedHashes(); foreach (MixtapeInfo availableMixtape in AudioMaster.AvailableMixtapes) { bool flag = true; for (int i = availableMixtape.FirstSongIndex; i <= availableMixtape.LastSongIndex; i++) { if (!AudioMaster.IndexToSongHash.TryGetValue(i, out var value) || !_playedSongHashes.Contains(value)) { flag = false; break; } } if (flag && availableMixtape.SongCount > 0) { ReportProgress("AroundTheWorld"); PlayerPrefs.DeleteKey("sPEAKer_ATW_PlayedHashes"); PlayerPrefs.Save(); _playedSongHashes = null; break; } } } public (string MixtapeName, int Played, int Total)? GetBestMixtapeCompletion() { if (HasUnlocked("AroundTheWorld")) { return null; } if (_playedSongHashes == null) { _playedSongHashes = LoadPlayedHashes(); } string text = null; int item = 0; int item2 = 0; float num = -1f; foreach (MixtapeInfo availableMixtape in AudioMaster.AvailableMixtapes) { if (availableMixtape.SongCount <= 0) { continue; } int num2 = 0; for (int i = availableMixtape.FirstSongIndex; i <= availableMixtape.LastSongIndex; i++) { if (AudioMaster.IndexToSongHash.TryGetValue(i, out var value) && _playedSongHashes.Contains(value)) { num2++; } } float num3 = (float)num2 / (float)availableMixtape.SongCount; if (num3 > num) { num = num3; item = num2; item2 = availableMixtape.SongCount; text = availableMixtape.Name; } } if (text == null) { return null; } return (text, item, item2); } private HashSet LoadPlayedHashes() { string text = PlayerPrefs.GetString("sPEAKer_ATW_PlayedHashes", ""); if (string.IsNullOrEmpty(text)) { return new HashSet(); } try { List list = JsonConvert.DeserializeObject>(text); return (list != null) ? new HashSet(list) : new HashSet(); } catch { return new HashSet(); } } private void Update() { if (Plugin.MilestonesEnabled && _isMusicPlaying && !HasUnlocked("1000Rounds")) { _thousandRoundsAccumulator += Time.deltaTime; while (_thousandRoundsAccumulator >= 60f) { _thousandRoundsAccumulator -= 60f; ReportProgress("1000Rounds"); } } } public void CheckSoundtrack2MyLife() { Character localCharacter = Character.localCharacter; if ((Object)(object)localCharacter == (Object)null) { return; } string text = NormalizeName(localCharacter.characterName); if (string.IsNullOrEmpty(text)) { return; } foreach (MixtapeInfo availableMixtape in AudioMaster.AvailableMixtapes) { if (string.IsNullOrEmpty(availableMixtape.Author) || !(NormalizeName(availableMixtape.Author) == text)) { continue; } Instance.ReportProgress("Soundtrack2MyLife"); break; } } private static string NormalizeName(string name) { StringBuilder stringBuilder = new StringBuilder(name.Length); foreach (char c in name) { if (char.IsLetterOrDigit(c)) { stringBuilder.Append(char.ToLowerInvariant(c)); } } return (stringBuilder.Length > 0) ? stringBuilder.ToString() : name; } private void HandleCharacterPassedOut(Character character) { if (!Plugin.MilestonesEnabled) { return; } float value; if ((Object)(object)character == (Object)(object)Character.localCharacter) { Controller controllerInstance = TheObserver.Instance.GetControllerInstance(); if ((Object)(object)controllerInstance != (Object)null && (Object)(object)controllerInstance.item != (Object)null && (Object)(object)controllerInstance.item.lastHolderCharacter == (Object)(object)character) { ReportProgress("TheSickness"); } else if (TheObserver.Instance.IsPocketed() && TheObserver.Instance.IssPEAKerInInventory(character)) { ReportProgress("TheSickness"); } } else if (_laserHitTimestamps.TryGetValue(((MonoBehaviourPun)character).photonView.ViewID, out value) && Time.time - value <= 15f) { _laserHitTimestamps.Remove(((MonoBehaviourPun)character).photonView.ViewID); UnlockForAll("MurderOnTheDancefloor"); } } public void RecordLaserHit(int viewID) { _laserHitTimestamps[viewID] = Time.time; } } public enum MilestoneCategory { Tutorial, Standard, Progress, Hidden } public class MilestoneDefinition { public string Id { get; } public string DisplayName { get; } public string Description { get; } public string Hint { get; } public int XP { get; } public MilestoneCategory Category { get; } public int Threshold { get; } public MilestoneDefinition(string id, string displayName, string description, int xp, MilestoneCategory category, int threshold = 1, string hint = "") { Id = id; DisplayName = displayName; Description = description; XP = xp; Category = category; Threshold = threshold; Hint = hint; } } public class MilestoneStatus { public MilestoneDefinition Definition { get; } public int CurrentProgress { get; } public bool IsUnlocked { get; } public MilestoneStatus(MilestoneDefinition definition, int currentProgress, bool isUnlocked) { Definition = definition; CurrentProgress = currentProgress; IsUnlocked = isUnlocked; } } public static class LevelingBridge { private static MethodInfo _addExperienceMethod; private static bool _resolved; public static void AddExperience(int xp) { if (!_resolved) { try { _addExperienceMethod = (AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => a.GetName().Name == "Leveling")?.GetType("Leveling.LevelingAPI"))?.GetMethod("AddExperience", new Type[3] { typeof(int), typeof(bool), typeof(bool) }); } catch (Exception ex) { Plugin.Log("[LevelingBridge] Failed to resolve LevelingAPI: " + ex.Message, "error"); } _resolved = true; } if (_addExperienceMethod != null) { _addExperienceMethod.Invoke(null, new object[3] { xp, false, true }); } } } public class MilestoneUI : MonoBehaviour { private static MilestoneUI _instance; private GameObject _canvas; private GameObject _milestonePanel; private RectTransform _panelRect; private TextMeshProUGUI _titleText; private TextMeshProUGUI _milestoneNameText; private TextMeshProUGUI _xpText; private TextMeshProUGUI _descriptionText; private Image _backgroundImage; private Image _iconImage; private Dictionary _categoryIcons; private Queue _milestoneQueue = new Queue(); private bool _isShowingMilestone = false; private Coroutine _currentAnimation; private const float SLIDE_IN_DURATION = 0.4f; private const float DISPLAY_DURATION = 3.5f; private const float SLIDE_OUT_DURATION = 0.3f; private const float PANEL_WIDTH = 350f; private const float PANEL_HEIGHT = 120f; private readonly Vector2 OFF_SCREEN_POSITION = new Vector2(0f, -150f); private readonly Vector2 ON_SCREEN_POSITION = new Vector2(0f, 150f); public static MilestoneUI Instance { get { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown if ((Object)(object)_instance == (Object)null) { GameObject val = new GameObject("MilestoneUI"); _instance = val.AddComponent(); Object.DontDestroyOnLoad((Object)(object)val); _instance.Initialize(); } return _instance; } } public static void EnsureInitialized() { _ = Instance; } private void Awake() { if ((Object)(object)_instance != (Object)null && (Object)(object)_instance != (Object)(object)this) { Object.Destroy((Object)(object)((Component)this).gameObject); } else { _instance = this; } } private void Initialize() { Plugin.Log("[MilestoneUI] Initializing milestone UI..."); CreateCanvas(); CreateMilestonePanel(); if ((Object)(object)Plugin.PeakFont == (Object)null) { Plugin.OnFontLoaded += ApplyFont; } _milestonePanel.SetActive(false); Plugin.Log("[MilestoneUI] Milestone UI initialized!"); } public void LoadCategoryIcons() { //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) _categoryIcons = new Dictionary(); string[] array = new string[4] { "award1", "award2", "award3", "award4" }; MilestoneCategory[] array2 = new MilestoneCategory[4] { MilestoneCategory.Tutorial, MilestoneCategory.Standard, MilestoneCategory.Progress, MilestoneCategory.Hidden }; for (int i = 0; i < array.Length; i++) { Texture2D val = Plugin.Bundle.LoadAsset(array[i]); if ((Object)(object)val != (Object)null) { _categoryIcons[array2[i]] = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f)); } } } private void ApplyFont() { if (Object.op_Implicit((Object)(object)_titleText)) { ((TMP_Text)_titleText).font = Plugin.PeakFont; } if (Object.op_Implicit((Object)(object)_milestoneNameText)) { ((TMP_Text)_milestoneNameText).font = Plugin.PeakFont; } if (Object.op_Implicit((Object)(object)_xpText)) { ((TMP_Text)_xpText).font = Plugin.PeakFont; } if (Object.op_Implicit((Object)(object)_descriptionText)) { ((TMP_Text)_descriptionText).font = Plugin.PeakFont; } } private void CreateCanvas() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown _canvas = new GameObject("MilestoneCanvas"); _canvas.transform.SetParent(((Component)this).transform); Canvas val = _canvas.AddComponent(); val.renderMode = (RenderMode)0; val.sortingOrder = 9999; _canvas.AddComponent(); _canvas.AddComponent(); } private void CreateMilestonePanel() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0085: 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_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) _milestonePanel = new GameObject("MilestonePanel"); _milestonePanel.transform.SetParent(_canvas.transform, false); _panelRect = _milestonePanel.AddComponent(); _panelRect.anchorMin = new Vector2(0.5f, 0f); _panelRect.anchorMax = new Vector2(0.5f, 0f); _panelRect.pivot = new Vector2(0.5f, 0f); _panelRect.sizeDelta = new Vector2(350f, 120f); _panelRect.anchoredPosition = new Vector2(0f, -150f); _backgroundImage = _milestonePanel.AddComponent(); ((Graphic)_backgroundImage).color = new Color(0.1f, 0.1f, 0.15f, 0.95f); CreateIcon(); CreateTexts(); } private void CreateIcon() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_004c: 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_0078: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Icon"); val.transform.SetParent(_milestonePanel.transform, false); RectTransform val2 = val.AddComponent(); val2.anchorMin = new Vector2(0f, 0.5f); val2.anchorMax = new Vector2(0f, 0.5f); val2.pivot = new Vector2(0f, 0.5f); val2.anchoredPosition = new Vector2(10f, 0f); val2.sizeDelta = new Vector2(70f, 70f); _iconImage = val.AddComponent(); ((Graphic)_iconImage).color = new Color(0.2f, 0.6f, 0.2f, 1f); } private void CreateTexts() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_004c: 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_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected O, but got Unknown //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: 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_0168: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Expected O, but got Unknown //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Unknown result type (might be due to invalid IL or missing references) //IL_027d: 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_02a5: Expected O, but got Unknown //IL_02ce: 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_02fc: Unknown result type (might be due to invalid IL or missing references) //IL_0313: Unknown result type (might be due to invalid IL or missing references) //IL_032a: Unknown result type (might be due to invalid IL or missing references) //IL_038f: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("TextContainer"); val.transform.SetParent(_milestonePanel.transform, false); RectTransform val2 = val.AddComponent(); val2.anchorMin = new Vector2(0f, 0f); val2.anchorMax = new Vector2(1f, 1f); val2.offsetMin = new Vector2(90f, 10f); val2.offsetMax = new Vector2(-10f, -10f); GameObject val3 = new GameObject("MilestoneName"); val3.transform.SetParent(val.transform, false); RectTransform val4 = val3.AddComponent(); val4.anchorMin = new Vector2(0f, 0.8f); val4.anchorMax = new Vector2(1f, 0.8f); val4.pivot = new Vector2(0f, 0.5f); val4.anchoredPosition = new Vector2(0f, -5f); val4.sizeDelta = new Vector2(0f, 35f); _milestoneNameText = val3.AddComponent(); ((TMP_Text)_milestoneNameText).text = "Milestone Name"; ((TMP_Text)_milestoneNameText).fontSize = 22f; ((TMP_Text)_milestoneNameText).textWrappingMode = (TextWrappingModes)0; ((TMP_Text)_milestoneNameText).alignment = (TextAlignmentOptions)513; ((Graphic)_milestoneNameText).color = Color.white; ((TMP_Text)_milestoneNameText).font = Plugin.PeakFont; GameObject val5 = new GameObject("Description"); val5.transform.SetParent(val.transform, false); RectTransform val6 = val5.AddComponent(); val6.anchorMin = new Vector2(0f, 0.35f); val6.anchorMax = new Vector2(1f, 0.55f); val6.pivot = new Vector2(0f, 0.5f); val6.anchoredPosition = Vector2.zero; val6.sizeDelta = new Vector2(0f, 0f); _descriptionText = val5.AddComponent(); ((TMP_Text)_descriptionText).text = ""; ((TMP_Text)_descriptionText).fontSize = 12f; ((TMP_Text)_descriptionText).textWrappingMode = (TextWrappingModes)0; ((TMP_Text)_descriptionText).alignment = (TextAlignmentOptions)513; ((Graphic)_descriptionText).color = new Color(0.6f, 0.6f, 0.6f, 1f); ((TMP_Text)_descriptionText).font = Plugin.PeakFont; GameObject val7 = new GameObject("XP"); val7.transform.SetParent(val.transform, false); RectTransform val8 = val7.AddComponent(); val8.anchorMin = new Vector2(0f, 0f); val8.anchorMax = new Vector2(1f, 0.25f); val8.pivot = new Vector2(0f, 0f); val8.anchoredPosition = new Vector2(0f, 0f); val8.sizeDelta = new Vector2(0f, 20f); _xpText = val7.AddComponent(); ((TMP_Text)_xpText).text = "+10 XP"; ((TMP_Text)_xpText).fontSize = 16f; ((TMP_Text)_xpText).alignment = (TextAlignmentOptions)513; ((Graphic)_xpText).color = new Color(0.3f, 0.9f, 0.3f, 1f); ((TMP_Text)_xpText).font = Plugin.PeakFont; } public void ShowMilestone(MilestoneDefinition milestone) { MilestoneData item = new MilestoneData { Name = milestone.DisplayName, Description = milestone.Description, XP = milestone.XP, Category = milestone.Category, Id = milestone.Id }; _milestoneQueue.Enqueue(item); if (!_isShowingMilestone) { ProcessNextMilestone(); } } private void ProcessNextMilestone() { if (_milestoneQueue.Count == 0) { _isShowingMilestone = false; return; } _isShowingMilestone = true; MilestoneData data = _milestoneQueue.Dequeue(); if (_currentAnimation != null) { ((MonoBehaviour)this).StopCoroutine(_currentAnimation); } _currentAnimation = ((MonoBehaviour)this).StartCoroutine(AnimateMilestone(data)); } private IEnumerator AnimateMilestone(MilestoneData data) { ((TMP_Text)_milestoneNameText).text = data.Name; ((TMP_Text)_xpText).text = $"+{data.XP} XP"; ((TMP_Text)_descriptionText).text = LocalizedText.GetText("MS_DESC_" + data.Id, true); ApplyTheme(); if (_categoryIcons.TryGetValue(data.Category, out var icon)) { _iconImage.sprite = icon; ((Graphic)_iconImage).color = Color.white; } _milestonePanel.SetActive(true); _panelRect.anchoredPosition = OFF_SCREEN_POSITION; float elapsed = 0f; while (elapsed < 0.4f) { elapsed += Time.deltaTime; float t = elapsed / 0.4f; t = EaseOutBack(t); _panelRect.anchoredPosition = Vector2.Lerp(OFF_SCREEN_POSITION, ON_SCREEN_POSITION, t); yield return null; } _panelRect.anchoredPosition = ON_SCREEN_POSITION; yield return (object)new WaitForSeconds(3.5f); elapsed = 0f; while (elapsed < 0.3f) { elapsed += Time.deltaTime; float t2 = elapsed / 0.3f; t2 = EaseInQuad(t2); _panelRect.anchoredPosition = Vector2.Lerp(ON_SCREEN_POSITION, OFF_SCREEN_POSITION, t2); yield return null; } _milestonePanel.SetActive(false); ProcessNextMilestone(); } private float EaseOutBack(float t) { float num = 1.70158f; float num2 = num + 1f; return 1f + num2 * Mathf.Pow(t - 1f, 3f) + num * Mathf.Pow(t - 1f, 2f); } private float EaseInQuad(float t) { return t * t; } public void ApplyTheme() { //IL_0036: 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_007a: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) UITheme uITheme = QueueUI.Instance?.ActiveTheme; if (uITheme != null) { if (Object.op_Implicit((Object)(object)_backgroundImage)) { ((Graphic)_backgroundImage).color = uITheme.PanelBackgroundColor; } if (Object.op_Implicit((Object)(object)_titleText)) { ((Graphic)_titleText).color = uITheme.SecondaryTextColor; } if (Object.op_Implicit((Object)(object)_milestoneNameText)) { ((Graphic)_milestoneNameText).color = uITheme.PrimaryTextColor; } if (Object.op_Implicit((Object)(object)_descriptionText)) { ((Graphic)_descriptionText).color = uITheme.SecondaryTextColor; } } } } public class MilestoneData { public string Name; public string Description; public int XP; public MilestoneCategory Category; public string Id; } public static class AirportEditor { public enum ScreenIdentifier { FlightBoard1, FlightBoard2, FlightBoard3, FlightBoard4, BoardingSign } private static readonly int MainUI = Shader.PropertyToID("_MainUI"); private static readonly int Glow = Shader.PropertyToID("_Glow"); private static string _airportMediaPath = string.Empty; private static bool _isInitialized; public static void Initialize(string airportMediaPath) { if (_isInitialized) { Plugin.Log("[AirportEditor] Already initialized. Call Shutdown() before reinitializing.", "warning"); return; } _airportMediaPath = airportMediaPath; if (!Directory.Exists(_airportMediaPath)) { try { Directory.CreateDirectory(_airportMediaPath); Plugin.Log("[AirportEditor] Created airport media folder at: " + _airportMediaPath); } catch (Exception ex) { Plugin.Log("[AirportEditor] Failed to create airport media folder: " + ex.Message, "error"); return; } } _isInitialized = true; Plugin.Log("[AirportEditor] Initialized successfully."); } public static void Shutdown() { if (_isInitialized) { _airportMediaPath = string.Empty; _isInitialized = false; Plugin.Log("[AirportEditor] Shutdown complete."); } } public static bool PlayToScreen(ScreenIdentifier screen, string mediaFileName) { if (!_isInitialized) { Plugin.Log("[AirportEditor] Not initialized. Call Initialize() first.", "error"); return false; } string text = Path.Combine(_airportMediaPath, mediaFileName); if (!File.Exists(text)) { Plugin.Log("[AirportEditor] Media file not found: " + text, "error"); return false; } string screenName = GetScreenName(screen); GameObject val = GameObject.Find(screenName); if ((Object)(object)val == (Object)null) { Plugin.Log("[AirportEditor] Could not find screen object: " + screenName, "error"); return false; } ClearScreen(val); try { UpdateScreen(val, text); Plugin.Log($"[AirportEditor] Successfully set media '{mediaFileName}' on screen '{screen}'"); return true; } catch (Exception ex) { Plugin.Log($"[AirportEditor] Failed to update screen '{screen}': {ex.Message}", "error"); return false; } } private static void PlayVisualizerToScreen(ScreenIdentifier screen, AudioSource musicSource, VisualizerType type) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected O, but got Unknown //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Expected O, but got Unknown //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Expected O, but got Unknown //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Expected O, but got Unknown string screenName = GetScreenName(screen); GameObject val = GameObject.Find(screenName); if ((Object)(object)val == (Object)null) { return; } AudioVisualizer component = val.GetComponent(); if ((Object)(object)component != (Object)null) { component.SwitchAudioSource(musicSource); return; } ClearScreen(val); RenderTexture val2 = new RenderTexture(960, 540, 0, (RenderTextureFormat)0); val2.Create(); AudioVisualizer audioVisualizer = val.AddComponent(); if (1 == 0) { } string text = type switch { VisualizerType.Orbs => "OrbVisualizer", VisualizerType.Poly => "PolyVisualizer", VisualizerType.Star => "KaleidoscopeVisualizer", VisualizerType.Waveform => "WaveformVisualizer", VisualizerType.FrequencyBars => "HorizonVisualizer", _ => "Kiosk", }; if (1 == 0) { } string text2 = text; Shader val3 = Plugin.Bundle.LoadAsset(text2); Material mat; if ((Object)(object)val3 == (Object)null) { Plugin.Log("Failed to load " + text2 + "! Falling back to Kiosk.", "error"); mat = new Material(Shader.Find("Kiosk")); } else { mat = new Material(val3); } audioVisualizer.Initialize(musicSource, val2, type, mat); Shader val4 = Shader.Find("Kiosk"); Material val5 = new Material(val4); val5.SetTexture(MainUI, (Texture)(object)val2); val5.SetFloat(Glow, 1.5f); MeshRenderer component2 = val.GetComponent(); Material[] materials = ((Renderer)component2).materials; if (materials.Length > 1 && (Object)(object)materials[1] != (Object)null) { Object.Destroy((Object)(object)materials[1]); } materials[1] = val5; ((Renderer)component2).materials = materials; } public static void PlayAllVisualizers(AudioSource musicSource) { if (Plugin.MilestonesEnabled && Plugin.RenderVisualizers) { MilestoneTracker.Instance.ReportProgress("LightSwitch"); } PlayVisualizerToScreen(ScreenIdentifier.FlightBoard1, musicSource, VisualizerType.FrequencyBars); PlayVisualizerToScreen(ScreenIdentifier.FlightBoard2, musicSource, VisualizerType.Orbs); PlayVisualizerToScreen(ScreenIdentifier.FlightBoard3, musicSource, VisualizerType.Poly); PlayVisualizerToScreen(ScreenIdentifier.FlightBoard4, musicSource, VisualizerType.Star); } private static string GetScreenName(ScreenIdentifier screen) { if (1 == 0) { } string result = screen switch { ScreenIdentifier.FlightBoard2 => "Flight Board (3)", ScreenIdentifier.FlightBoard1 => "Flight Board (2)", ScreenIdentifier.FlightBoard3 => "Flight Board (1)", ScreenIdentifier.FlightBoard4 => "Flight Board", ScreenIdentifier.BoardingSign => "Boarding Sign", _ => throw new ArgumentOutOfRangeException("screen", screen, null), }; if (1 == 0) { } return result; } private static void ClearScreen(GameObject flightBoard) { VideoPlayer component = flightBoard.GetComponent(); if ((Object)(object)component != (Object)null) { component.Stop(); Object.Destroy((Object)(object)component); } AudioSource component2 = flightBoard.GetComponent(); if ((Object)(object)component2 != (Object)null) { component2.Stop(); Object.Destroy((Object)(object)component2); } AudioVisualizer component3 = flightBoard.GetComponent(); if ((Object)(object)component3 != (Object)null) { Object.Destroy((Object)(object)component3); } MeshRenderer component4 = flightBoard.GetComponent(); if (!((Object)(object)component4 != (Object)null) || ((Renderer)component4).materials.Length < 2) { return; } Material[] materials = ((Renderer)component4).materials; if ((Object)(object)materials[1] != (Object)null) { Texture texture = materials[1].GetTexture(MainUI); if ((Object)(object)texture != (Object)null && texture is RenderTexture) { Object.Destroy((Object)(object)texture); } Object.Destroy((Object)(object)materials[1]); materials[1] = null; } ((Renderer)component4).materials = materials; } private static void UpdateScreen(GameObject flightBoard, string mediaPath) { //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Expected O, but got Unknown //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Expected O, but got Unknown //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Expected O, but got Unknown //IL_020c: Unknown result type (might be due to invalid IL or missing references) //IL_0213: Expected O, but got Unknown Texture val = null; string text = Path.GetExtension(mediaPath).ToLowerInvariant(); if (text == ".mp4") { VideoPlayer val2 = flightBoard.AddComponent(); val2.audioOutputMode = (VideoAudioOutputMode)1; AudioSource val3 = flightBoard.GetComponent() ?? flightBoard.AddComponent(); val3.spatialBlend = 1f; val3.minDistance = 1f; val3.maxDistance = 10f; val3.dopplerLevel = 0f; val3.volume = 0.2f; AnimationCurve val4 = new AnimationCurve(); val4.AddKey(0f, 1f); val4.AddKey(0.1f, 0.85f); val4.AddKey(0.25f, 0.2f); val4.AddKey(0.4f, 0f); for (int i = 0; i < val4.length; i++) { val4.SmoothTangents(i, 0f); } val3.rolloffMode = (AudioRolloffMode)2; val3.SetCustomCurve((AudioSourceCurveType)0, val4); val2.SetTargetAudioSource((ushort)0, val3); RenderTexture val5 = new RenderTexture(1920, 1080, 0); val2.source = (VideoSource)1; val2.url = "file://" + mediaPath; val2.renderMode = (VideoRenderMode)2; val2.targetTexture = val5; val2.isLooping = true; val2.Play(); val = (Texture)(object)val5; } else { if (!(text == ".png")) { throw new ArgumentException("Unsupported media format: " + text); } byte[] array = File.ReadAllBytes(mediaPath); Texture2D val6 = new Texture2D(2, 2); if (ImageConversion.LoadImage(val6, array)) { val = (Texture)(object)val6; } } MeshRenderer component = flightBoard.GetComponent(); if ((Object)(object)component == (Object)null) { throw new InvalidOperationException("Flight board has no MeshRenderer component"); } Shader val7 = Shader.Find("Kiosk"); if ((Object)(object)val7 == (Object)null) { throw new InvalidOperationException("Kiosk shader not found"); } Material val8 = new Material(val7); if ((Object)(object)val != (Object)null) { val8.SetTexture(MainUI, val); val8.SetFloat(Glow, 1.5f); } Material[] materials = ((Renderer)component).materials; if (materials.Length < 2) { throw new InvalidOperationException("Flight board does not have enough material slots"); } if ((Object)(object)materials[1] != (Object)null) { Object.Destroy((Object)(object)materials[1]); } materials[1] = val8; ((Renderer)component).materials = materials; } } public enum VisualizerType { FrequencyBars, Orbs, Waveform, Star, Poly } public class AudioVisualizer : MonoBehaviour { private AudioSource audioSource; private VisualizerType type; private float[] spectrumData = new float[64]; private RenderTexture renderTexture; private Material displayMat; private const int numBars = 24; private float[] shaderBarData = new float[24]; private float[] barPeaks = new float[24]; private float[] smoothedSpectrum = new float[24]; private const float smoothing = 0.15f; public float orbIntensity = 2.5f; private float[] circleSmoothed = new float[9]; private Vector2[] satellitePositions = (Vector2[])(object)new Vector2[8]; private Vector2[] satelliteDrift = (Vector2[])(object)new Vector2[8]; private Vector4[] orbData = (Vector4[])(object)new Vector4[9]; private Vector4[] orbColors = (Vector4[])(object)new Vector4[9]; private static readonly int OrbsID = Shader.PropertyToID("_Orbs"); private static readonly int ColorsID = Shader.PropertyToID("_Colors"); private float smoothMid; private float smoothMean; private float bassImpact; private static readonly int PulseID = Shader.PropertyToID("_AudioPulse"); private static readonly int HighFreqID = Shader.PropertyToID("_HighFreq"); private static readonly int StarRotID = Shader.PropertyToID("_StarRot"); private static readonly int BGRotID = Shader.PropertyToID("_BGRot"); private float smoothedPulseStar; private float smoothedShimmerStar; private float starRotation; private float bgRotation; private float heartbeatAlpha = 0f; private float polyBaseTime = 0f; private float bpmEstimator = 0f; public float polyDecaySpeed = 2.2f; public float polyPulseSensitivity = 10f; public float polyGlowBoost = 0.5f; public void Initialize(AudioSource source, RenderTexture rt, VisualizerType visualizerType, Material mat) { //IL_0032: 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_00e6: 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) audioSource = source; renderTexture = rt; type = visualizerType; displayMat = mat; RenderTexture active = RenderTexture.active; RenderTexture.active = renderTexture; GL.Clear(true, true, Color.black); RenderTexture.active = active; VisualizerType visualizerType2 = type; VisualizerType visualizerType3 = visualizerType2; if (visualizerType3 == VisualizerType.Orbs) { circleSmoothed = new float[9]; satellitePositions = (Vector2[])(object)new Vector2[8]; satelliteDrift = (Vector2[])(object)new Vector2[8]; for (int i = 0; i < 8; i++) { float num = (float)i / 8f * MathF.PI * 2f; satellitePositions[i] = new Vector2(Mathf.Cos(num) * 250f, Mathf.Sin(num) * 250f); satelliteDrift[i] = new Vector2(Random.Range(-1f, 1f), Random.Range(-1f, 1f)); } } } public void SwitchAudioSource(AudioSource newSource) { audioSource = newSource; } private void Update() { if (!((Object)(object)audioSource == (Object)null) && audioSource.isPlaying) { switch (type) { case VisualizerType.FrequencyBars: UpdateFrequencyBars(); break; case VisualizerType.Orbs: UpdateOrbs(); break; case VisualizerType.Waveform: UpdateWaveform(); break; case VisualizerType.Star: UpdateStar(); break; case VisualizerType.Poly: UpdatePoly(); break; } } } private void OnDestroy() { if ((Object)(object)displayMat != (Object)null) { Object.Destroy((Object)(object)displayMat); } if ((Object)(object)renderTexture != (Object)null) { renderTexture.Release(); Object.Destroy((Object)(object)renderTexture); } } private void UpdateFrequencyBars() { audioSource.GetSpectrumData(spectrumData, 0, (FFTWindow)5); for (int i = 0; i < 24; i++) { float num; if (i < 16) { num = spectrumData[i] * (1f + (float)i * 0.8f); } else { int num2 = 16 + (i - 16) * 2; num = (spectrumData[num2] + spectrumData[num2 + 1]) / 2f; num *= 1f + (float)i * 0.8f; } smoothedSpectrum[i] = ((num > smoothedSpectrum[i]) ? Mathf.Lerp(smoothedSpectrum[i], num, 0.3f) : Mathf.Lerp(smoothedSpectrum[i], num, 0.05f)); float num3 = Mathf.Clamp01(smoothedSpectrum[i] * 3f * orbIntensity); shaderBarData[i] = num3; if (num3 > barPeaks[i]) { barPeaks[i] = num3; } else { barPeaks[i] -= 0.15f * Time.deltaTime; } } displayMat.SetFloatArray("_Bars", shaderBarData); displayMat.SetFloatArray("_Peaks", barPeaks); Graphics.Blit((Texture)null, renderTexture, displayMat); } private void UpdateOrbs() { //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: 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_0057: Expected O, but got Unknown //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0245: Unknown result type (might be due to invalid IL or missing references) //IL_0268: Unknown result type (might be due to invalid IL or missing references) //IL_0282: Unknown result type (might be due to invalid IL or missing references) //IL_0287: Unknown result type (might be due to invalid IL or missing references) //IL_028c: Unknown result type (might be due to invalid IL or missing references) //IL_02a1: Unknown result type (might be due to invalid IL or missing references) //IL_02b2: Unknown result type (might be due to invalid IL or missing references) //IL_02b7: Unknown result type (might be due to invalid IL or missing references) //IL_02ca: Unknown result type (might be due to invalid IL or missing references) //IL_02d7: Unknown result type (might be due to invalid IL or missing references) //IL_02e1: Unknown result type (might be due to invalid IL or missing references) //IL_02e6: Unknown result type (might be due to invalid IL or missing references) //IL_02eb: Unknown result type (might be due to invalid IL or missing references) //IL_0328: Unknown result type (might be due to invalid IL or missing references) //IL_0332: Unknown result type (might be due to invalid IL or missing references) //IL_0337: Unknown result type (might be due to invalid IL or missing references) //IL_033c: Unknown result type (might be due to invalid IL or missing references) //IL_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_03ac: Unknown result type (might be due to invalid IL or missing references) //IL_03b1: Unknown result type (might be due to invalid IL or missing references) //IL_03c5: Unknown result type (might be due to invalid IL or missing references) //IL_03ca: Unknown result type (might be due to invalid IL or missing references) //IL_03cf: Unknown result type (might be due to invalid IL or missing references) //IL_036f: Unknown result type (might be due to invalid IL or missing references) //IL_0376: Unknown result type (might be due to invalid IL or missing references) //IL_038d: Unknown result type (might be due to invalid IL or missing references) //IL_0392: Unknown result type (might be due to invalid IL or missing references) //IL_0397: Unknown result type (might be due to invalid IL or missing references) //IL_045e: Unknown result type (might be due to invalid IL or missing references) //IL_0463: Unknown result type (might be due to invalid IL or missing references) //IL_04b7: Unknown result type (might be due to invalid IL or missing references) //IL_04cb: Unknown result type (might be due to invalid IL or missing references) //IL_04d2: Unknown result type (might be due to invalid IL or missing references) //IL_04d7: Unknown result type (might be due to invalid IL or missing references) //IL_04dc: Unknown result type (might be due to invalid IL or missing references) //IL_048d: Unknown result type (might be due to invalid IL or missing references) //IL_0492: Unknown result type (might be due to invalid IL or missing references) //IL_0497: Unknown result type (might be due to invalid IL or missing references) int[] array = new int[8] { 45, 55, 40, 60, 50, 45, 55, 48 }; if ((Object)(object)displayMat == (Object)null) { Shader val = Plugin.Bundle.LoadAsset("OrbVisualizer"); if (!((Object)(object)val != (Object)null)) { return; } displayMat = new Material(val); Vector4[] array2 = (Vector4[])(object)new Vector4[9] { new Vector4(2f / 9f, 0f, 0f, 0f), default(Vector4), default(Vector4), default(Vector4), default(Vector4), default(Vector4), default(Vector4), default(Vector4), default(Vector4) }; for (int i = 0; i < 8; i++) { array2[i + 1] = new Vector4((float)array[i] / 540f, 0f, 0f, 0f); } displayMat.SetVectorArray("_BaseRadius", array2); } audioSource.GetSpectrumData(spectrumData, 0, (FFTWindow)5); float num = (spectrumData[0] + spectrumData[1] + spectrumData[2] + spectrumData[3]) / 4f; circleSmoothed[0] = Mathf.Lerp(circleSmoothed[0], num, 0.15f); float num2 = Mathf.Clamp(120f + circleSmoothed[0] * 2500f * orbIntensity, 120f, 600f) / 540f; orbData[0] = new Vector4(0.5f, 0.5f, num2, 0f); orbColors[0] = Color.op_Implicit(new Color(1f, 0.3f, 0.2f)); Vector2 val2 = default(Vector2); for (int j = 0; j < 8; j++) { int num3 = 4 + j * 7; if (num3 >= 64) { num3 = 63; } float num4 = spectrumData[num3]; float num5 = 3f + (float)j / 8f * 10f; num4 *= num5; circleSmoothed[j + 1] = Mathf.Lerp(circleSmoothed[j + 1], num4, 0.15f); float num6 = 1f + circleSmoothed[j + 1] * 10f; ref Vector2 reference = ref satelliteDrift[j]; reference += new Vector2(Random.Range(-2f, 2f), Random.Range(-2f, 2f)) * (Time.deltaTime * 100f * orbIntensity * num6); satelliteDrift[j] = Vector2.ClampMagnitude(satelliteDrift[j], 15f * orbIntensity); ref Vector2 reference2 = ref satellitePositions[j]; reference2 += satelliteDrift[j] * Time.deltaTime; float num7 = (float)j / 8f * MathF.PI * 2f; ((Vector2)(ref val2))..ctor(Mathf.Cos(num7) * 300f, Mathf.Sin(num7) * 300f); Vector2 val3 = val2 - satellitePositions[j]; float magnitude = ((Vector2)(ref val3)).magnitude; float num8 = 250f * orbIntensity; if (magnitude > num8) { ref Vector2 reference3 = ref satellitePositions[j]; reference3 += ((Vector2)(ref val3)).normalized * (Time.deltaTime * 350f * orbIntensity); } else { ref Vector2 reference4 = ref satellitePositions[j]; reference4 += val3 * (Time.deltaTime * 0.4f * orbIntensity); } float num9 = (480f + satellitePositions[j].x) / 960f; float num10 = (270f + satellitePositions[j].y) / 540f; float num11 = Mathf.Clamp((float)array[j] + circleSmoothed[j + 1] * 1200f * orbIntensity, (float)array[j], 300f) / 540f; orbData[j + 1] = new Vector4(num9, num10, num11, 0f); if (j == 0) { orbColors[j + 1] = Color.op_Implicit(new Color(0.22f, 1f, 0.08f)); continue; } float num12 = (float)(j - 1) / 7f; orbColors[j + 1] = Color.op_Implicit(Color.Lerp(Color.cyan, new Color(1f, 0f, 1f), num12)); } displayMat.SetVectorArray(OrbsID, orbData); displayMat.SetVectorArray(ColorsID, orbColors); Graphics.Blit((Texture)null, renderTexture, displayMat); } private void UpdateWaveform() { audioSource.GetSpectrumData(spectrumData, 0, (FFTWindow)5); float num = (spectrumData[0] + spectrumData[1]) / 2f; float num2 = (spectrumData[8] + spectrumData[9]) / 2f; float num3 = 0f; for (int i = 10; i < 64; i++) { num3 += spectrumData[i]; } float num4 = num3 / 54f; float num5 = 0.25f; float num6 = 0.15f; float num7 = num * 0.7f; if (num7 > bassImpact) { bassImpact = num7; } else { bassImpact = Mathf.Lerp(bassImpact, num7, 0.1f); } smoothMid = ((num2 > smoothMid) ? Mathf.Lerp(smoothMid, num2, num5) : Mathf.Lerp(smoothMid, num2, num6)); smoothMean = ((num4 > smoothMean) ? Mathf.Lerp(smoothMean, num4, num5) : Mathf.Lerp(smoothMean, num4, num6)); displayMat.SetFloat("_BassFreq", bassImpact * 25f * orbIntensity); displayMat.SetFloat("_MidFreq", smoothMid * 35f * orbIntensity); displayMat.SetFloat("_MeanFreq", smoothMean * 50f * orbIntensity); Graphics.Blit((Texture)null, renderTexture, displayMat); } private void UpdateStar() { audioSource.GetSpectrumData(spectrumData, 0, (FFTWindow)5); float num = 0f; for (int i = 0; i < 8; i++) { num += spectrumData[i]; } num /= 8f; float num2 = 0f; for (int j = 8; j < 16; j++) { num2 += spectrumData[j]; } num2 /= 8f; smoothedPulseStar = Mathf.Lerp(smoothedPulseStar, num * 18f * orbIntensity, 0.15f); smoothedShimmerStar = Mathf.Lerp(smoothedShimmerStar, num2 * 60f * orbIntensity, 0.15f); bgRotation -= (0.05f + smoothedPulseStar * 0.15f) * Time.deltaTime; starRotation += (0.5f + smoothedShimmerStar * 4.5f) * Time.deltaTime; displayMat.SetFloat(PulseID, smoothedPulseStar); displayMat.SetFloat(HighFreqID, smoothedShimmerStar); displayMat.SetFloat(StarRotID, starRotation); displayMat.SetFloat(BGRotID, bgRotation); Graphics.Blit((Texture)null, renderTexture, displayMat); } private void UpdatePoly() { audioSource.GetSpectrumData(spectrumData, 0, (FFTWindow)5); float num = spectrumData[1]; float num2 = Mathf.Max(0f, num - 0.001f) * polyPulseSensitivity * orbIntensity; float num3 = (float)((Texture)renderTexture).width / (float)((Texture)renderTexture).height; if (num2 > heartbeatAlpha) { heartbeatAlpha = num2; } else { heartbeatAlpha -= polyDecaySpeed * Time.deltaTime; } heartbeatAlpha = Mathf.Clamp01(heartbeatAlpha); bpmEstimator = Mathf.Lerp(bpmEstimator, num, 0.005f); float num4 = (0.28f + bpmEstimator * 4f) * 2.5f; polyBaseTime += num4 * Time.deltaTime; displayMat.SetFloat("_AspectRatio", num3); displayMat.SetFloat("_BaseTime", polyBaseTime); displayMat.SetFloat("_Vibrant", heartbeatAlpha); displayMat.SetFloat("_GlowBoost", polyGlowBoost); Graphics.Blit((Texture)null, renderTexture, displayMat); } } public class PlaybackSyncIndicator : MonoBehaviour { private static PlaybackSyncIndicator _instance; private GameObject indicatorPanel; private Image fillImage; private TextMeshProUGUI iconText; private CanvasGroup canvasGroup; private int totalClients = 0; private int readyClients = 0; private bool isSynced = false; private Coroutine fadeOutCoroutine; public static PlaybackSyncIndicator Instance { get { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown if ((Object)(object)_instance == (Object)null) { GameObject val = new GameObject("PlaybackSyncIndicator"); _instance = val.AddComponent(); Object.DontDestroyOnLoad((Object)(object)val); } return _instance; } } 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)((Component)this).gameObject); } public void CreateUI() { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected O, but got Unknown //IL_0069: 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_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: 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_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Expected O, but got Unknown //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Expected O, but got Unknown //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Unknown result type (might be due to invalid IL or missing references) //IL_022d: Unknown result type (might be due to invalid IL or missing references) //IL_023a: Unknown result type (might be due to invalid IL or missing references) //IL_024a: Unknown result type (might be due to invalid IL or missing references) //IL_0251: Expected O, but got Unknown //IL_02b0: Unknown result type (might be due to invalid IL or missing references) //IL_02c6: Unknown result type (might be due to invalid IL or missing references) //IL_02d3: Unknown result type (might be due to invalid IL or missing references) //IL_02e0: Unknown result type (might be due to invalid IL or missing references) Canvas val = Object.FindAnyObjectByType(); if ((Object)(object)val == (Object)null) { Plugin.Log("[PlaybackSyncIndicator] No canvas found", "warning"); return; } indicatorPanel = new GameObject("SyncIndicatorPanel"); indicatorPanel.transform.SetParent(((Component)val).transform, false); RectTransform val2 = indicatorPanel.AddComponent(); val2.anchorMin = new Vector2(1f, 0f); val2.anchorMax = new Vector2(1f, 0f); val2.pivot = new Vector2(1f, 0f); val2.anchoredPosition = new Vector2(-20f, 20f); val2.sizeDelta = new Vector2(60f, 60f); canvasGroup = indicatorPanel.AddComponent(); canvasGroup.alpha = 0f; GameObject val3 = new GameObject("Background"); val3.transform.SetParent(indicatorPanel.transform, false); Image val4 = val3.AddComponent(); val4.sprite = CreateCircleSprite(); ((Graphic)val4).color = new Color(0.2f, 0.2f, 0.2f, 0.8f); RectTransform component = val3.GetComponent(); component.anchorMin = Vector2.zero; component.anchorMax = Vector2.one; component.sizeDelta = Vector2.zero; GameObject val5 = new GameObject("Fill"); val5.transform.SetParent(indicatorPanel.transform, false); fillImage = val5.AddComponent(); fillImage.sprite = CreateCircleSprite(); ((Graphic)fillImage).color = new Color(1f, 1f, 1f, 0.3f); fillImage.type = (Type)3; fillImage.fillMethod = (FillMethod)4; fillImage.fillOrigin = 2; fillImage.fillAmount = 0f; RectTransform component2 = val5.GetComponent(); component2.anchorMin = Vector2.zero; component2.anchorMax = Vector2.one; component2.sizeDelta = Vector2.zero; GameObject val6 = new GameObject("Icon"); val6.transform.SetParent(indicatorPanel.transform, false); iconText = val6.AddComponent(); ((TMP_Text)iconText).text = ""; ((TMP_Text)iconText).fontSize = 32f; ((TMP_Text)iconText).alignment = (TextAlignmentOptions)514; ((Graphic)iconText).color = Color.white; RectTransform component3 = val6.GetComponent(); component3.anchorMin = Vector2.zero; component3.anchorMax = Vector2.one; component3.sizeDelta = Vector2.zero; indicatorPanel.SetActive(false); } public void UpdateSyncState(int ready, int total, bool synced) { //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)indicatorPanel == (Object)null) { CreateUI(); } totalClients = total; readyClients = ready; isSynced = synced; if (total <= 1) { Hide(); return; } Show(); float num = ((total > 0) ? ((float)ready / (float)total) : 0f); fillImage.fillAmount = num; if (fadeOutCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(fadeOutCoroutine); fadeOutCoroutine = null; } if (synced) { ((Graphic)fillImage).color = new Color(0.2f, 0.8f, 0.2f, 1f); ((TMP_Text)iconText).text = ""; canvasGroup.alpha = 1f; fadeOutCoroutine = ((MonoBehaviour)this).StartCoroutine(FadeOutAfterDelay(2f)); } else if (ready == total) { ((Graphic)fillImage).color = new Color(1f, 0.8f, 0.2f, 1f); ((TMP_Text)iconText).text = ""; canvasGroup.alpha = 1f; } else { ((Graphic)fillImage).color = new Color(1f, 1f, 1f, 0.3f); ((TMP_Text)iconText).text = $"{ready}/{total}"; ((TMP_Text)iconText).fontSize = 20f; ((Graphic)iconText).color = new Color(1f, 1f, 1f, 0.7f); canvasGroup.alpha = 0.3f + num * 0.7f; } Plugin.Log($"[PlaybackSyncIndicator] Updated: {ready}/{total} ready, synced={synced}"); } private IEnumerator FadeOutAfterDelay(float delay) { yield return (object)new WaitForSeconds(delay); float duration = 0.5f; float elapsed = 0f; float startAlpha = canvasGroup.alpha; while (elapsed < duration) { elapsed += Time.deltaTime; canvasGroup.alpha = Mathf.Lerp(startAlpha, 0f, elapsed / duration); yield return null; } Hide(); fadeOutCoroutine = null; } public void Show() { if ((Object)(object)indicatorPanel != (Object)null) { indicatorPanel.SetActive(true); } } public void Hide() { if ((Object)(object)indicatorPanel != (Object)null) { indicatorPanel.SetActive(false); } } private Sprite CreateCircleSprite() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_0099: 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_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_004f: 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) Texture2D val = new Texture2D(100, 100); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(50f, 50f); float num = 50f; for (int i = 0; i < 100; i++) { for (int j = 0; j < 100; j++) { float num2 = Vector2.Distance(new Vector2((float)j, (float)i), val2); val.SetPixel(j, i, (num2 <= num) ? Color.white : Color.clear); } } val.Apply(); return Sprite.Create(val, new Rect(0f, 0f, 100f, 100f), new Vector2(0.5f, 0.5f)); } } public class QueueUI : MenuWindow { private class MilestoneRowRef { public string MilestoneId; public GameObject Root; public Image BackgroundImage; public TextMeshProUGUI NameText; public TextMeshProUGUI DescriptionText; public TextMeshProUGUI RightText; public MilestoneCategory Category; } private enum QueueUIScreen { Queue, Help, Settings, Milestones, AddTrack } private enum AddTrackMode { AddToEnd, PlayNext, RestartRadio } private enum AddTrackInputClass { Empty, ValidUrl, InvalidUrl, FreeText } [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static UnityAction <>9__140_0; public static Action <>9__142_1; public static Action <>9__142_2; public static Action <>9__142_3; public static Action <>9__142_4; public static Action <>9__142_6; public static UnityAction <>9__148_0; public static Func <>9__246_0; public static Func <>9__247_0; internal void b__140_0() { Controller.Static_PrimaryUse(); } internal void b__142_1(bool isOn) { Plugin.ShowTrackDuration = isOn; Plugin.Instance.showTrackDurationConfig.Value = isOn; if (isOn) { AudioSource activeSource = AudioMaster.ActiveSource; if ((Object)(object)activeSource != (Object)null && (Object)(object)activeSource.clip != (Object)null) { sPEAKerUI.Instance.ShowDuration(activeSource.clip.length); } } else { sPEAKerUI.Instance.HideDuration(); } } internal void b__142_2(bool isOn) { Plugin.DisplayAuthorInfo = isOn; Plugin.Instance.displayAuthorInfoConfig.Value = isOn; if (isOn) { string currentMixtapeAuthor = AudioMaster.Instance.GetCurrentMixtapeAuthor(); if (!string.IsNullOrEmpty(currentMixtapeAuthor)) { sPEAKerUI.Instance.ShowAuthorInfo(currentMixtapeAuthor, AudioMaster.IsYouTubeMode); } } else { sPEAKerUI.Instance.HideAuthorInfo(); } } internal void b__142_3(bool isOn) { Plugin.UseSpatialAudio = isOn; Plugin.Instance.useSpatialAudioConfig.Value = isOn; AudioMaster.UpdateSpatialAudioSetting(isOn); } internal void b__142_4(bool isOn) { Plugin.AutoPlay = isOn; Plugin.Instance.autoPlayConfig.Value = isOn; } internal void b__142_6(int value) { int predownloadCount = Plugin.PredownloadCount; Plugin.PredownloadCount = value; Plugin.Instance.predownloadCountConfig.Value = value; if (value > predownloadCount) { ytModuleCompatibility.TriggerPreload(); } } internal void b__148_0() { try { string path = Path.Combine(Paths.BepInExRootPath, "LogOutput.log"); string systemCopyBuffer; using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { using StreamReader streamReader = new StreamReader(stream); systemCopyBuffer = streamReader.ReadToEnd(); } GUIUtility.systemCopyBuffer = systemCopyBuffer; sPEAKerUI.Instance.ShowToast("Logs copied to clipboard"); Plugin.Log("[QueueUI] Logs copied to clipboard."); } catch (Exception ex) { sPEAKerUI.Instance.ShowToast("Failed to copy logs"); Plugin.Log("[QueueUI] Failed to copy logs: " + ex.Message, "error"); } } internal SongQueueItem b__246_0(GameObject go) { return go.GetComponent(); } internal SongQueueItem b__247_0(GameObject go) { return go.GetComponent(); } } public GameObject uiCanvas; private RectTransform contentPanel; private Button shuffleButton; private Button _closeButton; private Button resetButton; private ScrollRect _scrollRect; private Image _resetProgressImage; private GameObject _resetProgressGO; private Button _clearCacheButton; private Image _clearCacheProgressImage; private GameObject _clearCacheProgressGO; private Button _helpTabButton; private Button _queueTabButton; private Button _milestonesTabButton; private Image _helpTabImage; private Image _queueTabImage; private Image _milestonesTabImage; private Button _settingsTabButton; private Image _settingsTabImage; private Button _previousButton; private Button _playPauseButton; private Button _nextButton; private Button _loopButton; private TextMeshProUGUI _playPauseText; private TextMeshProUGUI _loopText; private Slider _seekSlider; private bool _isSeekingManually = false; private Coroutine _seekUpdateCoroutine; private RectTransform _queuePanel; private RectTransform _helpPanel; private RectTransform _settingsPanel; private RectTransform _milestonesPanel; private RectTransform _milestonesContentArea; private GameObject _queueOffPanel; private TextMeshProUGUI _queueOffText; private Button _turnOnButton; private GameObject _queueScrollAreaGO; private GameObject _queueFooter; private RectTransform _addTrackPanel; private TMP_InputField _addTrackInputField; private TextMeshProUGUI _addTrackStatusText; private TMP_Dropdown _addTrackModeDropdown; private GameObject _addTrackActionRow; private Button _addTrackSubmitButton; private GameObject _queueHeaderGO; private LayoutElement _inlineAddTrackLayout; private CanvasGroup _inlineAddTrackCanvasGroup; private bool _addTrackExpanded = false; private Coroutine _addTrackAnimCoroutine; private TextMeshProUGUI _addTrackToggleText; private const float AddTrackPanelHeight = 178f; private const float AddTrackAnimDuration = 0.25f; private GameObject _searchResultsSection; private LayoutElement _searchResultsLayout; private Image _searchResultsBackground; private RectTransform _searchResultsContent; private GameObject _searchingPlaceholderGO; private TextMeshProUGUI _searchingPlaceholderText; private GameObject _noResultsPlaceholderGO; private TextMeshProUGUI _noResultsPlaceholderText; private Button _clearSearchButton; private List _searchResults = new List(); private bool _searchInFlight = false; private Coroutine _searchCoroutine; private float _searchSpinnerTimer = 0f; private int _searchSpinnerIndex = 0; private const float SearchSpinnerInterval = 0.1f; private static readonly string[] SearchSpinnerPattern = new string[4] { "\\", "|", "/", "-" }; private const float SearchResultsSectionHeight = 208f; private const float SearchResultRowHeight = 30f; private bool _playlistLoading; private GameObject _playlistLoadingItem; private TextMeshProUGUI _playlistLoadingSpinner; private float _playlistSpinnerTimer; private int _playlistSpinnerIndex; private List _activeQueueItems = new List(); private Stack _queueItemPool = new Stack(); private TextMeshProUGUI _helpTextComponent; private SongQueueItem _currentlySelectedItem; private bool _shouldScrollOnNextPopulation = false; private Coroutine _resetPressCoroutine; private Coroutine _clearCachePressCoroutine; private QueueUIScreen _currentScreen = QueueUIScreen.Milestones; private bool _milestonesBuilt = false; private readonly List _milestoneRows = new List(); private TextMeshProUGUI _milestoneLevelingPrompt; private GameObject _milestoneLevelingPromptGO; private TextMeshProUGUI _milestoneSummaryText; private GameObject _milestoneSummaryGO; private GameObject _milestonePlaylistSectionGO; private const string MilestonesPlaylistUrl = "https://music.youtube.com/playlist?list=PLH1kutie6eIM"; private Texture2D _gradientTexture; private Sprite _whitePixelSprite; private readonly Dictionary _spriteCache = new Dictionary(); private List _themePanelImages = new List(); private List _themeViewportImages = new List(); private List _themeButtonImages = new List(); private List _themeSliderFillImages = new List(); private List _themeSliderKnobImages = new List(); private List _themePrimaryTexts = new List(); private List _themeSecondaryTexts = new List(); private const int AddTrackModeSpacerIndex = 3; private bool _dropdownPlaylistMode; private static readonly AddTrackMode[] PlaylistModes = new AddTrackMode[2] { AddTrackMode.AddToEnd, AddTrackMode.RestartRadio }; private TextMeshProUGUI _diskSpaceValueText; private readonly Dictionary _themeRegistry = new Dictionary(); public static QueueUI Instance { get; private set; } public override bool openOnStart => false; public override bool selectOnOpen => true; public override bool closeOnPause => false; public override bool closeOnUICancel => false; public override bool blocksPlayerInput => true; public override bool showCursorWhileOpen => true; public override bool autoHideOnClose => true; public override Selectable objectToSelectOnOpen => (Selectable)(object)_closeButton; public UITheme ActiveTheme { get; private set; } private int CurrentSpacerIndex() { return _dropdownPlaylistMode ? PlaylistModes.Length : 3; } private AddTrackMode CurrentAddTrackMode() { TMP_Dropdown addTrackModeDropdown = _addTrackModeDropdown; int num = ((addTrackModeDropdown != null) ? addTrackModeDropdown.value : 0); if (_dropdownPlaylistMode) { return PlaylistModes[Mathf.Clamp(num, 0, PlaylistModes.Length - 1)]; } return (AddTrackMode)Mathf.Clamp(num, 0, 2); } private void Awake() { if ((Object)(object)Instance == (Object)null) { Instance = this; CreateUI(); } else { Object.Destroy((Object)(object)((Component)this).gameObject); } } private void OnEnable() { AudioMaster.OnSongChanged += HandleSongChanged; AudioMaster.OnQueueSynced += OnQueueSynced; AudioMaster.OnPlaybackStateChanged += HandlePlaybackStateChanged; AudioMaster.OnYouTubeModeChanged += OnYouTubeModeChanged; AudioMaster.OnLoopStateChanged += HandleLoopStateChanged; LocalizedText.OnLangugageChanged = (Action)Delegate.Combine(LocalizedText.OnLangugageChanged, new Action(RefreshDynamicLocalization)); if (ytModuleCompatibility.enabled) { ytModuleCompatibility.SubscribeToQueueChanged(OnYouTubeQueueChanged); ytModuleCompatibility.SubscribeToSongChanged(OnYouTubeSongChanged); ytModuleCompatibility.SubscribeToTrackStateChanged(OnYouTubeTrackStateChanged); ytModuleCompatibility.SubscribeToLoadingStateChanged(OnYouTubeLoadingStateChanged); ytModuleCompatibility.SubscribeToDownloadStateChanged(OnYouTubeDownloadStateChanged); YouTubePlayer.OnYouTubeLoopStateChanged += HandleLoopStateChanged; } } private void OnDisable() { AudioMaster.OnSongChanged -= HandleSongChanged; AudioMaster.OnQueueSynced -= OnQueueSynced; AudioMaster.OnPlaybackStateChanged -= HandlePlaybackStateChanged; AudioMaster.OnYouTubeModeChanged -= OnYouTubeModeChanged; AudioMaster.OnLoopStateChanged -= HandleLoopStateChanged; LocalizedText.OnLangugageChanged = (Action)Delegate.Remove(LocalizedText.OnLangugageChanged, new Action(RefreshDynamicLocalization)); if (ytModuleCompatibility.enabled) { ytModuleCompatibility.UnsubscribeFromQueueChanged(OnYouTubeQueueChanged); ytModuleCompatibility.UnsubscribeFromSongChanged(OnYouTubeSongChanged); ytModuleCompatibility.UnsubscribeFromTrackStateChanged(OnYouTubeTrackStateChanged); ytModuleCompatibility.UnsubscribeFromLoadingStateChanged(OnYouTubeLoadingStateChanged); ytModuleCompatibility.UnsubscribeFromDownloadStateChanged(OnYouTubeDownloadStateChanged); YouTubePlayer.OnYouTubeLoopStateChanged -= HandleLoopStateChanged; } } private void LateUpdate() { if (((MenuWindow)this).isOpen) { UpdateQueueTabVisibility(); TickSearchSpinner(); TickPlaylistLoadingSpinner(); if (Input.GetKeyDown((KeyCode)27)) { ((MenuWindow)this).Close(); } } } private void CreateUI() { //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Expected O, but got Unknown //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Expected O, but got Unknown //IL_011d: 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_0149: Unknown result type (might be due to invalid IL or missing references) //IL_015f: 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_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Expected O, but got Unknown //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) RegisterAllThemes(); LoadTheme(Plugin.QueueUITheme); if ((Object)(object)_whitePixelSprite == (Object)null) { Texture2D val = new Texture2D(1, 1); val.SetPixel(0, 0, Color.white); val.Apply(); _whitePixelSprite = Sprite.Create(val, new Rect(0f, 0f, 1f, 1f), new Vector2(0.5f, 0.5f)); } _gradientTexture = CreateGradientTexture(); uiCanvas = new GameObject("QueueCanvas"); uiCanvas.transform.SetParent(((Component)this).transform); Canvas val2 = uiCanvas.AddComponent(); val2.renderMode = (RenderMode)0; val2.sortingOrder = 2000; uiCanvas.AddComponent(); uiCanvas.AddComponent(); GameObject val3 = new GameObject("BackgroundPanel"); val3.transform.SetParent(uiCanvas.transform, false); RectTransform val4 = val3.AddComponent(); val4.anchorMin = new Vector2(0.5f, 0.5f); val4.anchorMax = new Vector2(0.5f, 0.5f); val4.pivot = new Vector2(0.5f, 0.5f); val4.sizeDelta = new Vector2(500f, 600f); Image val5 = val3.AddComponent(); ((Graphic)val5).color = ActiveTheme.PanelBackgroundColor; _themePanelImages.Add(val5); VerticalLayoutGroup val6 = val3.AddComponent(); ((HorizontalOrVerticalLayoutGroup)val6).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)val6).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)val6).childForceExpandHeight = false; ((HorizontalOrVerticalLayoutGroup)val6).childForceExpandWidth = true; ((HorizontalOrVerticalLayoutGroup)val6).spacing = 0f; CreateTabBar(val3.transform); GameObject val7 = new GameObject("ScreenContainer"); val7.transform.SetParent(val3.transform, false); val7.AddComponent(); val7.AddComponent().flexibleHeight = 1f; CreateAddTrackScreen(val7.transform); CreateQueueScreen(val7.transform); CreateHelpScreen(val7.transform); CreateSettingsScreen(val7.transform); CreateMilestonesScreen(val7.transform); ((MenuWindow)this).StartClosed(); if ((Object)(object)Plugin.PeakFont == (Object)null) { Plugin.OnFontLoaded += ApplyFont; } } private void ApplyFont() { foreach (TextMeshProUGUI themePrimaryText in _themePrimaryTexts) { if ((Object)(object)themePrimaryText != (Object)null) { ((TMP_Text)themePrimaryText).font = Plugin.PeakFont; } } } private void CreateTabBar(Transform parent) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Expected O, but got Unknown //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Expected O, but got Unknown //IL_020b: Unknown result type (might be due to invalid IL or missing references) //IL_0215: Expected O, but got Unknown //IL_02a2: Unknown result type (might be due to invalid IL or missing references) //IL_02ac: Expected O, but got Unknown //IL_0311: Unknown result type (might be due to invalid IL or missing references) //IL_031b: Expected O, but got Unknown GameObject val = new GameObject("TabBar"); val.transform.SetParent(parent, false); val.AddComponent(); LayoutElement val2 = val.AddComponent(); val2.preferredHeight = 35f; val2.flexibleHeight = 0f; HorizontalLayoutGroup val3 = val.AddComponent(); ((LayoutGroup)val3).padding = new RectOffset(5, 5, 3, 0); ((HorizontalOrVerticalLayoutGroup)val3).spacing = 5f; ((HorizontalOrVerticalLayoutGroup)val3).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)val3).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)val3).childForceExpandHeight = true; ((HorizontalOrVerticalLayoutGroup)val3).childForceExpandWidth = false; _helpTabButton = CreateButton("HelpTab", val.transform, "?", 22); LayoutElement component = ((Component)_helpTabButton).GetComponent(); component.preferredHeight = 28f; component.preferredWidth = 40f; component.flexibleHeight = 0f; _helpTabImage = ((Component)_helpTabButton).GetComponent(); ((UnityEvent)_helpTabButton.onClick).AddListener((UnityAction)delegate { SwitchScreen(QueueUIScreen.Help); }); _settingsTabButton = CreateButton("SettingsTab", val.transform, "!", 22); LayoutElement component2 = ((Component)_settingsTabButton).GetComponent(); component2.preferredHeight = 28f; component2.preferredWidth = 40f; component2.flexibleHeight = 0f; _settingsTabImage = ((Component)_settingsTabButton).GetComponent(); ((UnityEvent)_settingsTabButton.onClick).AddListener((UnityAction)delegate { SwitchScreen(QueueUIScreen.Settings); }); _queueTabButton = CreateButton("QueueTab", val.transform, "Queue", 18); LayoutElement component3 = ((Component)_queueTabButton).GetComponent(); component3.preferredHeight = 28f; component3.flexibleWidth = 1f; component3.flexibleHeight = 0f; _queueTabImage = ((Component)_queueTabButton).GetComponent(); AddLocalization(((Component)_queueTabButton).GetComponentInChildren(), "QUEUE"); ((UnityEvent)_queueTabButton.onClick).AddListener((UnityAction)delegate { SwitchScreen(QueueUIScreen.Queue); }); _milestonesTabButton = CreateButton("MilestonesTab", val.transform, "Milestones", 18); LayoutElement component4 = ((Component)_milestonesTabButton).GetComponent(); component4.preferredHeight = 28f; component4.flexibleWidth = 1f; component4.flexibleHeight = 0f; _milestonesTabImage = ((Component)_milestonesTabButton).GetComponent(); AddLocalization(((Component)_milestonesTabButton).GetComponentInChildren(), "MILESTONES"); ((UnityEvent)_milestonesTabButton.onClick).AddListener((UnityAction)delegate { SwitchScreen(QueueUIScreen.Milestones); }); _closeButton = CreateButton("CloseTab", val.transform, "X", 24); LayoutElement component5 = ((Component)_closeButton).GetComponent(); component5.preferredHeight = 28f; component5.preferredWidth = 40f; component5.flexibleHeight = 0f; ((UnityEvent)_closeButton.onClick).AddListener(new UnityAction(base.Close)); } private void CreateQueueScreen(Transform parent) { //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) (RectTransform rootPanel, RectTransform contentArea) tuple = CreateScreenBase(parent); RectTransform item = tuple.rootPanel; RectTransform item2 = tuple.contentArea; _queuePanel = item; contentPanel = item2; _scrollRect = ((Component)contentPanel).GetComponentInParent(); _queueScrollAreaGO = ((Component)_scrollRect).gameObject; CreateQueueHeader(_queuePanel); ((Transform)_addTrackPanel).SetParent((Transform)(object)_queuePanel, false); ((Transform)_addTrackPanel).SetSiblingIndex(1); _inlineAddTrackCanvasGroup = ((Component)_addTrackPanel).gameObject.AddComponent(); _inlineAddTrackCanvasGroup.alpha = 0f; _addTrackPanel.anchorMin = new Vector2(0f, 1f); _addTrackPanel.anchorMax = new Vector2(1f, 1f); _addTrackPanel.offsetMin = Vector2.zero; _addTrackPanel.offsetMax = Vector2.zero; _inlineAddTrackLayout = ((Component)_addTrackPanel).gameObject.GetComponent(); if ((Object)(object)_inlineAddTrackLayout == (Object)null) { _inlineAddTrackLayout = ((Component)_addTrackPanel).gameObject.AddComponent(); } _inlineAddTrackLayout.preferredHeight = 0f; _inlineAddTrackLayout.flexibleHeight = 0f; _inlineAddTrackLayout.minHeight = 0f; ((Component)_addTrackPanel).gameObject.SetActive(true); CreateQueueFooter(_queuePanel); CreateQueueOffPanel(_queuePanel); } private void CreateQueueHeader(RectTransform parent) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Expected O, but got Unknown //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Expected O, but got Unknown _queueHeaderGO = new GameObject("QueueHeader"); _queueHeaderGO.transform.SetParent((Transform)(object)parent, false); _queueHeaderGO.transform.SetSiblingIndex(0); _queueHeaderGO.AddComponent(); LayoutElement val = _queueHeaderGO.AddComponent(); val.preferredHeight = 36f; val.flexibleHeight = 0f; HorizontalLayoutGroup val2 = _queueHeaderGO.AddComponent(); ((LayoutGroup)val2).padding = new RectOffset(10, 10, 4, 4); ((HorizontalOrVerticalLayoutGroup)val2).spacing = 8f; ((HorizontalOrVerticalLayoutGroup)val2).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)val2).childControlWidth = false; ((HorizontalOrVerticalLayoutGroup)val2).childForceExpandHeight = true; ((HorizontalOrVerticalLayoutGroup)val2).childForceExpandWidth = false; Button val3 = CreateButton("AddTrackButton", _queueHeaderGO.transform, "Add Track", 14); _addTrackToggleText = ((Component)val3).GetComponentInChildren(); AddLocalization(_addTrackToggleText, "ADD_TRACK_BUTTON"); LayoutElement component = ((Component)val3).GetComponent(); component.preferredWidth = 80f; component.preferredHeight = 28f; component.flexibleHeight = 0f; ((UnityEvent)val3.onClick).AddListener(new UnityAction(ToggleAddTrackPanel)); _queueHeaderGO.SetActive(false); } private void CreateQueueFooter(RectTransform parent) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Expected O, but got Unknown //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Expected O, but got Unknown //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Expected O, but got Unknown //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Expected O, but got Unknown //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_01f7: Expected O, but got Unknown //IL_01fd: Unknown result type (might be due to invalid IL or missing references) //IL_0204: Expected O, but got Unknown //IL_031a: Unknown result type (might be due to invalid IL or missing references) //IL_0324: Expected O, but got Unknown //IL_0344: Unknown result type (might be due to invalid IL or missing references) //IL_034e: Expected O, but got Unknown GameObject val = new GameObject("QueueFooter"); val.transform.SetParent((Transform)(object)parent, false); _queueFooter = val; val.AddComponent(); LayoutElement val2 = val.AddComponent(); val2.preferredHeight = 115f; val2.flexibleHeight = 0f; VerticalLayoutGroup val3 = val.AddComponent(); ((LayoutGroup)val3).padding = new RectOffset(10, 10, 5, 5); ((HorizontalOrVerticalLayoutGroup)val3).spacing = 5f; ((HorizontalOrVerticalLayoutGroup)val3).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)val3).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)val3).childForceExpandHeight = true; ((HorizontalOrVerticalLayoutGroup)val3).childForceExpandWidth = true; CreateSeekBar(val.transform); GameObject val4 = new GameObject("PlaybackRow"); val4.transform.SetParent(val.transform, false); val4.AddComponent(); val4.AddComponent().flexibleHeight = 1f; HorizontalLayoutGroup val5 = val4.AddComponent(); ((HorizontalOrVerticalLayoutGroup)val5).spacing = 10f; ((HorizontalOrVerticalLayoutGroup)val5).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)val5).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)val5).childForceExpandHeight = true; ((HorizontalOrVerticalLayoutGroup)val5).childForceExpandWidth = true; _previousButton = CreateButton("PreviousButton", val4.transform, "Previous"); AddLocalization(((Component)_previousButton).GetComponentInChildren(), "PREVIOUS"); _playPauseButton = CreateButton("PlayPauseButton", val4.transform, "Play"); _nextButton = CreateButton("NextButton", val4.transform, "Next"); AddLocalization(((Component)_nextButton).GetComponentInChildren(), "NEXT"); _playPauseText = ((Component)_playPauseButton).GetComponentInChildren(); ((UnityEvent)_previousButton.onClick).AddListener(new UnityAction(OnPreviousClicked)); ((UnityEvent)_playPauseButton.onClick).AddListener(new UnityAction(OnPlayPauseClicked)); ((UnityEvent)_nextButton.onClick).AddListener(new UnityAction(OnNextClicked)); GameObject val6 = new GameObject("QueueControlRow"); val6.transform.SetParent(val.transform, false); val6.AddComponent(); val6.AddComponent().flexibleHeight = 1f; HorizontalLayoutGroup val7 = val6.AddComponent(); ((HorizontalOrVerticalLayoutGroup)val7).spacing = 10f; ((HorizontalOrVerticalLayoutGroup)val7).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)val7).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)val7).childForceExpandHeight = true; ((HorizontalOrVerticalLayoutGroup)val7).childForceExpandWidth = true; shuffleButton = CreateButton("ShuffleButton", val6.transform, "Shuffle"); AddLocalization(((Component)shuffleButton).GetComponentInChildren(), "SHUFFLE"); resetButton = CreateButton("ResetButton", val6.transform, "Reset"); AddLocalization(((Component)resetButton).GetComponentInChildren(), "RESET"); _loopButton = CreateButton("LoopButton", val6.transform, "Loop Off"); _loopText = ((Component)_loopButton).GetComponentInChildren(); ((UnityEvent)shuffleButton.onClick).AddListener(new UnityAction(OnShuffleClicked)); SetupResetButton(resetButton); ((UnityEvent)_loopButton.onClick).AddListener(new UnityAction(OnLoopClicked)); } private void CreateSeekBar(Transform parent) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Expected O, but got Unknown //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_0101: 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_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Expected O, but got Unknown //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0168: 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_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Expected O, but got Unknown //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Unknown result type (might be due to invalid IL or missing references) //IL_0228: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Expected O, but got Unknown //IL_0259: Unknown result type (might be due to invalid IL or missing references) //IL_0270: Unknown result type (might be due to invalid IL or missing references) //IL_0287: 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_02c8: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("SeekBar"); val.transform.SetParent(parent, false); val.AddComponent(); LayoutElement val2 = val.AddComponent(); val2.preferredHeight = 30f; val2.flexibleHeight = 0f; Image val3 = val.AddComponent(); val3.sprite = _whitePixelSprite; ((Graphic)val3).color = ActiveTheme.ViewportBackgroundColor; _themeViewportImages.Add(val3); _seekSlider = val.AddComponent(); _seekSlider.minValue = 0f; _seekSlider.maxValue = 100f; _seekSlider.value = 0f; _seekSlider.wholeNumbers = false; GameObject val4 = new GameObject("Fill Area"); val4.transform.SetParent(val.transform, false); RectTransform val5 = val4.AddComponent(); val5.anchorMin = Vector2.zero; val5.anchorMax = Vector2.one; val5.offsetMin = Vector2.zero; val5.offsetMax = Vector2.zero; GameObject val6 = new GameObject("Fill"); val6.transform.SetParent(val4.transform, false); RectTransform val7 = val6.AddComponent(); val7.anchorMin = Vector2.zero; val7.anchorMax = new Vector2(0f, 1f); val7.offsetMin = Vector2.zero; val7.offsetMax = Vector2.zero; Image val8 = val6.AddComponent(); val8.sprite = _whitePixelSprite; ((Graphic)val8).color = ActiveTheme.TabActiveColor; _themeSliderFillImages.Add(val8); _seekSlider.fillRect = val7; GameObject val9 = new GameObject("Handle Slide Area"); val9.transform.SetParent(val.transform, false); RectTransform val10 = val9.AddComponent(); val10.anchorMin = Vector2.zero; val10.anchorMax = Vector2.one; val10.offsetMin = Vector2.zero; val10.offsetMax = Vector2.zero; GameObject val11 = new GameObject("Handle"); val11.transform.SetParent(val9.transform, false); RectTransform val12 = val11.AddComponent(); val12.sizeDelta = new Vector2(15f, 15f); val12.anchorMin = new Vector2(0f, 0.5f); val12.anchorMax = new Vector2(0f, 0.5f); val12.pivot = new Vector2(0.5f, 0.5f); Image val13 = val11.AddComponent(); val13.sprite = _whitePixelSprite; ((Graphic)val13).color = ActiveTheme.PlayingItemColor; _themeSliderKnobImages.Add(val13); _seekSlider.handleRect = val12; SeekBarPointerHandler seekBarPointerHandler = val.AddComponent(); seekBarPointerHandler.OnPointerDownAction = delegate { _isSeekingManually = true; }; seekBarPointerHandler.OnPointerUpAction = delegate { AudioSource activeSource = AudioMaster.ActiveSource; if ((Object)(object)activeSource != (Object)null && (Object)(object)activeSource.clip != (Object)null) { float seekTime = _seekSlider.value / 100f * activeSource.clip.length; if (AudioMaster.IsYouTubeMode && ytModuleCompatibility.enabled) { Netcode.Instance.SendYouTubeSeekRequest(seekTime); } else { Netcode.Instance.SendRequestSeek(seekTime); } } _isSeekingManually = false; }; } private void CreateQueueOffPanel(RectTransform parent) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: 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_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_0106: 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_0178: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Expected O, but got Unknown _queueOffPanel = new GameObject("QueueOffPanel"); _queueOffPanel.transform.SetParent((Transform)(object)parent, false); _queueOffPanel.AddComponent(); _queueOffPanel.AddComponent().flexibleHeight = 1f; _queueOffText = CreateText("OffText", _queueOffPanel.transform, "sPEAKer is turned off!", 22, (TextAlignmentOptions)514); ((Graphic)_queueOffText).color = ActiveTheme.PrimaryTextColor; AddLocalization(_queueOffText, "SPEAKER_OFF"); RectTransform component = ((Component)_queueOffText).GetComponent(); component.anchorMin = new Vector2(0f, 0.5f); component.anchorMax = new Vector2(1f, 0.5f); component.pivot = new Vector2(0.5f, 0.5f); component.anchoredPosition = new Vector2(0f, 20f); component.sizeDelta = new Vector2(0f, 40f); _turnOnButton = CreateButton("TurnOnButton", _queueOffPanel.transform, "Turn On"); AddLocalization(((Component)_turnOnButton).GetComponentInChildren(), "TURN ON"); RectTransform component2 = ((Component)_turnOnButton).GetComponent(); component2.anchorMin = new Vector2(0.3f, 0.5f); component2.anchorMax = new Vector2(0.7f, 0.5f); component2.pivot = new Vector2(0.5f, 0.5f); component2.anchoredPosition = new Vector2(0f, -30f); component2.sizeDelta = new Vector2(0f, 45f); ButtonClickedEvent onClick = _turnOnButton.onClick; object obj = <>c.<>9__140_0; if (obj == null) { UnityAction val = delegate { Controller.Static_PrimaryUse(); }; <>c.<>9__140_0 = val; obj = (object)val; } ((UnityEvent)onClick).AddListener((UnityAction)obj); _queueOffPanel.SetActive(false); } private void CreateHelpScreen(Transform parent) { (RectTransform rootPanel, RectTransform contentArea) tuple = CreateScreenBase(parent); RectTransform item = tuple.rootPanel; RectTransform item2 = tuple.contentArea; _helpPanel = item; ((Component)_helpPanel).gameObject.SetActive(false); _helpTextComponent = CreateText("HelpText", (Transform)(object)item2, "", 20, (TextAlignmentOptions)257); ((TMP_Text)_helpTextComponent).textWrappingMode = (TextWrappingModes)1; ((TMP_Text)_helpTextComponent).richText = true; } private void CreateSettingsScreen(Transform parent) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Expected O, but got Unknown //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: 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_0235: Unknown result type (might be due to invalid IL or missing references) //IL_024c: Unknown result type (might be due to invalid IL or missing references) //IL_0263: Unknown result type (might be due to invalid IL or missing references) //IL_0270: Unknown result type (might be due to invalid IL or missing references) //IL_0280: Unknown result type (might be due to invalid IL or missing references) //IL_0287: Expected O, but got Unknown //IL_02b1: Unknown result type (might be due to invalid IL or missing references) //IL_02c8: Unknown result type (might be due to invalid IL or missing references) //IL_02df: Unknown result type (might be due to invalid IL or missing references) //IL_0311: Unknown result type (might be due to invalid IL or missing references) //IL_033b: Unknown result type (might be due to invalid IL or missing references) //IL_03a3: Unknown result type (might be due to invalid IL or missing references) //IL_03aa: Expected O, but got Unknown //IL_03ca: Unknown result type (might be due to invalid IL or missing references) //IL_03d7: Unknown result type (might be due to invalid IL or missing references) //IL_03e4: Unknown result type (might be due to invalid IL or missing references) //IL_03f1: Unknown result type (might be due to invalid IL or missing references) //IL_03fe: Unknown result type (might be due to invalid IL or missing references) //IL_041a: Unknown result type (might be due to invalid IL or missing references) //IL_0454: Unknown result type (might be due to invalid IL or missing references) //IL_045b: Expected O, but got Unknown //IL_0485: Unknown result type (might be due to invalid IL or missing references) //IL_049c: Unknown result type (might be due to invalid IL or missing references) //IL_04b3: Unknown result type (might be due to invalid IL or missing references) //IL_04ca: Unknown result type (might be due to invalid IL or missing references) //IL_0523: Unknown result type (might be due to invalid IL or missing references) //IL_052a: Expected O, but got Unknown //IL_0554: Unknown result type (might be due to invalid IL or missing references) //IL_057f: Unknown result type (might be due to invalid IL or missing references) //IL_0586: Expected O, but got Unknown //IL_05a6: Unknown result type (might be due to invalid IL or missing references) //IL_05b3: Unknown result type (might be due to invalid IL or missing references) //IL_05c0: Unknown result type (might be due to invalid IL or missing references) //IL_05ea: Unknown result type (might be due to invalid IL or missing references) //IL_0608: Unknown result type (might be due to invalid IL or missing references) //IL_060f: Expected O, but got Unknown //IL_0639: Unknown result type (might be due to invalid IL or missing references) //IL_0650: Unknown result type (might be due to invalid IL or missing references) //IL_0667: Unknown result type (might be due to invalid IL or missing references) //IL_067e: Unknown result type (might be due to invalid IL or missing references) //IL_0695: Unknown result type (might be due to invalid IL or missing references) //IL_06bf: Unknown result type (might be due to invalid IL or missing references) //IL_06f5: Unknown result type (might be due to invalid IL or missing references) //IL_0702: Unknown result type (might be due to invalid IL or missing references) //IL_0719: Unknown result type (might be due to invalid IL or missing references) //IL_0726: Unknown result type (might be due to invalid IL or missing references) //IL_07bf: Unknown result type (might be due to invalid IL or missing references) //IL_07c9: Expected O, but got Unknown //IL_0808: Unknown result type (might be due to invalid IL or missing references) //IL_0812: Expected O, but got Unknown (RectTransform rootPanel, RectTransform contentArea) tuple = CreateScreenBase(parent); RectTransform item = tuple.rootPanel; RectTransform item2 = tuple.contentArea; _settingsPanel = item; ((Component)_settingsPanel).gameObject.SetActive(false); GameObject val = new GameObject("ThemeRow"); val.transform.SetParent((Transform)(object)item2, false); LayoutElement val2 = val.AddComponent(); val2.preferredHeight = 40f; val2.flexibleHeight = 0f; HorizontalLayoutGroup val3 = val.AddComponent(); ((HorizontalOrVerticalLayoutGroup)val3).spacing = 15f; ((HorizontalOrVerticalLayoutGroup)val3).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)val3).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)val3).childForceExpandHeight = true; ((HorizontalOrVerticalLayoutGroup)val3).childForceExpandWidth = false; TextMeshProUGUI val4 = CreateText("ThemeLabel", val.transform, "Theme", 20, (TextAlignmentOptions)4097); ((Component)val4).gameObject.AddComponent().preferredWidth = 100f; AddLocalization(val4, "THEME"); GameObject val5 = new GameObject("ThemeDropdown"); val5.transform.SetParent(val.transform, false); val5.AddComponent().flexibleWidth = 1f; Image val6 = val5.AddComponent(); val6.sprite = _whitePixelSprite; ((Graphic)val6).color = ActiveTheme.ButtonDefaultColor; _themeButtonImages.Add(val6); TMP_Dropdown dropdown = val5.AddComponent(); TextMeshProUGUI val7 = CreateText("Caption", val5.transform, "Select...", 18, (TextAlignmentOptions)4097); RectTransform component = ((Component)val7).GetComponent(); component.anchorMin = Vector2.zero; component.anchorMax = Vector2.one; component.offsetMin = new Vector2(10f, 0f); component.offsetMax = new Vector2(-30f, 0f); dropdown.captionText = (TMP_Text)(object)val7; TextMeshProUGUI val8 = CreateText("Arrow", val5.transform, "▼", 14, (TextAlignmentOptions)514); RectTransform component2 = ((Component)val8).GetComponent(); component2.anchorMin = new Vector2(1f, 0f); component2.anchorMax = new Vector2(1f, 1f); component2.sizeDelta = new Vector2(30f, 0f); component2.pivot = new Vector2(1f, 0.5f); component2.anchoredPosition = Vector2.zero; GameObject val9 = new GameObject("Template"); val9.transform.SetParent(val5.transform, false); RectTransform val10 = val9.AddComponent(); val10.anchorMin = new Vector2(0f, 0f); val10.anchorMax = new Vector2(1f, 0f); val10.pivot = new Vector2(0.5f, 1f); float num = Mathf.Min((float)_themeRegistry.Count * 35f, 450f); val10.sizeDelta = new Vector2(0f, num); Image val11 = val9.AddComponent(); val11.sprite = _whitePixelSprite; ((Graphic)val11).color = ActiveTheme.PanelBackgroundColor; _themePanelImages.Add(val11); ScrollRect val12 = val9.AddComponent(); val12.horizontal = false; val12.movementType = (MovementType)2; Canvas val13 = val9.AddComponent(); val13.overrideSorting = true; val13.sortingOrder = 30000; val9.AddComponent(); val9.AddComponent(); GameObject val14 = new GameObject("Viewport"); val14.transform.SetParent(val9.transform, false); RectTransform val15 = val14.AddComponent(); val15.anchorMin = Vector2.zero; val15.anchorMax = Vector2.one; val15.sizeDelta = Vector2.zero; val15.offsetMin = Vector2.zero; val15.offsetMax = Vector2.zero; Image val16 = val14.AddComponent(); ((Graphic)val16).color = ActiveTheme.ViewportBackgroundColor; _themeViewportImages.Add(val16); Mask val17 = val14.AddComponent(); val17.showMaskGraphic = false; val12.viewport = val15; GameObject val18 = new GameObject("Content"); val18.transform.SetParent(val14.transform, false); RectTransform val19 = val18.AddComponent(); val19.anchorMin = new Vector2(0f, 1f); val19.anchorMax = new Vector2(1f, 1f); val19.pivot = new Vector2(0.5f, 1f); val19.sizeDelta = new Vector2(0f, 0f); VerticalLayoutGroup val20 = val18.AddComponent(); ((HorizontalOrVerticalLayoutGroup)val20).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)val20).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)val20).childForceExpandWidth = true; ((HorizontalOrVerticalLayoutGroup)val20).childForceExpandHeight = false; ContentSizeFitter val21 = val18.AddComponent(); val21.verticalFit = (FitMode)2; val12.content = val19; GameObject val22 = new GameObject("Item"); val22.transform.SetParent(val18.transform, false); RectTransform val23 = val22.AddComponent(); val23.sizeDelta = new Vector2(0f, 35f); val22.AddComponent().preferredHeight = 35f; Toggle val24 = val22.AddComponent(); GameObject val25 = new GameObject("Item Background"); val25.transform.SetParent(val22.transform, false); RectTransform val26 = val25.AddComponent(); val26.anchorMin = Vector2.zero; val26.anchorMax = Vector2.one; val26.sizeDelta = Vector2.zero; Image val27 = val25.AddComponent(); val27.sprite = _whitePixelSprite; ((Graphic)val27).color = ActiveTheme.ButtonDefaultColor; _themeButtonImages.Add(val27); GameObject val28 = new GameObject("Item Checkmark"); val28.transform.SetParent(val22.transform, false); RectTransform val29 = val28.AddComponent(); val29.anchorMin = new Vector2(0f, 0.5f); val29.anchorMax = new Vector2(0f, 0.5f); val29.pivot = new Vector2(0f, 0.5f); val29.sizeDelta = new Vector2(15f, 15f); val29.anchoredPosition = new Vector2(10f, 0f); Image val30 = val28.AddComponent(); val30.sprite = _whitePixelSprite; ((Graphic)val30).color = ActiveTheme.TabActiveColor; TextMeshProUGUI val31 = CreateText("Item Label", val22.transform, "Option", 18, (TextAlignmentOptions)4097); RectTransform component3 = ((Component)val31).GetComponent(); component3.anchorMin = Vector2.zero; component3.anchorMax = Vector2.one; component3.offsetMin = new Vector2(35f, 0f); component3.offsetMax = Vector2.zero; ((Selectable)val24).targetGraphic = (Graphic)(object)val27; val24.graphic = (Graphic)(object)val30; dropdown.template = val10; dropdown.itemText = (TMP_Text)(object)val31; ((Selectable)dropdown).targetGraphic = (Graphic)(object)val6; val9.SetActive(false); dropdown.ClearOptions(); List themeIds = new List(); foreach (KeyValuePair item3 in _themeRegistry) { dropdown.options.Add(new OptionData(item3.Value.DisplayName)); themeIds.Add(item3.Key); } dropdown.options.Add(new OptionData(" ")); int num2 = themeIds.IndexOf(ActiveTheme.ThemeId); if (num2 >= 0) { dropdown.SetValueWithoutNotify(num2); } dropdown.RefreshShownValue(); ((UnityEvent)(object)dropdown.onValueChanged).AddListener((UnityAction)delegate(int index) { if (index >= themeIds.Count) { dropdown.SetValueWithoutNotify(themeIds.Count - 1); } else if (index >= 0 && index < themeIds.Count) { string text = themeIds[index]; LoadTheme(text); ApplyTheme(); Plugin.QueueUITheme = text; Plugin.Instance.queueUIThemeConfig.Value = text; } }); CreateSettingsToggle(item2, "SHOW_TRACK_DURATION", Plugin.ShowTrackDuration, delegate(bool isOn) { Plugin.ShowTrackDuration = isOn; Plugin.Instance.showTrackDurationConfig.Value = isOn; if (isOn) { AudioSource activeSource = AudioMaster.ActiveSource; if ((Object)(object)activeSource != (Object)null && (Object)(object)activeSource.clip != (Object)null) { sPEAKerUI.Instance.ShowDuration(activeSource.clip.length); } } else { sPEAKerUI.Instance.HideDuration(); } }); CreateSettingsToggle(item2, "DISPLAY_AUTHOR_INFO", Plugin.DisplayAuthorInfo, delegate(bool isOn) { Plugin.DisplayAuthorInfo = isOn; Plugin.Instance.displayAuthorInfoConfig.Value = isOn; if (isOn) { string currentMixtapeAuthor = AudioMaster.Instance.GetCurrentMixtapeAuthor(); if (!string.IsNullOrEmpty(currentMixtapeAuthor)) { sPEAKerUI.Instance.ShowAuthorInfo(currentMixtapeAuthor, AudioMaster.IsYouTubeMode); } } else { sPEAKerUI.Instance.HideAuthorInfo(); } }); CreateSettingsToggle(item2, "USE_SPATIAL_AUDIO", Plugin.UseSpatialAudio, delegate(bool isOn) { Plugin.UseSpatialAudio = isOn; Plugin.Instance.useSpatialAudioConfig.Value = isOn; AudioMaster.UpdateSpatialAudioSetting(isOn); }); if (ytModuleCompatibility.enabled) { CreateSettingsToggle(item2, "AUTO_PLAY", Plugin.AutoPlay, delegate(bool isOn) { Plugin.AutoPlay = isOn; Plugin.Instance.autoPlayConfig.Value = isOn; }); CreateSettingsSlider(item2, "MAX_CACHED_SONGS", Plugin.MaxCachedSongs, 10, 100, delegate(int value) { Plugin.MaxCachedSongs = value; Plugin.Instance.maxCachedSongsConfig.Value = value; ytModuleCompatibility.CleanupCache(); RefreshDiskSpaceDisplay(); }, "TOOLTIP_MAX_CACHED_SONGS"); CreateSettingsSlider(item2, "PREDOWNLOAD_COUNT", Plugin.PredownloadCount, 1, 10, delegate(int value) { int predownloadCount = Plugin.PredownloadCount; Plugin.PredownloadCount = value; Plugin.Instance.predownloadCountConfig.Value = value; if (value > predownloadCount) { ytModuleCompatibility.TriggerPreload(); } }, "TOOLTIP_PREDOWNLOAD_COUNT"); CreateDiskSpaceRow(item2); } CreateCopyLogsRow(_settingsPanel); } private void CreateSettingsToggle(RectTransform parent, string locKey, bool initialValue, Action onValueChanged) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Expected O, but got Unknown //IL_0147: 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_0169: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Expected O, but got Unknown //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(locKey + "Row"); val.transform.SetParent((Transform)(object)parent, false); LayoutElement val2 = val.AddComponent(); val2.preferredHeight = 40f; val2.flexibleHeight = 0f; HorizontalLayoutGroup val3 = val.AddComponent(); ((HorizontalOrVerticalLayoutGroup)val3).spacing = 15f; ((HorizontalOrVerticalLayoutGroup)val3).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)val3).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)val3).childForceExpandHeight = true; ((HorizontalOrVerticalLayoutGroup)val3).childForceExpandWidth = false; TextMeshProUGUI val4 = CreateText(locKey + "Label", val.transform, locKey, 20, (TextAlignmentOptions)4097); ((Component)val4).gameObject.AddComponent().flexibleWidth = 1f; AddLocalization(val4, locKey); GameObject val5 = new GameObject("Toggle"); val5.transform.SetParent(val.transform, false); LayoutElement val6 = val5.AddComponent(); val6.preferredWidth = 50f; val6.preferredHeight = 30f; Image toggleBgImage = val5.AddComponent(); toggleBgImage.sprite = _whitePixelSprite; ((Graphic)toggleBgImage).color = (initialValue ? ActiveTheme.TabActiveColor : ActiveTheme.ButtonDefaultColor); _themeButtonImages.Add(toggleBgImage); GameObject val7 = new GameObject("Checkmark"); val7.transform.SetParent(val5.transform, false); RectTransform val8 = val7.AddComponent(); val8.anchorMin = new Vector2(0.5f, 0.5f); val8.anchorMax = new Vector2(0.5f, 0.5f); val8.sizeDelta = new Vector2(20f, 20f); TextMeshProUGUI val9 = val7.AddComponent(); ((TMP_Text)val9).text = "✓"; ((TMP_Text)val9).fontSize = 18f; ((TMP_Text)val9).alignment = (TextAlignmentOptions)514; ((Graphic)val9).color = ActiveTheme.PrimaryTextColor; ((TMP_Text)val9).font = Plugin.PeakFont; _themePrimaryTexts.Add(val9); Toggle val10 = val5.AddComponent(); val10.isOn = initialValue; ((Selectable)val10).targetGraphic = (Graphic)(object)toggleBgImage; val10.graphic = (Graphic)(object)val9; ((UnityEvent)(object)val10.onValueChanged).AddListener((UnityAction)delegate(bool isOn) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) ((Graphic)toggleBgImage).color = (isOn ? ActiveTheme.TabActiveColor : ActiveTheme.ButtonDefaultColor); onValueChanged?.Invoke(isOn); }); } private void CreateSettingsSlider(RectTransform parent, string locKey, int initialValue, int minValue, int maxValue, Action onValueChanged, string tooltipLocKey = null) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Expected O, but got Unknown //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Expected O, but got Unknown //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: Unknown result type (might be due to invalid IL or missing references) //IL_0203: Expected O, but got Unknown //IL_022d: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Unknown result type (might be due to invalid IL or missing references) //IL_025b: Unknown result type (might be due to invalid IL or missing references) //IL_0272: Unknown result type (might be due to invalid IL or missing references) //IL_0282: Unknown result type (might be due to invalid IL or missing references) //IL_0289: Expected O, but got Unknown //IL_02a9: Unknown result type (might be due to invalid IL or missing references) //IL_02b6: Unknown result type (might be due to invalid IL or missing references) //IL_02c3: Unknown result type (might be due to invalid IL or missing references) //IL_02ed: Unknown result type (might be due to invalid IL or missing references) //IL_030b: Unknown result type (might be due to invalid IL or missing references) //IL_0312: Expected O, but got Unknown //IL_0332: Unknown result type (might be due to invalid IL or missing references) //IL_033f: Unknown result type (might be due to invalid IL or missing references) //IL_0356: Unknown result type (might be due to invalid IL or missing references) //IL_036d: Unknown result type (might be due to invalid IL or missing references) //IL_037d: Unknown result type (might be due to invalid IL or missing references) //IL_0384: Expected O, but got Unknown //IL_03ae: Unknown result type (might be due to invalid IL or missing references) //IL_03d8: Unknown result type (might be due to invalid IL or missing references) //IL_0460: Unknown result type (might be due to invalid IL or missing references) //IL_0467: Expected O, but got Unknown //IL_0487: Unknown result type (might be due to invalid IL or missing references) //IL_0494: Unknown result type (might be due to invalid IL or missing references) //IL_04a1: Unknown result type (might be due to invalid IL or missing references) //IL_04ae: Unknown result type (might be due to invalid IL or missing references) //IL_04e7: Unknown result type (might be due to invalid IL or missing references) //IL_052c: Unknown result type (might be due to invalid IL or missing references) //IL_0533: Expected O, but got Unknown //IL_0573: Unknown result type (might be due to invalid IL or missing references) //IL_0596: Unknown result type (might be due to invalid IL or missing references) //IL_05a3: Unknown result type (might be due to invalid IL or missing references) //IL_05b0: Unknown result type (might be due to invalid IL or missing references) //IL_05bd: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(locKey + "Row"); val.transform.SetParent((Transform)(object)parent, false); LayoutElement val2 = val.AddComponent(); val2.preferredHeight = 40f; val2.flexibleHeight = 0f; HorizontalLayoutGroup val3 = val.AddComponent(); ((HorizontalOrVerticalLayoutGroup)val3).spacing = 15f; ((HorizontalOrVerticalLayoutGroup)val3).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)val3).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)val3).childForceExpandHeight = true; ((HorizontalOrVerticalLayoutGroup)val3).childForceExpandWidth = false; TextMeshProUGUI val4 = CreateText(locKey + "Label", val.transform, locKey, 20, (TextAlignmentOptions)4097); ((Component)val4).gameObject.AddComponent().flexibleWidth = 1f; AddLocalization(val4, locKey); if (!string.IsNullOrEmpty(tooltipLocKey)) { Button val5 = CreateInfoButton(val.transform, tooltipLocKey); ((Component)val5).gameObject.AddComponent().preferredWidth = 24f; } GameObject val6 = new GameObject("SliderContainer"); val6.transform.SetParent(val.transform, false); LayoutElement val7 = val6.AddComponent(); val7.preferredWidth = 150f; HorizontalLayoutGroup val8 = val6.AddComponent(); ((HorizontalOrVerticalLayoutGroup)val8).spacing = 10f; ((HorizontalOrVerticalLayoutGroup)val8).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)val8).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)val8).childForceExpandHeight = true; ((HorizontalOrVerticalLayoutGroup)val8).childForceExpandWidth = false; GameObject val9 = new GameObject("Slider"); val9.transform.SetParent(val6.transform, false); LayoutElement val10 = val9.AddComponent(); val10.preferredWidth = 100f; val10.preferredHeight = 20f; Image val11 = val9.AddComponent(); val11.sprite = _whitePixelSprite; ((Graphic)val11).color = ActiveTheme.ButtonDefaultColor; _themeButtonImages.Add(val11); GameObject val12 = new GameObject("Fill Area"); val12.transform.SetParent(val9.transform, false); RectTransform val13 = val12.AddComponent(); val13.anchorMin = new Vector2(0f, 0.25f); val13.anchorMax = new Vector2(1f, 0.75f); val13.offsetMin = new Vector2(5f, 0f); val13.offsetMax = new Vector2(-5f, 0f); GameObject val14 = new GameObject("Fill"); val14.transform.SetParent(val12.transform, false); RectTransform val15 = val14.AddComponent(); val15.anchorMin = Vector2.zero; val15.anchorMax = Vector2.one; val15.sizeDelta = Vector2.zero; Image val16 = val14.AddComponent(); val16.sprite = _whitePixelSprite; ((Graphic)val16).color = ActiveTheme.TabActiveColor; _themeSliderFillImages.Add(val16); GameObject val17 = new GameObject("Handle Slide Area"); val17.transform.SetParent(val9.transform, false); RectTransform val18 = val17.AddComponent(); val18.anchorMin = Vector2.zero; val18.anchorMax = Vector2.one; val18.offsetMin = new Vector2(10f, 0f); val18.offsetMax = new Vector2(-10f, 0f); GameObject val19 = new GameObject("Handle"); val19.transform.SetParent(val17.transform, false); RectTransform val20 = val19.AddComponent(); val20.sizeDelta = new Vector2(20f, 0f); Image val21 = val19.AddComponent(); val21.sprite = _whitePixelSprite; ((Graphic)val21).color = ActiveTheme.PlayingItemColor; _themeSliderKnobImages.Add(val21); Slider slider = val9.AddComponent(); slider.fillRect = val15; slider.handleRect = val20; slider.minValue = minValue; slider.maxValue = maxValue; slider.wholeNumbers = true; slider.value = initialValue; GameObject val22 = new GameObject("ValueInput"); val22.transform.SetParent(val6.transform, false); RectTransform val23 = val22.AddComponent(); val23.anchorMin = Vector2.zero; val23.anchorMax = Vector2.one; val23.offsetMin = Vector2.zero; val23.offsetMax = Vector2.zero; LayoutElement val24 = val22.AddComponent(); val24.preferredWidth = 30f; val24.flexibleWidth = 0f; Image val25 = val22.AddComponent(); ((Graphic)val25).color = Color.clear; TMP_InputField valueInput = val22.AddComponent(); valueInput.contentType = (ContentType)2; valueInput.lineType = (LineType)0; ((Selectable)valueInput).targetGraphic = (Graphic)(object)val25; GameObject val26 = new GameObject("Text"); val26.transform.SetParent(val22.transform, false); TextMeshProUGUI val27 = val26.AddComponent(); ((TMP_Text)val27).fontSize = 18f; ((TMP_Text)val27).alignment = (TextAlignmentOptions)4100; ((Graphic)val27).color = ActiveTheme.PrimaryTextColor; ((TMP_Text)val27).font = Plugin.PeakFont; RectTransform component = val26.GetComponent(); component.anchorMin = Vector2.zero; component.anchorMax = Vector2.one; component.offsetMin = Vector2.zero; component.offsetMax = Vector2.zero; _themePrimaryTexts.Add(val27); valueInput.textViewport = val23; valueInput.textComponent = (TMP_Text)(object)val27; valueInput.SetTextWithoutNotify(initialValue.ToString()); bool isUpdatingValue = false; ((UnityEvent)(object)slider.onValueChanged).AddListener((UnityAction)delegate(float value) { ApplySliderValue(Mathf.RoundToInt(value), fromSlider: true); }); ((UnityEvent)(object)valueInput.onEndEdit).AddListener((UnityAction)delegate(string text) { CommitInput(text); }); ((UnityEvent)(object)valueInput.onSubmit).AddListener((UnityAction)delegate(string text) { CommitInput(text); valueInput.DeactivateInputField(false); if ((Object)(object)EventSystem.current != (Object)null && (Object)(object)EventSystem.current.currentSelectedGameObject == (Object)(object)((Component)valueInput).gameObject) { EventSystem.current.SetSelectedGameObject((GameObject)null); } }); void ApplySliderValue(int intValue, bool fromSlider) { intValue = Mathf.Clamp(intValue, minValue, maxValue); if (!isUpdatingValue) { isUpdatingValue = true; if (!fromSlider) { slider.SetValueWithoutNotify((float)intValue); } valueInput.SetTextWithoutNotify(intValue.ToString()); onValueChanged?.Invoke(intValue); isUpdatingValue = false; } } void CommitInput(string text) { if (!int.TryParse(text, out var result)) { valueInput.SetTextWithoutNotify(Mathf.RoundToInt(slider.value).ToString()); } else { ApplySliderValue(result, fromSlider: false); } } } private Button CreateInfoButton(Transform parent, string tooltipLocKey) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Expected O, but got Unknown GameObject val = new GameObject("InfoButton"); val.transform.SetParent(parent, false); Image val2 = val.AddComponent(); val2.sprite = _whitePixelSprite; ((Graphic)val2).color = ActiveTheme.ButtonDefaultColor; _themeButtonImages.Add(val2); Button val3 = val.AddComponent