Decompiled source of DynamicMusicMeetings v1.2.0

DanganronpaVotes.dll

Decompiled 4 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net.Http;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using BepInEx.Unity.IL2CPP.Utils.Collections;
using HarmonyLib;
using Il2CppInterop.Runtime.Attributes;
using Il2CppInterop.Runtime.Injection;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSystem.Collections.Generic;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.Events;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("DanganronpaVotes")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("DanganronpaVotes")]
[assembly: AssemblyTitle("DanganronpaVotes")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.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 DanganronpaVotes
{
	[BepInPlugin("com.kris.danganronpavotes", "DanganronpaVotes", "1.1.0")]
	public class DanganronpaVotesPlugin : BasePlugin
	{
		private enum Tier
		{
			Calm,
			Tense,
			Climax
		}

		public const string PluginGuid = "com.kris.danganronpavotes";

		public const string PluginName = "DanganronpaVotes";

		public const string PluginVersion = "1.1.0";

		private static readonly string[] CrewmateTrackFiles = new string[3] { "trial_calm.wav", "trial_tense.wav", "trial_climax.wav" };

		private static readonly string[] ImpostorTrackFiles = new string[3] { "trial_impostor_calm.wav", "trial_impostor_tense.wav", "trial_impostor_climax.wav" };

		private const float FadeInSeconds = 2f;

		private const float FadeOutSeconds = 1f;

		private const float CrossfadeSeconds = 1.5f;

		private const float TargetVolume = 0.8f;

		private const float MuffledCutoffFrequency = 800f;

		private const float MuffleTransitionSeconds = 0.4f;

		private const string MainMenuSceneName = "MainMenu";

		private static AudioSource _sourceA;

		private static AudioSource _sourceB;

		private static bool _usingA = true;

		private static DanganronpaVotesRunner _runner;

		private static string _currentTrackName;

		private static bool _isMuffled;

		private static bool _pendingLocalVoteMuffle;

		private static bool _meetingActive;

		private static bool _isFirstMeetingOfMatch = true;

		private static bool _anyDeathObservedThisMatch;

		private static bool _matchTotalsCaptured;

		private static int _matchTotalImpostors;

		private static int _matchTotalCrewmates;

		private static readonly Dictionary<string, AudioClip> _cleanClipCache = new Dictionary<string, AudioClip>();

		private static readonly Dictionary<string, AudioClip> _muffledClipCache = new Dictionary<string, AudioClip>();

		private static readonly Dictionary<string, Task<float[]>> _pendingMuffleTasks = new Dictionary<string, Task<float[]>>();

		private static readonly Dictionary<string, (int channels, int sampleRate)> _pendingMuffleInfo = new Dictionary<string, (int, int)>();

		private const string AudioBaseUrl = "https://github.com/BdeBrunoDR/dynamicmeetingsaudio/releases/download/audio-v1/";

		private static bool _downloadRunning;

		internal static ManualLogSource Log;

		private const int FirstMeetingMarginBonus = 2;

		private static AudioSource ActiveSource => _usingA ? _sourceA : _sourceB;

		private static AudioSource InactiveSource => _usingA ? _sourceB : _sourceA;

		public static string BasePath
		{
			get
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0008: Invalid comparison between Unknown and I4
				if ((int)Application.platform == 11)
				{
					string environmentVariable = Environment.GetEnvironmentVariable("STAR_DATA_PATH");
					if (!string.IsNullOrEmpty(environmentVariable))
					{
						return environmentVariable + "/DanganronpaVotes";
					}
				}
				return Path.Combine(Paths.PluginPath, "DanganronpaVotes");
			}
		}

		public static string AudioPath => Path.Combine(BasePath, "Audio");

		private static Tier BaseTierForMargin(int margin)
		{
			if (margin >= 5)
			{
				return Tier.Calm;
			}
			if (margin >= 3)
			{
				return Tier.Tense;
			}
			return Tier.Climax;
		}

		public static bool IsLocalPlayerImpostor()
		{
			PlayerControl localPlayer = PlayerControl.LocalPlayer;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)localPlayer.Data == (Object)null || (Object)(object)localPlayer.Data.Role == (Object)null)
			{
				return false;
			}
			return localPlayer.Data.Role.IsImpostor;
		}

		public static bool IsLocalPlayerGhost()
		{
			PlayerControl localPlayer = PlayerControl.LocalPlayer;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)localPlayer.Data == (Object)null)
			{
				return false;
			}
			return localPlayer.Data.IsDead;
		}

		public static (int aliveImpostors, int aliveCrewmates, int totalImpostors, int totalCrewmates) CountRoles()
		{
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			Enumerator<NetworkedPlayerInfo> enumerator = GameData.Instance.AllPlayers.GetEnumerator();
			while (enumerator.MoveNext())
			{
				NetworkedPlayerInfo current = enumerator.Current;
				if (current.Disconnected)
				{
					continue;
				}
				if ((Object)(object)current.Role != (Object)null && current.Role.IsImpostor)
				{
					num3++;
					if (!current.IsDead)
					{
						num++;
					}
				}
				else
				{
					num4++;
					if (!current.IsDead)
					{
						num2++;
					}
				}
			}
			return (aliveImpostors: num, aliveCrewmates: num2, totalImpostors: num3, totalCrewmates: num4);
		}

		public static string PickTrackForVote(int aliveImpostors, int aliveCrewmates, int totalImpostors, bool isLocalImpostor, bool forceCalmBase = false, int marginBonus = 0)
		{
			int num = aliveCrewmates - aliveImpostors;
			Tier tier = ((!forceCalmBase) ? BaseTierForMargin(num + marginBonus) : Tier.Calm);
			if (!isLocalImpostor)
			{
				return CrewmateTrackFiles[(int)tier];
			}
			Tier tier2;
			if (totalImpostors <= 1)
			{
				tier2 = tier;
			}
			else
			{
				bool flag = aliveImpostors <= 1;
				tier2 = ((num <= 1) ? Tier.Climax : ((Tier)((!flag) ? Math.Min(1, (int)tier) : Math.Min(2, (int)(tier + 1)))));
			}
			return ImpostorTrackFiles[(int)tier2];
		}

		private static void NormalizeAudioState()
		{
			if (!((Object)(object)_runner == (Object)null) && !((Object)(object)_sourceA == (Object)null) && !((Object)(object)_sourceB == (Object)null))
			{
				_runner.CancelFade();
				if (ActiveSource.isPlaying)
				{
					ActiveSource.volume = 0.8f;
				}
				if (InactiveSource.isPlaying)
				{
					_runner.StopAndSilenceImmediate(InactiveSource);
				}
			}
		}

		internal static void EnsureAudioFilesDownloaded()
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Expected O, but got Unknown
			if (_downloadRunning)
			{
				return;
			}
			bool flag = default(bool);
			try
			{
				Directory.CreateDirectory(AudioPath);
			}
			catch (Exception ex)
			{
				ManualLogSource log = Log;
				BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(38, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Could not create the audio folder '");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(AudioPath);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("': ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
				}
				log.LogError(val);
				return;
			}
			List<string> missing = new List<string>();
			string[] crewmateTrackFiles = CrewmateTrackFiles;
			foreach (string text in crewmateTrackFiles)
			{
				if (!File.Exists(Path.Combine(AudioPath, text)))
				{
					missing.Add(text);
				}
			}
			string[] impostorTrackFiles = ImpostorTrackFiles;
			foreach (string text2 in impostorTrackFiles)
			{
				if (!File.Exists(Path.Combine(AudioPath, text2)))
				{
					missing.Add(text2);
				}
			}
			if (missing.Count == 0)
			{
				return;
			}
			_downloadRunning = true;
			ManualLogSource log2 = Log;
			BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(47, 2, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(missing.Count);
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" audio files are missing, downloading from ");
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>("https://github.com/BdeBrunoDR/dynamicmeetingsaudio/releases/download/audio-v1/");
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" ...");
			}
			log2.LogInfo(val2);
			Task.Run(async delegate
			{
				try
				{
					using HttpClient http = new HttpClient
					{
						Timeout = TimeSpan.FromMinutes(2.0)
					};
					bool flag2 = default(bool);
					foreach (string file in missing)
					{
						string dest = Path.Combine(AudioPath, file);
						string tmp = dest + ".part";
						try
						{
							File.WriteAllBytes(tmp, await http.GetByteArrayAsync("https://github.com/BdeBrunoDR/dynamicmeetingsaudio/releases/download/audio-v1/" + file));
							if (File.Exists(dest))
							{
								File.Delete(dest);
							}
							File.Move(tmp, dest);
							ManualLogSource log3 = Log;
							BepInExInfoLogInterpolatedStringHandler val3 = new BepInExInfoLogInterpolatedStringHandler(12, 1, ref flag2);
							if (flag2)
							{
								((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("Downloaded: ");
								((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<string>(file);
							}
							log3.LogInfo(val3);
						}
						catch (Exception ex2)
						{
							ManualLogSource log4 = Log;
							BepInExErrorLogInterpolatedStringHandler val4 = new BepInExErrorLogInterpolatedStringHandler(21, 2, ref flag2);
							if (flag2)
							{
								((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("Could not download ");
								((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<string>(file);
								((BepInExLogInterpolatedStringHandler)val4).AppendLiteral(": ");
								((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<string>(ex2.Message);
							}
							log4.LogError(val4);
							try
							{
								if (File.Exists(tmp))
								{
									File.Delete(tmp);
								}
							}
							catch
							{
							}
						}
					}
				}
				finally
				{
					_downloadRunning = false;
				}
			});
		}

		public override void Load()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			Log = ((BasePlugin)this).Log;
			ClassInjector.RegisterTypeInIl2Cpp<DanganronpaVotesRunner>();
			Harmony val = new Harmony("com.kris.danganronpavotes");
			val.PatchAll(Assembly.GetExecutingAssembly());
			SceneManager.activeSceneChanged += UnityAction<Scene, Scene>.op_Implicit((Action<Scene, Scene>)OnActiveSceneChanged);
			EnsureAudioFilesDownloaded();
			Log.LogInfo((object)"DanganronpaVotes loaded successfully (IL2CPP).");
		}

		private static void OnActiveSceneChanged(Scene oldScene, Scene newScene)
		{
			if (((Scene)(ref newScene)).name == "MainMenu")
			{
				Log.LogInfo((object)"Returned to the main menu, stopping the music immediately.");
				StopVoteMusicImmediate();
			}
		}

		private static void EnsureAudioSetup()
		{
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Expected O, but got Unknown
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected O, but got Unknown
			if ((Object)(object)_sourceA != (Object)null && (Object)(object)_sourceB != (Object)null)
			{
				return;
			}
			GameObject val = new GameObject("DanganronpaVotesAudio");
			Object.DontDestroyOnLoad((Object)(object)val);
			_sourceA = val.AddComponent<AudioSource>();
			_sourceA.playOnAwake = false;
			_sourceA.loop = true;
			_sourceA.volume = 0f;
			_sourceB = val.AddComponent<AudioSource>();
			_sourceB.playOnAwake = false;
			_sourceB.loop = true;
			_sourceB.volume = 0f;
			try
			{
				AudioMixerGroup val2 = (((Object)(object)SoundManager.Instance != (Object)null) ? SoundManager.Instance.MusicChannel : null);
				if ((Object)(object)val2 != (Object)null)
				{
					_sourceA.outputAudioMixerGroup = val2;
					_sourceB.outputAudioMixerGroup = val2;
					Log.LogInfo((object)"AudioSources routed to the game's music channel: the volume slider now affects them.");
				}
				else
				{
					Log.LogWarning((object)"SoundManager.Instance.MusicChannel is null; the mod's music will not follow the game's volume slider.");
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Log;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val3 = new BepInExErrorLogInterpolatedStringHandler(38, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("Could not route to the music channel: ");
					((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<Exception>(ex);
				}
				log.LogError(val3);
			}
			_usingA = true;
			_runner = val.AddComponent<DanganronpaVotesRunner>();
			Log.LogInfo((object)"Audio object (re)created.");
		}

		public static void PlayVoteMusic()
		{
			EnsureAudioSetup();
			_meetingActive = true;
			_pendingLocalVoteMuffle = false;
			UpdateVoteMusic("Voting started");
		}

		private static AudioClip GetOrLoadCleanClip(string track)
		{
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Expected O, but got Unknown
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Expected O, but got Unknown
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Expected O, but got Unknown
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Expected O, but got Unknown
			bool flag = default(bool);
			if (_cleanClipCache.TryGetValue(track, out var value))
			{
				if ((Object)(object)value != (Object)null)
				{
					return value;
				}
				_cleanClipCache.Remove(track);
				ManualLogSource log = Log;
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(89, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("The cached clean clip for '");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(track);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' is no longer valid (released by Unity); reloading from disk.");
				}
				log.LogInfo(val);
			}
			string text = Path.Combine(AudioPath, track);
			if (!File.Exists(text))
			{
				ManualLogSource log2 = Log;
				BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(22, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Audio file not found: ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(text);
				}
				log2.LogWarning(val2);
				return null;
			}
			try
			{
				byte[] fileBytes = File.ReadAllBytes(text);
				if (!WavUtility.TryParse(fileBytes, out var samples, out var channels, out var sampleRate))
				{
					ManualLogSource log3 = Log;
					BepInExErrorLogInterpolatedStringHandler val3 = new BepInExErrorLogInterpolatedStringHandler(47, 1, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("Could not decode the wav (unsupported format): ");
						((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<string>(text);
					}
					log3.LogError(val3);
					return null;
				}
				AudioClip val4 = WavUtility.CreateClip(track, samples, channels, sampleRate);
				_cleanClipCache[track] = val4;
				EnsureMuffledClipQueued(track, samples, channels, sampleRate);
				return val4;
			}
			catch (Exception ex)
			{
				ManualLogSource log4 = Log;
				BepInExErrorLogInterpolatedStringHandler val3 = new BepInExErrorLogInterpolatedStringHandler(21, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("Error loading audio: ");
					((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<Exception>(ex);
				}
				log4.LogError(val3);
				return null;
			}
		}

		private static void EnsureMuffledClipQueued(string track, float[] samples, int channels, int sampleRate)
		{
			if (_muffledClipCache.TryGetValue(track, out var value))
			{
				if ((Object)(object)value != (Object)null)
				{
					return;
				}
				_muffledClipCache.Remove(track);
			}
			if (!_pendingMuffleTasks.ContainsKey(track))
			{
				_pendingMuffleInfo[track] = (channels, sampleRate);
				_pendingMuffleTasks[track] = Task.Run(() => WavUtility.ApplyLowPass(samples, channels, sampleRate, 800f));
			}
		}

		internal static void PollPendingMuffleClips()
		{
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Expected O, but got Unknown
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Expected O, but got Unknown
			if (_pendingMuffleTasks.Count == 0)
			{
				return;
			}
			List<string> list = null;
			foreach (KeyValuePair<string, Task<float[]>> pendingMuffleTask in _pendingMuffleTasks)
			{
				if (pendingMuffleTask.Value.IsCompleted)
				{
					(list ?? (list = new List<string>())).Add(pendingMuffleTask.Key);
				}
			}
			if (list == null)
			{
				return;
			}
			bool flag = default(bool);
			foreach (string item in list)
			{
				Task<float[]> task = _pendingMuffleTasks[item];
				_pendingMuffleTasks.Remove(item);
				var (channels, sampleRate) = _pendingMuffleInfo[item];
				_pendingMuffleInfo.Remove(item);
				if (task.IsFaulted)
				{
					ManualLogSource log = Log;
					BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(44, 2, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Error generating the muffled version of '");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(item);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("': ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<AggregateException>(task.Exception);
					}
					log.LogError(val);
					continue;
				}
				AudioClip value = WavUtility.CreateClip(item + "_muffled", task.Result, channels, sampleRate);
				_muffledClipCache[item] = value;
				ManualLogSource log2 = Log;
				BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(31, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Muffled version of '");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(item);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("' is ready.");
				}
				log2.LogInfo(val2);
				bool flag2 = IsLocalPlayerGhost();
				bool pendingLocalVoteMuffle = _pendingLocalVoteMuffle;
				if (item == _currentTrackName && !_isMuffled && (flag2 || pendingLocalVoteMuffle) && TryApplyMuffle())
				{
					_pendingLocalVoteMuffle = false;
					Log.LogInfo((object)(flag2 ? "You are a ghost: music muffled as soon as it finished processing." : "Pending vote: music muffled as soon as it finished processing (the vote came in late)."));
				}
			}
		}

		private static bool TryApplyMuffle()
		{
			if ((Object)(object)_runner == (Object)null || _currentTrackName == null)
			{
				return false;
			}
			NormalizeAudioState();
			if ((Object)(object)ActiveSource == (Object)null || !ActiveSource.isPlaying || _isMuffled)
			{
				return false;
			}
			if (!_muffledClipCache.TryGetValue(_currentTrackName, out var value))
			{
				return false;
			}
			if ((Object)(object)value == (Object)null)
			{
				_muffledClipCache.Remove(_currentTrackName);
				return false;
			}
			AudioSource activeSource = ActiveSource;
			AudioSource inactiveSource = InactiveSource;
			inactiveSource.clip = value;
			inactiveSource.time = activeSource.time % value.length;
			inactiveSource.volume = 0f;
			inactiveSource.Play();
			_runner.StartCrossfade(activeSource, inactiveSource, 0.4f, 0.8f);
			_usingA = !_usingA;
			_isMuffled = true;
			return true;
		}

		public static void OnLocalVoteCast()
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			Log.LogInfo((object)"OnLocalVoteCast() called.");
			if (IsLocalPlayerGhost())
			{
				return;
			}
			if (!TryApplyMuffle())
			{
				_pendingLocalVoteMuffle = true;
				ManualLogSource log = Log;
				bool flag = default(bool);
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(186, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("OnLocalVoteCast: not applied yet (runner/track is null, nothing playing, already muffled, or the muffled version of '");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(_currentTrackName);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' is not ready yet); it stays pending and will be applied once ready.");
				}
				log.LogInfo(val);
			}
			else
			{
				Log.LogInfo((object)"Vote cast: muffling the music.");
			}
		}

		public static void OnPlayerLeft()
		{
			if (!((Object)(object)_sourceA == (Object)null) && !((Object)(object)_sourceB == (Object)null) && _meetingActive)
			{
				Log.LogInfo((object)"A player left the game, recalculating the tension level.");
				UpdateVoteMusic("Recalculation after a player left");
			}
		}

		private static void UpdateVoteMusic(string reason)
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: 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
			(int aliveImpostors, int aliveCrewmates, int totalImpostors, int totalCrewmates) tuple = CountRoles();
			int item = tuple.aliveImpostors;
			int item2 = tuple.aliveCrewmates;
			int item3 = tuple.totalImpostors;
			int item4 = tuple.totalCrewmates;
			int num = item2 - item;
			bool flag = default(bool);
			BepInExInfoLogInterpolatedStringHandler val;
			if (!_matchTotalsCaptured)
			{
				_matchTotalImpostors = item3;
				_matchTotalCrewmates = item4;
				_matchTotalsCaptured = true;
				ManualLogSource log = Log;
				val = new BepInExInfoLogInterpolatedStringHandler(60, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Match snapshot set: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(_matchTotalImpostors);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" initial impostors / ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(_matchTotalCrewmates);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" initial crewmates.");
				}
				log.LogInfo(val);
			}
			if (item2 < _matchTotalCrewmates || item < _matchTotalImpostors)
			{
				_anyDeathObservedThisMatch = true;
			}
			bool flag2 = !_anyDeathObservedThisMatch;
			bool flag3 = _isFirstMeetingOfMatch && flag2;
			int num2 = ((_isFirstMeetingOfMatch && !flag2) ? 2 : 0);
			bool flag4 = IsLocalPlayerImpostor();
			string text = PickTrackForVote(item, item2, _matchTotalImpostors, flag4, flag3, num2);
			ManualLogSource log2 = Log;
			val = new BepInExInfoLogInterpolatedStringHandler(151, 10, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(reason);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("] Alive impostors: ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(item);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" (of ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(_matchTotalImpostors);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" initial) | Alive crewmates: ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(item2);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" (of ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(_matchTotalCrewmates);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" initial) | Margin: ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(num);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" | First vote without deaths: ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<bool>(flag3);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" | First-vote bonus: ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(num2);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" -> role: ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(flag4 ? "Impostor" : "Crewmate");
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" -> track: ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(text);
			}
			log2.LogInfo(val);
			SwitchToTrack(text);
			if (IsLocalPlayerGhost() && TryApplyMuffle())
			{
				Log.LogInfo((object)"You are a ghost: music muffled from the start of the voting.");
			}
		}

		private static void SwitchToTrack(string track)
		{
			NormalizeAudioState();
			bool isPlaying = ActiveSource.isPlaying;
			if (isPlaying && _currentTrackName == track)
			{
				return;
			}
			AudioClip orLoadCleanClip = GetOrLoadCleanClip(track);
			if (!((Object)(object)orLoadCleanClip == (Object)null))
			{
				if (!isPlaying)
				{
					ActiveSource.clip = orLoadCleanClip;
					ActiveSource.volume = 0f;
					ActiveSource.Play();
					_runner.StartFadeIn(ActiveSource, 2f, 0.8f);
				}
				else
				{
					AudioSource activeSource = ActiveSource;
					AudioSource inactiveSource = InactiveSource;
					inactiveSource.clip = orLoadCleanClip;
					inactiveSource.volume = 0f;
					inactiveSource.Play();
					_runner.StartCrossfade(activeSource, inactiveSource, 1.5f, 0.8f);
					_usingA = !_usingA;
				}
				_currentTrackName = track;
				_isMuffled = false;
			}
		}

		public static void StopVoteMusic()
		{
			if (!((Object)(object)_sourceA == (Object)null) && !((Object)(object)_sourceB == (Object)null))
			{
				_meetingActive = false;
				_pendingLocalVoteMuffle = false;
				_isFirstMeetingOfMatch = false;
				if (ActiveSource.isPlaying)
				{
					_runner.StartFadeOut(ActiveSource, 1f);
				}
				if (InactiveSource.isPlaying)
				{
					_runner.StopAndSilenceImmediate(InactiveSource);
				}
			}
		}

		public static void StopVoteMusicImmediate()
		{
			if (!((Object)(object)_sourceA == (Object)null) && !((Object)(object)_sourceB == (Object)null))
			{
				_meetingActive = false;
				_pendingLocalVoteMuffle = false;
				_runner?.CancelFade();
				_runner?.StopAndSilenceImmediate(_sourceA);
				_runner?.StopAndSilenceImmediate(_sourceB);
				_currentTrackName = null;
				_isMuffled = false;
				_isFirstMeetingOfMatch = true;
				_anyDeathObservedThisMatch = false;
				_matchTotalsCaptured = false;
				_matchTotalImpostors = 0;
				_matchTotalCrewmates = 0;
			}
		}
	}
	public class DanganronpaVotesRunner : MonoBehaviour
	{
		private Coroutine _activeFade;

		public DanganronpaVotesRunner(IntPtr ptr)
			: base(ptr)
		{
		}

		public void Update()
		{
			DanganronpaVotesPlugin.PollPendingMuffleClips();
		}

		public void StartFadeIn(AudioSource source, float duration, float targetVolume)
		{
			CancelFade();
			_activeFade = ((MonoBehaviour)this).StartCoroutine(CollectionExtensions.WrapToIl2Cpp(FadeInRoutine(source, duration, targetVolume)));
		}

		public void StartFadeOut(AudioSource source, float duration)
		{
			CancelFade();
			_activeFade = ((MonoBehaviour)this).StartCoroutine(CollectionExtensions.WrapToIl2Cpp(FadeOutRoutine(source, duration)));
		}

		public void StartCrossfade(AudioSource outSource, AudioSource inSource, float duration, float targetVolume)
		{
			CancelFade();
			_activeFade = ((MonoBehaviour)this).StartCoroutine(CollectionExtensions.WrapToIl2Cpp(CrossfadeRoutine(outSource, inSource, duration, targetVolume)));
		}

		public void StopAndSilenceImmediate(AudioSource source)
		{
			if (!((Object)(object)source == (Object)null))
			{
				if (source.isPlaying)
				{
					source.Stop();
				}
				source.volume = 0f;
			}
		}

		public void CancelFade()
		{
			if (_activeFade != null)
			{
				((MonoBehaviour)this).StopCoroutine(_activeFade);
				_activeFade = null;
			}
		}

		[HideFromIl2Cpp]
		private IEnumerator FadeInRoutine(AudioSource source, float duration, float targetVolume)
		{
			float elapsed = 0f;
			float startVolume = source.volume;
			while (elapsed < duration)
			{
				elapsed += Time.deltaTime;
				source.volume = Mathf.Lerp(startVolume, targetVolume, elapsed / duration);
				yield return null;
			}
			source.volume = targetVolume;
			_activeFade = null;
		}

		[HideFromIl2Cpp]
		private IEnumerator FadeOutRoutine(AudioSource source, float duration)
		{
			float elapsed = 0f;
			float startVolume = source.volume;
			while (elapsed < duration)
			{
				elapsed += Time.deltaTime;
				source.volume = Mathf.Lerp(startVolume, 0f, elapsed / duration);
				yield return null;
			}
			source.volume = 0f;
			source.Stop();
			_activeFade = null;
		}

		[HideFromIl2Cpp]
		private IEnumerator CrossfadeRoutine(AudioSource outSource, AudioSource inSource, float duration, float targetVolume)
		{
			float elapsed = 0f;
			float outStartVolume = outSource.volume;
			float inStartVolume = inSource.volume;
			while (elapsed < duration)
			{
				elapsed += Time.deltaTime;
				float t = elapsed / duration;
				outSource.volume = Mathf.Lerp(outStartVolume, 0f, t);
				inSource.volume = Mathf.Lerp(inStartVolume, targetVolume, t);
				yield return null;
			}
			outSource.volume = 0f;
			outSource.Stop();
			inSource.volume = targetVolume;
			_activeFade = null;
		}
	}
	public static class WavUtility
	{
		public static bool TryParse(byte[] fileBytes, out float[] samples, out int channels, out int sampleRate)
		{
			samples = null;
			channels = 0;
			sampleRate = 0;
			if (fileBytes.Length < 44)
			{
				return false;
			}
			string text = Encoding.ASCII.GetString(fileBytes, 0, 4);
			string text2 = Encoding.ASCII.GetString(fileBytes, 8, 4);
			if (text != "RIFF" || text2 != "WAVE")
			{
				return false;
			}
			int num = 0;
			int num2 = -1;
			int num3 = 0;
			int num4 = 12;
			while (num4 + 8 <= fileBytes.Length)
			{
				string text3 = Encoding.ASCII.GetString(fileBytes, num4, 4);
				int num5 = BitConverter.ToInt32(fileBytes, num4 + 4);
				int num6 = num4 + 8;
				if (text3 == "fmt ")
				{
					channels = BitConverter.ToInt16(fileBytes, num6 + 2);
					sampleRate = BitConverter.ToInt32(fileBytes, num6 + 4);
					num = BitConverter.ToInt16(fileBytes, num6 + 14);
				}
				else if (text3 == "data")
				{
					num2 = num6;
					num3 = ((num5 >= 0 && num6 + num5 <= fileBytes.Length) ? num5 : (fileBytes.Length - num6));
					break;
				}
				if (num5 < 0 || num6 + num5 > fileBytes.Length)
				{
					break;
				}
				num4 = num6 + num5 + num5 % 2;
			}
			if (num2 < 0 || channels <= 0 || sampleRate <= 0 || num <= 0)
			{
				return false;
			}
			int num7 = num / 8;
			if (num7 <= 0 || num2 + num3 > fileBytes.Length)
			{
				return false;
			}
			int num8 = num3 / num7;
			samples = new float[num8];
			switch (num)
			{
			case 16:
			{
				for (int j = 0; j < num8; j++)
				{
					short num10 = BitConverter.ToInt16(fileBytes, num2 + j * 2);
					samples[j] = (float)num10 / 32768f;
				}
				break;
			}
			case 8:
			{
				for (int k = 0; k < num8; k++)
				{
					samples[k] = (float)(fileBytes[num2 + k] - 128) / 128f;
				}
				break;
			}
			case 32:
			{
				for (int i = 0; i < num8; i++)
				{
					int num9 = BitConverter.ToInt32(fileBytes, num2 + i * 4);
					samples[i] = (float)num9 / 2.1474836E+09f;
				}
				break;
			}
			default:
				return false;
			}
			return true;
		}

		public static AudioClip CreateClip(string name, float[] samples, int channels, int sampleRate)
		{
			int num = samples.Length / channels;
			AudioClip val = AudioClip.Create(name, num, channels, sampleRate, false);
			val.SetData(Il2CppStructArray<float>.op_Implicit(samples), 0);
			return val;
		}

		public static float[] ApplyLowPass(float[] samples, int channels, int sampleRate, float cutoffHz)
		{
			float[] array = new float[samples.Length];
			float num = 1f / ((float)Math.PI * 2f * Mathf.Max(cutoffHz, 10f));
			float num2 = 1f / (float)Mathf.Max(sampleRate, 1);
			float num3 = num2 / (num + num2);
			for (int i = 0; i < channels; i++)
			{
				float num4 = 0f;
				for (int j = i; j < samples.Length; j += channels)
				{
					num4 = (array[j] = num4 + num3 * (samples[j] - num4));
				}
			}
			return array;
		}
	}
	[HarmonyPatch(typeof(MeetingHud), "Start")]
	public static class MeetingHudStartPatch
	{
		public static void Postfix(MeetingHud __instance)
		{
			DanganronpaVotesPlugin.PlayVoteMusic();
		}
	}
	[HarmonyPatch(typeof(MeetingHud), "Close")]
	public static class MeetingHudClosePatch
	{
		public static void Postfix(MeetingHud __instance)
		{
			DanganronpaVotesPlugin.StopVoteMusic();
		}
	}
	[HarmonyPatch(typeof(MeetingHud), "VotingComplete")]
	public static class MeetingHudVotingCompletePatch
	{
		public static void Postfix()
		{
			DanganronpaVotesPlugin.Log.LogInfo((object)"VotingComplete fired: results screen is starting, stopping the music.");
			DanganronpaVotesPlugin.StopVoteMusic();
		}
	}
	[HarmonyPatch(typeof(AmongUsClient), "OnPlayerLeft")]
	public static class AmongUsClientOnPlayerLeftPatch
	{
		public static void Postfix()
		{
			DanganronpaVotesPlugin.OnPlayerLeft();
		}
	}
	[HarmonyPatch(typeof(PlayerVoteArea), "VoteForMe")]
	public static class PlayerVoteAreaVoteForMePatch
	{
		public static void Postfix(PlayerVoteArea __instance)
		{
			DanganronpaVotesPlugin.Log.LogInfo((object)"VoteForMe Postfix fired: local vote confirmed.");
			DanganronpaVotesPlugin.OnLocalVoteCast();
		}
	}
}