Decompiled source of DynamicMusicMeetings v1.1.1

DanganronpaVotes.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
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.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)>();

		internal static ManualLogSource Log;

		private const int FirstMeetingMarginBonus = 2;

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

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

		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);
				}
			}
		}

		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);
			Log.LogInfo((object)"DanganronpaVotes cargado correctamente (IL2CPP).");
		}

		private static void OnActiveSceneChanged(Scene oldScene, Scene newScene)
		{
			if (((Scene)(ref newScene)).name == "MainMenu")
			{
				Log.LogInfo((object)"Vuelta al menú principal detectada, parando música en seco.");
				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 enrutados al canal de música del juego: el slider de volumen ahora nos afecta.");
				}
				else
				{
					Log.LogWarning((object)"SoundManager.Instance.MusicChannel es null; la música del mod no seguirá el slider de volumen del juego.");
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Log;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val3 = new BepInExErrorLogInterpolatedStringHandler(39, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("No se pudo enrutar al canal de música: ");
					((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<Exception>(ex);
				}
				log.LogError(val3);
			}
			_usingA = true;
			_runner = val.AddComponent<DanganronpaVotesRunner>();
			Log.LogInfo((object)"Objeto de audio (re)creado.");
		}

		public static void PlayVoteMusic()
		{
			EnsureAudioSetup();
			_meetingActive = true;
			_pendingLocalVoteMuffle = false;
			UpdateVoteMusic("Inicio de votación");
		}

		private static AudioClip GetOrLoadCleanClip(string track)
		{
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Expected O, but got Unknown
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: 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_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: 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(92, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("El clip limpio en caché de '");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(track);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' ya no era válido (liberado por Unity); recargando desde disco.");
				}
				log.LogInfo(val);
			}
			string text = Path.Combine(Paths.PluginPath, "DanganronpaVotes", "Audio", track);
			if (!File.Exists(text))
			{
				ManualLogSource log2 = Log;
				BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(36, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("No se encontro el archivo de audio: ");
					((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(54, 1, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("No se pudo decodificar el wav (formato no soportado): ");
						((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(22, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("Error cargando 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(46, 2, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Error generando la versión amortiguada de '");
						((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(32, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Versión amortiguada de '");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(item);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("' lista.");
				}
				log2.LogInfo(val2);
				bool flag2 = IsLocalPlayerGhost();
				bool pendingLocalVoteMuffle = _pendingLocalVoteMuffle;
				if (item == _currentTrackName && !_isMuffled && (flag2 || pendingLocalVoteMuffle) && TryApplyMuffle())
				{
					_pendingLocalVoteMuffle = false;
					Log.LogInfo((object)(flag2 ? "Eres un fantasma: música difuminada en cuanto ha terminado de calcularse." : "Voto pendiente: música difuminada en cuanto ha terminado de calcularse (llegó tarde al votar)."));
				}
			}
		}

		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() invocado.");
			if (IsLocalPlayerGhost())
			{
				return;
			}
			if (!TryApplyMuffle())
			{
				_pendingLocalVoteMuffle = true;
				ManualLogSource log = Log;
				bool flag = default(bool);
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(191, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("OnLocalVoteCast: no se aplica todavía (runner/pista nulos, nada sonando, ya difuminado, o la versión amortiguada de '");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(_currentTrackName);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' aún no está lista); queda pendiente para aplicarse en cuanto esté lista.");
				}
				log.LogInfo(val);
			}
			else
			{
				Log.LogInfo((object)"Voto emitido: difuminando la música.");
			}
		}

		public static void OnPlayerLeft()
		{
			if (!((Object)(object)_sourceA == (Object)null) && !((Object)(object)_sourceB == (Object)null) && _meetingActive)
			{
				Log.LogInfo((object)"Jugador abandonó la partida, recalculando nivel de tensión.");
				UpdateVoteMusic("Recalculo por abandono de jugador");
			}
		}

		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(65, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Snapshot de partida fijado: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(_matchTotalImpostors);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" impostores / ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(_matchTotalCrewmates);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" tripulantes iniciales.");
				}
				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(161, 10, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(reason);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("] Impostores vivos: ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(item);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" (de ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(_matchTotalImpostors);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" iniciales) | Tripulantes vivos: ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(item2);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" (de ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(_matchTotalCrewmates);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" iniciales) | Margen: ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(num);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" | Primera votación sin muertes: ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<bool>(flag3);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" | Bonus 1ª votación: ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(num2);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" -> rol: ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(flag4 ? "Impostor" : "Tripulante");
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" -> pista: ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(text);
			}
			log2.LogInfo(val);
			SwitchToTrack(text);
			if (IsLocalPlayerGhost() && TryApplyMuffle())
			{
				Log.LogInfo((object)"Eres un fantasma: música difuminada desde el inicio de la votación.");
			}
		}

		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 disparado: empieza la pantalla de resultados, parando música.");
			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 disparado: voto local confirmado.");
			DanganronpaVotesPlugin.OnLocalVoteCast();
		}
	}
}