Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of CartFM v1.2.0
CartRadio.dll
Decompiled 2 days agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ExitGames.Client.Photon; using HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; using Photon.Realtime; using Photon.Voice; using Photon.Voice.Unity; using Strobotnik.Klattersynth; using UnityEngine; using UnityEngine.Networking; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("CartRadio")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("CartRadio")] [assembly: AssemblyTitle("CartRadio")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace CartRadio { internal class RadioPusher : IAudioPusher<float>, IAudioDesc, IDisposable { public const int Rate = 24000; private Action<float[]> callback; private ObjectFactory<float[], int> bufferFactory; public int SamplingRate => 24000; public int Channels => 1; public string Error => null; public void SetCallback(Action<float[]> callback, ObjectFactory<float[], int> bufferFactory) { this.callback = callback; this.bufferFactory = bufferFactory; } public void Push(float[] samples, int count) { if (callback != null && count > 0) { float[] array = bufferFactory.New(count); Array.Copy(samples, array, count); callback(array); } } public void Dispose() { callback = null; } } internal class RadioMixer { private class Track { public float[] data; public int channels; public int frequency; public double position; public float gain; } private readonly Dictionary<AudioClip, float[]> cache = new Dictionary<AudioClip, float[]>(); private readonly List<Track> shots = new List<Track>(); private Track music; private float[] mix = new float[24000]; public float Volume = 0.35f; public const float MusicGain = 0.55f; private Track MakeTrack(AudioClip clip, float gain) { if (!cache.TryGetValue(clip, out var value)) { value = new float[clip.samples * clip.channels]; clip.GetData(value, 0); cache[clip] = value; } return new Track { data = value, channels = clip.channels, frequency = clip.frequency, gain = gain }; } public void PlayMusic(AudioClip clip, float offset) { music = MakeTrack(clip, 0.55f); music.position = offset * (float)clip.frequency; } public void StopMusic() { music = null; } public void PlayShot(AudioClip clip, float gain) { shots.Add(MakeTrack(clip, gain)); } public void Clear() { music = null; shots.Clear(); } public float[] Render(int count) { if (mix.Length < count) { mix = new float[count]; } Array.Clear(mix, 0, count); if (music != null && !AddTrack(music, count)) { music = null; } for (int num = shots.Count - 1; num >= 0; num--) { if (!AddTrack(shots[num], count)) { shots.RemoveAt(num); } } for (int i = 0; i < count; i++) { mix[i] = Mathf.Clamp(mix[i] * Volume, -1f, 1f); } return mix; } private bool AddTrack(Track track, int count) { int num = track.data.Length / track.channels; double num2 = (double)track.frequency / 24000.0; for (int i = 0; i < count; i++) { int num3 = (int)track.position; if (num3 >= num - 1) { return false; } float num4 = (float)(track.position - (double)num3); float num5 = track.data[num3 * track.channels]; float num6 = track.data[(num3 + 1) * track.channels]; mix[i] += (num5 + (num6 - num5) * num4) * track.gain; track.position += num2; } return true; } } [BepInPlugin("CartFM.Radio", "Cart FM", "1.2.0")] public class Plugin : BaseUnityPlugin { private class Radio { public AudioSource music; public AudioSource djAudio; public Speech dj; public int djFrequency; } private const byte SyncEvent = 177; private const float HostTimeout = 15f; private const float HeartbeatInterval = 4f; internal static ManualLogSource Log; private static ConfigEntry<bool> radioOn; private static ConfigEntry<float> volume; private static ConfigEntry<float> hearingDistance; private static ConfigEntry<KeyCode> toggleKey; private static ConfigEntry<bool> djOn; private static ConfigEntry<int> songsBetweenJokes; private static ConfigEntry<int> djPitch; private static ConfigEntry<float> botPitch; private static ConfigEntry<bool> episodesInOrder; private static ConfigEntry<KeyCode> onAirKey; private static ConfigEntry<float> onAirVolume; private static ConfigEntry<KeyCode> onAirLouderKey; private static ConfigEntry<KeyCode> onAirQuieterKey; private static ConfigEntry<KeyCode> echoTestKey; private static Plugin instance; private readonly RadioMixer mixer = new RadioMixer(); private RadioPusher pusher; private bool onAir; private Recorder onAirRecorder; private double lastDspTime; private float onAirMessageUntil; private float volumeMessageUntil; private static ConfigEntry<KeyCode> volumeDownKey; private static ConfigEntry<KeyCode> volumeUpKey; private static ConfigEntry<float> djVolume; private static ConfigEntry<float> sfxVolume; private static readonly FieldRef<PlayerVoiceChat, Recorder> VoiceRecorder = AccessTools.FieldRefAccess<PlayerVoiceChat, Recorder>("recorder"); private readonly List<AudioClip> playlist = new List<AudioClip>(); private readonly List<List<string>> bits = new List<List<string>>(); private readonly List<List<string>> bitDeck = new List<List<string>>(); private readonly Queue<string> bitLines = new Queue<string>(); private float lineWaitUntil; private bool lineSpeaking; private const float PauseAfterLine = 0.7f; private readonly Dictionary<string, AudioClip> sfx = new Dictionary<string, AudioClip>(); private readonly Dictionary<string, AudioClip> voiceLines = new Dictionary<string, AudioClip>(); private readonly SortedDictionary<int, List<string>> episodes = new SortedDictionary<int, List<string>>(); private bool stationOpened; private int songsUntilBit; private readonly Dictionary<PhysGrabCart, Radio> radios = new Dictionary<PhysGrabCart, Radio>(); private int song; private int songsSinceJoke; private float songStart; private AudioClip currentClip; private float lastHostMessage = -999f; private float heartbeatTimer; private bool djTalking; private float djStart; private float scanTimer; private static readonly string[] SpeakerNames = new string[3] { "DJ", "BOT", "PANIC" }; private GUIStyle onAirStyle; private bool FollowingHost { get { if (PhotonNetwork.InRoom && !PhotonNetwork.IsMasterClient) { return Time.time - lastHostMessage < 15f; } return false; } } private static bool Leading { get { if (PhotonNetwork.InRoom) { return PhotonNetwork.IsMasterClient; } return false; } } private void Awake() { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Expected O, but got Unknown //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Expected O, but got Unknown //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Expected O, but got Unknown //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Expected O, but got Unknown //IL_021a: Unknown result type (might be due to invalid IL or missing references) //IL_0224: Expected O, but got Unknown //IL_0257: Unknown result type (might be due to invalid IL or missing references) //IL_0261: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; radioOn = ((BaseUnityPlugin)this).Config.Bind<bool>("Radio", "On", true, "Carts play music."); volume = ((BaseUnityPlugin)this).Config.Bind<float>("Radio", "Volume", 0.3f, new ConfigDescription("Radio volume (also scaled by the game's Music volume).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); hearingDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Radio", "HearingDistance", 12f, new ConfigDescription("How far away (m) you can still hear a cart's radio.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(3f, 40f), Array.Empty<object>())); toggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Radio", "ToggleKey", (KeyCode)121, "Turns the radio on or off."); volumeDownKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Radio", "VolumeDownKey", (KeyCode)45, "Turns your radio down (music, DJ and sound effects together)."); volumeUpKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Radio", "VolumeUpKey", (KeyCode)61, "Turns your radio up (music, DJ and sound effects together)."); djVolume = ((BaseUnityPlugin)this).Config.Bind<float>("Radio", "DJVolume", 1f, new ConfigDescription("DJ voice loudness compared to the music.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>())); sfxVolume = ((BaseUnityPlugin)this).Config.Bind<float>("Radio", "EffectsVolume", 0.5f, new ConfigDescription("Sound effect loudness (air horns, crashes...) compared to the music. Lower it if they make you jump.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>())); djOn = ((BaseUnityPlugin)this).Config.Bind<bool>("DJ", "On", true, "A robot DJ reads a joke from jokes.txt between songs."); songsBetweenJokes = ((BaseUnityPlugin)this).Config.Bind<int>("DJ", "SongsBetweenJokes", 1, new ConfigDescription("How many songs play before each joke.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20), Array.Empty<object>())); episodesInOrder = ((BaseUnityPlugin)this).Config.Bind<bool>("DJ", "EpisodesInOrder", false, "Play the W-A-S-T-E FM episodes first, in order (1, 2, 3...). Off = everything is shuffled."); djPitch = ((BaseUnityPlugin)this).Config.Bind<int>("DJ", "Pitch", 0, new ConfigDescription("DJ voice pitch in Hz (0 = same as the players' robot voice).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 400), Array.Empty<object>())); botPitch = ((BaseUnityPlugin)this).Config.Bind<float>("DJ", "BotVoicePitch", 0.55f, new ConfigDescription("Pitch of the deep repo-bot voice (\"BOT:\" lines), relative to the DJ.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.2f, 2f), Array.Empty<object>())); instance = this; string directoryName = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location); Directory.CreateDirectory(Path.Combine(directoryName, "music")); Directory.CreateDirectory(Path.Combine(directoryName, "sfx")); LoadJokes(Path.Combine(directoryName, "jokes.txt")); ((MonoBehaviour)this).StartCoroutine(LoadSongs(Path.Combine(directoryName, "music"))); } private void Start() { PhotonNetwork.NetworkingClient.EventReceived += OnEvent; } private void OnDestroy() { PhotonNetwork.NetworkingClient.EventReceived -= OnEvent; } private static void Send(string kind, string text = "", float number = 0f) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002a: 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_0039: Expected O, but got Unknown if (Leading) { PhotonNetwork.RaiseEvent((byte)177, (object)new object[3] { kind, text, number }, new RaiseEventOptions { Receivers = (ReceiverGroup)0 }, SendOptions.SendReliable); } } private void OnEvent(EventData e) { if (e.Code != 177 || PhotonNetwork.MasterClient == null || e.Sender != PhotonNetwork.MasterClient.ActorNumber || !(e.CustomData is object[] array) || array.Length < 3) { return; } if (!FollowingHost) { bitLines.Clear(); djTalking = false; foreach (Radio value in radios.Values) { Speech dj = value.dj; if (dj != null) { dj.stop(true); } } } lastHostMessage = Time.time; string text = (array[1] as string) ?? ""; float num = ((array[2] is float num2) ? num2 : 0f); switch (array[0] as string) { case "song": { AudioClip val = ((IEnumerable<AudioClip>)playlist).FirstOrDefault((Func<AudioClip, bool>)((AudioClip c) => ((Object)c).name == text)); if ((Object)(object)val == (Object)null) { StopMusicLocal(); } else if ((Object)(object)val != (Object)(object)currentClip || Mathf.Abs(Time.time - songStart - num) > 1.5f) { PlaySongLocal(val, num); } break; } case "stop": StopMusicLocal(); break; case "say": SayLocal(text, Mathf.RoundToInt(num)); break; case "sfx": SfxLocal(text); break; } } private void LoadJokes(string file) { if (!File.Exists(file)) { Log.LogWarning((object)"No jokes.txt found; the DJ has nothing to say."); return; } List<string> list = new List<string>(); foreach (string item in File.ReadAllLines(file).Append("")) { string text = item.Trim(); if (text.StartsWith("#")) { continue; } if (text.Length > 0) { list.Add(text); if (IsTag(text, "episode", out var argument)) { episodes[(int)Number(argument, 1f, 0f, 999f)] = list; } } else if (list.Count > 0) { bits.Add(list); list = new List<string>(); } } if (!episodesInOrder.Value) { episodes.Clear(); } Log.LogInfo((object)$"Cart Radio DJ loaded {bits.Count} joke(s) and bit(s)."); } private static bool IsTag(string line, string tag, out string argument) { argument = ""; if (!line.StartsWith("(" + tag, StringComparison.OrdinalIgnoreCase) || !line.EndsWith(")")) { return false; } string text = line.Substring(tag.Length + 1, line.Length - tag.Length - 2); if (text.Length > 0 && text[0] != ' ') { return false; } argument = text.Trim(); return true; } private static float Number(string text, float fallback, float min, float max) { if (float.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return Mathf.Clamp(result, min, max); } return fallback; } private static IEnumerator LoadClips(string folder, Action<AudioClip> loaded) { string[] array = (from f in Directory.GetFiles(folder) where (int)AudioTypeOf(f) > 0 orderby f select f).ToArray(); string[] array2 = array; foreach (string file in array2) { UnityWebRequest request = UnityWebRequestMultimedia.GetAudioClip(new Uri(file).AbsoluteUri, AudioTypeOf(file)); try { yield return request.SendWebRequest(); if ((int)request.result != 1) { Log.LogWarning((object)("Couldn't load " + Path.GetFileName(file) + ": " + request.error)); continue; } AudioClip content = DownloadHandlerAudioClip.GetContent(request); ((Object)content).name = Path.GetFileNameWithoutExtension(file); loaded(content); } finally { ((IDisposable)request)?.Dispose(); } } } private IEnumerator LoadSongs(string folder) { yield return LoadClips(Path.Combine(Path.GetDirectoryName(folder), "sfx"), delegate(AudioClip c) { sfx[((Object)c).name.ToLowerInvariant()] = c; }); if (sfx.Count > 0) { Log.LogInfo((object)$"Cart Radio loaded {sfx.Count} sound effect(s)."); } string text = Path.Combine(Path.GetDirectoryName(folder), "voice"); if (Directory.Exists(text)) { yield return LoadClips(text, delegate(AudioClip c) { voiceLines[((Object)c).name.ToLowerInvariant()] = c; }); Log.LogInfo((object)$"Cart Radio loaded {voiceLines.Count} recorded DJ line(s)."); } yield return LoadClips(folder, playlist.Add); if (playlist.Count == 0) { Log.LogWarning((object)("No songs found. Put .wav, .ogg or .mp3 files in " + folder)); yield break; } Log.LogInfo((object)string.Format("Cart Radio loaded {0} song(s): {1}. Press {2} to turn it on or off.", playlist.Count, string.Join(", ", playlist.Select((AudioClip c) => ((Object)c).name)), toggleKey.Value)); song = Random.Range(0, playlist.Count); PlaySongLocal(playlist[song], 0f); } private static AudioType AudioTypeOf(string file) { return (AudioType)(Path.GetExtension(file).ToLowerInvariant() switch { ".wav" => 20, ".ogg" => 14, ".mp3" => 13, _ => 0, }); } private void Update() { //IL_0005: 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_0080: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) if (Input.GetKeyDown(toggleKey.Value)) { radioOn.Value = !radioOn.Value; Log.LogInfo((object)("Cart Radio " + (radioOn.Value ? "on" : "off") + ".")); } if (Input.GetKeyDown(volumeUpKey.Value) || Input.GetKeyDown(volumeDownKey.Value)) { float num = (Input.GetKeyDown(volumeUpKey.Value) ? 0.05f : (-0.05f)); volume.Value = Mathf.Clamp(Mathf.Round((volume.Value + num) * 20f) / 20f, 0f, 1f); volumeMessageUntil = Time.time + 2f; } if (playlist.Count == 0) { return; } if ((scanTimer -= Time.deltaTime) <= 0f) { scanTimer = 1f; AddRadios(); } if (!FollowingHost) { AdvanceTimeline(); if (Leading && (heartbeatTimer -= Time.deltaTime) <= 0f) { heartbeatTimer = 4f; if ((Object)(object)currentClip != (Object)null) { Send("song", ((Object)currentClip).name, Time.time - songStart); } else { Send("hb"); } } } bool flag = true; foreach (Radio value2 in radios.Values) { if (!((Object)(object)value2.music == (Object)null)) { value2.music.volume = volume.Value; value2.djAudio.volume = Mathf.Min(1f, volume.Value * 1.6f * djVolume.Value); AudioSource music = value2.music; float maxDistance = (value2.djAudio.maxDistance = hearingDistance.Value); music.maxDistance = maxDistance; AudioSource music2 = value2.music; maxDistance = (value2.djAudio.spatialBlend = (onAir ? 0f : 1f)); music2.spatialBlend = maxDistance; AudioSource music3 = value2.music; bool mute = (value2.djAudio.mute = !radioOn.Value || (onAir && !flag)); music3.mute = mute; flag = false; } } } private void AdvanceTimeline() { if (djTalking) { if (lineSpeaking) { if ((radios.Values.Any((Radio r) => (Object)(object)r.dj != (Object)null && r.dj.isTalking()) || Time.time - djStart < 0.4f) && Time.time - djStart < 30f) { return; } lineSpeaking = false; lineWaitUntil = Time.time + 0.7f; } if (!(Time.time < lineWaitUntil)) { if (bitLines.Count > 0) { SayNextLine(); return; } djTalking = false; StartSong(NextSong()); } } else if ((Object)(object)currentClip == (Object)null) { StartSong(NextSong()); } else if (!(Time.time - songStart < currentClip.length)) { int num = ((songsUntilBit > 0) ? songsUntilBit : songsBetweenJokes.Value); if (djOn.Value && bits.Count > 0 && ++songsSinceJoke >= num && radios.Count > 0) { songsSinceJoke = 0; songsUntilBit = 0; TellJoke(); } else { StartSong(NextSong()); } } } private int NextSong() { if (playlist.Count < 2) { return 0; } int num = Random.Range(0, playlist.Count - 1); if (num < song) { return num; } return num + 1; } private void StartSong(int index) { song = index; PlaySongLocal(playlist[song], 0f); Send("song", ((Object)playlist[song]).name); } private void PlaySongLocal(AudioClip clip, float offset) { currentClip = clip; offset = Mathf.Clamp(offset, 0f, clip.length - 0.1f); songStart = Time.time - offset; mixer.PlayMusic(clip, offset); foreach (Radio value in radios.Values) { if ((Object)(object)value.music != (Object)null) { value.music.clip = clip; value.music.Play(); value.music.time = offset; } } } private void StopMusicLocal() { currentClip = null; mixer.StopMusic(); foreach (Radio value in radios.Values) { AudioSource music = value.music; if (music != null) { music.Stop(); } } } private float SayLocal(string line, int speaker) { //IL_00fd: Unknown result type (might be due to invalid IL or missing references) speaker = Mathf.Clamp(speaker, 0, SpeakerNames.Length - 1); if (voiceLines.TryGetValue(VoiceKey(speaker, line), out var value)) { foreach (Radio value2 in radios.Values) { AudioSource djAudio = value2.djAudio; if (djAudio != null) { djAudio.PlayOneShot(value); } } mixer.PlayShot(value, djVolume.Value); return value.length; } float num = speaker switch { 2 => 1.6f, 1 => botPitch.Value, _ => 1f, }; foreach (Radio value3 in radios.Values) { if ((Object)(object)value3.dj != (Object)null) { int num2 = Mathf.Clamp(Mathf.RoundToInt((float)value3.djFrequency * num), 40, 600); value3.dj.speak(num2, value3.dj.voicingSource, line.ToLowerInvariant(), false, (string)null); } } return 0f; } private float SfxLocal(string name) { if (!sfx.TryGetValue(name.ToLowerInvariant(), out var value)) { Log.LogWarning((object)("Sound effect '" + name + "' not found in the sfx folder.")); return 0f; } foreach (Radio value2 in radios.Values) { AudioSource music = value2.music; if (music != null) { music.PlayOneShot(value, sfxVolume.Value); } } mixer.PlayShot(value, sfxVolume.Value); return value.length; } private void TellJoke() { if (bitDeck.Count == 0) { bitDeck.AddRange(bits.OrderBy((List<string> _) => Random.value)); } List<string> list = bitDeck[0]; if (episodes.Count > 0) { int key = episodes.Keys.First(); list = episodes[key]; episodes.Remove(key); } stationOpened = true; bitDeck.Remove(list); bitLines.Clear(); foreach (string item in list) { bitLines.Enqueue(item); } djTalking = true; lineSpeaking = false; lineWaitUntil = Time.time + 0.5f; StopMusicLocal(); Send("stop"); } private void SayNextLine() { string line = bitLines.Dequeue(); string argument2; if (IsTag(line, "pause", out var argument)) { lineWaitUntil = Time.time + Number(argument, 1.5f, 0.1f, 10f); } else if (IsTag(line, "songs", out argument2)) { songsUntilBit = (int)Number(argument2, 1f, 1f, 20f); } else { if (IsTag(line, "episode", out var _)) { return; } if (IsTag(line, "sfx", out var argument4)) { lineWaitUntil = Time.time + SfxLocal(argument4); Send("sfx", argument4); return; } int num = 0; if (TrimSpeaker(ref line, "BOT:")) { num = 1; } else if (TrimSpeaker(ref line, "PANIC:")) { num = 2; } else { TrimSpeaker(ref line, "DJ:"); } float num2 = SayLocal(line, num); Send("say", line, num); if (num2 > 0f) { lineWaitUntil = Time.time + num2 + 0.7f; return; } lineSpeaking = true; djStart = Time.time; } } private static string VoiceKey(int speaker, string line) { using SHA1 sHA = SHA1.Create(); return BitConverter.ToString(sHA.ComputeHash(Encoding.UTF8.GetBytes(SpeakerNames[speaker] + "|" + line.Trim().ToLowerInvariant())), 0, 8).Replace("-", "").ToLowerInvariant(); } private static bool TrimSpeaker(ref string line, string speaker) { if (!line.StartsWith(speaker, StringComparison.OrdinalIgnoreCase)) { return false; } line = line.Substring(speaker.Length).Trim(); return true; } private void OnAirKeys() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: 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 (Input.GetKeyDown(onAirKey.Value)) { onAir = !onAir; if (!onAir) { StopOnAir(); } Log.LogInfo((object)("Cart FM on air: " + (onAir ? "yes" : "no") + ".")); onAirMessageUntil = Time.time + 3f; } if (Input.GetKeyDown(echoTestKey.Value) && (Object)(object)onAirRecorder != (Object)null) { onAirRecorder.DebugEchoMode = !onAirRecorder.DebugEchoMode; Log.LogInfo((object)("Cart FM echo test: " + (onAirRecorder.DebugEchoMode ? "on" : "off") + ".")); onAirMessageUntil = Time.time + 3f; } float num = 0f; if (Input.GetKeyDown(onAirLouderKey.Value)) { num = 0.05f; } else if (Input.GetKeyDown(onAirQuieterKey.Value)) { num = -0.05f; } if (num != 0f) { onAirVolume.Value = Mathf.Clamp(Mathf.Round((onAirVolume.Value + num) * 20f) / 20f, 0.05f, 1f); onAirMessageUntil = Time.time + 3f; } mixer.Volume = onAirVolume.Value; } private void OnAirTick() { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Invalid comparison between Unknown and I4 if (!onAir) { return; } PlayerVoiceChat val = PlayerVoiceChat.instance; Recorder val2 = (((Object)(object)val != (Object)null) ? VoiceRecorder.Invoke(val) : null); if ((Object)(object)val2 == (Object)null) { return; } if ((Object)(object)val2 != (Object)(object)onAirRecorder || (int)val2.SourceType != 2) { if (pusher == null) { pusher = new RadioPusher(); } val2.InputFactory = () => (IAudioDesc)(object)pusher; val2.SourceType = (InputSourceType)2; val2.RecordingEnabled = true; val2.RestartRecording(); onAirRecorder = val2; lastDspTime = AudioSettings.dspTime; } double dspTime = AudioSettings.dspTime; int num = Mathf.Min((int)((dspTime - lastDspTime) * 24000.0), 12000); if (num > 0) { lastDspTime += (double)num / 24000.0; if (dspTime - lastDspTime > 0.5) { lastDspTime = dspTime; } pusher.Push(mixer.Render(num), num); } } private void StopOnAir() { if ((Object)(object)onAirRecorder != (Object)null) { onAirRecorder.DebugEchoMode = false; onAirRecorder.SourceType = (InputSourceType)0; onAirRecorder.InputFactory = null; onAirRecorder.RestartRecording(); } onAirRecorder = null; } private static void KeepTransmitting(PlayerVoiceChat __instance) { if ((Object)(object)instance != (Object)null && instance.onAir && (Object)(object)__instance == (Object)(object)PlayerVoiceChat.instance && (Object)(object)instance.onAirRecorder != (Object)null && (Object)(object)VoiceRecorder.Invoke(__instance) == (Object)(object)instance.onAirRecorder) { instance.onAirRecorder.TransmitEnabled = true; } } private void OnGUI() { //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_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0085: 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_0105: 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) //IL_01ba: 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_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: 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) if (onAirStyle == null) { onAirStyle = new GUIStyle(GUI.skin.label) { fontSize = 16, fontStyle = (FontStyle)1 }; } if (Time.time < volumeMessageUntil) { GUI.Label(new Rect(16f, 40f, 500f, 30f), $"Cart FM volume (just for you): {Mathf.RoundToInt(volume.Value * 100f)}% [{volumeDownKey.Value}] [{volumeUpKey.Value}]", onAirStyle); } if (onAir || !(Time.time > onAirMessageUntil)) { string text = (onAir ? $"● CART FM ON AIR {Mathf.RoundToInt(onAirVolume.Value * 100f)}% [{onAirQuieterKey.Value}] [{onAirLouderKey.Value}] volume, [{onAirKey.Value}] off" : "Cart FM off air (mic back on)"); if (onAir && (Object)(object)onAirRecorder != (Object)null) { text += (onAirRecorder.DebugEchoMode ? $" ECHO TEST ON: you hear what others hear [{echoTestKey.Value}]" : $" [{echoTestKey.Value}] echo test"); } else if (onAir) { text += " (waiting for voice chat: needs a multiplayer lobby)"; } GUI.color = (Color)(onAir ? new Color(1f, 0.35f, 0.35f) : Color.white); GUI.Label(new Rect(16f, 12f, 700f, 30f), text, onAirStyle); GUI.color = Color.white; } } private void AddRadios() { //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Expected O, but got Unknown foreach (PhysGrabCart item in radios.Keys.Where((PhysGrabCart c) => (Object)(object)c == (Object)null).ToList()) { radios.Remove(item); } PhysGrabCart[] array = Object.FindObjectsOfType<PhysGrabCart>(); foreach (PhysGrabCart val in array) { if (!radios.ContainsKey(val)) { Radio radio = new Radio(); GameObject val2 = new GameObject("Cart Radio"); val2.transform.SetParent(((Component)val).transform, false); radio.music = MakeSource(val2); if ((Object)(object)currentClip != (Object)null) { radio.music.clip = currentClip; radio.music.Play(); radio.music.time = Mathf.Clamp(Time.time - songStart, 0f, currentClip.length - 0.1f); } radio.dj = MakeDJ(((Component)val).transform, out radio.djAudio); radio.djFrequency = radio.dj.voiceBaseFrequency; radios[val] = radio; } } if (!stationOpened && !FollowingHost && bits.Count > 0 && djOn.Value && radios.Count > 0 && !djTalking) { TellJoke(); } } private static AudioSource MakeSource(GameObject holder) { AudioSource val = holder.AddComponent<AudioSource>(); val.playOnAwake = false; val.spatialBlend = 1f; val.rolloffMode = (AudioRolloffMode)1; val.minDistance = 1.5f; val.maxDistance = hearingDistance.Value; val.dopplerLevel = 0f; val.volume = volume.Value; val.mute = !radioOn.Value; if ((Object)(object)AudioManager.instance != (Object)null) { val.outputAudioMixerGroup = AudioManager.instance.MusicMasterGroup; } return val; } private static Speech MakeDJ(Transform cart, out AudioSource audio) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Cart Radio DJ"); val.SetActive(false); val.transform.SetParent(cart, false); audio = MakeSource(val); Speech val2 = val.AddComponent<Speech>(); TTSVoice val3 = Object.FindObjectOfType<TTSVoice>(); Speech val4 = (((Object)(object)val3 != (Object)null && val3.voices != null && val3.voices.Length != 0) ? val3.voices[0] : null); if ((Object)(object)val4 != (Object)null) { val2.voiceBaseFrequency = val4.voiceBaseFrequency; val2.voicingSource = val4.voicingSource; val2.msPerSpeechFrame = val4.msPerSpeechFrame; val2.flutter = val4.flutter; val2.flutterSpeed = val4.flutterSpeed; } if (djPitch.Value > 0) { val2.voiceBaseFrequency = djPitch.Value; } val.SetActive(true); return val2; } } }